#!/usr/bin/python # coding: utf-8 -*- # Copyright: (c) 2017, Wayne Witzel III # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = ''' --- module: credential author: "Wayne Witzel III (@wwitzel3)" short_description: create, update, or destroy Automation Platform Controller credential. description: - Create, update, or destroy Automation Platform Controller credentials. See U(https://www.ansible.com/tower) for an overview. options: name: description: - The name to use for the credential. required: True type: str new_name: description: - Setting this option will change the existing name (looked up via the name field. required: False type: str copy_from: description: - Name or id to copy the credential from. - This will copy an existing credential and change any parameters supplied. - The new credential name will be the one provided in the name parameter. - The organization parameter is not used in this, to facilitate copy from one organization to another. - Provide the id or use the lookup plugin to provide the id if multiple credentials share the same name. type: str description: description: - The description to use for the credential. type: str organization: description: - Organization that should own the credential. type: str credential_type: description: - The credential type being created. - Can be a built-in credential type such as "Machine", or a custom credential type such as "My Credential Type" type: str inputs: description: - >- Credential inputs where the keys are var names used in templating. Refer to the Automation Platform Controller documentation for example syntax. - authorize (use this for net type) - authorize_password (password for net credentials that require authorize) - client (client or application ID for azure_rm type) - security_token (STS token for aws type) - secret (secret token for azure_rm type) - tenant (tenant ID for azure_rm type) - subscription (subscription ID for azure_rm type) - domain (domain for openstack type) - become_method (become method to use for privilege escalation; some examples are "None", "sudo", "su", "pbrun") - become_username (become username; use "ASK" and launch job to be prompted) - become_password (become password; use "ASK" and launch job to be prompted) - vault_password (the vault password; use "ASK" and launch job to be prompted) - project (project that should use this credential for GCP) - host (the host for this credential) - username (the username for this credential; ``access_key`` for AWS) - password (the password for this credential; ``secret_key`` for AWS, ``api_key`` for RAX) - ssh_key_data (SSH private key content; to extract the content from a file path, use the lookup function (see examples)) - vault_id (the vault identifier; this parameter is only valid if C(kind) is specified as C(vault).) - ssh_key_unlock (unlock password for ssh_key; use "ASK" and launch job to be prompted) type: dict update_secrets: description: - C(true) will always update encrypted values. - C(false) will only updated encrypted values if a change is absolutely known to be needed. type: bool default: true user: description: - User that should own this credential. type: str team: description: - Team that should own this credential. type: str state: description: - Desired state of the resource. choices: ["present", "absent"] default: "present" type: str extends_documentation_fragment: awx.awx.auth notes: - Values `inputs` and the other deprecated fields (such as `tenant`) are replacements of existing values. See the last 4 examples for details. ''' EXAMPLES = ''' - name: Add machine credential credential: name: Team Name description: Team Description organization: test-org credential_type: Machine state: present controller_config_file: "~/tower_cli.cfg" - name: Create a valid SCM credential from a private_key file credential: name: SCM Credential organization: Default state: present credential_type: Source Control inputs: username: joe password: secret ssh_key_data: "{{ lookup('file', '/tmp/id_rsa') }}" ssh_key_unlock: "passphrase" - name: Fetch private key slurp: src: '$HOME/.ssh/aws-private.pem' register: aws_ssh_key - name: Add Credential credential: name: Workshop Credential credential_type: Machine organization: Default inputs: ssh_key_data: "{{ aws_ssh_key['content'] | b64decode }}" run_once: true delegate_to: localhost - name: Add Credential with Custom Credential Type credential: name: Workshop Credential credential_type: MyCloudCredential organization: Default controller_username: admin controller_password: ansible controller_host: https://localhost - name: Create a Vaiult credential (example for notes) credential: name: Example password credential_type: Vault organization: Default inputs: vault_password: 'hello' vault_id: 'My ID' - name: Bad password update (will replace vault_id) credential: name: Example password credential_type: Vault organization: Default inputs: vault_password: 'new_password' - name: Another bad password update (will replace vault_id) credential: name: Example password credential_type: Vault organization: Default vault_password: 'new_password' - name: A safe way to update a password and keep vault_id credential: name: Example password credential_type: Vault organization: Default inputs: vault_password: 'new_password' vault_id: 'My ID' - name: Copy Credential credential: name: Copy password copy_from: Example password credential_type: Vault organization: Foo ''' from ..module_utils.controller_api import ControllerAPIModule def main(): # Any additional arguments that are not fields of the item can be added here argument_spec = dict( name=dict(required=True), new_name=dict(), copy_from=dict(), description=dict(), organization=dict(), credential_type=dict(), inputs=dict(type='dict', no_log=True), update_secrets=dict(type='bool', default=True, no_log=False), user=dict(), team=dict(), state=dict(choices=['present', 'absent'], default='present'), ) # Create a module for ourselves module = ControllerAPIModule(argument_spec=argument_spec) # Extract our parameters name = module.params.get('name') new_name = module.params.get('new_name') copy_from = module.params.get('copy_from') description = module.params.get('description') organization = module.params.get('organization') credential_type = module.params.get('credential_type') inputs = module.params.get('inputs') user = module.params.get('user') team = module.params.get('team') state = module.params.get('state') cred_type_id = module.resolve_name_to_id('credential_types', credential_type) if organization: org_id = module.resolve_name_to_id('organizations', organization) # Attempt to look up the object based on the provided name, credential type and optional organization lookup_data = { 'credential_type': cred_type_id, } # Create a copy of lookup data for copying without org. copy_lookup_data = lookup_data if organization: lookup_data['organization'] = org_id credential = module.get_one('credentials', name_or_id=name, **{'data': lookup_data}) # Attempt to look up credential to copy based on the provided name if copy_from: # a new existing item is formed when copying and is returned. credential = module.copy_item( credential, copy_from, name, endpoint='credentials', item_type='credential', copy_lookup_data=copy_lookup_data, ) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this module.delete_if_needed(credential) # Attempt to look up the related items the user specified (these will fail the module if not found) if user: user_id = module.resolve_name_to_id('users', user) if team: team_id = module.resolve_name_to_id('teams', team) # Create the data that gets sent for create and update credential_fields = { 'name': new_name if new_name else (module.get_item_name(credential) if credential else name), 'credential_type': cred_type_id, } if inputs: credential_fields['inputs'] = inputs if description: credential_fields['description'] = description if organization: credential_fields['organization'] = org_id # If we don't already have a credential (and we are creating one) we can add user/team # The API does not appear to do anything with these after creation anyway # NOTE: We can't just add these on a modification because they are never returned from a GET so it would always cause a changed=True if not credential: if user: credential_fields['user'] = user_id if team: credential_fields['team'] = team_id # If the state was present we can let the module build or update the existing group, this will return on its own module.create_or_update_if_needed(credential, credential_fields, endpoint='credentials', item_type='credential') if __name__ == '__main__': main()