├── .github ├── CODEOWNERS └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── custom-cred-types └── cyberark-pas-restapi │ ├── README.md │ ├── injector.yml │ └── input.yml ├── docs ├── aimprovider.md ├── cyberark_account.md ├── cyberark_authentication.md ├── cyberark_credential.md ├── cyberark_eda.md ├── cyberark_user.md └── images │ ├── cyberark_logo.jpg │ ├── eda-pta-syslog.png │ ├── eda-syslog.png │ ├── eda_disable_user_syslog.png │ ├── eda_disableuser_kafka.png │ ├── eda_disableuser_webhook.png │ ├── eda_pta_disable_user_syslog.png │ ├── full-cyberark-logo.jpg │ ├── platform_account_properties.JPG │ ├── rsyslog-kafka.png │ └── rsyslog-webhook.png ├── extensions └── eda │ └── plugins │ └── event_source │ ├── cyberark-eda-json-v1.0.xsl │ └── syslog.py ├── galaxy.yml ├── meta └── runtime.yml ├── plugins └── modules │ ├── cyberark_account.py │ ├── cyberark_authentication.py │ ├── cyberark_credential.py │ └── cyberark_user.py ├── roles └── aimprovider │ ├── README.md │ ├── defaults │ └── main.yml │ └── tasks │ ├── installAIMProvider.yml │ ├── main.yml │ └── uninstallAIMProvider.yml ├── rulebooks ├── cyberark_test_rule.yml ├── disable_pas_user_kafka.yml ├── disable_pas_user_webhook.yml ├── disable_user.yml ├── inventory.yml ├── pta_disable_notify.yml └── pta_notify.yml ├── tests ├── change_test.yml ├── changepolicy.yml ├── deprovision_account.yml ├── deprovision_user.yml ├── disable_user.yml ├── enable_user.yml ├── provision_account.yml ├── provision_user.yml ├── reset_user_password.yml ├── retrieve_account.yml └── test.yml └── tox.ini /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @cyberark-bizdev @enunez-cyberark @infamousjoeg 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # README FIRST 2 | # 1. If you don't have unit tests, remove that section. 3 | # 2. If your collection depends on other collections ensure they are installed, 4 | # add them to the "test-deps" input. 5 | # 3. For the comprehensive list of the inputs supported by the 6 | # ansible-community/ansible-test-gh-action GitHub Action, see 7 | # https://github.com/marketplace/actions/ansible-test. 8 | # 4. If you want to prevent merging PRs that do not pass all tests, 9 | # make sure to add the "check" job to your repository branch 10 | # protection once this workflow is added. 11 | # It is also possible to tweak which jobs are allowed to fail. See 12 | # https://github.com/marketplace/actions/alls-green#gotchas for more detail. 13 | # 5. If you need help please ask in #ansible-community on the Libera.chat IRC 14 | # network. 15 | 16 | name: CI 17 | on: 18 | # Run CI against all pushes (direct commits, also merged PRs), Pull Requests 19 | push: 20 | branches: 21 | - main 22 | - master 23 | - stable-* 24 | pull_request: 25 | # Run CI once per day (at 06:00 UTC) 26 | # This ensures that even if there haven't been commits that we are still 27 | # testing against latest version of ansible-test for each ansible-core 28 | # version 29 | schedule: 30 | - cron: '0 6 * * *' 31 | 32 | concurrency: 33 | group: >- 34 | ${{ github.workflow }}-${{ 35 | github.event.pull_request.number || github.sha 36 | }} 37 | cancel-in-progress: true 38 | 39 | jobs: 40 | 41 | ### 42 | # Sanity tests (REQUIRED) 43 | # 44 | # https://docs.ansible.com/ansible/latest/dev_guide/testing_sanity.html 45 | 46 | sanity: 47 | name: Sanity (Ⓐ${{ matrix.ansible }}) 48 | strategy: 49 | matrix: 50 | ansible: 51 | # It's important that Sanity is tested against all stable-X.Y branches 52 | # Testing against `devel` may fail as new tests are added. 53 | # An alternative to `devel` is the `milestone` branch with 54 | # gets synchronized with `devel` every few weeks and therefore 55 | # tends to be a more stable target. Be aware that it is not updated 56 | # around creation of a new stable branch, this might cause a problem 57 | # that two different versions of ansible-test use the same sanity test 58 | # ignore.txt file. 59 | - stable-2.9 # Only if your collection supports Ansible 2.9 60 | - stable-2.10 # Only if your collection supports ansible-base 2.10 61 | - stable-2.11 62 | - stable-2.12 63 | - stable-2.13 64 | - stable-2.14 65 | - devel 66 | # - milestone 67 | # Ansible-test on various stable branches does not yet work well with cgroups v2. 68 | # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04 69 | # image for these stable branches. The list of branches where this is necessary will 70 | # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28 71 | # for the latest list. 72 | runs-on: >- 73 | ${{ contains(fromJson( 74 | '["stable-2.9", "stable-2.10", "stable-2.11"]' 75 | ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }} 76 | steps: 77 | # Run sanity tests inside a Docker container. 78 | # The docker container has all the pinned dependencies that are 79 | # required and all Python versions Ansible supports. 80 | - name: Perform sanity testing 81 | uses: ansible-community/ansible-test-gh-action@release/v1 82 | with: 83 | ansible-core-version: ${{ matrix.ansible }} 84 | testing-type: sanity 85 | # OPTIONAL If your sanity tests require code 86 | # from other collections, install them like this 87 | # test-deps: >- 88 | # ansible.netcommon 89 | # ansible.utils 90 | 91 | check: # This job does nothing and is only used for the branch protection 92 | # or multi-stage CI jobs, like making sure that all tests pass before 93 | # a publishing job is started. 94 | if: always() 95 | 96 | needs: 97 | - sanity 98 | 99 | runs-on: ubuntu-latest 100 | 101 | steps: 102 | - name: Decide whether the needed jobs succeeded or failed 103 | uses: re-actors/alls-green@release/v1 104 | with: 105 | jobs: ${{ toJSON(needs) }} 106 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | meta/.galaxy_install_info 3 | docs/.DS_Store 4 | .DS_Store 5 | .vscode/settings.json 6 | *.gz 7 | .ansible/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.35 2 | 3 | - Cleaned all tox issues (ruff, pylint, darglint) 4 | 5 | ## 1.0.34 6 | 7 | - Fixed ruff and additional lint issues 8 | 9 | ## 1.0.33 10 | 11 | - Fixed pylint issues 12 | 13 | ## 1.0.32 14 | 15 | - Fixed tags in GitHub 16 | 17 | ## 1.0.31 18 | 19 | - Changed in cyberark_authentication module to not reveal payload on failure 20 | 21 | ## 1.0.30 22 | 23 | - Added ability to retrieve password 24 | 25 | ## 1.0.29 26 | 27 | - Added documentation to update password only in Vault 28 | 29 | ## 1.0.27 30 | 31 | - Fixed Pep8 & pylint for publication in Automation Hub 32 | 33 | ## 1.0.26 34 | 35 | - Updated runtime.yml requirements to minimum 2.14.0 36 | - Moved EDA collection to new/correct path 37 | - Added CHANGELOG.md 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the Ansible Security Automation Collection 2 | 3 | Thanks for your interest in the Ansible Security Automation collection. 4 | 5 | For general community guidelines, please see the [community repo](https://github.com/cyberark/community). 6 | 7 | ## Pull Request Workflow 8 | 9 | Currently, this repository is source-available and not open to contributions. Please continue to follow this repository for updates and open-source availability 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 CyberArk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CyberArk Ansible Security Automation Collection 2 | 3 | 4 | ![cyberark logo|](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/full-cyberark-logo.jpg?raw=true) 5 | 6 | ## Description 7 | 8 | This collection is the CyberArk Ansible Security Automation project and can be found on [ansible galaxy](https://galaxy.ansible.com/cyberark/pas). This is aimed to enable the automation of securing privileged access by storing privileged accounts in the Enterprise Password Vault (EPV), controlling user's access to privileged accounts in EPV, and securely retreiving secrets using Application Access Manager (AAM). 9 | The collection includes [support for Event-Driven Ansible](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_eda.md) by providing an event-source plugin for syslog and also guidance on how to use it. 10 | 11 | ## Requirements 12 | 13 | - Ansible Core 2.13.x or above 14 | - CyberArk Privileged Account Security Web Services SDK 15 | - CyberArk AAM Central Credential Provider (**Only required for cyberark_credential**) 16 | 17 | ## Installation 18 | 19 | Before using this collection, you need to install it with the Ansible Galaxy command-line tool: 20 | 21 | ``` 22 | ansible-galaxy collection install cyberark.pas 23 | ``` 24 | 25 | You can also include it in a requirements.yml file and install it with ansible-galaxy collection install -r requirements.yml, using the format: 26 | 27 | 28 | ```yaml 29 | collections: 30 | - name: cyberark.pas 31 | ``` 32 | 33 | Note that if you install any collections from Ansible Galaxy, they will not be upgraded automatically when you upgrade the Ansible package. 34 | To upgrade the collection to the latest available version, run the following command: 35 | 36 | ``` 37 | ansible-galaxy collection install cyberark.pas --upgrade 38 | ``` 39 | 40 | You can also install a specific version of the collection, for example, if you need to downgrade when something is broken in the latest version (please report an issue in this repository). Use the following syntax to install version 1.0.0: 41 | 42 | ``` 43 | ansible-galaxy collection install cyberark.pas:==1.0.0 44 | ``` 45 | 46 | See [using Ansible collections](https://docs.ansible.com/ansible/devel/user_guide/collections_using.html) for more details. 47 | 48 | ## Use Cases 49 | 50 | There is a list of different modules to perform different tasks: 51 | 52 | - Add, Delete, Update CyberArk Users 53 | - Add, Delete, Update CyberArk Accounts 54 | - Rotate Account Credentials 55 | 56 | ### Modules 57 | 58 | #### cyberark_authentication 59 | 60 | - Using the CyberArk Web Services SDK, authenticate and obtain an auth token to be passed as a variable in playbooks 61 | - Logoff of an authenticated REST API session
62 | [Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_authentication.md) 63 | 64 | #### cyberark_user 65 | 66 | - Add a CyberArk User 67 | - Delete a CyberArk User 68 | - Update a CyberArk User's account parameters 69 | - Enable/Disable, change password, mark for change at next login, etc 70 |
[Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_user.md)
71 | 72 | #### cyberark_account 73 | 74 | - Add Privileged Account to the EPV 75 | - Delete account objects 76 | - Modify account properties 77 | - Rotate privileged credentials 78 | - Retrieve account password
79 | [Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_account.md) 80 | 81 | #### cyberark_credential 82 | 83 | - Using AAM Central Credential Provider (CCP), to securely retreive secrets and account properties from EPV to be registered for use in playbooks
84 | [Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_credential.md) 85 | 86 | ### Roles 87 | 88 | #### aimprovider 89 | 90 | - Install agent-based Credential Provider (AIM) on Linux hosts 91 | [Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/aimprovider.md) 92 | 93 | ## Contributing 94 | 95 | Please see the [contributing guidelines](https://github.com/cyberark/ansible-security-automation-collection/blob/master/CONTRIBUTING.md) 96 | 97 | ### Author Information 98 | - CyberArk Business Development Technical Team 99 | - @enunez-cyberark 100 | - @cyberark-bizdev 101 | 102 | ## Support 103 | 104 | As Red Hat Ansible Certified Content, this collection is entitled to support through the Ansible Automation Platform (AAP) using the Create issue button on the top right corner. If a support case cannot be opened with Red Hat and the collection has been obtained either from Galaxy or GitHub, there may community help available on the [Ansible Forum](https://forum.ansible.com/). 105 | 106 | ## License 107 | 108 | MIT License 109 | 110 | Copyright (c) 2017 CyberArk 111 | 112 | Permission is hereby granted, free of charge, to any person obtaining a copy 113 | of this software and associated documentation files (the "Software"), to deal 114 | in the Software without restriction, including without limitation the rights 115 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 116 | copies of the Software, and to permit persons to whom the Software is 117 | furnished to do so, subject to the following conditions: 118 | 119 | For the full license text see [LICENSE](https://github.com/cyberark/ansible-security-automation-collection/blob/master/LICENSE) -------------------------------------------------------------------------------- /custom-cred-types/cyberark-pas-restapi/README.md: -------------------------------------------------------------------------------- 1 | # CyberArk PAS REST API 2 | 3 | Custom Credential Type for Ansible Tower 4 | 5 | ## Installation 6 | 7 | 1. Login to Ansible Tower as a Tower Administrator. 8 | 2. Under Administration, click on Credential Type. 9 | 3. Click the green [ + ] in the top right-hand corner to create a new Custom Credential Type. 10 | 4. Set the name of your Credential Type. e.g. `CyberArk PAS REST API` 11 | 5. Under `Input Configuration` select `YAML`. 12 | 6. Copy and paste the [input.yml](input.yml) into the text field for `Input Configuration`. 13 | 7. Under `Injector Configuration` select `YAML`. 14 | 8. Copy and paste the [injector.yml](injector.yml) into the text field for `Injector Configuration`. 15 | 9. Click `Save` at the bottom to save the Custom Credential Type. 16 | 17 | ## Usage 18 | 19 | Reference the following environment variables within your Ansible Playbook when using this Credential Type: 20 | 21 | * `CYBERARK_API_URL` \ 22 | This is the Base URI of your CyberArk Password Vault Web Access (PVWA). _e.g. `https://pvwa.cyberark.com`_ 23 | 24 | * `CYBERARK_API_USERNAME` \ 25 | This is the username to use when logging into the CyberArk PAS Web Services SDK (REST API). 26 | 27 | * `CYBERARK_API_PASSWORD` \ 28 | This is the password associated with the username provided for login. 29 | 30 | ## Maintainer 31 | 32 | Joe Garcia, CISSP - DevOps Security Engineer, CyberArk - [@infamousjoeg](https://github.com/infamousjoeg) 33 | -------------------------------------------------------------------------------- /custom-cred-types/cyberark-pas-restapi/injector.yml: -------------------------------------------------------------------------------- 1 | extra_vars: 2 | CYBERARK_API_PASSWORD: '{{ password }}' 3 | CYBERARK_API_URL: '{{ pvwa_url }}' 4 | CYBERARK_API_USERNAME: '{{ username }}' 5 | -------------------------------------------------------------------------------- /custom-cred-types/cyberark-pas-restapi/input.yml: -------------------------------------------------------------------------------- 1 | fields: 2 | - id: username 3 | type: string 4 | label: Username 5 | help_text: CyberArk REST API Username 6 | - id: password 7 | type: string 8 | label: Password 9 | secret: true 10 | help_text: CyberArk REST API Password 11 | - id: pvwa_url 12 | type: string 13 | label: PVWA URL 14 | help_text: 'CyberArk PVWA URL e.g. https://pvwa.cyberark.com' 15 | required: 16 | - username 17 | - password 18 | - pvwa_url 19 | -------------------------------------------------------------------------------- /docs/aimprovider.md: -------------------------------------------------------------------------------- 1 | cyberark.pas.aimprovider 2 | ==================== 3 | 4 | Role to install/uninstall CyberArk's AIM Credential Provider. 5 | 6 | Requirements 7 | ------------ 8 | 9 | - CyberArk Privileged Account Security Web Services SDK. 10 | - `cyberark.pas` Collection from Ansible Galaxy or Automation Hub 11 | 12 | Role Variables 13 | -------------- 14 | ``` 15 | # CyberArk's Privileged Account Security Web Services SDK api base URL (example: https://components.cyberark.local) 16 | rest_api_url: "" 17 | 18 | # Whether to validate certificates for REST api calls. If false, SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. 19 | validate_certs: true 20 | 21 | # Zip file with distribution of AIM Provider (example: /tmp/binaries/RHELinux x64-Rls-v9.8.zip); this file is located in the Ansible server, and it will be copied to the Ansible nodes. It should point to the current version of AIM distribution to be used when delivering to the nodes in a central folder within the Ansible server. 22 | zip_file_name: "" 23 | 24 | # Folder name within the ZIP file that will be used. By default, it's taken from zip file name, for example: "RHELinux x64" 25 | folder_name: '{{zip_file_name.split("/")[-1].split("-Rls")[0]}}' 26 | 27 | # CyberArk location for App Provider user to be created 28 | app_provider_user_location: "\\Applications" 29 | 30 | # CyberArk Vault Address 31 | vault_address: "" 32 | 33 | # Whether to use shared logon authentication. If true, it will use the "Shared Logon Authentication" as described in the CyberArk's document "Privileged Account Security Web Services SDK Implementation Guide" 34 | use_shared_logon_authentication: false 35 | 36 | # State - can be "present"/"absent" for install/uninstall. 37 | state: "present" 38 | ``` 39 | 40 | 41 | Additionally: 42 | - **app_provider_user_group**: The name of the group the Provider user will be added to. 43 | 44 | Dependencies 45 | ------------ 46 | 47 | None. 48 | 49 | 50 | Example Playbook 51 | ---------------- 52 | 53 | 1) Install CyberArk AIM Provider. 54 | 55 | ``` 56 | --- 57 | - hosts: all 58 | 59 | roles: 60 | 61 | - role: cyberark.pas.aimprovider 62 | api_base_url: "https://components.cyberark.local" 63 | validate_certs: false 64 | zip_file_name: "/tmp/binaries/RHELinux x64-Rls-v9.8.zip" 65 | vault_address: "10.0.1.10" 66 | use_shared_logon_authentication: true 67 | ``` 68 | 69 | 2) Uninstall CyberArk AIM Provider. 70 | ``` 71 | --- 72 | - hosts: all 73 | 74 | roles: 75 | 76 | - role: cyberark.pas.aimprovider 77 | api_base_url: "https://components.cyberark.local" 78 | use_shared_logon_authentication: true 79 | state: "absent" 80 | validate_certs: false 81 | ``` 82 | 83 | License 84 | ------- 85 | 86 | MIT 87 | 88 | Author Information 89 | ------------------ 90 | 91 | - Edward Nunez (edward.nunez@cyberark.com) 92 | -------------------------------------------------------------------------------- /docs/cyberark_account.md: -------------------------------------------------------------------------------- 1 | # cyberark_account 2 | 3 | Allows for adding, deleting, modifying a privileged credential within the Cyberark Vault. The request uses the Privileged Account Security Web Services SDK.
4 | 5 | The ability to modify consists of the following: 6 | 7 | * Password (see secret_management) 8 | * Safe 9 | * Platform 10 | * Address 11 | * Object Name 12 | * Username 13 | * Platform Account Properties 14 | * These are the parameters listed in the Platform under `UI & Workflows -> Properties` and are unique to each Platform (see image below) 15 | * Remote Machines Access 16 | 17 | ![Platform Account Properties](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/platform_account_properties.JPG?raw=true) 18 | 19 | ### secret_management 20 | The `secret_management` dictionary provides the capability to set a CPM password rotation flag on an existing account. 21 | 22 | The available options are as follows:
23 | 24 | `automatic_management_enabled`: bool
25 | `manual_management_reason`: This is a string value that populates the Reason field is you have set an account to not be managed by the CPM. This value is only necessary if `automatic_management_enabled` is set to false.
26 | `management_action`: This value indicates what type CPM management flag will be placed on the account 27 | * change -
28 | * change_immediately -
29 | * reconcile -
30 | 31 | `new_secret`: This parameter is available to set the value of the new password
32 | `perform_secret_management`: This parameter was allows the option to place a CPM management flag on an account upon creation of an account object. 33 | * always - All `secret_management` actions will follow the table below at all times. 34 | * on_create - Will place a CPM management flag according to the table below ONLY on creation of an account object. 35 | 36 | #### Secret Management Action Table 37 | | management_action | new_secret | Action | 38 | | :---------: | :----: | :----- | 39 | | change | populated | change password to set value at next scheduled rotation | 40 | | change | NULL | rotate password at next scheduled rotation | 41 | | change_immediately | populated | change immediately to the set value | 42 | | change_immediately | NULL | rotate immediately | 43 | | reconcile | populated | reconcile immediately NOT to set value | 44 | | reconcile | NULL | reconcile immediately | 45 | | NULL | populated | set value in Vault ONLY | 46 | 47 | 48 | ### identified_by 49 | This property allows for the module to confidently identify the account object needing to be identified. If multiple accounts are returned from the modules initial `Get Accounts` it will use the value(s) set in the `identified_by` parameter to direct which account is selected from the list. 50 | 51 | **EXAMPLE:** 52 | ``` 53 | -Playbook Parameters- 54 | 55 | cyberark_account: 56 | identified_by: "address,username,platform_id" 57 | safe: "testSafe" 58 | address: "dev.local" 59 | username: "admin" 60 | platform_id: WinDomain 61 | 62 | -This is the query sent to CyberArk Web SDK: 63 | /api/Accounts?filter=safeName eq testSafe&search= admin dev.local 64 | 65 | **This could return multiple accounts in the testSafe** 66 | 67 | RETURNED: 68 | account1 69 | username: administrator 70 | address: cyberark.dev.local 71 | safe: testSafe 72 | policyID: WinDomain 73 | 74 | account2 75 | username: admin 76 | address: dev.local 77 | safe: testSafe 78 | policyID: WinDomain 79 | ``` 80 | With the `identified_by` parameter set the `cyberark_account` module will select the account2 object becauses the values of the `address`, `username` and `platform_id` parameters are identical matches to the values of account2 properties. 81 | 82 | #### Limitations 83 | **Idempotency** - All actions taken in the module adhere to the Ansible idempotency guidelines _except_ for password change. If you have the playbook set to modify a password it will send a password change request every time the playbook is run, even if you are defining the next password value and it is the same password that is set in other runs.
84 | **Remote Machines Access** - When modifying the values in the `remote_machines_access` dictionary be mindful of the `platform_id` value. Remote Machines Access values are stored at the Vault database level and not stored as File Categories. It is a function that is only available with the `WinDomain` platform and if you attempt to assign these values to another platform it will cause errors in the PSM functionality. 85 | 86 | 87 | #### Available Fields 88 | ``` 89 | options: 90 | state: 91 | description: 92 | - Assert the desired state of the account C(present) to creat or update and account object. Set to C(absent) for deletion of an account object 93 | required: true 94 | default: present 95 | choices: [present, absent] 96 | type: str 97 | logging_level: 98 | description: 99 | - Parameter used to define the level of troubleshooting output to the C(logging_file) value 100 | required: true 101 | choices: [NOTSET, DEBUG, INFO] 102 | type: str 103 | logging_file: 104 | description: 105 | - Setting the log file name and location for troubleshooting logs 106 | required: false 107 | default: /tmp/ansible_cyberark.log 108 | type: str 109 | api_base_url: 110 | description: 111 | - A string containing the base URL of the server hosting CyberArk's Privileged Account Security Web Services SDK 112 | - Example: U(https:///PasswordVault/api/) 113 | required: true 114 | type: str 115 | validate_certs: 116 | description: 117 | - If C(false), SSL certificate chain will not be validated. This should only set to C(true) if you have a root CA certificate installed on each node. 118 | required: false 119 | default: true 120 | type: bool 121 | cyberark_session: 122 | description: 123 | - Dictionary set by a CyberArk authentication containing the different values to perform actions on a logged-on CyberArk session, please see M(cyberark_authentication) module for an example of cyberark_session. 124 | required: true 125 | type: dict 126 | identified_by: 127 | description: 128 | - When an API call is made to Get Accounts, often times the default parameters passed will identify more than one account. This parameter is used to confidently identify a single account when the default query can return multiple results. 129 | required: false 130 | default: username,address,platform_id 131 | type: str 132 | safe: 133 | description: 134 | - The safe in the Vault where the privileged account is to be located 135 | required: true 136 | type: str 137 | platform_id: 138 | description: 139 | - The PolicyID of the Platform that is to be managing the account 140 | required: false 141 | type: str 142 | address: 143 | description: 144 | - The adress of the endpoint where the privileged account is located 145 | required: false 146 | type: str 147 | name: 148 | description: 149 | - The ObjectID of the account 150 | required: false 151 | type: str 152 | secret_type: 153 | description: 154 | - The value that identifies what type of account it will be. 155 | required: false 156 | default: password 157 | choices: [password, key] 158 | type: str 159 | secret: 160 | description: 161 | - The initial password for the creation of the account 162 | required: false 163 | type: str 164 | username: 165 | description: 166 | - The username associated with the account 167 | required: false 168 | type: str 169 | secret_management 170 | description: 171 | - Set of parameters associated with the management of the credential 172 | required: false 173 | suboptions: 174 | automatic_management_enabled: 175 | description: 176 | - Parameter that indicates whether the CPM will manage the password or not 177 | default: true 178 | type: bool 179 | manual_management_reason: 180 | description: 181 | - String value indicating why the CPM will NOT manage the password 182 | type: str 183 | management_action: 184 | description: 185 | - CPM action flag to be placed on the account object for credential rotation 186 | choices: [change, change_immediately, reconcile] 187 | type: str 188 | new_secret: 189 | description: 190 | - The actual password value that will be assigned for the CPM action to be taken 191 | type: str 192 | perform_management_action: 193 | description: 194 | - C(always) will perform the management action in every action 195 | - C(on_create) will only perform the management action right after the account is created 196 | choices: [always, on_create] 197 | default: always 198 | type: str 199 | remote_machines_access: 200 | description: 201 | - Set of parameters for defining PSM endpoint access targets 202 | required: false 203 | type: dict 204 | suboptions: 205 | remote_machines: 206 | description: 207 | - List of targets allowed for this account 208 | type: str 209 | access_restricted_to_remote_machines: 210 | description: 211 | - Whether or not to restrict access only to specified remote machines 212 | type: bool 213 | platform_account_properties: 214 | description: 215 | - Object containing key-value pairs to associate with the account, as defined by the account platform. These properties are validated against the mandatory and optional properties of the specified platform's definition. Optional properties that do not exist on the account will not be returned here. Internal properties are not returned. 216 | required: false 217 | type: dict 218 | suboptions: 219 | KEY: 220 | description: 221 | - Freeform key value associated to the mandatory or optional property assigned to the specified Platform's definition. 222 | aliases: [Port, ExtrPass1Name, database] 223 | type: str 224 | ``` 225 | 226 | ## Example Playbooks 227 | 228 | 229 | ```yaml 230 | tasks: 231 | 232 | - name: Logon to CyberArk Vault using PAS Web Services SDK 233 | cyberark.pas.cyberark_authentication: 234 | api_base_url: "http://components.cyberark.local" 235 | validate_certs: false 236 | username: "bizdev" 237 | password: "Cyberark1" 238 | 239 | - name: Creating an Account using the PAS WebServices SDK 240 | cyberark.pas.cyberark_account: 241 | logging_level: DEBUG 242 | identified_by: "address,username" 243 | safe: "Test" 244 | address: "cyberark.local" 245 | username: "administrator-x" 246 | platform_id: WinServerLocal 247 | secret: "@N&Ibl3!" 248 | platform_account_properties: 249 | LogonDomain: "cyberark" 250 | OwnerName: "ansible_user" 251 | secret_management: 252 | automatic_management_enabled: true 253 | state: present 254 | cyberark_session: "{{ cyberark_session }}" 255 | register: cyberarkaction 256 | 257 | - name: Rotate credential via reconcile and providing the password to be changed to 258 | cyberark.pas.cyberark_account: 259 | identified_by: "address,username" 260 | safe: "Domain_Admins" 261 | address: "prod.cyberark.local" 262 | username: "admin" 263 | platform_id: WinDomain 264 | platform_account_properties: 265 | LogonDomain: "PROD" 266 | secret_management: 267 | new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" 268 | management_action: "reconcile" 269 | automatic_management_enabled: true 270 | state: present 271 | cyberark_session: "{{ cyberark_session }}" 272 | register: reconcileaccount 273 | 274 | - name: Update password only in VAULT 275 | cyberark.pas.cyberark_account: 276 | identified_by: "address,username" 277 | safe: "Domain_Admins" 278 | address: "prod.cyberark.local" 279 | username: "admin" 280 | platform_id: Generic 281 | new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" 282 | state: present 283 | cyberark_session: "{{ cyberark_session }}" 284 | register: updateaccount 285 | 286 | - name: Retrieve account and password 287 | cyberark.pas.cyberark_account: 288 | identified_by: "address,username" 289 | safe: "Domain_Admins" 290 | address: "prod.cyberark.local" 291 | username: "admin" 292 | state: retrieve 293 | cyberark_session: "{{ cyberark_session }}" 294 | register: retrieveaccount 295 | 296 | - name: Logoff from CyberArk Vault 297 | cyberark.pas.cyberark_authentication: 298 | state: absent 299 | cyberark_session: "{{ cyberark_session }}" 300 | ``` 301 | -------------------------------------------------------------------------------- /docs/cyberark_authentication.md: -------------------------------------------------------------------------------- 1 | # cyberark_authentication 2 | 3 | 4 | Authenticates to CyberArk Vault using Privileged Account Security Web Services SDK and creates a session fact that can be used by other modules. It returns an Ansible fact called `cyberark_session`. Every module can use this fact as `cyberark_session` parameter. 5 | 6 | 7 | #### Available Fields 8 | ``` 9 | options: 10 | state: 11 | default: present 12 | choices: [present, absent] 13 | description: 14 | - Specifies if an authentication logon/logoff and a cyberark_session should be added/removed. 15 | username: 16 | description: 17 | - The name of the user who will logon to the Vault. 18 | password: 19 | description: 20 | - The password of the user. 21 | new_password: 22 | description: 23 | - The new password of the user. This parameter is optional, and enables you to change a password. 24 | api_base_url: 25 | description: 26 | - A string containing the base URL of the server hosting CyberArk's Privileged Account Security Web Services SDK. 27 | validate_certs: 28 | type: bool 29 | default: 'true' 30 | description: 31 | - If C(false), SSL certificates will not be validated. This should only 32 | set to C(false) used on personally controlled sites using self-signed 33 | certificates. 34 | use_shared_logon_authentication: 35 | type: bool 36 | default: 'false' 37 | description: 38 | - Whether or not Shared Logon Authentication will be used. 39 | use_radius_authentication: 40 | type: bool 41 | default: 'false' 42 | description: 43 | - Whether or not users will be authenticated via a RADIUS server. Valid values are true/false. 44 | cyberark_session: 45 | description: 46 | - Dictionary set by a CyberArk authentication containing the different values to perform actions on a logged-on CyberArk session. 47 | timeout: 48 | type: int 49 | default: 10 50 | description: 51 | - Allows you set a timeout for when your authenticating to Cyberark 52 | ``` 53 | ## Example Playbooks 54 | 55 | **Shared Logon Authentication.**
56 | Shared authentication is based on a user credential file that is stored in the PVWA web server. During shared authentication, only the user defined in the credential file can log on to the PVWA, but multiple users can use the logon token. 57 | 58 | This type of authentication requires the playbook to manage the users as the Vault can't identify which specific user performs each action. 59 | 60 | Multiple concurrent connections can be created using the same token, without affecting each other. 61 | 62 | The shared user is defined in a user credential file, whose location is specified in the WSCredentialFile parameter, in the appsettings section of the PVWAweb.config file: 63 | 64 | ```xml 65 | 66 | ``` 67 | > Make sure that this user can access the PVWA interface.
68 | > Make sure the user only has the permissions in the Vault that they require. 69 | 70 | It is recommended to secure connections between Ansible and the REST Web Services when using Shared Logon Authentication, using Client Authentication. 71 | 72 | In addition to SSL, use Client Authentication to authenticate Ansible using a client certificate. 73 | 74 | [Configuring client authentication via certificates](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/SDK/Configuring%20Client%20Authentication%20via%20Client%20Certificates.htm) 75 | 76 | ```yaml 77 | - name: Logon to CyberArk Vault using PAS Web Services SDK - use_shared_logon_authentication 78 | cyberark_authentication: 79 | api_base_url: "{{ web_services_base_url }}" 80 | use_shared_logon_authentication: true 81 | ``` 82 | 83 | **CyberArk Authentication**
84 | This method authenticates a user to the Vault and returns a token that can be used in subsequent web services calls. In addition, this method allows you to set a new password. 85 | 86 | Users can authenticate using **CyberArk**, **LDAP** or **RADIUS** authentication. 87 | 88 | ```yaml 89 | - name: Logon to CyberArk Vault using PAS Web Services SDK - Not use_shared_logon_authentication 90 | cyberark_authentication: 91 | api_base_url: "{{ web_services_base_url }}" 92 | username: "{{ password_object.password }}" 93 | password: "{{ password_object.passprops.username }}" 94 | use_shared_logon_authentication: false 95 | ``` 96 | **Logoff**
97 | This method logs off the user and removes the Vault session. 98 | 99 | ```yaml 100 | - name: Logoff from CyberArk Vault 101 | cyberark_authentication: 102 | state: absent 103 | cyberark_session: "{{ cyberark_session }} 104 | ``` 105 | -------------------------------------------------------------------------------- /docs/cyberark_credential.md: -------------------------------------------------------------------------------- 1 | # cyberark_credential 2 | 3 | Creates a URI for retrieving a credential from a password object stored in the Cyberark Vault. The request uses the Privileged Account Security Web Services SDK through the Central Credential Provider by requesting access with an Application ID. 4 | 5 | **Requirements:** 6 | - CyberArk AAM Central Credential Provider 7 | - ApplicationID with the following permissions on the safe containing the credential being requested: 8 | - List Accounts 9 | - Retrieve Accounts 10 | > **NOTE:** The CCP's Provider user (Prov_hostaname) needs to have the following permissions on the safe containing the credential being requested: 11 | >> List Accounts
12 | >> Retrieve Accounts
13 | >> View Safe Members
14 | 15 | ## Query 16 | This field is semicolon delimited value that is the exact syntax that goes in the URI
17 | If you use the `object` parameter then there is no need to use any other parameter as the ObjectID is a unique value.
18 | **Example:** 19 | ``` 20 | query: "Safe=test;UserName=admin" 21 | OR 22 | query: "Object=OperatingSystem-administrator-dev.local" 23 | ``` 24 | 25 | ## Available Fields 26 | 27 | ``` 28 | options: 29 | api_base_url: 30 | description: 31 | - A string containing the base URL of the server hosting the Central Credential Provider 32 | required: true 33 | type: string 34 | validate_certs: 35 | description: 36 | - If C(false), SSL certificate chain will not be validated. This should only set to C(true) if you have a root CA certificate installed on each node. 37 | type: bool 38 | required: false 39 | default: false 40 | type: bool 41 | app_id: 42 | description: 43 | - A string containing the Application ID authorized for retrieving the credential 44 | required: true 45 | type: string 46 | query: 47 | description: 48 | - A string containing details of the object being queried 49 | required: true 50 | parameters: 51 | Safe= 52 | Folder= 53 | Object= 54 | UserName= 55 | Address=
56 | Database= 57 | PolicyID= 58 | connection_timeout: 59 | description: 60 | - An integer value of the allowed time before the request returns failed 61 | required: false 62 | default: '30' 63 | type: integer 64 | query_format: 65 | description: 66 | - The format for which your Query will be received by the CCP 67 | required: false 68 | default: 'Exact' 69 | choices: [Exact, Regexp] 70 | type: choice 71 | fail_request_on_password_change: 72 | description: 73 | - A boolean parameter for completing the request in the middle of a password change of the requested credential 74 | required: false 75 | default: false 76 | type: bool 77 | client_cert: 78 | description: 79 | - A string containing the file location and name of the client certificate used for authentication 80 | required: false 81 | type: string 82 | client_key: 83 | description: 84 | - A string containing the file location and name of the private key of the client certificate used for authentication 85 | required: false 86 | type: string 87 | reason: 88 | description: 89 | - Reason for requesting credential if required by policy 90 | required: false 91 | type: string 92 | ``` 93 | 94 | 95 | 96 | ## Example Playbooks 97 | 98 | ```yaml 99 | - name: credential retrieval basic 100 | cyberark_credential: 101 | api_base_url: "http://10.10.0.1" 102 | app_id: "TestID" 103 | query: "Safe=test;UserName=admin" 104 | register: result 105 | 106 | result: 107 | { api_base_url }"/AIMWebService/api/Accounts?AppId="{ app_id }"&Query="{ query } 108 | 109 | 110 | - name: credential retrieval advanced 111 | cyberark_credential: 112 | api_base_url: "https://components.cyberark.local" 113 | validate_certs: true 114 | client_cert: /etc/pki/ca-trust/source/client.pem 115 | client_key: /etc/pki/ca-trust/source/priv-key.pem 116 | app_id: "TestID" 117 | query: "Safe=test;UserName=admin" 118 | connection_timeout: 60 119 | query_format: Exact 120 | fail_request_on_password_change: true 121 | reason: "requesting credential for Ansible deployment" 122 | register: result 123 | 124 | result: 125 | { api_base_url }"/AIMWebService/api/Accounts?AppId="{ app_id }"&Query="{ query }"&ConnectionTimeout="{ connection_timeout }"&QueryFormat="{ query_format }"&FailRequestOnPasswordChange="{ fail_request_on_password_change } 126 | 127 | - name: credential retrieval custom path 128 | cyberark_credential: 129 | api_base_url: "http://10.10.0.1" 130 | app_id: "TestID" 131 | query: "Safe=test;UserName=admin" 132 | path: AimWebServiceCustom 133 | register: result 134 | 135 | result: 136 | { api_base_url } { path } "?AppId="{ app_id }"&Query="{ query } 137 | ``` 138 | -------------------------------------------------------------------------------- /docs/cyberark_eda.md: -------------------------------------------------------------------------------- 1 | # Event-Driven Ansible 2 | 3 | The CyberArk PAM Self-Hosted solution can be configured to export syslogs to Ansible as an event source. Once Ansible EDA ingests the event it will act on rule-based criteria contained in an Ansible Rulebook and will call Ansible Playbooks to initiate action when the conditions contained in the rulebook are met. 4 | 5 | The following options will be available to configure CyberArk as Event Source: 6 | 7 | **VAULT** 8 | * CyberArk to Rsyslog to EDA Webhook 9 | * CyberArk to Rsyslog to EDA Kafka Topic 10 | * CyberArk Syslog as EDA event source (UDP Protocol) 11 | 12 | **PTA** 13 | * CyberArk PTA Syslog to EDA event source (UDP Protocol) 14 | 15 | 16 | **NOTE**: For Rsyslog work, it was tested successfully with rsyslogd 8.2306.0.master (aka 2023.06) running on Ubuntu 17 | 18 | 19 | ## CyberArk to Rsyslog to EDA Webhook 20 | 21 | ![CyberArk to Rsyslog to EDA Webhook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/rsyslog-webhook.png?raw=true) 22 | 23 | ### Vault Configuration 24 | Follow the steps under [Security Information and Event Management (SIEM) Applications](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PASIMP/DV-Integrating-with-SIEM-Applications.htm) documentation to setup the integration: 25 | 26 | * Copy the [cyberark-eda-json-v1.0.xsl](https://github.com/cyberark/ansible-security-automation-collection/blob/master/plugins/event_source/cyberark-eda-json-v1.0.xsl) XSL Translator file to the Server\Syslog folder. 27 | * See sample syslog configuration for DBPARM.ini below 28 | * Recommended to use TCP and port 514 as it's default rsyslog port. 29 | 30 | ``` 31 | [SYSLOG] 32 | UseLegacySyslogFormat=Yes 33 | SyslogTranslatorFile=Syslog\cyberark-eda-json-v1.0.xsl 34 | SyslogServerIP= 35 | SyslogServerPort=514 36 | SyslogServerProtocol=TCP 37 | ``` 38 | 39 | ### Rsyslog server configuration to forward to event to webhook 40 | Create a conf file in /etc/rsyslog.d/webhook.conf with the following content: 41 | ``` 42 | template(name="CyberarkFormat" type="string" 43 | string="%syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n") 44 | module(load="omprog") 45 | if ($hostname == 'VAULT') then 46 | { 47 | action(type="omprog" 48 | binary="/tmp/run_curl.py" 49 | output="/tmp/output.log" 50 | template="CyberarkFormat") 51 | } 52 | ``` 53 | 54 | Hostname will need be changed accordingly to your environment. If the syslog message are from a vault server, it will forward the event using the python script to the EDA webhook. 55 | 56 | Here is an example of a python script to forward the information using the webhook: 57 | ``` 58 | #!/usr/bin/python 59 | import urllib2 60 | import sys 61 | value = sys.stdin.readline() 62 | file = open('/tmp/debug.txt', 'a') 63 | file.write(value) 64 | file.close 65 | #data = '{"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"}' #test data 66 | data = value 67 | url = 'http://ubuntu:5000/endpoint' 68 | req = urllib2.Request(url, data, {'Content-Type': 'application/json'}) 69 | f = urllib2.urlopen(req) 70 | for x in f: 71 | print(x) 72 | f.close() 73 | ``` 74 | 75 | ![Sample run of disable_pas_user_webhook.yml](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_disableuser_webhook.png?raw=true) 76 | 77 | ## CyberArk to Rsyslog to EDA Kafka Topic 78 | 79 | ![CyberArk to Rsyslog to EDA Kafka Topic](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/rsyslog-kafka.png?raw=true) 80 | 81 | ### Vault Configuration 82 | Follow the steps under [Security Information and Event Management (SIEM) Applications](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PASIMP/DV-Integrating-with-SIEM-Applications.htm) documentation to setup the integration: 83 | 84 | * Copy the [cyberark-eda-json-v1.0.xsl](https://github.com/cyberark/ansible-security-automation-collection/blob/master/plugins/event_source/cyberark-eda-json-v1.0.xsl) XSL Translator file to the Server\Syslog folder. 85 | * See sample syslog configuration for DBPARM.ini below 86 | * Recommended to use TCP and port 514 as it's default rsyslog port. 87 | 88 | ``` 89 | [SYSLOG] 90 | UseLegacySyslogFormat=Yes 91 | SyslogTranslatorFile=Syslog\ansible-json-v1.0.xsl 92 | SyslogServerIP= 93 | SyslogServerPort=514 94 | SyslogServerProtocol=TCP 95 | ``` 96 | 97 | ### Rsyslog server configuration to forward to Kafka topic 98 | Create a conf file in /etc/rsyslog.d/ansible_kafka.conf with the following content: 99 | 100 | ``` 101 | $EscapeControlCharactersOnReceive off 102 | template(name="CyberarkFormat" type="string" 103 | string="%syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n") 104 | if ($hostname == 'VAULT') then 105 | { 106 | action(type="omkafka" broker=["ubuntu:9092"] topic="ansible" confParam="compression.codec=snappy" template="CyberarkFormat") 107 | } 108 | ``` 109 | 110 | Hostname will need be changed accordingly to your environment. If the syslog message are from a vault server, it will forward to a Kafka topic on Kafka server in ubuntu on port 9092. 111 | 112 | ### Kafka Topic 113 | Rsyslog use the omkafka plugin to forward the vault rsyslog to the Kafka topic. This plugin may need to be install by your admin as it's not enabled by default. 114 | ``` 115 | yum install rsyslog-kafka 116 | ``` 117 | 118 | Refer to Kafka documentation: https://kafka.apache.org/quickstart how to stand up a Kafka server and view topic. 119 | 120 | ![Sample rulebook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_disableuser_kafka.png?raw=true) 121 | 122 | The rulebook above listens on a Kafka ansible topic for suspended user and will disable the user and email admin via email. 123 | 124 | ## CyberArk Syslog as EDA event source (UDP Protocol) 125 | This EDA plugin acts as a syslog listener on specific port using UDP bypassing the need of an existing rsyslog server. 126 | 127 | ![CyberArk Syslog as EDA event source (UDP Protocol)](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda-syslog.png?raw=true) 128 | 129 | Follow the steps under [Security Information and Event Management (SIEM) Applications](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PASIMP/DV-Integrating-with-SIEM-Applications.htm) documentation to setup the integration: 130 | 131 | 132 | * Copy the [cyberark-eda-json-v1.0.xsl](https://github.com/cyberark/ansible-security-automation-collection/blob/master/plugins/event_source/cyberark-eda-json-v1.0.xsl) XSL Translator file to the Server\Syslog folder. 133 | * See sample syslog configuration for DBPARM.ini below 134 | * • Currently only UDP is supported for the syslog event-source plugin 135 | 136 | ``` 137 | [SYSLOG] 138 | UseLegacySyslogFormat=Yes 139 | SyslogTranslatorFile=Syslog\ansible-json-v1.0.xsl 140 | SyslogServerIP= 141 | SyslogServerPort=1514 142 | SyslogServerProtocol=UDP 143 | ``` 144 | 145 | ![Sample rulebook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_disable_user_syslog.png?raw=true) 146 | 147 | 148 | ## CyberArk PTA Syslog to EDA event source (UDP Protocol) 149 | 150 | ![CyberArk PTA Syslog to EDA event source (UDP Protocol)](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda-pta-syslog.png?raw=true) 151 | 152 | Please refer to the following documentation for instructions on how to setup PTA to sent data to SIEM: 153 | [Send PTA syslog Records to SIEM](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PTA/Outbound-Sending-%20PTA-syslog-Records-to-SIEM.htm?tocpath=Administrator%7CComponents%7CPrivileged%20Threat%20Analytics%7CConfigure%20Privileged%20Threat%20Analytics%7CSend%20PTA%20Data%7CSend%20PTA%20syslog%20Records%20to%20SIEM%7C_____0) 154 | 155 | In the PTA server's local systemparm.properties file have a line with: 156 | 157 | ``` 158 | syslog_outbound=[{\"siem\": \"SIEM\", \"format\": \"CEF\", \"host\": \"ANSIBLE_EDA_SERVER\", \"port\": << PORT FOR THE ANSIBLE EVENT-SOURCE EDA PLUGIN >>, \"protocol\": \"UDP\"}] 159 | ``` 160 | 161 | ![Sample rulebook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_pta_disable_user_syslog.png?raw=true) -------------------------------------------------------------------------------- /docs/cyberark_user.md: -------------------------------------------------------------------------------- 1 | # cyberark_user 2 | 3 | This module allows admins to Add, Delete, and Modify CyberArk Vault Users. The ability to modify consists of the following: 4 | 5 | * Enable User
6 | * Disable User
7 | * Add/Remove Group
8 | * Set New Password
9 | * Force "change password at next login"
10 | * Modify User Information Fields
11 | * Email
12 | * First Name
13 | * Last Name
14 | * Expiry Date
15 | * User Type
16 | * Location
17 | 18 | #### Limitations 19 | **Idempotency** - All actions taken in the playbook adhere to the Ansible idempotency guidelines _except_ for password change. If you have the playbook set to modify a password it will "modify" the password every time the playbook is run, even if it is the same password.
20 | **Group Creation** - If the value for `group_name` does not exist in the Vault it will not create that group, the user action that was expected will fail. 21 | 22 | #### Available Fields 23 | 24 | ``` 25 | options: 26 | username: 27 | description: 28 | - The name of the user who will be queried (for details), added, updated or deleted. 29 | type: str 30 | required: true 31 | state: 32 | description: 33 | - Specifies the state needed for the user present for create user, absent for delete user. 34 | type: str 35 | choices: [ absent, present ] 36 | default: present 37 | cyberark_session: 38 | description: 39 | - Dictionary set by a CyberArk authentication containing the different values to perform actions on a logged-on CyberArk session, 40 | please see M(cyberark_authentication) module for an example of cyberark_session. 41 | type: dict 42 | required: true 43 | initial_password: 44 | description: 45 | - The password that the new user will use to log on the first time. 46 | - This password must meet the password policy requirements. 47 | - This parameter is required when state is present -- Add User. 48 | type: str 49 | new_password: 50 | description: 51 | - The user updated password. Make sure that this password meets the password policy requirements. 52 | type: str 53 | email: 54 | description: 55 | - The user email address. 56 | type: str 57 | first_name: 58 | description: 59 | - The user first name. 60 | type: str 61 | last_name: 62 | description: 63 | - The user last name. 64 | type: str 65 | change_password_on_the_next_logon: 66 | description: 67 | - Whether or not the user must change their password in their next logon. 68 | type: bool 69 | default: false 70 | expiry_date: 71 | description: 72 | - The date and time when the user account will expire and become disabled. 73 | type: str 74 | user_type_name: 75 | description: 76 | - The type of user. 77 | - The parameter defaults to C(EPVUser). 78 | type: str 79 | disabled: 80 | description: 81 | - Whether or not the user will be disabled. 82 | type: bool 83 | default: false 84 | location: 85 | description: 86 | - The Vault Location for the user. 87 | type: str 88 | group_name: 89 | description: 90 | - The name of the group the user will be added to. 91 | type: str 92 | ``` 93 | ## Example Playbooks 94 | 95 | This playbook will check if username `admin` exists, if it does not, it will provision the user in the Vault, add it to the `Auditors` group and set the account to be changed at first logon. 96 | 97 | ```yaml 98 | - name: Logon to CyberArk Vault using PAS Web Services SDK 99 | cyberark_authentication: 100 | api_base_url: https://components.cyberark.local 101 | use_shared_logon_authentication: true 102 | 103 | - name: Create user, add to Group 104 | cyberark_user: 105 | username: admin 106 | first_name: "Cyber" 107 | last_name: "Admin" 108 | email: "cyber.admin@ansibledev.com" 109 | initial_password: PA$$Word123 110 | user_type_name: EPVUser 111 | change_password_on_the_next_logon: true 112 | group_name: Auditors 113 | state: present 114 | cyberark_session: '{{ cyberark_session }}' 115 | register: cyberarkaction 116 | 117 | - name: Logoff from CyberArk Vault 118 | cyberark_authentication: 119 | state: absent 120 | cyberark_session: '{{ cyberark_session }}' 121 | ``` 122 | 123 | This playbook will identify the user and delete it from the CyberArk Vault based on the `state: absent` parameter. 124 | 125 | ```yaml 126 | - name: Logon to CyberArk Vault using PAS Web Services SDK - use_shared_logon_authentication 127 | cyberark_authentication: 128 | api_base_url: "{{ web_services_base_url }}" 129 | use_shared_logon_authentication: true 130 | 131 | - name: Removing a CyberArk User 132 | cyberark_user: 133 | username: "ansibleuser" 134 | state: absent 135 | cyberark_session: "{{ cyberark_session }}" 136 | register: cyberarkaction 137 | 138 | - name: Logoff from CyberArk Vault 139 | cyberark_authentication: 140 | state: absent 141 | cyberark_session: "{{ cyberark_session }}" 142 | ``` 143 | This playbook is an example of disabling a user based on the `disabled: true` value with that authentication using the credential set in Tower. 144 | ```yaml 145 | - name: Logon to CyberArk Vault using PAS Web Services SDK - Not use_shared_logon_authentication 146 | cyberark_authentication: 147 | api_base_url: "{{ web_services_base_url }}" 148 | username: "{{ password_object.password }}" 149 | password: "{{ password_object.passprops.username }}" 150 | use_shared_logon_authentication: false 151 | 152 | - name: Disabling a CyberArk User 153 | cyberark_user: 154 | username: "ansibleuser" 155 | disabled: true 156 | cyberark_session: "{{ cyberark_session }}" 157 | register: cyberarkaction 158 | 159 | - name: Logoff from CyberArk Vault 160 | cyberark_authentication: 161 | state: absent 162 | cyberark_session: "{{ cyberark_session }}" 163 | ``` 164 | -------------------------------------------------------------------------------- /docs/images/cyberark_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/cyberark_logo.jpg -------------------------------------------------------------------------------- /docs/images/eda-pta-syslog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/eda-pta-syslog.png -------------------------------------------------------------------------------- /docs/images/eda-syslog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/eda-syslog.png -------------------------------------------------------------------------------- /docs/images/eda_disable_user_syslog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/eda_disable_user_syslog.png -------------------------------------------------------------------------------- /docs/images/eda_disableuser_kafka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/eda_disableuser_kafka.png -------------------------------------------------------------------------------- /docs/images/eda_disableuser_webhook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/eda_disableuser_webhook.png -------------------------------------------------------------------------------- /docs/images/eda_pta_disable_user_syslog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/eda_pta_disable_user_syslog.png -------------------------------------------------------------------------------- /docs/images/full-cyberark-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/full-cyberark-logo.jpg -------------------------------------------------------------------------------- /docs/images/platform_account_properties.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/platform_account_properties.JPG -------------------------------------------------------------------------------- /docs/images/rsyslog-kafka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/rsyslog-kafka.png -------------------------------------------------------------------------------- /docs/images/rsyslog-webhook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/ansible-security-automation-collection/4a28b9898f040491e924d828e85de326072da5ce/docs/images/rsyslog-webhook.png -------------------------------------------------------------------------------- /extensions/eda/plugins/event_source/cyberark-eda-json-v1.0.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {"format":" 17 | ","version":" 18 | " 19 | 20 | 21 | ,"raw": 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | , 30 | 31 | 32 | } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | > 41 | 42 | 43 | 44 | 45 | 46 | 47 | " 48 | ": 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | : 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | { 83 | 84 | 85 | 86 | :[ 87 | 88 | ]} 89 | 90 | 91 | { 92 | 93 | , 94 | 95 | } 96 | 97 | 98 | , 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | : 107 | 108 | 109 | 110 | , 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | " 145 | 146 | 147 | 148 | 149 | 150 | " 151 | 152 | 153 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /extensions/eda/plugins/event_source/syslog.py: -------------------------------------------------------------------------------- 1 | """syslog.py. 2 | 3 | An ansible-rulebook event source module for receiving events via a syslog. 4 | 5 | Arguments: 6 | --------- 7 | host: The hostname to listen to. Set to 0.0.0.0 to listen on all 8 | interfaces. Defaults to 127.0.0.1 9 | port: The TCP port to listen to. Defaults to 1514 10 | 11 | """ 12 | from __future__ import (absolute_import, division, print_function) 13 | import asyncio 14 | import json 15 | import logging 16 | import re 17 | from typing import Any 18 | 19 | 20 | __metaclass__ = type 21 | BASIC_CEF_HEADER_SIZE = 6 22 | 23 | 24 | def parse(str_input: str) -> dict[str, str]: # pylint: disable=unsubscriptable-object 25 | """Parse a string in CEF format and return a dict with header values and extension data. 26 | 27 | # noqa: DAR201 28 | # noqa: DAR101 29 | """ 30 | logger = logging.getLogger() 31 | # Create the empty dict we'll return later 32 | values = {} 33 | 34 | # This regex separates the string into the CEF header and the extension 35 | # data. Once we do this, it's easier to use other regexes to parse each 36 | # part. 37 | header_re = r"((CEF:\d+)([^=\\]+\|){,7})(.*)" 38 | 39 | res = re.search(header_re, str_input) 40 | 41 | if res: 42 | header = res.group(1) 43 | extension = res.group(4) 44 | 45 | # Split the header on the "|" char. Uses a negative lookbehind 46 | # assertion to ensure we don't accidentally split on escaped chars, 47 | # though. 48 | spl = re.split(r"(? BASIC_CEF_HEADER_SIZE: 67 | values["Severity"] = spl[6] 68 | values["DeviceSeverity"] = spl[6] 69 | 70 | # The first value is actually the CEF version, formatted like 71 | # "CEF:#". Ignore anything before that (like a date from a syslog message). 72 | # We then split on the colon and use the second value as the 73 | # version number. 74 | cef_start = spl[0].find("CEF") 75 | if cef_start == -1: 76 | return None 77 | (_, version) = spl[0][cef_start:].split(":") # pylint: disable=disallowed-name 78 | values["CEFVersion"] = version 79 | 80 | # The ugly, gnarly regex here finds a single key=value pair, 81 | # taking into account multiple whitespaces, escaped '=' and '|' 82 | # chars. It returns an iterator of tuples. 83 | spl = re.findall(r"([^=\s]+)=((?:[\\]=|[^=])+)(?:\s|$)", extension) 84 | for i in spl: 85 | # Split the tuples and put them into the dictionary 86 | values[i[0]] = i[1] 87 | 88 | # Process custom field labels 89 | for key in list(values.keys()): 90 | # If the key string ends with Label, replace it in the appropriate 91 | # custom field 92 | if key[-5:] == "Label": 93 | customlabel = key[:-5] 94 | # Find the corresponding customfield and replace with the label 95 | for customfield in list(values.keys()): 96 | if customfield == customlabel: 97 | values[values[key]] = values[customfield] 98 | del values[customfield] 99 | del values[key] 100 | else: 101 | # return None if our regex had now output 102 | return None 103 | 104 | # Now we're done! 105 | logger.debug("Returning values: %s", str(values)) 106 | return values 107 | 108 | 109 | class SyslogProtocol(asyncio.DatagramProtocol): 110 | """Provides Syslog Protocol functionality.""" 111 | 112 | def __init__(self, edaqueue: asyncio.Queue) -> None: 113 | """Init Constructor. 114 | 115 | # noqa: DAR101 116 | """ 117 | super().__init__() 118 | self.edaQueue = edaqueue 119 | self.transport = None 120 | 121 | def connection_made(self, transport: asyncio.DatagramTransport) -> None: 122 | """connection_made: Standard for asyncio. 123 | 124 | # noqa: DAR101 125 | """ 126 | self.transport = transport 127 | 128 | def datagram_received(self, data: bytes, addr: Any) -> None: # pylint: disable=unsubscriptable-object 129 | """datagram_received: Standard method for protocol. 130 | 131 | # noqa: DAR101 132 | """ 133 | asyncio.get_event_loop().create_task(self.datagram_received_async(data, addr)) 134 | 135 | async def datagram_received_async(self, indata: Any, addr: Any) -> None: 136 | """datagram_received_async: Standard method for protocol. 137 | 138 | # noqa: DAR101 139 | """ 140 | # Syslog event data received, and processed for EDA 141 | logger = logging.getLogger() 142 | rcvdata = indata.decode() 143 | logger.info("Received Syslog message: %s - addr: %s", rcvdata, addr) 144 | data = parse(rcvdata) 145 | 146 | if data is None: 147 | # if not CEF, we will try JSON load of the text from first curly brace 148 | try: 149 | value = rcvdata[rcvdata.index("{"):len(rcvdata)] 150 | data = json.loads(value) 151 | except json.decoder.JSONDecodeError: 152 | logger.exception("JSON Decode Error") 153 | data = rcvdata 154 | except UnicodeError: 155 | logger.exception("UnicodeError") 156 | 157 | if data: 158 | queue = self.edaQueue 159 | await queue.put({"cyberark": data}) 160 | 161 | 162 | async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: # pylint: disable=unsubscriptable-object 163 | """Perform main functionality. 164 | 165 | # noqa: DAR101 166 | """ 167 | logger = logging.getLogger() 168 | 169 | _ = asyncio.get_event_loop() # pylint: disable=disallowed-name 170 | host = args.get("host") or "0.0.0.0" 171 | port = args.get("port") or 1514 172 | transport, _ = await asyncio.get_running_loop().create_datagram_endpoint( # pylint: disable=disallowed-name 173 | lambda: SyslogProtocol(queue), 174 | local_addr=((host, port))) 175 | logger.info("Starting cyberark.pas.syslog [Host=%s, port=%s]", host, port) 176 | try: 177 | while True: 178 | await asyncio.sleep(3600) 179 | finally: 180 | transport.close() 181 | 182 | 183 | if __name__ == "__main__": 184 | 185 | class MockQueue: 186 | """simple mock queue.""" 187 | 188 | async def put(self, event: Any) -> None: 189 | """put: Put method. 190 | 191 | # noqa: DAR101 192 | """ 193 | 194 | asyncio.run(main(MockQueue(), {})) 195 | -------------------------------------------------------------------------------- /galaxy.yml: -------------------------------------------------------------------------------- 1 | namespace: "cyberark" 2 | name: "pas" 3 | version: "1.0.35" 4 | readme: README.md 5 | authors: 6 | - CyberArk Business Development (@cyberark-bizdev) 7 | - Edward Nunez (@enunez-cyberark) 8 | - Joe Garcia (@infamousjoeg) 9 | description: "This is a Collection of the CyberArk Ansible Security Automation toolkit." 10 | license: ["MIT"] 11 | tags: 12 | - cyberark 13 | - access 14 | - security 15 | - account 16 | - epv 17 | - vault 18 | - identity 19 | - credential 20 | - secret 21 | - privileged 22 | repository: "https://github.com/cyberark/ansible-security-automation-collection" 23 | issues: https://github.com/cyberark/ansible-security-automation-collection/issues 24 | -------------------------------------------------------------------------------- /meta/runtime.yml: -------------------------------------------------------------------------------- 1 | --- 2 | requires_ansible: '>=2.15.0' 3 | -------------------------------------------------------------------------------- /plugins/modules/cyberark_account.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright: (c) 2017, Ansible Project 3 | # GNU General Public License v3.0+ 4 | # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | from __future__ import (absolute_import, division, print_function) 6 | 7 | 8 | __metaclass__ = type 9 | 10 | ANSIBLE_METADATA = { 11 | "metadata_version": "1.1", 12 | "status": ["preview"], 13 | "supported_by": "community", 14 | } 15 | 16 | DOCUMENTATION = """ 17 | --- 18 | module: cyberark_account 19 | short_description: Module for CyberArk Account object creation, deletion, 20 | modification, and password retrieval using PAS Web Services SDK. 21 | author: 22 | - CyberArk BizDev (@cyberark-bizdev) 23 | - Edward Nunez (@enunez-cyberark) 24 | - James Stutes (@jimmyjamcabd) 25 | version_added: '1.0.0' 26 | description: 27 | - Creates a URI for adding, deleting, modifying, and retrieving a privileged credential 28 | within the Cyberark Vault. The request uses the Privileged Account 29 | Security Web Services SDK. 30 | 31 | 32 | options: 33 | state: 34 | description: 35 | - Assert the desired state of the account C(present) to create or 36 | update and account object. Set to C(absent) for deletion of an 37 | account object. Set to C(retrieve) to get the account object including the password. 38 | required: false 39 | default: present 40 | choices: [present, absent, retrieve] 41 | type: str 42 | logging_level: 43 | description: 44 | - Parameter used to define the level of troubleshooting output to 45 | the C(logging_file) value. 46 | required: false 47 | choices: [NOTSET, DEBUG, INFO] 48 | type: str 49 | logging_file: 50 | description: 51 | - Setting the log file name and location for troubleshooting logs. 52 | required: false 53 | default: /tmp/ansible_cyberark.log 54 | type: str 55 | api_base_url: 56 | description: 57 | - A string containing the base URL of the server hosting CyberArk's 58 | Privileged Account Security Web Services SDK. 59 | - Example U(https:///PasswordVault/api/) 60 | required: false 61 | type: str 62 | validate_certs: 63 | description: 64 | - If C(false), SSL certificate chain will not be validated. This 65 | should only set to C(true) if you have a root CA certificate 66 | installed on each node. 67 | required: false 68 | default: true 69 | type: bool 70 | cyberark_session: 71 | description: 72 | - Dictionary set by a CyberArk authentication containing the 73 | different values to perform actions on a logged-on CyberArk 74 | session, please see M(cyberark.pas.cyberark_authentication) module for an 75 | example of cyberark_session. 76 | required: true 77 | type: dict 78 | identified_by: 79 | description: 80 | - When an API call is made to Get Accounts, often times the default 81 | parameters passed will identify more than one account. This 82 | parameter is used to confidently identify a single account when 83 | the default query can return multiple results. 84 | required: false 85 | default: username,address,platform_id 86 | type: str 87 | safe: 88 | description: 89 | - The safe in the Vault where the privileged account is to be 90 | located. 91 | required: true 92 | type: str 93 | platform_id: 94 | description: 95 | - The PolicyID of the Platform that is to be managing the account 96 | required: false 97 | type: str 98 | address: 99 | description: 100 | - The address of the endpoint where the privileged account is 101 | located. 102 | required: false 103 | type: str 104 | name: 105 | description: 106 | - The ObjectID of the account 107 | required: false 108 | type: str 109 | secret_type: 110 | description: 111 | - The value that identifies what type of account it will be. 112 | required: false 113 | default: password 114 | choices: [password, key] 115 | type: str 116 | secret: 117 | description: 118 | - The initial password for the creation of the account 119 | required: false 120 | type: str 121 | new_secret: 122 | description: 123 | - The new secret/password to be stored in CyberArk Vault. 124 | type: str 125 | username: 126 | description: 127 | - The username associated with the account. 128 | required: false 129 | type: str 130 | secret_management: 131 | description: 132 | - Set of parameters associated with the management of the 133 | credential. 134 | required: false 135 | type: dict 136 | suboptions: 137 | automatic_management_enabled: 138 | description: 139 | - Parameter that indicates whether the CPM will manage 140 | the password or not. 141 | default: false 142 | type: bool 143 | manual_management_reason: 144 | description: 145 | - String value indicating why the CPM will NOT manage 146 | the password. 147 | type: str 148 | management_action: 149 | description: 150 | - CPM action flag to be placed on the account object 151 | for credential rotation. 152 | choices: [change, change_immediately, reconcile] 153 | type: str 154 | new_secret: 155 | description: 156 | - The actual password value that will be assigned for 157 | the CPM action to be taken. 158 | type: str 159 | perform_management_action: 160 | description: 161 | - C(always) will perform the management action in 162 | every action. 163 | - C(on_create) will only perform the management action 164 | right after the account is created. 165 | choices: [always, on_create] 166 | default: always 167 | type: str 168 | remote_machines_access: 169 | description: 170 | - Set of parameters for defining PSM endpoint access targets. 171 | required: false 172 | type: dict 173 | suboptions: 174 | remote_machines: 175 | description: 176 | - List of targets allowed for this account. 177 | type: str 178 | access_restricted_to_remote_machines: 179 | description: 180 | - Whether or not to restrict access only to specified 181 | remote machines. 182 | type: bool 183 | platform_account_properties: 184 | description: 185 | - Object containing key-value pairs to associate with the account, 186 | as defined by the account platform. These properties are 187 | validated against the mandatory and optional properties of the 188 | specified platform's definition. Optional properties that do not 189 | exist on the account will not be returned here. Internal 190 | properties are not returned. 191 | required: false 192 | type: dict 193 | suboptions: 194 | KEY: 195 | description: 196 | - Freeform key value associated to the mandatory or 197 | optional property assigned to the specified 198 | Platform's definition. 199 | aliases: [Port, ExtrPass1Name, database] 200 | type: str 201 | """ 202 | 203 | EXAMPLES = """ 204 | - name: Logon to CyberArk Vault using PAS Web Services SDK 205 | cyberark.pas.cyberark_authentication: 206 | api_base_url: "http://components.cyberark.local" 207 | validate_certs: false 208 | username: "bizdev" 209 | password: "Cyberark1" 210 | 211 | - name: Creating an Account using the PAS WebServices SDK 212 | cyberark.pas.cyberark_account: 213 | logging_level: DEBUG 214 | identified_by: "address,username" 215 | safe: "Test" 216 | address: "cyberark.local" 217 | username: "administrator-x" 218 | platform_id: WinServerLocal 219 | secret: "@N&Ibl3!" 220 | platform_account_properties: 221 | LogonDomain: "cyberark" 222 | OwnerName: "ansible_user" 223 | secret_management: 224 | automatic_management_enabled: true 225 | state: present 226 | cyberark_session: "{{ cyberark_session }}" 227 | register: cyberarkaction 228 | 229 | - name: Rotate credential via reconcile and providing the password to be changed to 230 | cyberark.pas.cyberark_account: 231 | identified_by: "address,username" 232 | safe: "Domain_Admins" 233 | address: "prod.cyberark.local" 234 | username: "admin" 235 | platform_id: WinDomain 236 | platform_account_properties: 237 | LogonDomain: "PROD" 238 | secret_management: 239 | new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" 240 | management_action: "reconcile" 241 | automatic_management_enabled: true 242 | state: present 243 | cyberark_session: "{{ cyberark_session }}" 244 | register: reconcileaccount 245 | 246 | - name: Update password only in VAULT 247 | cyberark.pas.cyberark_account: 248 | identified_by: "address,username" 249 | safe: "Domain_Admins" 250 | address: "prod.cyberark.local" 251 | username: "admin" 252 | platform_id: Generic 253 | new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" 254 | state: present 255 | cyberark_session: "{{ cyberark_session }}" 256 | register: updateaccount 257 | 258 | - name: Retrieve account and password 259 | cyberark.pas.cyberark_account: 260 | identified_by: "address,username" 261 | safe: "Domain_Admins" 262 | address: "prod.cyberark.local" 263 | username: "admin" 264 | state: retrieve 265 | cyberark_session: "{{ cyberark_session }}" 266 | register: retrieveaccount 267 | 268 | - name: Logoff from CyberArk Vault 269 | cyberark.pas.cyberark_authentication: 270 | state: absent 271 | cyberark_session: "{{ cyberark_session }}" 272 | """ 273 | 274 | RETURN = """ 275 | changed: 276 | description: 277 | - Identify if the playbook run resulted in a change to the account in 278 | any way. 279 | returned: always 280 | type: bool 281 | failed: 282 | description: Whether playbook run resulted in a failure of any kind. 283 | returned: always 284 | type: bool 285 | status_code: 286 | description: Result HTTP Status code. 287 | returned: success 288 | type: int 289 | sample: "200, 201, -1, 204" 290 | result: 291 | description: A json dump of the resulting action. 292 | returned: success 293 | type: complex 294 | contains: 295 | address: 296 | description: 297 | - The adress of the endpoint where the privileged account is 298 | located. 299 | returned: successful addition and modification 300 | type: str 301 | sample: dev.local 302 | createdTime: 303 | description: 304 | - Timeframe calculation of the timestamp of account creation. 305 | returned: successful addition and modification 306 | type: int 307 | sample: "1567824520" 308 | id: 309 | description: Internal ObjectID for the account object identified 310 | returned: successful addition and modification 311 | type: int 312 | sample: "25_21" 313 | name: 314 | description: The external ObjectID of the account 315 | returned: successful addition and modification 316 | type: str 317 | sample: 318 | - Operating System-WinServerLocal-cyberark.local-administrator 319 | platformAccountProperties: 320 | description: 321 | - Object containing key-value pairs to associate with the 322 | account, as defined by the account platform. 323 | returned: successful addition and modification 324 | type: complex 325 | contains: 326 | KEY VALUE: 327 | description: 328 | - Object containing key-value pairs to associate with the 329 | account, as defined by the account platform. 330 | returned: successful addition and modification 331 | type: str 332 | sample: 333 | - "LogonDomain": "cyberark" 334 | - "Port": "22" 335 | platformId: 336 | description: 337 | - The PolicyID of the Platform that is to be managing the 338 | account. 339 | returned: successful addition and modification 340 | type: str 341 | sample: WinServerLocal 342 | safeName: 343 | description: 344 | - The safe in the Vault where the privileged account is to 345 | be located. 346 | returned: successful addition and modification 347 | type: str 348 | sample: Domain_Admins 349 | secretManagement: 350 | description: 351 | - Set of parameters associated with the management of 352 | the credential. 353 | returned: successful addition and modification 354 | type: complex 355 | contains: 356 | automaticManagementEnabled: 357 | description: 358 | - Parameter that indicates whether the CPM will manage 359 | the password or not. 360 | returned: successful addition and modification 361 | type: bool 362 | lastModifiedTime: 363 | description: 364 | - Timeframe calculation of the timestamp of account 365 | modification. 366 | returned: successful addition and modification 367 | type: int 368 | sample: "1567824520" 369 | manualManagementReason: 370 | description: 371 | - Reason for disabling automatic management of the account 372 | returned: if C(automaticManagementEnabled) is set to false 373 | type: str 374 | sample: This is a static account 375 | secretType: 376 | description: 377 | - The value that identifies what type of account it will be 378 | returned: successful addition and modification 379 | type: list 380 | sample: 381 | - key 382 | - password 383 | userName: 384 | description: The username associated with the account 385 | returned: successful addition and modification 386 | type: str 387 | sample: administrator 388 | """ 389 | 390 | 391 | from ansible.module_utils._text import to_text 392 | from ansible.module_utils.basic import AnsibleModule 393 | from ansible.module_utils.urls import open_url 394 | from ansible.module_utils.six.moves.urllib.error import HTTPError 395 | from ansible.module_utils.six.moves.urllib.parse import quote 396 | from ansible.module_utils.six.moves.http_client import HTTPException 397 | import json 398 | import logging 399 | 400 | _empty = object() 401 | 402 | ansible_specific_parameters = [ 403 | "state", 404 | "api_base_url", 405 | "validate_certs", 406 | "cyberark_session", 407 | "identified_by", 408 | "logging_level", 409 | "logging_file", 410 | "new_secret", 411 | "secret_management.management_action", 412 | "secret_management.new_secret", 413 | "management_action", 414 | "secret_management.perform_management_action", 415 | ] 416 | 417 | cyberark_fixed_properties = [ 418 | "createdTime", 419 | "id", 420 | "name", 421 | "lastModifiedTime", 422 | "safeName", 423 | "secretType", 424 | "secret", 425 | ] 426 | 427 | removal_value = "NO_VALUE" 428 | 429 | cyberark_reference_fieldnames = { 430 | "username": "userName", 431 | "safe": "safeName", 432 | "platform_id": "platformId", 433 | "secret_type": "secretType", 434 | "platform_account_properties": "platformAccountProperties", 435 | "secret_management": "secretManagement", 436 | "manual_management_reason": "manualManagementReason", 437 | "automatic_management_enabled": "automaticManagementEnabled", 438 | "remote_machines_access": "remoteMachinesAccess", 439 | "access_restricted_to_remote_machines": "accessRestrictedToRemoteMachines", 440 | "remote_machines": "remoteMachines", 441 | } 442 | 443 | ansible_reference_fieldnames = { 444 | "userName": "username", 445 | "safeName": "safe", 446 | "platformId": "platform_id", 447 | "secretType": "secret_type", 448 | "platformAccountProperties": "platform_account_properties", 449 | "secretManagement": "secret_management", 450 | "manualManagementReason": "manual_management_reason", 451 | "automaticManagementEnabled": "automatic_management_enabled", 452 | "remoteMachinesAccess": "remote_machines_access", 453 | "accessRestrictedToRemoteMachines": "access_testricted_to_remoteMachines", 454 | "remoteMachines": "remote_machines", 455 | } 456 | 457 | 458 | def equal_value(existing, parameter): 459 | if isinstance(existing, str): 460 | return existing == str(parameter) 461 | elif isinstance(parameter, str): 462 | return str(existing) == parameter 463 | else: 464 | return existing == parameter 465 | 466 | 467 | def update_account(module, existing_account): 468 | 469 | logging.debug("Updating Account") 470 | 471 | cyberark_session = module.params["cyberark_session"] 472 | api_base_url = cyberark_session["api_base_url"] 473 | validate_certs = cyberark_session["validate_certs"] 474 | 475 | # Prepare result, end_point, and headers 476 | result = {"result": existing_account} 477 | changed = False 478 | last_status_code = -1 479 | 480 | HTTPMethod = "PATCH" 481 | end_point = "/PasswordVault/api/Accounts/%s" % existing_account["id"] 482 | 483 | headers = { 484 | "Content-Type": "application/json", 485 | "Authorization": cyberark_session["token"], 486 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 487 | } 488 | 489 | payload = {"Operations": []} 490 | 491 | # Determining whether to add or update properties 492 | for parameter_name in list(module.params.keys()): 493 | if ( 494 | parameter_name not in ansible_specific_parameters 495 | and module.params[parameter_name] is not None 496 | ): 497 | module_parm_value = module.params[parameter_name] 498 | cyberark_property_name = referenced_value( 499 | parameter_name, cyberark_reference_fieldnames, default=parameter_name 500 | ) 501 | existing_account_value = referenced_value( 502 | cyberark_property_name, 503 | existing_account, 504 | keys=list(existing_account.keys()), 505 | ) 506 | if cyberark_property_name not in cyberark_fixed_properties: 507 | if module_parm_value is not None and isinstance( 508 | module_parm_value, dict 509 | ): 510 | # Internal child values 511 | replacing = {} 512 | adding = {} 513 | removing = {} 514 | for child_parm_name in list(module_parm_value.keys()): 515 | nested_parm_name = "%s.%s" % (parameter_name, child_parm_name) 516 | if nested_parm_name not in ansible_specific_parameters: 517 | child_module_parm_value = module_parm_value[child_parm_name] 518 | child_cyberark_property_name = referenced_value( 519 | child_parm_name, 520 | cyberark_reference_fieldnames, 521 | default=child_parm_name, 522 | ) 523 | child_existing_account_value = referenced_value( 524 | child_cyberark_property_name, 525 | existing_account_value, 526 | list(existing_account_value.keys()) 527 | if existing_account_value is not None 528 | else {}, 529 | ) 530 | path_value = "/%s/%s" % ( 531 | cyberark_property_name, 532 | child_cyberark_property_name, 533 | ) 534 | if child_existing_account_value is not None: 535 | logging.debug( 536 | ( 537 | "child_module_parm_value: %s " 538 | "child_existing_account_value=%s path=%s" 539 | ), 540 | child_module_parm_value, 541 | child_existing_account_value, 542 | path_value 543 | ) 544 | if child_module_parm_value == removal_value: 545 | removing.update( 546 | { 547 | child_cyberark_property_name: child_existing_account_value 548 | } 549 | ) 550 | elif ( 551 | child_module_parm_value is not None 552 | and not equal_value( 553 | child_existing_account_value, 554 | child_module_parm_value, 555 | ) 556 | ): 557 | # Updating a property 558 | replacing.update( 559 | { 560 | child_cyberark_property_name: child_module_parm_value 561 | } 562 | ) 563 | elif ( 564 | child_module_parm_value is not None 565 | and child_module_parm_value != removal_value 566 | ): 567 | # Adding a property value 568 | adding.update( 569 | { 570 | child_cyberark_property_name: child_module_parm_value 571 | } 572 | ) 573 | logging.debug( 574 | "parameter_name=%s value=%s existing=%s", 575 | path_value, 576 | child_module_parm_value, 577 | child_existing_account_value 578 | ) 579 | # Processing child operations 580 | if len(list(adding.keys())) > 0: 581 | payload["Operations"].append( 582 | { 583 | "op": "add", 584 | "path": "/%s" % cyberark_property_name, 585 | "value": adding, 586 | } 587 | ) 588 | if len(list(replacing.keys())) > 0: 589 | payload["Operations"].append( 590 | { 591 | "op": "replace", 592 | "path": "/%s" % cyberark_property_name, 593 | "value": replacing, 594 | } 595 | ) 596 | if len(removing) > 0: 597 | payload["Operations"].append( 598 | { 599 | "op": "remove", 600 | "path": "/%s" % cyberark_property_name, 601 | "value": removing, 602 | } 603 | ) 604 | else: 605 | if existing_account_value is not None: 606 | if module_parm_value == removal_value: 607 | payload["Operations"].append( 608 | {"op": "remove", "path": "/%s" % cyberark_property_name} 609 | ) 610 | elif not equal_value(existing_account_value, module_parm_value): 611 | # Updating a property 612 | payload["Operations"].append( 613 | { 614 | "op": "replace", 615 | "value": module_parm_value, 616 | "path": "/%s" % cyberark_property_name, 617 | } 618 | ) 619 | elif module_parm_value != removal_value: 620 | # Adding a property value 621 | payload["Operations"].append( 622 | { 623 | "op": "add", 624 | "value": module_parm_value, 625 | "path": "/%s" % cyberark_property_name, 626 | } 627 | ) 628 | logging.debug( 629 | "parameter_name=%s value=%s existing=%s", 630 | parameter_name, module_parm_value, existing_account_value 631 | ) 632 | 633 | if len(payload["Operations"]) != 0: 634 | if module.check_mode: 635 | logging.debug("Proceeding with Update Account (CHECK_MODE)") 636 | logging.debug("Operations => %s", json.dumps(payload)) 637 | result = {"result": existing_account} 638 | changed = True 639 | last_status_code = -1 640 | else: 641 | logging.debug("Proceeding with Update Account") 642 | 643 | logging.debug( 644 | "Processing invidual operations (%d) => %s", 645 | len(payload["Operations"]), 646 | json.dumps(payload), 647 | ) 648 | for operation in payload["Operations"]: 649 | individual_payload = [operation] 650 | try: 651 | logging.debug(" ==> %s", json.dumps([operation])) 652 | response = open_url( 653 | api_base_url + end_point, 654 | method=HTTPMethod, 655 | headers=headers, 656 | data=json.dumps(individual_payload), 657 | validate_certs=validate_certs, 658 | ) 659 | 660 | result = {"result": json.loads(response.read())} 661 | changed = True 662 | last_status_code = response.getcode() 663 | 664 | # return (True, result, response.getcode()) 665 | 666 | except (HTTPError, HTTPException) as http_exception: 667 | 668 | if isinstance(http_exception, HTTPError): 669 | res = json.load(http_exception) 670 | else: 671 | res = to_text(http_exception) 672 | 673 | module.fail_json( 674 | msg=( 675 | "Error while performing update_account." 676 | "Please validate parameters provided." 677 | "\n*** end_point=%s%s\n ==> %s" 678 | % (api_base_url, end_point, res) 679 | ), 680 | payload=individual_payload, 681 | headers=headers, 682 | status_code=http_exception.code, 683 | ) 684 | 685 | except Exception as unknown_exception: 686 | 687 | module.fail_json( 688 | msg=( 689 | "Unknown error while performing update_account." 690 | "\n*** end_point=%s%s\n%s" 691 | % (api_base_url, end_point, to_text(unknown_exception)) 692 | ), 693 | payload=individual_payload, 694 | headers=headers, 695 | status_code=-1, 696 | ) 697 | 698 | return (changed, result, last_status_code) 699 | 700 | 701 | def add_account(module): 702 | 703 | logging.debug("Adding Account") 704 | 705 | cyberark_session = module.params["cyberark_session"] 706 | api_base_url = cyberark_session["api_base_url"] 707 | validate_certs = cyberark_session["validate_certs"] 708 | 709 | # Prepare result, end_point, and headers 710 | result = {} 711 | HTTPMethod = "POST" 712 | end_point = "/PasswordVault/api/Accounts" 713 | 714 | headers = { 715 | "Content-Type": "application/json", 716 | "Authorization": cyberark_session["token"], 717 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 718 | } 719 | 720 | payload = {"safeName": module.params["safe"]} 721 | 722 | for parameter_name in list(module.params.keys()): 723 | if ( 724 | parameter_name not in ansible_specific_parameters 725 | and module.params[parameter_name] is not None 726 | ): 727 | cyberark_property_name = referenced_value( 728 | parameter_name, cyberark_reference_fieldnames, default=parameter_name 729 | ) 730 | if isinstance(module.params[parameter_name], dict): 731 | payload[cyberark_property_name] = {} 732 | for dict_key in list(module.params[parameter_name].keys()): 733 | cyberark_child_property_name = referenced_value( 734 | dict_key, cyberark_reference_fieldnames, default=dict_key 735 | ) 736 | logging.debug( 737 | ( 738 | "parameter_name =%s.%s cyberark_property_name=%s " 739 | "cyberark_child_property_name=%s" 740 | ), 741 | parameter_name, 742 | dict_key, 743 | cyberark_property_name, 744 | cyberark_child_property_name, 745 | ) 746 | if ( 747 | parameter_name + "." + dict_key 748 | not in ansible_specific_parameters 749 | and module.params[parameter_name][dict_key] is not None 750 | ): 751 | payload[cyberark_property_name][ 752 | cyberark_child_property_name 753 | ] = deep_get( 754 | module.params[parameter_name], dict_key, _empty, False 755 | ) 756 | else: 757 | if parameter_name not in cyberark_reference_fieldnames: 758 | module_parm_value = deep_get( 759 | module.params, parameter_name, _empty, False 760 | ) 761 | if ( 762 | module_parm_value is not None 763 | and module_parm_value != removal_value 764 | ): 765 | payload[ 766 | parameter_name 767 | ] = module_parm_value # module.params[parameter_name] 768 | else: 769 | module_parm_value = deep_get( 770 | module.params, parameter_name, _empty, True 771 | ) 772 | if ( 773 | module_parm_value is not None 774 | and module_parm_value != removal_value 775 | ): 776 | payload[ 777 | cyberark_reference_fieldnames[parameter_name] 778 | ] = module_parm_value # module.params[parameter_name] 779 | logging.debug("parameter_name =%s", parameter_name) 780 | 781 | logging.debug("Add Account Payload => %s", json.dumps(payload)) 782 | 783 | try: 784 | 785 | if module.check_mode: 786 | logging.debug("Proceeding with Add Account (CHECK_MODE)") 787 | return (True, {"result": None}, -1) 788 | else: 789 | logging.debug("Proceeding with Add Account") 790 | response = open_url( 791 | api_base_url + end_point, 792 | method=HTTPMethod, 793 | headers=headers, 794 | data=json.dumps(payload), 795 | validate_certs=validate_certs, 796 | ) 797 | 798 | result = {"result": json.loads(response.read())} 799 | 800 | return (True, result, response.getcode()) 801 | 802 | except (HTTPError, HTTPException) as http_exception: 803 | 804 | if isinstance(http_exception, HTTPError): 805 | res = json.load(http_exception) 806 | else: 807 | res = to_text(http_exception) 808 | 809 | module.fail_json( 810 | msg=( 811 | "Error while performing add_account." 812 | "Please validate parameters provided." 813 | "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, res) 814 | ), 815 | payload=payload, 816 | headers=headers, 817 | status_code=http_exception.code, 818 | ) 819 | 820 | except Exception as unknown_exception: 821 | 822 | module.fail_json( 823 | msg=( 824 | "Unknown error while performing add_account." 825 | "\n*** end_point=%s%s\n%s" 826 | % (api_base_url, end_point, to_text(unknown_exception)) 827 | ), 828 | payload=payload, 829 | headers=headers, 830 | status_code=-1, 831 | ) 832 | 833 | 834 | def delete_account(module, existing_account): 835 | 836 | if module.check_mode: 837 | logging.debug("Deleting Account (CHECK_MODE)") 838 | return (True, {"result": None}, -1) 839 | else: 840 | logging.debug("Deleting Account") 841 | 842 | cyberark_session = module.params["cyberark_session"] 843 | api_base_url = cyberark_session["api_base_url"] 844 | validate_certs = cyberark_session["validate_certs"] 845 | 846 | # Prepare result, end_point, and headers 847 | result = {} 848 | HTTPMethod = "DELETE" 849 | end_point = "/PasswordVault/api/Accounts/%s" % existing_account["id"] 850 | 851 | headers = { 852 | "Content-Type": "application/json", 853 | "Authorization": cyberark_session["token"], 854 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 855 | } 856 | 857 | try: 858 | 859 | response = open_url( 860 | api_base_url + end_point, 861 | method=HTTPMethod, 862 | headers=headers, 863 | validate_certs=validate_certs, 864 | ) 865 | 866 | result = {"result": None} 867 | 868 | return (True, result, response.getcode()) 869 | 870 | except (HTTPError, HTTPException) as http_exception: 871 | 872 | if isinstance(http_exception, HTTPError): 873 | res = json.load(http_exception) 874 | else: 875 | res = to_text(http_exception) 876 | 877 | module.fail_json( 878 | msg=( 879 | "Error while performing delete_account." 880 | "Please validate parameters provided." 881 | "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, res) 882 | ), 883 | headers=headers, 884 | status_code=http_exception.code, 885 | ) 886 | 887 | except Exception as unknown_exception: 888 | 889 | module.fail_json( 890 | msg=( 891 | "Unknown error while performing delete_account." 892 | "\n*** end_point=%s%s\n%s" 893 | % (api_base_url, end_point, to_text(unknown_exception)) 894 | ), 895 | headers=headers, 896 | status_code=-1, 897 | ) 898 | 899 | 900 | def reset_account_if_needed(module, existing_account): 901 | 902 | cyberark_session = module.params["cyberark_session"] 903 | api_base_url = cyberark_session["api_base_url"] 904 | validate_certs = cyberark_session["validate_certs"] 905 | 906 | # Credential changes 907 | management_action = deep_get( 908 | module.params, "secret_management.management_action", "NOT_FOUND", False 909 | ) 910 | cpm_new_secret = deep_get( 911 | module.params, "secret_management.new_secret", "NOT_FOUND", False 912 | ) 913 | logging.debug( 914 | "management_action: %s cpm_new_secret: %s", management_action, cpm_new_secret 915 | ) 916 | 917 | # Prepare result, end_point, and headers 918 | result = {} 919 | end_point = None 920 | payload = {} 921 | existing_account_id = None 922 | if existing_account is not None: 923 | existing_account_id = existing_account["id"] 924 | elif module.check_mode: 925 | existing_account_id = 9999 926 | 927 | if ( 928 | management_action == "change" 929 | and cpm_new_secret is not None 930 | and cpm_new_secret != "NOT_FOUND" 931 | ): 932 | logging.debug("CPM change secret for next CPM cycle") 933 | end_point = ( 934 | "/PasswordVault/API/Accounts/%s/SetNextPassword" 935 | ) % existing_account_id 936 | payload["ChangeImmediately"] = False 937 | payload["NewCredentials"] = cpm_new_secret 938 | elif management_action == "change_immediately" and ( 939 | cpm_new_secret == "NOT_FOUND" or cpm_new_secret is None 940 | ): 941 | logging.debug("CPM change_immediately with random secret") 942 | end_point = ("/PasswordVault/API/Accounts/%s/Change") % existing_account_id 943 | payload["ChangeEntireGroup"] = True 944 | elif management_action == "change_immediately" and ( 945 | cpm_new_secret is not None and cpm_new_secret != "NOT_FOUND" 946 | ): 947 | logging.debug("CPM change immediately secret for next CPM cycle") 948 | end_point = ( 949 | "/PasswordVault/API/Accounts/%s/SetNextPassword" 950 | ) % existing_account_id 951 | payload["ChangeImmediately"] = True 952 | payload["NewCredentials"] = cpm_new_secret 953 | elif management_action == "reconcile": 954 | logging.debug("CPM reconcile secret") 955 | end_point = ("/PasswordVault/API/Accounts/%s/Reconcile") % existing_account_id 956 | elif ( 957 | "new_secret" in list(module.params.keys()) 958 | and module.params["new_secret"] is not None 959 | ): 960 | logging.debug("Change Credential in Vault") 961 | end_point = ( 962 | "/PasswordVault/API/Accounts/%s/Password/Update" 963 | ) % existing_account_id 964 | payload["ChangeEntireGroup"] = True 965 | payload["NewCredentials"] = module.params["new_secret"] 966 | 967 | if end_point is not None: 968 | 969 | if module.check_mode: 970 | logging.debug("Proceeding with Credential Rotation (CHECK_MODE)") 971 | return (True, result, -1) 972 | else: 973 | logging.debug("Proceeding with Credential Rotation") 974 | 975 | result = {"result": None} 976 | headers = { 977 | "Content-Type": "application/json", 978 | "Authorization": cyberark_session["token"], 979 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 980 | } 981 | HTTPMethod = "POST" 982 | try: 983 | 984 | response = open_url( 985 | api_base_url + end_point, 986 | method=HTTPMethod, 987 | headers=headers, 988 | data=json.dumps(payload), 989 | validate_certs=validate_certs, 990 | ) 991 | 992 | return (True, result, response.getcode()) 993 | 994 | except (HTTPError, HTTPException) as http_exception: 995 | 996 | if isinstance(http_exception, HTTPError): 997 | res = json.load(http_exception) 998 | else: 999 | res = to_text(http_exception) 1000 | 1001 | module.fail_json( 1002 | msg=( 1003 | "Error while performing reset_account." 1004 | "Please validate parameters provided." 1005 | "\n*** end_point=%s%s\n ==> %s" 1006 | ) 1007 | % (api_base_url, end_point, res), 1008 | headers=headers, 1009 | payload=payload, 1010 | status_code=http_exception.code, 1011 | ) 1012 | 1013 | except Exception as unknown_exception: 1014 | 1015 | module.fail_json( 1016 | msg=( 1017 | "Unknown error while performing delete_account." 1018 | "\n*** end_point=%s%s\n%s" 1019 | % (api_base_url, end_point, to_text(unknown_exception)) 1020 | ), 1021 | headers=headers, 1022 | payload=payload, 1023 | status_code=-1, 1024 | ) 1025 | 1026 | else: 1027 | return (False, result, -1) 1028 | 1029 | 1030 | def referenced_value(field, dct, keys=None, default=None): 1031 | return dct[field] if field in (keys if keys is not None else dct) else default 1032 | 1033 | 1034 | def deep_get(dct, dotted_path, default=_empty, use_reference_table=True): 1035 | result_dct = {} 1036 | for key in dotted_path.split("."): 1037 | try: 1038 | key_field = key 1039 | if use_reference_table: 1040 | key_field = referenced_value( 1041 | key, cyberark_reference_fieldnames, default=key 1042 | ) 1043 | 1044 | if len(list(result_dct.keys())) == 0: # No result_dct set yet 1045 | result_dct = dct 1046 | 1047 | logging.debug( 1048 | "keys=%s key_field=>%s key=>%s", 1049 | ",".join(list(result_dct.keys())), 1050 | key_field, 1051 | key, 1052 | ) 1053 | result_dct = ( 1054 | result_dct[key_field] 1055 | if key_field in list(result_dct.keys()) 1056 | else result_dct[key] 1057 | ) 1058 | if result_dct is None: 1059 | return default 1060 | 1061 | except KeyError as e: 1062 | logging.debug("KeyError %s", to_text(e)) 1063 | if default is _empty: 1064 | raise 1065 | return default 1066 | return result_dct 1067 | 1068 | 1069 | def get_account(module): 1070 | 1071 | logging.debug("Finding Account") 1072 | 1073 | identified_by_fields = module.params["identified_by"].split(",") 1074 | logging.debug("Identified_by: %s", json.dumps(identified_by_fields)) 1075 | safe_filter = ( 1076 | quote("safeName eq ") + quote(module.params["safe"]) 1077 | if "safe" in module.params and module.params["safe"] is not None 1078 | else None 1079 | ) 1080 | search_string = None 1081 | for field in identified_by_fields: 1082 | if field not in ansible_specific_parameters: 1083 | search_string = "%s%s" % ( 1084 | search_string + " " if search_string is not None else "", 1085 | deep_get(module.params, field, "NOT FOUND", False), 1086 | ) 1087 | 1088 | logging.debug("Search_String => %s", search_string) 1089 | logging.debug("Safe Filter => %s", safe_filter) 1090 | 1091 | cyberark_session = module.params["cyberark_session"] 1092 | api_base_url = cyberark_session["api_base_url"] 1093 | validate_certs = cyberark_session["validate_certs"] 1094 | 1095 | end_point = None 1096 | if search_string is not None and safe_filter is not None: 1097 | end_point = "/PasswordVault/api/accounts?filter=%s&search=%s" % ( 1098 | safe_filter, 1099 | quote(search_string.lstrip()), 1100 | ) 1101 | elif search_string is not None: 1102 | end_point = ("/PasswordVault/api/accounts?search=%s") % (search_string.lstrip()) 1103 | else: 1104 | end_point = "/PasswordVault/api/accounts?filter=%s" % (safe_filter) 1105 | 1106 | logging.debug("End Point => %s", end_point) 1107 | 1108 | headers = { 1109 | "Content-Type": "application/json", 1110 | "Authorization": cyberark_session["token"], 1111 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 1112 | } 1113 | 1114 | try: 1115 | 1116 | logging.debug("Executing: " + api_base_url + end_point) 1117 | response = open_url( 1118 | api_base_url + end_point, 1119 | method="GET", 1120 | headers=headers, 1121 | validate_certs=validate_certs, 1122 | ) 1123 | 1124 | result_string = response.read() 1125 | accounts_data = json.loads(result_string) 1126 | 1127 | logging.debug("RESULT => %s", json.dumps(accounts_data)) 1128 | 1129 | if accounts_data["count"] == 0: 1130 | return (False, None, response.getcode()) 1131 | else: 1132 | how_many = 0 1133 | first_record_found = None 1134 | for account_record in accounts_data["value"]: 1135 | logging.debug("Acct Record => %s", json.dumps(account_record)) 1136 | found = False 1137 | for field in identified_by_fields: 1138 | record_field_value = deep_get(account_record, field, "NOT FOUND") 1139 | logging.debug( 1140 | ( 1141 | "Comparing field %s | record_field_name=%s " 1142 | "record_field_value=%s module.params_value=%s" 1143 | ), 1144 | field, 1145 | field, 1146 | record_field_value, 1147 | deep_get(module.params, field, "NOT FOUND"), 1148 | ) 1149 | if record_field_value != "NOT FOUND" and ( 1150 | record_field_value 1151 | == deep_get(module.params, field, "NOT FOUND", False) 1152 | ): 1153 | found = True 1154 | else: 1155 | found = False 1156 | break 1157 | if found: 1158 | how_many = how_many + 1 1159 | if first_record_found is None: 1160 | first_record_found = account_record 1161 | 1162 | logging.debug( 1163 | "How Many: %d First Record Found => %s", 1164 | how_many, 1165 | json.dumps(first_record_found), 1166 | ) 1167 | if how_many > 1: # too many records found 1168 | module.fail_json( 1169 | msg=( 1170 | "Error while performing get_account. " 1171 | "Too many rows (%d) found matching your criteria!" 1172 | ) 1173 | % how_many 1174 | ) 1175 | else: 1176 | return (how_many == 1, first_record_found, response.getcode()) 1177 | 1178 | except (HTTPError, HTTPException) as http_exception: 1179 | 1180 | if http_exception.code == 404: 1181 | return (False, None, http_exception.code) 1182 | else: 1183 | module.fail_json( 1184 | msg=( 1185 | "Error while performing get_account." 1186 | "Please validate parameters provided." 1187 | "\n*** end_point=%s%s\n ==> %s" 1188 | % (api_base_url, end_point, to_text(http_exception)) 1189 | ), 1190 | headers=headers, 1191 | status_code=http_exception.code, 1192 | ) 1193 | 1194 | except Exception as unknown_exception: 1195 | 1196 | module.fail_json( 1197 | msg=( 1198 | "Unknown error while performing get_account." 1199 | "\n*** end_point=%s%s\n%s" 1200 | % (api_base_url, end_point, to_text(unknown_exception)) 1201 | ), 1202 | headers=headers, 1203 | status_code=-1, 1204 | ) 1205 | 1206 | 1207 | def retrieve_password(module, existing_account): 1208 | logging.debug("Retrieving Password") 1209 | 1210 | cyberark_session = module.params["cyberark_session"] 1211 | api_base_url = cyberark_session["api_base_url"] 1212 | validate_certs = cyberark_session["validate_certs"] 1213 | 1214 | result = existing_account 1215 | HTTPMethod = "POST" 1216 | end_point = "/PasswordVault/api/Accounts/%s/Password/Retrieve" % existing_account["id"] 1217 | 1218 | headers = { 1219 | "Content-Type": "application/json", 1220 | "Authorization": cyberark_session["token"], 1221 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 1222 | } 1223 | 1224 | try: 1225 | 1226 | response = open_url( 1227 | api_base_url + end_point, 1228 | method=HTTPMethod, 1229 | headers=headers, 1230 | validate_certs=validate_certs, 1231 | ) 1232 | 1233 | password = response.read().decode('utf-8') 1234 | 1235 | if not (password.startswith('"') and password.endswith('"')): 1236 | module.fail_json( 1237 | msg=( 1238 | "Error while performing retrieve_password." 1239 | "The returned value was not formatted as expected." 1240 | "\n*** end_point=%s%s\n" % (api_base_url, end_point) 1241 | ), 1242 | headers=headers, 1243 | status_coode=-1 1244 | ) 1245 | 1246 | password = password[1:-1] 1247 | 1248 | result["password"] = password 1249 | 1250 | logging.debug("Password Retrieved") 1251 | 1252 | return (False, result, response.getcode()) 1253 | 1254 | except (HTTPError, HTTPException) as http_exception: 1255 | 1256 | res = '' 1257 | if isinstance(http_exception, HTTPError): 1258 | res = json.load(http_exception) 1259 | else: 1260 | res = to_text(http_exception) 1261 | 1262 | module.fail_json( 1263 | msg=( 1264 | "Error while performing retrieve_password." 1265 | "Please validate parameters provided." 1266 | "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, res) 1267 | ), 1268 | headers=headers, 1269 | status_code=http_exception.code, 1270 | ) 1271 | 1272 | except Exception as unknown_exception: 1273 | 1274 | module.fail_json( 1275 | msg=( 1276 | "Unknown error while performing retrieve_password." 1277 | "\n*** end_point=%s%s\n%s" 1278 | % (api_base_url, end_point, to_text(unknown_exception)) 1279 | ), 1280 | headers=headers, 1281 | status_code=-1, 1282 | ) 1283 | 1284 | 1285 | def main(): 1286 | 1287 | fields = { 1288 | "state": { 1289 | "type": "str", 1290 | "choices": ["present", "absent", "retrieve"], 1291 | "default": "present", 1292 | }, 1293 | "logging_level": {"type": "str", "choices": ["NOTSET", "DEBUG", "INFO"]}, 1294 | "logging_file": {"type": "str", "default": "/tmp/ansible_cyberark.log"}, 1295 | "api_base_url": {"type": "str"}, 1296 | "validate_certs": {"type": "bool", "default": "true"}, 1297 | "cyberark_session": {"required": True, "type": "dict", "no_log": True}, 1298 | "identified_by": { 1299 | "required": False, 1300 | "type": "str", 1301 | "default": "username,address,platform_id", 1302 | }, 1303 | "safe": {"required": True, "type": "str"}, 1304 | "platform_id": {"required": False, "type": "str"}, 1305 | "address": {"required": False, "type": "str"}, 1306 | "name": {"required": False, "type": "str"}, 1307 | "secret_type": { 1308 | "required": False, 1309 | "type": "str", 1310 | "choices": ["password", "key"], 1311 | "default": "password", 1312 | }, 1313 | "secret": {"required": False, "type": "str", "no_log": True}, 1314 | "new_secret": {"required": False, "type": "str", "no_log": True}, 1315 | "username": {"required": False, "type": "str"}, 1316 | "secret_management": { 1317 | "required": False, 1318 | "type": "dict", 1319 | "options": { 1320 | "automatic_management_enabled": { 1321 | "type": "bool", 1322 | "default": False, 1323 | }, 1324 | "manual_management_reason": {"type": "str"}, 1325 | "management_action": { 1326 | "type": "str", 1327 | "choices": ["change", "change_immediately", "reconcile"], 1328 | }, 1329 | "new_secret": {"type": "str", "no_log": True}, 1330 | "perform_management_action": { 1331 | "type": "str", 1332 | "choices": ["on_create", "always"], 1333 | "default": "always", 1334 | }, 1335 | }, 1336 | "no_log": False, 1337 | }, 1338 | "remote_machines_access": { 1339 | "required": False, 1340 | "type": "dict", 1341 | "options": { 1342 | "remote_machines": {"type": "str"}, 1343 | "access_restricted_to_remote_machines": {"type": "bool"}, 1344 | }, 1345 | }, 1346 | "platform_account_properties": {"required": False, "type": "dict"}, 1347 | } 1348 | 1349 | module = AnsibleModule(argument_spec=fields, supports_check_mode=True) 1350 | 1351 | if module.params["logging_level"] is not None: 1352 | logging.basicConfig( 1353 | filename=module.params["logging_file"], level=module.params["logging_level"] 1354 | ) 1355 | 1356 | logging.info("Starting Module") 1357 | 1358 | state = module.params["state"] 1359 | 1360 | (found, account_record, status_code) = get_account(module) 1361 | logging.debug( 1362 | "Account was %s, status_code=%s", "FOUND" if found else "NOT FOUND", status_code 1363 | ) 1364 | 1365 | changed = False 1366 | result = {"result": account_record} 1367 | 1368 | if state == "present": 1369 | 1370 | if found: # Account already exists 1371 | (changed, result, status_code) = update_account(module, account_record) 1372 | else: # Account does not exist 1373 | (changed, result, status_code) = add_account(module) 1374 | 1375 | perform_management_action = "always" 1376 | if "secret_management" in list(module.params.keys()): 1377 | secret_management = module.params["secret_management"] 1378 | if secret_management is not None and "perform_management_action" in list( 1379 | secret_management.keys() 1380 | ): 1381 | perform_management_action = secret_management[ 1382 | "perform_management_action" 1383 | ] 1384 | 1385 | logging.debug("Result=>%s", json.dumps(result)) 1386 | if perform_management_action == "always" or ( 1387 | perform_management_action == "on_create" and not found 1388 | ): 1389 | (account_reset, no_result, no_status_code) = reset_account_if_needed( 1390 | module, result["result"] 1391 | ) 1392 | if account_reset: 1393 | changed = True 1394 | 1395 | elif found and state == "absent": 1396 | (changed, result, status_code) = delete_account(module, account_record) 1397 | 1398 | elif found and state == "retrieve": 1399 | (changed, result, status_code) = retrieve_password(module, account_record) 1400 | 1401 | module.exit_json(changed=changed, result=result, status_code=status_code) 1402 | 1403 | 1404 | if __name__ == "__main__": 1405 | main() 1406 | -------------------------------------------------------------------------------- /plugins/modules/cyberark_authentication.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright: (c) 2017, Ansible Project 3 | # GNU General Public License v3.0+ 4 | # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | from __future__ import (absolute_import, division, print_function) 6 | 7 | 8 | __metaclass__ = type 9 | 10 | ANSIBLE_METADATA = { 11 | "metadata_version": "1.1", 12 | "status": ["preview"], 13 | "supported_by": "certified", 14 | } 15 | 16 | DOCUMENTATION = """ 17 | --- 18 | module: cyberark_authentication 19 | short_description: CyberArk Authentication using PAS Web Services SDK. 20 | author: 21 | - Edward Nunez (@enunez-cyberark) 22 | - Cyberark Bizdev (@cyberark-bizdev) 23 | version_added: '1.0.0' 24 | description: 25 | - Authenticates to CyberArk Vault using Privileged Account Security 26 | Web Services SDK and creates a session fact that can be used by other 27 | modules. It returns an Ansible fact called I(cyberark_session). Every 28 | module can use this fact as C(cyberark_session) parameter. 29 | options: 30 | state: 31 | default: present 32 | choices: [present, absent] 33 | description: 34 | - Specifies if an authentication logon/logoff and a 35 | cyberark_session should be added/removed. 36 | type: str 37 | username: 38 | description: 39 | - The name of the user who will logon to the Vault. 40 | type: str 41 | password: 42 | description: 43 | - The password of the user. 44 | type: str 45 | new_password: 46 | description: 47 | - The new password of the user. This parameter is optional, 48 | and enables you to change a password. 49 | type: str 50 | api_base_url: 51 | description: 52 | - A string containing the base URL of the server hosting 53 | CyberArk's Privileged Account Security Web Services SDK. 54 | type: str 55 | validate_certs: 56 | type: bool 57 | default: 'true' 58 | description: 59 | - If C(false), SSL certificates will not be validated. This 60 | should only set to C(false) used on personally controlled 61 | sites using self-signed certificates. 62 | use_ldap_authentication: 63 | type: bool 64 | default: 'false' 65 | description: 66 | - Whether or not LDAP will be used. 67 | use_windows_authentication: 68 | type: bool 69 | default: 'false' 70 | description: 71 | - Whether or not Windows will be used. 72 | use_cyberark_authentication: 73 | type: bool 74 | default: 'false' 75 | description: 76 | - Whether or not LDAP will be used. 77 | use_radius_authentication: 78 | type: bool 79 | default: 'false' 80 | description: 81 | - Whether or not users will be authenticated via a RADIUS 82 | server. Valid values are true/false. 83 | connection_number: 84 | type: int 85 | description: 86 | - To support multiple connections for same user specify 87 | - different value for this parameter. 88 | concurrentSession: 89 | type: bool 90 | default: false 91 | description: 92 | - Whether or not to allow concurrent sessions for the same user. 93 | cyberark_session: 94 | description: 95 | - Dictionary set by a CyberArk authentication containing the 96 | different values to perform actions on a logged-on CyberArk 97 | session. 98 | type: dict 99 | timeout: 100 | description: 101 | - Allows you set a timeout for when your authenticating to Cyberark 102 | default: 10 103 | type: int 104 | """ 105 | 106 | EXAMPLES = """ 107 | - name: Logon - use_shared_logon_authentication 108 | cyberark_authentication: 109 | api_base_url: "{{ web_services_base_url }}" 110 | use_shared_logon_authentication: true 111 | 112 | - name: Logon - Not use_shared_logon_authentication 113 | cyberark_authentication: 114 | api_base_url: "{{ web_services_base_url }}" 115 | username: "{{ password_object.password }}" 116 | password: "{{ password_object.passprops.username }}" 117 | use_shared_logon_authentication: false 118 | 119 | - name: Logoff from CyberArk Vault 120 | cyberark_authentication: 121 | state: absent 122 | cyberark_session: "{{ cyberark_session }}" 123 | """ 124 | 125 | RETURN = """ 126 | cyberark_session: 127 | description: Authentication facts. 128 | returned: success 129 | type: complex 130 | contains: 131 | api_base_url: 132 | description: 133 | - Base URL for API calls. Returned in the cyberark_session, 134 | so it can be used in subsequent calls. 135 | type: str 136 | returned: always 137 | token: 138 | description: 139 | - The token that identifies the session, encoded in BASE 64. 140 | type: str 141 | returned: always 142 | use_shared_logon_authentication: 143 | description: 144 | - Whether or not Shared Logon Authentication was used to 145 | establish the session. 146 | type: bool 147 | returned: always 148 | validate_certs: 149 | description: Whether or not SSL certificates should be validated. 150 | type: bool 151 | returned: always 152 | """ 153 | 154 | from ansible.module_utils._text import to_text 155 | from ansible.module_utils.basic import AnsibleModule 156 | from ansible.module_utils.urls import open_url 157 | from ansible.module_utils.six.moves.urllib.error import HTTPError 158 | from ansible.module_utils.six.moves.http_client import HTTPException 159 | import json 160 | 161 | 162 | def processAuthentication(module): 163 | 164 | # Getting parameters from module 165 | 166 | api_base_url = module.params["api_base_url"] 167 | validate_certs = module.params["validate_certs"] 168 | username = module.params["username"] 169 | password = module.params["password"] 170 | new_password = module.params["new_password"] 171 | 172 | use_radius = module.params["use_radius_authentication"] 173 | use_ldap = module.params["use_ldap_authentication"] 174 | use_windows = module.params["use_windows_authentication"] 175 | use_cyberark = module.params["use_cyberark_authentication"] 176 | 177 | # connection_number = module.params["connection_number"] 178 | state = module.params["state"] 179 | cyberark_session = module.params["cyberark_session"] 180 | 181 | concurrentSession = module.params["concurrentSession"] 182 | 183 | timeout = module.params["timeout"] 184 | 185 | # if in check mode it will not perform password changes 186 | if module.check_mode and new_password is not None: 187 | new_password = None 188 | 189 | # Defining initial values for open_url call 190 | headers = { 191 | "Content-Type": "application/json", 192 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 193 | } 194 | 195 | payload = "" 196 | 197 | if state == "present": # Logon Action 198 | 199 | # Different end_points based on the use of desired method of auth 200 | 201 | if use_ldap: 202 | end_point = "/PasswordVault/API/Auth/LDAP/Logon" 203 | 204 | elif use_radius: 205 | end_point = "/PasswordVault/API/Auth/radius/Logon" 206 | 207 | elif use_windows: 208 | end_point = "/PasswordVault/API/auth/Windows/Logon" 209 | 210 | else: 211 | use_cyberark = True 212 | end_point = "/PasswordVault/API/Auth/CyberArk/Logon" 213 | 214 | # The payload will contain username, password 215 | # and optionally use_radius_authentication and new_password 216 | payload_dict = {"username": username, "password": password} 217 | 218 | if new_password is not None and use_cyberark: 219 | payload_dict["newPassword"] = new_password 220 | 221 | # COMMENT: I dont know what this is for and the old api seems like it didnt have this field 222 | # if connection_number is not None: 223 | # payload_dict["connectionNumber"] = connection_number 224 | 225 | if concurrentSession: 226 | payload_dict["concurrentSession"] = True 227 | 228 | payload = json.dumps(payload_dict) 229 | 230 | else: # Logoff Action 231 | 232 | # Get values from cyberark_session already established 233 | api_base_url = cyberark_session["api_base_url"] 234 | validate_certs = cyberark_session["validate_certs"] 235 | 236 | headers["Authorization"] = cyberark_session["token"] 237 | 238 | # All off the logoff with the same endpoint 239 | end_point = "/PasswordVault/API/Auth/Logoff" 240 | 241 | result = None 242 | changed = False 243 | response = None 244 | 245 | try: 246 | 247 | response = open_url( 248 | api_base_url + end_point, 249 | method="POST", 250 | headers=headers, 251 | data=payload, 252 | validate_certs=validate_certs, 253 | timeout=timeout, 254 | ) 255 | 256 | except (HTTPError, HTTPException) as http_exception: 257 | 258 | module.fail_json( 259 | msg=( 260 | "Error while performing authentication." 261 | "Please validate parameters provided, and ability to logon to " 262 | "CyberArk.\n*** end_point=%s%s\n ==> %s" 263 | ) 264 | % (api_base_url, end_point, to_text(http_exception)), 265 | headers=headers, 266 | status_code=http_exception.code, 267 | ) 268 | 269 | except Exception as unknown_exception: 270 | 271 | module.fail_json( 272 | msg=( 273 | "Unknown error while performing authentication." 274 | "\n*** end_point=%s%s\n%s" 275 | % (api_base_url, end_point, to_text(unknown_exception)) 276 | ), 277 | headers=headers, 278 | status_code=-1, 279 | ) 280 | 281 | if response.getcode() == 200: # Success 282 | 283 | if state == "present": # Logon Action 284 | 285 | # Result token from REST Api uses a different key based 286 | # the use of shared logon authentication 287 | token = "" 288 | try: 289 | token = str(json.loads(response.read())) 290 | 291 | # the new one just returns a token 292 | # if use: 293 | # token = json.loads(response.read())["LogonResult"] 294 | # else: 295 | # token = json.loads(response.read())["CyberArkLogonResult"] 296 | except Exception as e: 297 | module.fail_json( 298 | msg="Error obtaining token\n%s" % (to_text(e)), 299 | payload=payload, 300 | headers=headers, 301 | status_code=-1, 302 | ) 303 | 304 | # Preparing result of the module 305 | result = { 306 | "cyberark_session": { 307 | "token": token, 308 | "api_base_url": api_base_url, 309 | "validate_certs": validate_certs, 310 | } 311 | } 312 | 313 | if new_password is not None: 314 | # Only marks change if new_password was received resulting 315 | # in a password change 316 | changed = True 317 | 318 | else: # Logoff Action clears cyberark_session 319 | 320 | result = {"cyberark_session": {}} 321 | 322 | return (changed, result, response.getcode()) 323 | 324 | else: 325 | module.fail_json(msg="error in end_point=>" + end_point, headers=headers) 326 | 327 | 328 | def main(): 329 | 330 | fields = { 331 | "api_base_url": {"type": "str"}, 332 | "validate_certs": {"type": "bool", "default": "true"}, 333 | "username": {"type": "str"}, 334 | "password": {"type": "str", "no_log": True}, 335 | "new_password": {"type": "str", "no_log": True}, 336 | "use_radius_authentication": {"default": False, "type": "bool"}, 337 | "use_windows_authentication": {"default": False, "type": "bool"}, 338 | "use_ldap_authentication": {"default": False, "type": "bool"}, 339 | "use_cyberark_authentication": {"default": False, "type": "bool"}, 340 | "concurrentSession": {"default": False, "type": "bool"}, 341 | "connection_number": {"type": "int"}, 342 | "state": { 343 | "type": "str", 344 | "choices": ["present", "absent"], 345 | "default": "present", 346 | }, 347 | "cyberark_session": {"type": "dict"}, 348 | "timeout": {"default": 10, "type": "int"}, 349 | } 350 | 351 | # cyberark and radius -> mutually_exclusive is cyberark and ldap 352 | # ldap and radius 353 | # windows has to be by itself 354 | 355 | mutually_exclusive = [ 356 | [ 357 | "use_windows_authentication", 358 | "use_ldap_authentication", 359 | "use_cyberark_authentication", 360 | "use_radius_authentication", 361 | ], 362 | ["use_radius_authentication", "new_password"], 363 | ["use_windows_authentication", "new_password"], 364 | ["use_ldap_authentication", "new_password"], 365 | ["api_base_url", "cyberark_session"], 366 | ] 367 | 368 | required_if = [ 369 | ("state", "present", ["api_base_url"]), 370 | ("state", "absent", ["cyberark_session"]), 371 | ] 372 | 373 | required_together = [["username", "password"]] 374 | 375 | module = AnsibleModule( 376 | argument_spec=fields, 377 | mutually_exclusive=mutually_exclusive, 378 | required_if=required_if, 379 | required_together=required_together, 380 | supports_check_mode=True, 381 | ) 382 | 383 | (changed, result, status_code) = processAuthentication(module) 384 | 385 | module.exit_json(changed=changed, ansible_facts=result, status_code=status_code) 386 | 387 | 388 | if __name__ == "__main__": 389 | main() 390 | -------------------------------------------------------------------------------- /plugins/modules/cyberark_credential.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright: (c) 2017, Ansible Project 3 | # GNU General Public License v3.0+ 4 | # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | from __future__ import (absolute_import, division, print_function) 6 | 7 | 8 | __metaclass__ = type 9 | 10 | ANSIBLE_METADATA = { 11 | "metadata_version": "1.1", 12 | "status": ["preview"], 13 | "supported_by": "community", 14 | } 15 | 16 | DOCUMENTATION = """ 17 | --- 18 | module: cyberark_credential 19 | short_description: Credential retrieval using AAM Central Credential Provider. 20 | author: 21 | - Edward Nunez (@enunez-cyberark) 22 | - CyberArk BizDev (@cyberark-bizdev) 23 | - Erasmo Acosta (@erasmix) 24 | - James Stutes (@JimmyJamCABD) 25 | version_added: '1.0.0' 26 | description: 27 | - Creates a URI for retrieving a credential from a password object stored 28 | in the Cyberark Vault. The request uses the Privileged Account Security 29 | Web Services SDK through the Central Credential Provider by requesting 30 | access with an Application ID. 31 | 32 | options: 33 | api_base_url: 34 | type: str 35 | required: true 36 | description: 37 | - A string containing the base URL of the server hosting the 38 | Central Credential Provider. 39 | validate_certs: 40 | type: bool 41 | required: false 42 | default: true 43 | description: 44 | - If C(false), SSL certificate chain will not be validated. This 45 | should only set to C(true) if you have a root CA certificate 46 | installed on each node. 47 | app_id: 48 | type: str 49 | required: true 50 | description: 51 | - A string containing the Application ID authorized for retrieving 52 | the credential. 53 | query: 54 | type: str 55 | required: true 56 | description: 57 | - A string containing details of the object being queried; 58 | - Possible parameters could be Safe, Folder, Object 59 | - (internal account name), UserName, Address, Database, 60 | - PolicyID. 61 | connection_timeout: 62 | type: int 63 | required: false 64 | default: '30' 65 | description: 66 | - An integer value of the allowed time before the request returns 67 | failed. 68 | query_format: 69 | type: str 70 | required: false 71 | default: Exact 72 | choices: [Exact, Regexp] 73 | description: 74 | - The format for which your Query will be received by the CCP. 75 | fail_request_on_password_change: 76 | type: bool 77 | required: false 78 | default: false 79 | description: 80 | - A boolean parameter for completing the request in the middle of 81 | a password change of the requested credential. 82 | client_cert: 83 | type: str 84 | required: false 85 | description: 86 | - A string containing the file location and name of the client 87 | certificate used for authentication. 88 | client_key: 89 | type: str 90 | required: false 91 | description: 92 | - A string containing the file location and name of the private 93 | key of the client certificate used for authentication. 94 | reason: 95 | type: str 96 | required: false 97 | description: 98 | - Reason for requesting credential if required by policy; 99 | - It must be specified if the Policy managing the object 100 | - requires it. 101 | """ 102 | 103 | EXAMPLES = """ 104 | - name: credential retrieval basic 105 | cyberark.pas.cyberark_credential: 106 | api_base_url: "http://10.10.0.1" 107 | app_id: "TestID" 108 | query: "Safe=test;UserName=admin" 109 | register: result 110 | 111 | - name: credential retrieval advanced 112 | cyberark.pas.cyberark_credential: 113 | api_base_url: "https://components.cyberark.local" 114 | validate_certs: true 115 | client_cert: /etc/pki/ca-trust/source/client.pem 116 | client_key: /etc/pki/ca-trust/source/priv-key.pem 117 | app_id: "TestID" 118 | query: "Safe=test;UserName=admin" 119 | connection_timeout: 60 120 | query_format: Exact 121 | fail_request_on_password_change: true 122 | reason: "requesting credential for Ansible deployment" 123 | register: result 124 | """ 125 | 126 | RETURN = """ 127 | changed: 128 | description: 129 | - Identify if the playbook run resulted in a change to the account in 130 | any way. 131 | returned: always 132 | type: bool 133 | failed: 134 | description: Whether playbook run resulted in a failure of any kind. 135 | returned: always 136 | type: bool 137 | status_code: 138 | description: Result HTTP Status code. 139 | returned: success 140 | type: int 141 | sample: "200, 201, -1, 204" 142 | result: 143 | description: A json dump of the resulting action. 144 | returned: success 145 | type: complex 146 | contains: 147 | Address: 148 | description: The target address of the credential being queried 149 | type: str 150 | returned: if required 151 | Content: 152 | description: The password for the object being queried 153 | type: str 154 | returned: always 155 | CreationMethod: 156 | description: This is how the object was created in the Vault 157 | type: str 158 | returned: always 159 | DeviceType: 160 | description: 161 | - An internal File Category for more granular management of 162 | Platforms. 163 | type: str 164 | returned: always 165 | Folder: 166 | description: 167 | - The folder within the Safe where the credential is stored. 168 | type: str 169 | returned: always 170 | Name: 171 | description: 172 | - The Cyberark unique object ID of the credential being 173 | queried. 174 | type: str 175 | returned: always 176 | PasswordChangeInProcess: 177 | description: If the password has a change flag placed by the CPM 178 | type: bool 179 | returned: always 180 | PolicyID: 181 | description: Whether or not SSL certificates should be validated. 182 | type: str 183 | returned: if assigned to a policy 184 | Safe: 185 | description: The safe where the queried credential is stored 186 | type: str 187 | returned: always 188 | Username: 189 | description: The username of the credential being queried 190 | type: str 191 | returned: if required 192 | LogonDomain: 193 | description: The Address friendly name resolved by the CPM 194 | type: str 195 | returned: if populated 196 | CPMDisabled: 197 | description: 198 | - A description of why this vaulted credential is not being 199 | managed by the CPM. 200 | type: str 201 | returned: if CPM management is disabled and a reason is given 202 | """ 203 | 204 | from ansible.module_utils._text import to_text 205 | from ansible.module_utils.basic import AnsibleModule 206 | from ansible.module_utils.urls import open_url 207 | from ansible.module_utils.six.moves.urllib.error import HTTPError 208 | from ansible.module_utils.six.moves.urllib.parse import quote 209 | from ansible.module_utils.six.moves.http_client import HTTPException 210 | import json 211 | 212 | 213 | def retrieve_credential(module): 214 | 215 | # Getting parameters from module 216 | 217 | api_base_url = module.params["api_base_url"] 218 | validate_certs = module.params["validate_certs"] 219 | app_id = module.params["app_id"] 220 | query = module.params["query"] 221 | connection_timeout = module.params["connection_timeout"] 222 | query_format = module.params["query_format"] 223 | fail_request_on_password_change = module.params["fail_request_on_password_change"] 224 | client_cert = None 225 | client_key = None 226 | path = "/AIMWebService/api/Accounts" 227 | 228 | if "client_cert" in module.params: 229 | client_cert = module.params["client_cert"] 230 | if "client_key" in module.params: 231 | client_key = module.params["client_key"] 232 | 233 | if "path" in module.params: 234 | path = module.params["path"] 235 | 236 | end_point = ( 237 | "%s?AppId=%s&Query=%s&" 238 | "ConnectionTimeout=%s&QueryFormat=%s" 239 | "&FailRequestOnPasswordChange=%s" 240 | ) % ( 241 | path, 242 | quote(app_id), 243 | quote(query), 244 | connection_timeout, 245 | query_format, 246 | fail_request_on_password_change, 247 | ) 248 | 249 | if "reason" in module.params and module.params["reason"] is not None: 250 | reason = quote(module.params["reason"]) 251 | end_point = end_point + "&reason=%s" % reason 252 | 253 | result = None 254 | response = None 255 | 256 | try: 257 | 258 | response = open_url( 259 | api_base_url + end_point, 260 | method="GET", 261 | validate_certs=validate_certs, 262 | client_cert=client_cert, 263 | client_key=client_key, 264 | ) 265 | 266 | except (HTTPError, HTTPException) as http_exception: 267 | 268 | module.fail_json( 269 | msg=( 270 | "Error while retrieving credential." 271 | "Please validate parameters provided, and permissions for " 272 | "the application and provider in CyberArk." 273 | "\n*** end_point=%s%s\n ==> %s" 274 | % (api_base_url, end_point, to_text(http_exception)) 275 | ), 276 | status_code=http_exception.code, 277 | ) 278 | 279 | except Exception as unknown_exception: 280 | 281 | module.fail_json( 282 | msg=( 283 | "Unknown error while retrieving credential." 284 | "\n*** end_point=%s%s\n%s" 285 | % (api_base_url, end_point, to_text(unknown_exception)) 286 | ), 287 | status_code=-1, 288 | ) 289 | 290 | if response.getcode() == 200: # Success 291 | 292 | # Result token from REST Api uses a different key based 293 | try: 294 | result = json.loads(response.read()) 295 | except Exception as exc: 296 | module.fail_json( 297 | msg=("Error obtain cyberark credential result " "from http body\n%s") 298 | % (to_text(exc)), 299 | status_code=-1, 300 | ) 301 | 302 | return (result, response.getcode()) 303 | 304 | else: 305 | module.fail_json(msg="error in end_point=>" + end_point) 306 | 307 | 308 | def main(): 309 | 310 | fields = { 311 | "api_base_url": {"required": True, "type": "str"}, 312 | "app_id": {"required": True, "type": "str"}, 313 | "query": {"required": True, "type": "str"}, 314 | "reason": {"required": False, "type": "str"}, 315 | "connection_timeout": {"required": False, "type": "int", "default": 30}, 316 | "query_format": { 317 | "required": False, 318 | "type": "str", 319 | "choices": ["Exact", "Regexp"], 320 | "default": "Exact", 321 | }, 322 | "fail_request_on_password_change": { 323 | "required": False, 324 | "type": "bool", 325 | "default": False, 326 | }, 327 | "validate_certs": {"type": "bool", "default": True}, 328 | "client_cert": {"type": "str", "required": False}, 329 | "client_key": {"type": "str", "required": False, "no_log": True}, 330 | } 331 | 332 | module = AnsibleModule(argument_spec=fields, supports_check_mode=True) 333 | 334 | (result, status_code) = retrieve_credential(module) 335 | 336 | module.exit_json(changed=False, result=result, status_code=status_code) 337 | 338 | 339 | if __name__ == "__main__": 340 | main() 341 | -------------------------------------------------------------------------------- /plugins/modules/cyberark_user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright: (c) 2017, Ansible Project 5 | # GNU General Public License v3.0+ 6 | # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 7 | from __future__ import (absolute_import, division, print_function) 8 | 9 | 10 | __metaclass__ = type 11 | 12 | ANSIBLE_METADATA = { 13 | "metadata_version": "1.1", 14 | "status": ["preview"], 15 | "supported_by": "certified", 16 | } 17 | 18 | DOCUMENTATION = r""" 19 | --- 20 | module: cyberark_user 21 | short_description: CyberArk User Management using PAS Web Services SDK. 22 | author: 23 | - Edward Nunez (@enunez-cyberark) 24 | - Cyberark Bizdev (@cyberark-bizdev) 25 | - Erasmo Acosta (@erasmix) 26 | - James Stutes (@jimmyjamcabd) 27 | version_added: '1.0.0' 28 | description: 29 | - CyberArk User Management using PAS Web Services SDK, 30 | It currently supports the following actions Get User Details, Add User, 31 | Update User, Delete User. 32 | 33 | options: 34 | username: 35 | description: 36 | - The name of the user who will be queried (for details), added, 37 | updated or deleted. 38 | type: str 39 | required: true 40 | state: 41 | description: 42 | - Specifies the state needed for the user present for create user, 43 | absent for delete user. 44 | type: str 45 | choices: [ absent, present ] 46 | default: present 47 | logging_level: 48 | description: 49 | - Parameter used to define the level of troubleshooting output to 50 | the C(logging_file) value. 51 | required: false 52 | choices: [NOTSET, DEBUG, INFO] 53 | default: NOTSET 54 | type: str 55 | logging_file: 56 | description: 57 | - Setting the log file name and location for troubleshooting logs. 58 | required: false 59 | default: /tmp/ansible_cyberark.log 60 | type: str 61 | cyberark_session: 62 | description: 63 | - Dictionary set by a CyberArk authentication containing the 64 | different values to perform actions on a logged-on CyberArk 65 | session, please see M(cyberark.pas.cyberark_authentication) module for an 66 | example of cyberark_session. 67 | type: dict 68 | required: true 69 | initial_password: 70 | description: 71 | - The password that the new user will use to log on the first time. 72 | - This password must meet the password policy requirements. 73 | - This parameter is required when state is present -- Add User. 74 | type: str 75 | new_password: 76 | description: 77 | - The user updated password. Make sure that this password meets 78 | the password policy requirements. 79 | type: str 80 | email: 81 | description: 82 | - The user email address. 83 | type: str 84 | first_name: 85 | description: 86 | - The user first name. 87 | type: str 88 | last_name: 89 | description: 90 | - The user last name. 91 | type: str 92 | change_password_on_the_next_logon: 93 | description: 94 | - Whether or not the user must change their password in their 95 | next logon. 96 | type: bool 97 | default: false 98 | domain_name: 99 | description: 100 | - The name of the user domain. 101 | type: str 102 | member_type: 103 | description: 104 | - The type of member. 105 | type: str 106 | expiry_date: 107 | description: 108 | - The date and time when the user account will expire and become 109 | disabled. 110 | type: str 111 | user_type_name: 112 | description: 113 | - The type of user. 114 | - The parameter defaults to C(EPVUser). 115 | type: str 116 | disabled: 117 | description: 118 | - Whether or not the user will be disabled. 119 | type: bool 120 | default: false 121 | location: 122 | description: 123 | - The Vault Location for the user. 124 | type: str 125 | group_name: 126 | description: 127 | - The name of the group the user will be added to. 128 | - Causes an additional lookup in cyberark 129 | - Will be ignored if vault_id is used 130 | - Will cause a failure if group is missing or more than one group with that name exists 131 | type: str 132 | timeout: 133 | description: 134 | - How long to wait for the server to send data before giving up 135 | type: float 136 | default: 10 137 | vault_id: 138 | description: 139 | - The ID of the user group to add the user to 140 | - Prefered over group_name 141 | type: int 142 | authorization: 143 | description: 144 | - A list of authorization options for this user. 145 | - Options can include AddSafes and AuditUsers 146 | - The default provides backwards compatability with older versions of the collection 147 | type: list 148 | elements: str 149 | default: 150 | - AddSafes 151 | - AuditUsers 152 | """ 153 | 154 | EXAMPLES = r""" 155 | - name: Logon to CyberArk Vault using PAS Web Services SDK 156 | cyberark_authentication: 157 | api_base_url: https://components.cyberark.local 158 | use_shared_logon_authentication: true 159 | 160 | - name: Create user & immediately add it to a group 161 | cyberark_user: 162 | username: username 163 | initial_password: password 164 | user_type_name: EPVUser 165 | change_password_on_the_next_logon: false 166 | group_name: GroupOfUser 167 | state: present 168 | cyberark_session: '{{ cyberark_session }}' 169 | 170 | - name: Make sure user is present and reset user credential if present 171 | cyberark_user: 172 | username: Username 173 | new_password: password 174 | disabled: false 175 | state: present 176 | cyberark_session: '{{ cyberark_session }}' 177 | 178 | - name: Logoff from CyberArk Vault 179 | cyberark_authentication: 180 | state: absent 181 | cyberark_session: '{{ cyberark_session }}' 182 | """ 183 | 184 | RETURN = r""" 185 | changed: 186 | description: Whether there was a change done. 187 | type: bool 188 | returned: always 189 | cyberark_user: 190 | description: Dictionary containing result properties. 191 | returned: always 192 | type: complex 193 | contains: 194 | result: 195 | description: user properties when state is present 196 | type: dict 197 | returned: success 198 | status_code: 199 | description: Result HTTP Status code 200 | returned: success 201 | type: int 202 | sample: 200 203 | """ 204 | 205 | import json 206 | 207 | from ansible.module_utils.basic import AnsibleModule 208 | from ansible.module_utils._text import to_text 209 | from ansible.module_utils.six.moves import http_client as httplib 210 | from ansible.module_utils.six.moves.urllib.error import HTTPError 211 | from ansible.module_utils.urls import open_url 212 | from ansible.module_utils.six.moves.urllib.parse import quote 213 | import logging 214 | 215 | 216 | def construct_url(api_base_url, end_point): 217 | return "{baseurl}/{endpoint}".format(baseurl=api_base_url.rstrip("/"), endpoint=end_point.lstrip("/")) 218 | 219 | 220 | def user_details(module): 221 | 222 | # Get username from module parameters, and api base url 223 | # along with validate_certs from the cyberark_session established 224 | username = module.params["username"] 225 | cyberark_session = module.params["cyberark_session"] 226 | api_base_url = cyberark_session["api_base_url"] 227 | validate_certs = cyberark_session["validate_certs"] 228 | 229 | # Prepare result, end_point, and headers 230 | result = {} 231 | end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{pusername}".format(pusername=username) 232 | url = construct_url(api_base_url, end_point) 233 | 234 | headers = { 235 | "Content-Type": "application/json", 236 | "Authorization": cyberark_session["token"], 237 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 238 | } 239 | 240 | try: 241 | 242 | response = open_url( 243 | url, 244 | method="GET", 245 | headers=headers, 246 | validate_certs=validate_certs, 247 | timeout=module.params['timeout'], 248 | ) 249 | result = {"result": json.loads(response.read())} 250 | 251 | return (False, result, response.getcode()) 252 | 253 | except (HTTPError, httplib.HTTPException) as http_exception: 254 | 255 | if http_exception.code == 404: 256 | return (False, None, http_exception.code) 257 | else: 258 | module.fail_json( 259 | msg=( 260 | "Error while performing user_details." 261 | "Please validate parameters provided." 262 | "\n*** end_point=%s\n ==> %s" 263 | % (url, to_text(http_exception)) 264 | ), 265 | headers=headers, 266 | status_code=http_exception.code, 267 | ) 268 | 269 | except Exception as unknown_exception: 270 | 271 | module.fail_json( 272 | msg=( 273 | "Unknown error while performing user_details." 274 | "\n*** end_point=%s\n%s" 275 | % (url, to_text(unknown_exception)) 276 | ), 277 | headers=headers, 278 | status_code=-1, 279 | ) 280 | 281 | 282 | def user_add_or_update(module, HTTPMethod, existing_info): 283 | 284 | # Get username from module parameters, and api base url 285 | # along with validate_certs from the cyberark_session established 286 | username = module.params["username"] 287 | cyberark_session = module.params["cyberark_session"] 288 | api_base_url = cyberark_session["api_base_url"] 289 | validate_certs = cyberark_session["validate_certs"] 290 | 291 | # Prepare result, paylod, and headers 292 | result = {} 293 | payload = {} 294 | headers = { 295 | "Content-Type": "application/json", 296 | "Authorization": cyberark_session["token"], 297 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 298 | } 299 | 300 | # end_point and payload sets different depending on POST/PUT 301 | # for POST -- create -- payload contains username 302 | # for PUT -- update -- username is part of the endpoint 303 | if HTTPMethod == "POST": 304 | end_point = "PasswordVault/api/Users" 305 | payload["UserName"] = username 306 | if ( 307 | "initial_password" in list(module.params.keys()) 308 | and module.params["initial_password"] is not None 309 | ): 310 | payload["InitialPassword"] = module.params["initial_password"] 311 | 312 | elif HTTPMethod == "PUT": 313 | # With the put in this old format, we can not update the vaultAuthorization 314 | end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{pusername}".format(pusername=username) 315 | 316 | # --- Optionally populate payload based on parameters passed --- 317 | if "new_password" in module.params and module.params["new_password"] is not None: 318 | payload["NewPassword"] = module.params["new_password"] 319 | 320 | if "email" in module.params and module.params["email"] is not None: 321 | payload["Email"] = module.params["email"] 322 | 323 | if "first_name" in module.params and module.params["first_name"] is not None: 324 | payload["FirstName"] = module.params["first_name"] 325 | 326 | if "last_name" in module.params and module.params["last_name"] is not None: 327 | payload["LastName"] = module.params["last_name"] 328 | 329 | if ( 330 | "change_password_on_the_next_logon" in module.params 331 | and module.params["change_password_on_the_next_logon"] is not None 332 | ): 333 | payload["ChangePasswordOnTheNextLogon"] = module.params[ 334 | "change_password_on_the_next_logon" 335 | ] 336 | 337 | if "expiry_date" in module.params and module.params["expiry_date"] is not None: 338 | payload["ExpiryDate"] = module.params["expiry_date"] 339 | 340 | if ( 341 | "user_type_name" in module.params 342 | and module.params["user_type_name"] is not None 343 | ): 344 | payload["UserTypeName"] = module.params["user_type_name"] 345 | # In API V2 the parameter is called userType, V2 ignores the UserTypeName 346 | payload["userType"] = module.params["user_type_name"] 347 | 348 | if "disabled" in module.params and module.params["disabled"] is not None: 349 | payload["Disabled"] = module.params["disabled"] 350 | 351 | if "location" in module.params and module.params["location"] is not None: 352 | payload["Location"] = module.params["location"] 353 | 354 | if module.params.get("authorization", None) is not None: 355 | payload["vaultAuthorization"] = module.params["authorization"] 356 | 357 | # -------------------------------------------------------------- 358 | logging.debug( 359 | "HTTPMethod = " + HTTPMethod + " module.params = " + json.dumps(module.params) 360 | ) 361 | logging.debug("Existing Info: %s", json.dumps(existing_info)) 362 | logging.debug("payload => %s", json.dumps(payload)) 363 | 364 | if HTTPMethod == "PUT" and ( 365 | "new_password" not in module.params or module.params["new_password"] is None 366 | ): 367 | logging.info("Verifying if needs to be updated") 368 | proceed = False 369 | updateable_fields = [ 370 | "Email", 371 | "FirstName", 372 | "LastName", 373 | "ChangePasswordOnTheNextLogon", 374 | "ExpiryDate", 375 | "UserTypeName", 376 | "Disabled", 377 | "Location", 378 | "UserTypeName", 379 | "vaultAuthorization", 380 | ] 381 | for field_name in updateable_fields: 382 | logging.debug("#### field_name : %s", field_name) 383 | if ( 384 | field_name in payload 385 | and field_name in existing_info 386 | and payload[field_name] != existing_info[field_name] 387 | ): 388 | logging.debug("Changing value for %s", field_name) 389 | proceed = True 390 | else: 391 | proceed = True 392 | 393 | if proceed: 394 | logging.info("Proceeding to either update or create") 395 | url = construct_url(api_base_url, end_point) 396 | try: 397 | 398 | # execute REST action 399 | response = open_url( 400 | url, 401 | method=HTTPMethod, 402 | headers=headers, 403 | data=json.dumps(payload), 404 | validate_certs=validate_certs, 405 | timeout=module.params['timeout'], 406 | ) 407 | 408 | result = {"result": json.loads(response.read())} 409 | 410 | return (True, result, response.getcode()) 411 | 412 | except (HTTPError, httplib.HTTPException) as http_exception: 413 | 414 | module.fail_json( 415 | msg=( 416 | "Error while performing user_add_or_update." 417 | "Please validate parameters provided." 418 | "\n*** end_point=%s\n ==> %s" 419 | % (url, to_text(http_exception)) 420 | ), 421 | payload=payload, 422 | headers=headers, 423 | status_code=http_exception.code, 424 | ) 425 | except Exception as unknown_exception: 426 | 427 | module.fail_json( 428 | msg=( 429 | "Unknown error while performing user_add_or_update." 430 | "\n*** end_point=%s\n%s" 431 | % (url, to_text(unknown_exception)) 432 | ), 433 | payload=payload, 434 | headers=headers, 435 | status_code=-1, 436 | ) 437 | else: 438 | return (False, existing_info, 200) 439 | 440 | 441 | def resolve_username_to_id(module): 442 | username = module.params["username"] 443 | cyberark_session = module.params["cyberark_session"] 444 | api_base_url = cyberark_session["api_base_url"] 445 | validate_certs = cyberark_session["validate_certs"] 446 | url = construct_url(api_base_url, "PasswordVault/api/Users?search={pusername}".format(pusername=username)) 447 | headers = { 448 | "Content-Type": "application/json", 449 | "Authorization": cyberark_session["token"], 450 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 451 | } 452 | try: 453 | response = open_url( 454 | url, 455 | method="GET", 456 | headers=headers, 457 | validate_certs=validate_certs, 458 | timeout=module.params['timeout'], 459 | ) 460 | users = json.loads(response.read()) 461 | # Return None if the user does not exist 462 | user_id = None 463 | # Say we have two users: 'someone' and 'someoneelse', a search on someone will return both 464 | # So we will lopp over and see if the username returned matches the username we searched for 465 | # If so, and we somehow found more than one raise an error 466 | for user in users['Users']: 467 | if user['username'] == username: 468 | if user_id is None: 469 | user_id = user['id'] 470 | else: 471 | module.fail_json(msg=("Found more than one user matching %s, this should be impossible" % (username))) 472 | 473 | # If we made it here we had 1 or 0 users, return them 474 | logging.debug("Resolved username {%s} to ID {%s}", username, user_id) 475 | return user_id 476 | 477 | except (HTTPError, httplib.HTTPException) as http_exception: 478 | exception_text = to_text(http_exception) 479 | module.fail_json(msg=( 480 | "Error while performing user_search." 481 | "Please validate parameters provided." 482 | "\n*** end_point=%s\n ==> %s" 483 | % (url, exception_text)), 484 | headers=headers, 485 | status_code=http_exception.code, 486 | ) 487 | except Exception as unknown_exception: 488 | module.fail_json(msg=( 489 | "Unknown error while performing user search." 490 | "\n*** end_point=%s\n%s" 491 | % (url, to_text(unknown_exception))), 492 | headers=headers, 493 | status_code=-1, 494 | ) 495 | 496 | 497 | def user_delete(module): 498 | 499 | # Get username from module parameters, and api base url 500 | # along with validate_certs from the cyberark_session established 501 | cyberark_session = module.params["cyberark_session"] 502 | api_base_url = cyberark_session["api_base_url"] 503 | validate_certs = cyberark_session["validate_certs"] 504 | 505 | # Prepare result, end_point, and headers 506 | result = {} 507 | vault_user_id = resolve_username_to_id(module) 508 | # If the user was not found by username we can return unchanged 509 | if vault_user_id is None: 510 | return (False, result, None) 511 | 512 | end_point = ("PasswordVault/api/Users/{pvaultuserid}").format(pvaultuserid=vault_user_id) 513 | 514 | headers = { 515 | "Content-Type": "application/json", 516 | "Authorization": cyberark_session["token"], 517 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 518 | } 519 | url = construct_url(api_base_url, end_point) 520 | 521 | try: 522 | 523 | # execute REST action 524 | response = open_url( 525 | url, 526 | method="DELETE", 527 | headers=headers, 528 | validate_certs=validate_certs, 529 | timeout=module.params['timeout'], 530 | ) 531 | 532 | result = {"result": {}} 533 | 534 | return (True, result, response.getcode()) 535 | 536 | except (HTTPError, httplib.HTTPException) as http_exception: 537 | 538 | exception_text = to_text(http_exception) 539 | if http_exception.code == 404 and "ITATS003E" in exception_text: 540 | # User does not exist 541 | result = {"result": {}} 542 | return (False, result, http_exception.code) 543 | else: 544 | module.fail_json( 545 | msg=( 546 | "Error while performing user_delete." 547 | "Please validate parameters provided." 548 | "\n*** end_point=%s\n ==> %s" 549 | % (url, exception_text) 550 | ), 551 | headers=headers, 552 | status_code=http_exception.code, 553 | ) 554 | 555 | except Exception as unknown_exception: 556 | 557 | module.fail_json( 558 | msg=( 559 | "Unknown error while performing user_delete." 560 | "\n*** end_point=%s\n%s" 561 | % (url, to_text(unknown_exception)) 562 | ), 563 | headers=headers, 564 | status_code=-1, 565 | ) 566 | 567 | 568 | def resolve_group_name_to_id(module): 569 | group_name = module.params["group_name"] 570 | cyberark_session = module.params["cyberark_session"] 571 | api_base_url = cyberark_session["api_base_url"] 572 | validate_certs = cyberark_session["validate_certs"] 573 | headers = { 574 | "Content-Type": "application/json", 575 | "Authorization": cyberark_session["token"], 576 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 577 | } 578 | url = construct_url(api_base_url, "/PasswordVault/api/UserGroups?search={pgroupname}".format(pgroupname=quote(group_name))) 579 | try: 580 | response = open_url( 581 | url, 582 | method="GET", 583 | headers=headers, 584 | validate_certs=validate_certs, 585 | timeout=module.params['timeout'], 586 | ) 587 | groups = json.loads(response.read()) 588 | # Return None if the user does not exist 589 | group_id = None 590 | # Say we have two groups: 'groupone' and 'grouptwo', a search on group will return both 591 | # So we will lopp over and see if the groupname returned matches the groupsname we searched for 592 | # If so, and we somehow found more than one raise an error 593 | for group in groups['value']: 594 | if group['groupName'] == group_name: 595 | if group_id is None: 596 | group_id = group['id'] 597 | else: 598 | module.fail_json(msg=("Found more than one group matching %s. Use vault_id instead" % (group_name))) 599 | # If we made it here we had 1 or 0 users, return them 600 | logging.debug("Resolved group_name %s to ID %s", group_name, group_id) 601 | return group_id 602 | 603 | except (HTTPError, httplib.HTTPException) as http_exception: 604 | module.fail_json(msg=( 605 | "Error while looking up group %s.\n*** end_point=%s\n ==> %s" 606 | % (group_name, url, to_text(http_exception))), 607 | payload={}, 608 | headers=headers, 609 | status_code=http_exception.code, 610 | ) 611 | except Exception as unknown_exception: 612 | module.fail_json(msg=( 613 | "Unknown error while looking up group %s.\n*** end_point=%s\n%s" 614 | % (group_name, url, to_text(unknown_exception))), 615 | payload={}, 616 | headers=headers, 617 | status_code=-1, 618 | ) 619 | 620 | 621 | def user_add_to_group(module): 622 | 623 | # Get username, and groupname from module parameters, and api base url 624 | # along with validate_certs from the cyberark_session established 625 | 626 | # Not needed for new version 627 | username = module.params["username"] 628 | group_name = module.params["group_name"] 629 | vault_id = module.params["vault_id"] 630 | member_type = ( 631 | "Vault" 632 | if module.params["member_type"] is None 633 | else module.params["member_type"] 634 | ) 635 | domain_name = module.params["domain_name"] if member_type == "domain" else None 636 | 637 | cyberark_session = module.params["cyberark_session"] 638 | api_base_url = cyberark_session["api_base_url"] 639 | validate_certs = cyberark_session["validate_certs"] 640 | 641 | # Prepare result, end_point, headers and payload 642 | result = {} 643 | headers = { 644 | "Content-Type": "application/json", 645 | "Authorization": cyberark_session["token"], 646 | "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" 647 | } 648 | 649 | # If we went "old school" and were provided a group_name instead of a vault_id we need to resolve it 650 | if group_name and not vault_id: 651 | # If we were given a group_name we need to lookup the vault_id 652 | vault_id = resolve_group_name_to_id(module) 653 | if vault_id is None: 654 | module.fail_json(msg="Unable to find a user group named {pgroupname}, please create that before adding a user to it".format(pgroupname=group_name)) 655 | 656 | end_point = ("/PasswordVault/api/UserGroups/{pvaultid}/Members").format(pvaultid=vault_id) 657 | 658 | # For some reason the group add uses username instead of id 659 | payload = {"memberId": username, "memberType": member_type} 660 | if domain_name: 661 | payload["domainName"] = domain_name 662 | 663 | url = construct_url(api_base_url, end_point) 664 | try: 665 | 666 | # execute REST action 667 | response = open_url( 668 | url, 669 | method="POST", 670 | headers=headers, 671 | data=json.dumps(payload), 672 | validate_certs=validate_certs, 673 | timeout=module.params['timeout'], 674 | ) 675 | 676 | result = {"result": {}} 677 | 678 | return (True, result, response.getcode()) 679 | 680 | except (HTTPError, httplib.HTTPException) as http_exception: 681 | 682 | exception_text = to_text(http_exception) 683 | exception_body = json.loads(http_exception.read().decode()) 684 | if http_exception.code == 409 and ("ITATS262E" in exception_text or exception_body.get("ErrorCode", "") == "PASWS213E"): 685 | # User is already member of Group 686 | return (False, None, http_exception.code) 687 | else: 688 | module.fail_json( 689 | msg=( 690 | "Error while performing user_add_to_group." 691 | "Please validate parameters provided." 692 | "\n*** end_point=%s\n ==> %s" 693 | % (url, exception_text) 694 | ), 695 | payload=payload, 696 | headers=headers, 697 | status_code=http_exception.code, 698 | response=http_exception.read().decode(), 699 | ) 700 | 701 | except Exception as unknown_exception: 702 | 703 | module.fail_json( 704 | msg=( 705 | "Unknown error while performing user_add_to_group." 706 | "\n*** end_point=%s\n%s" 707 | % (url, to_text(unknown_exception)) 708 | ), 709 | payload=payload, 710 | headers=headers, 711 | status_code=-1, 712 | ) 713 | 714 | 715 | def main(): 716 | 717 | module = AnsibleModule( 718 | argument_spec=dict( 719 | username=dict(type="str", required=True), 720 | state=dict(type="str", default="present", choices=["absent", "present"]), 721 | logging_level=dict( 722 | type="str", default="NOTSET", choices=["NOTSET", "DEBUG", "INFO"] 723 | ), 724 | logging_file=dict(type="str", default="/tmp/ansible_cyberark.log"), 725 | cyberark_session=dict(type="dict", required=True), 726 | initial_password=dict(type="str", no_log=True), 727 | new_password=dict(type="str", no_log=True), 728 | email=dict(type="str"), 729 | first_name=dict(type="str"), 730 | last_name=dict(type="str"), 731 | change_password_on_the_next_logon=dict(type="bool", default=False), 732 | expiry_date=dict(type="str"), 733 | user_type_name=dict(type="str"), 734 | disabled=dict(type="bool", default=False), 735 | location=dict(type="str"), 736 | group_name=dict(type="str"), 737 | vault_id=dict(type="int"), 738 | member_type=dict(type="str"), 739 | domain_name=dict(type="str"), 740 | timeout=dict(type="float", default=10), 741 | authorization=dict(type="list", elements="str", required=False, default=['AddSafes', 'AuditUsers']), 742 | ) 743 | ) 744 | 745 | if module.params["logging_level"] is not None: 746 | logging.basicConfig( 747 | filename=module.params["logging_file"], level=module.params["logging_level"] 748 | ) 749 | 750 | logging.info("Starting Module") 751 | 752 | state = module.params["state"] 753 | group_name = module.params["group_name"] 754 | vault_id = module.params["vault_id"] 755 | 756 | if state == "present": 757 | (changed, result, status_code) = user_details(module) 758 | 759 | if status_code == 200: 760 | # User already exists 761 | 762 | (changed, result, status_code) = user_add_or_update( 763 | module, "PUT", result["result"] 764 | ) 765 | 766 | elif status_code == 404: 767 | # User does not exist, proceed to create it 768 | (changed, result, status_code) = user_add_or_update(module, "POST", None) 769 | 770 | # Add user to group if needed 771 | if group_name is not None or vault_id is not None: 772 | (group_change, no_result, no_status_code) = user_add_to_group(module) 773 | changed = changed or group_change 774 | 775 | elif state == "absent": 776 | (changed, result, status_code) = user_delete(module) 777 | 778 | module.exit_json(changed=changed, cyberark_user=result, status_code=status_code) 779 | 780 | 781 | if __name__ == "__main__": 782 | main() 783 | -------------------------------------------------------------------------------- /roles/aimprovider/README.md: -------------------------------------------------------------------------------- 1 | cyberark.pas.aimprovider 2 | ==================== 3 | 4 | Role to install/uninstall CyberArk's AIM Credential Provider. 5 | 6 | Requirements 7 | ------------ 8 | 9 | - CyberArk Privileged Account Security Web Services SDK. 10 | - `cyberark.pas` Collection from Ansible Galaxy or Automation Hub 11 | 12 | Role Variables 13 | -------------- 14 | ``` 15 | # CyberArk's Privileged Account Security Web Services SDK api base URL (example: https://components.cyberark.local) 16 | aimprovider_rest_api_url: "" 17 | 18 | # Whether to validate certificates for REST api calls. If false, SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. 19 | aimprovider_validate_certs: true 20 | 21 | # Zip file with distribution of AIM Provider (example: /tmp/binaries/RHELinux x64-Rls-v9.8.zip); this file is located in the Ansible server, and it will be copied to the Ansible nodes. It should point to the current version of AIM distribution to be used when delivering to the nodes in a central folder within the Ansible server. 22 | aimprovider_aimprovider_zip_file_name: "" 23 | 24 | # Folder name within the ZIP file that will be used. By default, it's taken from zip file name, for example: "RHELinux x64" 25 | aimprovider_folder_name: '{{aimprovider_zip_file_name.split("/")[-1].split("-Rls")[0]}}' 26 | 27 | # CyberArk location for App Provider user to be created 28 | aimprovider_app_provider_user_location: "\\Applications" 29 | 30 | # CyberArk Vault Address 31 | aimprovider_vault_address: "" 32 | 33 | # Whether to use shared logon authentication. If true, it will use the "Shared Logon Authentication" as described in the CyberArk's document "Privileged Account Security Web Services SDK Implementation Guide" 34 | aimprovider_use_shared_logon_authentication: false 35 | 36 | # aimprovider_state - can be "present"/"absent" for install/uninstall. 37 | aimprovider_state: "present" 38 | ``` 39 | 40 | 41 | Additionally: 42 | - **app_provider_user_group**: The name of the group the Provider user will be added to. 43 | 44 | Dependencies 45 | ------------ 46 | 47 | None. 48 | 49 | 50 | Example Playbook 51 | ---------------- 52 | 53 | 1) Install CyberArk AIM Provider. 54 | 55 | ``` 56 | --- 57 | - hosts: all 58 | 59 | roles: 60 | 61 | - role: cyberark.pas.aimprovider 62 | aimprovider_api_base_url: "https://components.cyberark.local" 63 | aimprovider_validate_certs: false 64 | aimprovider_zip_file_name: "/tmp/binaries/RHELinux x64-Rls-v9.8.zip" 65 | aimprovider_vault_address: "10.0.1.10" 66 | aimprovider_use_shared_logon_authentication: true 67 | ``` 68 | 69 | 2) Uninstall CyberArk AIM Provider. 70 | ``` 71 | --- 72 | - hosts: all 73 | 74 | roles: 75 | 76 | - role: cyberark.pas.aimprovider 77 | aimprovider_api_base_url: "https://components.cyberark.local" 78 | aimprovider_use_shared_logon_authentication: true 79 | aimprovider_state: "absent" 80 | aimprovider_validate_certs: false 81 | ``` 82 | 83 | License 84 | ------- 85 | 86 | MIT 87 | 88 | Author Information 89 | ------------------ 90 | 91 | - Edward Nunez (edward.nunez@cyberark.com) 92 | -------------------------------------------------------------------------------- /roles/aimprovider/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Default values for possible input parameters 4 | 5 | # CyberArk's Privileged Account Security Web Services SDK api base URL 6 | aimprovider_rest_api_url: "" 7 | 8 | # Whether to validate certificates for REST api calls 9 | aimprovider_validate_certs: true 10 | 11 | # Zip file with distribution of AIM Provider 12 | aimprovider_zip_file_name: "" 13 | 14 | # Folder name within the ZIP file that will be used by default is taken from zip file name. 15 | aimprovider_folder_name: '{{ aimprovider_zip_file_name.split("/")[-1].split("-Rls")[0] }}' 16 | 17 | # CyberArk location for App Provider user to be created 18 | aimprovider_app_provider_user_location: "\\Applications" 19 | 20 | # CyberArk Vault Address 21 | aimprovider_vault_address: "" 22 | 23 | # Whether to use shared logon authentication 24 | aimprovider_use_shared_logon_authentication: false 25 | 26 | # State - the state of the provider: present mean installing the provide and Absent means uninstalling 27 | aimprovider_state: "present" 28 | -------------------------------------------------------------------------------- /roles/aimprovider/tasks/installAIMProvider.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Validating Role Parameters 3 | ansible.builtin.assert: 4 | that: 5 | - aimprovider_zip_file_name != '' 6 | - aimprovider_vault_address != '' 7 | - aimprovider_api_base_url != '' 8 | - aimprovider_folder_name != '' 9 | - aimprovider_app_provider_user_location != '' 10 | - aimprovider_state == "present" 11 | - (not aimprovider_use_shared_logon_authentication|default(false) and password_object is defined) or 12 | (aimprovider_use_shared_logon_authentication|default(false) and password_object is not defined) 13 | 14 | 15 | - name: Debug Message 16 | ansible.builtin.debug: 17 | msg: "Installation params => aimprovider_zip_file_name = {{ aimprovider_zip_file_name }} aimprovider_aimprovider_folder_name={{ aimprovider_folder_name }}" 18 | 19 | - name: Execution block 20 | block: 21 | 22 | - name: Copy provider zip to target and unzip 23 | ansible.builtin.unarchive: 24 | src: "{{ aimprovider_zip_file_name }}" 25 | dest: /tmp 26 | 27 | - name: Rename aimparms and copy to var/tmp 28 | ansible.builtin.command: cp "/tmp/{{ aimprovider_folder_name }}/aimparms.sample" /var/tmp/aimparms 29 | args: 30 | creates: /var/tmp/aimparms 31 | 32 | - name: Replace parameters" in /var/tmp/aimparms 33 | ansible.builtin.replace: 34 | dest: /var/tmp/aimparms 35 | regexp: "{{ item.regexp }}" 36 | replace: "{{ item.replace }}" 37 | with_items: 38 | - {regexp: '^AcceptCyberArkEULA=.*$', replace: 'AcceptCyberArkEULA=Yes'} 39 | - {regexp: '^LicensedProducts=.*$', replace: 'LicensedProducts=AIM'} 40 | - {regexp: '^.?CreateVaultEnvironment=.*$', replace: 'CreateVaultEnvironment=no'} 41 | - {regexp: '^VaultFilePath=.*$', replace: 'VaultFilePath=/tmp/{{ aimprovider_folder_name }}/Vault.ini'} 42 | 43 | 44 | - name: Change Vault.ini to the right address 45 | ansible.builtin.replace: 46 | dest: "/tmp/{{ aimprovider_folder_name }}/Vault.ini" 47 | regexp: '^ADDRESS=.*$' 48 | replace: 'ADDRESS={{ aimprovider_vault_address }}' 49 | 50 | - name: Change permission on createcredfile 51 | ansible.builtin.file: 52 | path: "/tmp/{{ aimprovider_folder_name }}/CreateCredFile" 53 | mode: "0755" 54 | 55 | - name: Find rpm 56 | ansible.builtin.find: 57 | paths: "/tmp/{{ aimprovider_folder_name }}" 58 | patterns: "CARKaim-*.rpm" 59 | register: aimrpm 60 | 61 | - name: Debug RPM file path 62 | ansible.builtin.debug: 63 | msg: "RPM file path: {{ aimrpm.files[0].path }}" 64 | 65 | - name: Install Provider 66 | ansible.builtin.package: 67 | name: "{{ aimrpm.files[0].path }}" 68 | state: present 69 | 70 | - name: Verify status of service after installing Provider 71 | ansible.builtin.service: 72 | name: aimprv 73 | state: started 74 | register: service_status 75 | failed_when: service_status.status != 0 76 | ignore_errors: true 77 | 78 | # debug: msg="status of service RC={{command_result.rc}}" 79 | 80 | - name: Logon to CyberArk Vault using PAS Web Services SDK - use_shared_logon_authentication 81 | cyberark.pas.cyberark_authentication: 82 | api_base_url: "{{ aimprovider_api_base_url }}" 83 | use_shared_logon_authentication: true 84 | validate_certs: "{{ aimprovider_validate_certs }}" 85 | changed_when: false 86 | when: (command_result.rc != 0 and aimprovider_use_shared_logon_authentication|default(false)) 87 | 88 | - name: Logon to CyberArk Vault using PAS Web Services SDK - Not use_shared_logon_authentication 89 | cyberark.pas.cyberark_authentication: 90 | api_base_url: "{{ aimprovider_api_base_url }}" 91 | username: "{{ password_object.password }}" 92 | password: "{{ password_object.passprops.username }}" 93 | validate_certs: "{{ aimprovider_validate_certs }}" 94 | changed_when: false 95 | 96 | - name: Debug CyberArk session token 97 | ansible.builtin.debug: 98 | msg: "{{ cyberark_session.token }}" 99 | when: (command_result.rc != 0) 100 | # msg: "{{ cyberark_session.token }}" 101 | # when: (command_result.rc != 0) 102 | 103 | - name: Create provider user 104 | cyberark.pas.cyberark_user: 105 | username: "Prov_{{ ansible_hostname }}" 106 | initial_password: "Cyberark1" 107 | user_type_name: "AppProvider" 108 | location: "{{ aimprovider_app_provider_user_location }}" 109 | group_name: "{{ app_provider_user_group }}" 110 | change_password_on_the_next_logon: false 111 | state: present 112 | cyberark_session: "{{ cyberark_session }}" 113 | register: cyberarkaction 114 | 115 | - name: Debug user creation status 116 | ansible.builtin.debug: 117 | msg: "USERCREATED => {{ cyberarkaction }}" 118 | when: (command_result.rc != 0 and cyberarkaction.status_code == 201) 119 | # msg: "USERCREATED => {{cyberarkaction}}" 120 | # when: (command_result.rc != 0 and cyberarkaction.status_code == 201) 121 | 122 | - name: Reset provider user credential 123 | cyberark.pas.cyberark_user: 124 | username: "Prov_{{ ansible_hostname }}" 125 | new_password: "Cyberark1" 126 | disabled: false 127 | state: present 128 | cyberark_session: "{{ cyberark_session }}" 129 | register: cyberarkaction 130 | when: (command_result.rc != 0 and cyberarkaction.status_code == 200) 131 | 132 | - name: Logoff from CyberArk Vault 133 | cyberark.pas.cyberark_authentication: 134 | state: absent 135 | cyberark_session: "{{ cyberark_session }}" 136 | changed_when: false 137 | when: (command_result.rc != 0) 138 | 139 | - name: Create Provider Initial Cred File 140 | ansible.builtin.command: 141 | cmd: /opt/CARKaim/bin/createcredfile /etc/opt/CARKaim/vault/appprovideruser.cred Password -Username Prov_{{ ansible_hostname }} -Password Cyberark1 142 | creates: /etc/opt/CARKaim/vault/appprovideruser.cred 143 | when: (command_result.rc != 0) 144 | 145 | - name: Set vault.ini Into Place 146 | ansible.builtin.command: 147 | cmd: cp "/tmp/{{ aimprovider_folder_name }}/Vault.ini" /etc/opt/CARKaim/vault/vault.ini 148 | creates: /etc/opt/CARKaim/vault/vault.ini 149 | 150 | - name: Start Provider Service 151 | ansible.builtin.service: 152 | name: aimprv 153 | state: started 154 | when: (command_result.rc != 0) 155 | 156 | - name: Remove /tmp/{{ aimprovider_folder_name }} 157 | ansible.builtin.file: 158 | path: '/tmp/{{ aimprovider_folder_name }}' 159 | state: absent 160 | 161 | - name: Remove /var/tmp/aimparms 162 | ansible.builtin.file: 163 | path: '/var/tmp/aimparms' 164 | state: absent 165 | 166 | rescue: 167 | 168 | - name: Remove /tmp/{{ aimprovider_folder_name }} 169 | ansible.builtin.file: 170 | path: '/tmp/{{ aimprovider_folder_name }}' 171 | state: absent 172 | 173 | - name: Failure to install 174 | ansible.builtin.fail: 175 | msg: "AIM Credential Provider Installation failed!" 176 | -------------------------------------------------------------------------------- /roles/aimprovider/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Validating Role Parameters 3 | ansible.builtin.assert: 4 | that: 5 | - aimprovider_api_base_url != '' 6 | - aimprovider_state in ["present", "absent"] 7 | - (not aimprovider_use_shared_logon_authentication|default(false) and password_object is defined) or 8 | (aimprovider_use_shared_logon_authentication|default(false) and password_object is not defined) 9 | 10 | - name: Verify status of aimprv service initially 11 | ansible.builtin.service: 12 | name: aimprv 13 | state: started 14 | register: service_already_running 15 | ignore_errors: true 16 | changed_when: false 17 | 18 | - name: Debug Message 19 | ansible.builtin.debug: 20 | msg: "status of service RC={{ service_already_running.rc }}" 21 | 22 | - name: Import installAIMProvider tasks 23 | ansible.builtin.import_tasks: installAIMProvider.yml 24 | when: (aimprovider_state == "present" and service_already_running.rc != 0) 25 | 26 | - name: Import uninstallAIMProvider tasks 27 | ansible.builtin.import_tasks: uninstallAIMProvider.yml 28 | when: (aimprovider_state == "absent" and service_already_running.rc == 0) 29 | -------------------------------------------------------------------------------- /roles/aimprovider/tasks/uninstallAIMProvider.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Uninstall AIM Provider Block 3 | block: 4 | 5 | - name: Uninstall Provider 6 | ansible.builtin.package: 7 | name: 'CARKaim' 8 | state: absent 9 | 10 | - name: Logon to CyberArk Vault using PAS Web Services SDK - aimprovider_use_shared_logon_authentication 11 | cyberark.pas.cyberark_authentication: 12 | api_base_url: "{{ aimprovider_api_base_url }}" 13 | use_shared_logon_authentication: true 14 | validate_certs: "{{ aimprovider_validate_certs }}" 15 | when: (aimprovider_use_shared_logon_authentication) 16 | 17 | - name: Logon to CyberArk Vault using PAS Web Services SDK - Not aimprovider_use_shared_logon_authentication 18 | cyberark.pas.cyberark_authentication: 19 | api_base_url: "{{ aimprovider_api_base_url }}" 20 | username: "{{ password_object.password }}" 21 | password: "{{ password_object.passprops.username }}" 22 | validate_certs: "{{ aimprovider_validate_certs }}" 23 | changed_when: false 24 | when: (not aimprovider_use_shared_logon_authentication) 25 | 26 | # name: Debug message 27 | # debug: 28 | # msg: "{{ cyberark_session }}" 29 | # when: (cyberark_session.token is defined) 30 | 31 | - name: Remove Provider User 32 | cyberark.pas.cyberark_user: 33 | username: "Prov_{{ ansible_hostname }}" 34 | state: absent 35 | cyberark_session: "{{ cyberark_session }}" 36 | register: cyberarkaction 37 | ignore_errors: true 38 | when: (cyberark_session.token is defined) 39 | 40 | # debug: 41 | # msg: "USERDETAILS => {{cyberarkaction}}" 42 | # when: (cyberarkaction.status_code == 200) 43 | 44 | - name: Logoff from CyberArk Vault 45 | cyberark.pas.cyberark_authentication: 46 | state: absent 47 | cyberark_session: "{{ cyberark_session }}" 48 | changed_when: false 49 | when: (cyberark_session.token is defined) 50 | 51 | - name: Remove /etc/opt/CARKaim 52 | ansible.builtin.file: 53 | path: '/etc/opt/CARKaim' 54 | state: absent 55 | 56 | - name: Remove /var/opt/CARKaim 57 | ansible.builtin.file: 58 | path: '/var/opt/CARKaim' 59 | state: absent 60 | 61 | rescue: 62 | 63 | - name: Fail the task if uninstall fails 64 | ansible.builtin.fail: 65 | msg: "AIM Credential Provider Uninstall failed!" 66 | -------------------------------------------------------------------------------- /rulebooks/cyberark_test_rule.yml: -------------------------------------------------------------------------------- 1 | - name: Demo rules with CyberArk syslog as source 2 | hosts: localhost 3 | sources: 4 | - cyberark.pas.syslog: 5 | host: 0.0.0.0 6 | port: 1514 7 | rules: 8 | - name: Check For User Suspension Event, Then Disable The User and Notify 9 | condition: event.cyberark.syslog.audit_record.Severity == "Error" and event.cyberark.syslog.audit_record.MessageID == "5" 10 | action: 11 | run_playbook: 12 | name: disable_user.yml 13 | extra_vars: 14 | username: "{{ event.cyberark.syslog.audit_record.Issuer }}" 15 | - name: Check For PTA irregular IP OR irregular Hours Access and Notify 16 | condition: event.cyberark.DeviceEventClassID == "25" or event.cyberark.DeviceEventClassID == "23" 17 | action: 18 | run_playbook: 19 | name: pta_disable_notify.yml 20 | extra_vars: 21 | username: "{{ event.cyberark.suser }}" 22 | eventname: "{{ event.cyberark.DeviceName }}" 23 | eventurl: "{{ event.cyberark.PTALink }}" 24 | station: "{{ event.cyberark.shost }}" 25 | -------------------------------------------------------------------------------- /rulebooks/disable_pas_user_kafka.yml: -------------------------------------------------------------------------------- 1 | - name: Demo rules with kafka as source 2 | hosts: localhost 3 | sources: 4 | - cyberark.eda.kafka: 5 | topic: ansible 6 | host: localhost 7 | port: 9092 8 | rules: 9 | - name: Check For User Suspension Event, Then Disable The User and Notify 10 | condition: event.body.syslog.audit_record.Severity == "Error" and event.body.syslog.audit_record.MessageID == "5" 11 | action: 12 | run_playbook: 13 | name: ../../../cyberark/pas/tests/disable_user.yml 14 | extra_vars: 15 | username: "{{ event.body.syslog.audit_record.Issuer }}" 16 | -------------------------------------------------------------------------------- /rulebooks/disable_pas_user_webhook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Demo rules with webhook as source 3 | hosts: all 4 | sources: 5 | - cyberark.eda.webhook: 6 | host: 0.0.0.0 7 | port: 5000 8 | 9 | rules: 10 | - name: Listen For User Suspension Event Webhook, Then Disble The User 11 | condition: event.payload.syslog.audit_record.Severity == "Error" and event.payload.syslog.audit_record.MessageID == "5" 12 | action: 13 | run_playbook: 14 | name: ../../../cyberark/pas/tests/disable_user.yml 15 | extra_vars: 16 | username: "{{ event.payload.syslog.audit_record.Issuer }}" 17 | -------------------------------------------------------------------------------- /rulebooks/disable_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Disable a CyberArk user 3 | hosts: all 4 | connection: local 5 | 6 | tasks: 7 | 8 | - name: Logon to CyberArk Vault using PAS Web Services SDK 9 | cyberark.pas.cyberark_authentication: 10 | api_base_url: "https://BASE_URL" 11 | validate_certs: false 12 | username: "USERNAME" 13 | password: "PASSWORD" 14 | 15 | - name: Disabling a CyberArk User 16 | cyberark.pas.cyberark_user: 17 | username: "{{ username }}" # this is password from the running yml when condition is met 18 | disabled: true 19 | cyberark_session: "{{ cyberark_session }}" 20 | register: cyberarkaction 21 | 22 | - name: Debug message 23 | ansible.builtin.debug: 24 | var: cyberarkaction 25 | 26 | - name: Logoff from CyberArk Vault 27 | cyberark.pas.cyberark_authentication: 28 | state: absent 29 | cyberark_session: "{{ cyberark_session }}" 30 | 31 | # - name: Sending an e-mail using Gmail SMTP servers 32 | # community.general.mail: 33 | # host: SMTPSERVER 34 | # port: PORT 35 | # username: username@mail.com 36 | # password: password 37 | # to: First Last 38 | # subject: Ansible-Rulebook Report 39 | # body: Ansible Rulebook disabled Cyberark user '{{ username }}' due to too many login attempts. 40 | # delegate_to: localhost 41 | -------------------------------------------------------------------------------- /rulebooks/inventory.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ungrouped: 3 | hosts: 4 | localhost: 5 | ansible_host: 127.0.0.1 6 | -------------------------------------------------------------------------------- /rulebooks/pta_disable_notify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Disable CyberArk User Notification 3 | hosts: all 4 | connection: local 5 | 6 | tasks: 7 | 8 | - name: Logon to CyberArk Vault using PAS Web Services SDK 9 | cyberark.pas.cyberark_authentication: 10 | api_base_url: "https://BASE_URL" 11 | validate_certs: false 12 | username: "USERNAME" 13 | password: "PASSWORD" 14 | 15 | - name: Disabling a CyberArk User 16 | cyberark.pas.cyberark_user: 17 | username: "{{ username | regex_search('.+?(?=\\()') }}" 18 | disabled: true 19 | cyberark_session: "{{ cyberark_session }}" 20 | register: cyberarkaction 21 | 22 | - name: Debug message 23 | ansible.builtin.debug: 24 | var: cyberarkaction 25 | 26 | - name: Logoff from CyberArk Vault 27 | cyberark.pas.cyberark_authentication: 28 | state: absent 29 | cyberark_session: "{{ cyberark_session }}" 30 | 31 | # - name: Sending an e-mail using Gmail SMTP servers 32 | # community.general.mail: 33 | # host: SMTPSERVER 34 | # port: PORT 35 | # username: username@mail.com 36 | # password: password 37 | # to: First Last 38 | # subject: Ansible-Rulebook Report 39 | # body: > 40 | # Ansible Rulebook notify of PTA Event {{ username }} - {{ eventname }} - 41 | # from host {{ station }} - For more info please visit - 42 | # {{ eventurl }} - user disabled! 43 | # delegate_to: localhost 44 | -------------------------------------------------------------------------------- /rulebooks/pta_notify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Notify PTA Event 3 | hosts: all 4 | connection: local 5 | 6 | tasks: 7 | 8 | # - name: Sending an e-mail using Gmail SMTP servers 9 | # community.general.mail.mail: 10 | # host: SMTPSERVER 11 | # port: PORT 12 | # username: username@mail.com 13 | # password: password 14 | # to: First Last 15 | # subject: Ansible-Rulebook Report 16 | # body: > 17 | # Ansible Rulebook notify of PTA Event 18 | # '{{ username | ansible.builtin.regex_search('^[a-zA-Z0-9_]+') }}' 19 | # '{{ eventname }}' from host '{{ station }}'. 20 | # For more info, please visit - '{{ eventurl }}' 21 | # delegate_to: localhost 22 | -------------------------------------------------------------------------------- /tests/change_test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Rotate Credential 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | username: "bizdev" 11 | password: "Cyberark1" 12 | 13 | - name: Rotate credential via reconcile and providing the password to be changed to 14 | cyberark.pas.cyberark_account: 15 | safe: "Test" 16 | identified_by: "address,username" 17 | address: "prod.cyberark.local" 18 | username: "admin" 19 | platform_id: WinDomain 20 | platform_account_properties: 21 | ReconcileAccount: "Operating System-WinServerLocal-cyberark.local-administrator-x" 22 | LogonDomain: "PROD" 23 | secret_management: 24 | automatic_management_enabled: true 25 | state: present 26 | cyberark_session: "{{ cyberark_session }}" 27 | register: reconcileaccount 28 | 29 | - name: Logoff from CyberArk Vault 30 | cyberark.pas.cyberark_authentication: 31 | state: absent 32 | cyberark_session: "{{ cyberark_session }}" 33 | -------------------------------------------------------------------------------- /tests/changepolicy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Change Account Policy 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | username: "bizdev" 11 | password: "Cyberark1" 12 | 13 | 14 | - name: Debug message 15 | ansible.builtin.debug: 16 | var: cyberark_session 17 | 18 | - name: Account 19 | cyberark.pas.cyberark_account: 20 | identified_by: "address,username" 21 | safe: "Test" 22 | address: "cyberark.local" 23 | username: "cyberark-administrator" 24 | platform_id: WinDomain-Level2 25 | cyberark_session: "{{ cyberark_session }}" 26 | register: cyberarkaction 27 | 28 | - name: Debug message 29 | ansible.builtin.debug: 30 | var: cyberarkaction 31 | 32 | - name: Logoff from CyberArk Vault 33 | cyberark.pas.cyberark_authentication: 34 | state: absent 35 | cyberark_session: "{{ cyberark_session }}" 36 | 37 | - name: Debug message 38 | ansible.builtin.debug: 39 | var: cyberark_session 40 | -------------------------------------------------------------------------------- /tests/deprovision_account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Deprovision CyberArk Account 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | 15 | - name: Debug message 16 | ansible.builtin.debug: 17 | var: cyberark_session 18 | 19 | - name: Account 20 | cyberark.pas.cyberark_account: 21 | logging_level: DEBUG 22 | identified_by: "address,username" 23 | safe: "Test" 24 | address: "cyberark.local" 25 | username: "cyberark-administrator" 26 | state: absent 27 | cyberark_session: "{{ cyberark_session }}" 28 | register: cyberarkaction 29 | 30 | - name: Debug message 31 | ansible.builtin.debug: 32 | var: cyberarkaction 33 | 34 | - name: Logoff from CyberArk Vault 35 | cyberark.pas.cyberark_authentication: 36 | state: absent 37 | cyberark_session: "{{ cyberark_session }}" 38 | 39 | - name: Debug message 40 | ansible.builtin.debug: 41 | var: cyberark_session 42 | -------------------------------------------------------------------------------- /tests/deprovision_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Deprovision a CyberArk User 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | 15 | - name: Debug message 16 | ansible.builtin.debug: 17 | var: cyberark_session 18 | 19 | - name: Removing a CyberArk User 20 | cyberark.pas.cyberark_user: 21 | username: "ansibleuser" 22 | state: absent 23 | cyberark_session: "{{ cyberark_session }}" 24 | register: cyberarkaction 25 | 26 | - name: Debug message 27 | ansible.builtin.debug: 28 | var: cyberarkaction 29 | 30 | 31 | - name: Logoff from CyberArk Vault 32 | cyberark.pas.cyberark_authentication: 33 | state: absent 34 | cyberark_session: "{{ cyberark_session }}" 35 | 36 | - name: Debug message 37 | ansible.builtin.debug: 38 | var: cyberark_session 39 | -------------------------------------------------------------------------------- /tests/disable_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Disable a CyberArk User 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | - name: Disabling a CyberArk User 15 | cyberark.pas.cyberark_user: 16 | username: "ansibleuser" 17 | disabled: true 18 | cyberark_session: "{{ cyberark_session }}" 19 | register: cyberarkaction 20 | 21 | - name: Debug message 22 | ansible.builtin.debug: 23 | var: cyberarkaction 24 | 25 | 26 | - name: Logoff from CyberArk Vault 27 | cyberark.pas.cyberark_authentication: 28 | state: absent 29 | cyberark_session: "{{ cyberark_session }}" 30 | -------------------------------------------------------------------------------- /tests/enable_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable CyberArk User 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | - name: Enabling a CyberArk User and forcing a password change at next logon 15 | cyberark.pas.cyberark_user: 16 | username: "ansibleuser" 17 | disabled: false 18 | state: present 19 | change_password_on_the_next_logon: true 20 | cyberark_session: "{{ cyberark_session }}" 21 | register: cyberarkaction 22 | 23 | - name: Debug message 24 | ansible.builtin.debug: 25 | var: cyberarkaction 26 | 27 | - name: Logoff from CyberArk Vault 28 | cyberark.pas.cyberark_authentication: 29 | state: absent 30 | cyberark_session: "{{ cyberark_session }}" 31 | -------------------------------------------------------------------------------- /tests/provision_account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Provision CyberArk Account 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | - name: Account 15 | cyberark.pas.cyberark_account: 16 | identified_by: "address,username" 17 | safe: "Test" 18 | address: "cyberark.local" 19 | username: "cyberark-administrator" 20 | platform_id: WinDomain-Level2 21 | secret: "CyberarkFirst" 22 | platform_account_properties: 23 | LogonDomain: "RedHatAnsible" 24 | OwnerName: "James Stutes" 25 | Port: 8080 26 | secret_management: 27 | automatic_management_enabled: true 28 | state: present 29 | cyberark_session: "{{ cyberark_session }}" 30 | register: cyberarkaction 31 | 32 | - name: Debug message 33 | ansible.builtin.debug: 34 | var: cyberarkaction 35 | 36 | - name: Logoff from CyberArk Vault 37 | cyberark.pas.cyberark_authentication: 38 | state: absent 39 | cyberark_session: "{{ cyberark_session }}" 40 | -------------------------------------------------------------------------------- /tests/provision_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Provision CyberArk User 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | - name: Creating a CyberArk User, setting a simple password but forcing a password change at next logon 15 | cyberark.pas.cyberark_user: 16 | username: "ansibleuser" 17 | first_name: "Ansible" 18 | last_name: "User" 19 | email: "ansibleuser@demo.com" 20 | initial_password: "Cyberark1" 21 | user_type_name: "EPVUser" 22 | group_name: "AnsibleAdmins" 23 | disabled: false 24 | state: present 25 | cyberark_session: "{{ cyberark_session }}" 26 | register: cyberarkaction 27 | 28 | - name: Debug message 29 | ansible.builtin.debug: 30 | var: cyberarkaction 31 | 32 | - name: Logoff from CyberArk Vault 33 | cyberark.pas.cyberark_authentication: 34 | state: absent 35 | cyberark_session: "{{ cyberark_session }}" 36 | -------------------------------------------------------------------------------- /tests/reset_user_password.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Reset CyberArk User Password 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | - name: Enabling a CyberArk User and forcing a password change at next logon 15 | cyberark.pas.cyberark_user: 16 | username: "ansibleuser" 17 | disabled: false 18 | new_password: Cyberark1 19 | state: present 20 | change_password_on_the_next_logon: true 21 | cyberark_session: "{{ cyberark_session }}" 22 | register: cyberarkaction 23 | 24 | - name: Debug message 25 | ansible.builtin.debug: 26 | var: cyberarkaction 27 | 28 | - name: Logoff from CyberArk Vault 29 | cyberark.pas.cyberark_authentication: 30 | state: absent 31 | cyberark_session: "{{ cyberark_session }}" 32 | -------------------------------------------------------------------------------- /tests/retrieve_account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Retrieve account from CyberArk 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | 15 | - name: Retrieve account and password 16 | cyberark.pas.cyberark_account: 17 | identified_by: "address,username" 18 | safe: "Test" 19 | address: "cyberark.local" 20 | username: "cyberark-administrator" 21 | state: retrieve 22 | cyberark_session: "{{ cyberark_session }}" 23 | register: retrieveaccount 24 | 25 | - name: Debug message 26 | ansible.builtin.debug: 27 | var: retrieveaccount 28 | 29 | - name: Logoff from CyberArk Vault 30 | cyberark.pas.cyberark_authentication: 31 | state: absent 32 | cyberark_session: "{{ cyberark_session }}" 33 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Manage CyberArk Vault 3 | hosts: localhost 4 | 5 | tasks: 6 | 7 | - name: Logon to CyberArk Vault using PAS Web Services SDK 8 | cyberark.pas.cyberark_authentication: 9 | api_base_url: "http://components.cyberark.local" 10 | validate_certs: false 11 | username: "bizdev" 12 | password: "Cyberark1" 13 | 14 | 15 | - name: Debug message 16 | ansible.builtin.debug: 17 | var: cyberark_session 18 | 19 | - name: User 20 | cyberark.pas.cyberark_user: 21 | username: "testuser" 22 | initial_password: "Cyberark1" 23 | user_type_name: "EPVUser" 24 | change_password_on_the_next_logon: false 25 | group_name: "Auditors" 26 | disabled: false 27 | state: present 28 | cyberark_session: "{{ cyberark_session }}" 29 | register: cyberarkaction 30 | 31 | - name: Debug message 32 | ansible.builtin.debug: 33 | var: cyberarkaction 34 | 35 | - name: Account 36 | cyberark.pas.cyberark_account: 37 | # logging_level: DEBUG 38 | identified_by: "address,username" 39 | name: "EDWARD_ACCOUNT" 40 | safe: "Test" 41 | address: "10.0.1.20" 42 | username: "james_test" 43 | platform_id: WinServerLocal 44 | platform_account_properties: 45 | LogonDomain: "10.0.1.20" 46 | secret_management: 47 | automatic_management_enabled: false 48 | manual_management_reason: "No Reason" 49 | state: present 50 | cyberark_session: "{{ cyberark_session }}" 51 | register: cyberarkaction 52 | 53 | - name: Debug message 54 | ansible.builtin.debug: 55 | var: cyberarkaction 56 | 57 | - name: Logoff from CyberArk Vault 58 | cyberark.pas.cyberark_authentication: 59 | state: absent 60 | cyberark_session: "{{ cyberark_session }}" 61 | 62 | - name: Debug message 63 | ansible.builtin.debug: 64 | var: cyberark_session 65 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = ruff, darglint, pylint 3 | skipsdist = true 4 | requires = 5 | ruff 6 | darglint 7 | pylint 8 | 9 | [testenv] 10 | allowlist_externals=* 11 | commands = 12 | {envpython} --version 13 | 14 | [testenv:ruff] 15 | deps = ruff 16 | commands = 17 | bash -c 'ruff check --exclude .tox --select ALL --ignore ANN401,ANN202,S104,ASYNC110,INP001,FA102,UP001,UP010,I001,FA100,PLR0913,E501 -q extensions/eda/plugins' 18 | 19 | [testenv:darglint] 20 | deps = darglint 21 | commands = 22 | bash -c 'darglint -s numpy -z full extensions/eda/plugins' 23 | 24 | [testenv:pylint] 25 | deps = pylint 26 | commands = 27 | bash -c 'find ./extensions/eda/plugins -name "*.py" -print0 | xargs -0 pylint --output-format=parseable -sn --disable R0801,E0401,C0103,R0913,R0902,R0903' 28 | --------------------------------------------------------------------------------