├── .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 | 
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 | 
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 | 
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 | 
76 |
77 | ## CyberArk to Rsyslog to EDA Kafka Topic
78 |
79 | 
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 | 
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 | 
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 | 
146 |
147 |
148 | ## CyberArk PTA Syslog to EDA event source (UDP Protocol)
149 |
150 | 
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 | 
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------