diff --git a/plugins/modules/aci_aaa_user_domain.py b/plugins/modules/aci_aaa_user_domain.py index 5e386456b..e864c8f92 100644 --- a/plugins/modules/aci_aaa_user_domain.py +++ b/plugins/modules/aci_aaa_user_domain.py @@ -2,30 +2,40 @@ # -*- coding: utf-8 -*- # Copyright: (c) 2022, Tim Cragg (@timcragg) +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) # GNU General Public License v3.0+ (see LICENSE 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'} +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"} -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: aci_aaa_user_domain -short_description: Manage AAA user domain (aaaUserDomain) objects. +short_description: Manage AAA user domains (aaa:UserDomain) description: - Manage AAA user domain configuration on Cisco ACI fabrics. options: aaa_user: description: - - Name of an existing AAA user + - The name of an existing AAA user type: str - required: yes - domain_name: + aliases: [ user_name ] + aaa_user_type: description: - - Name of the user domain + - Whether this is a normal user or an appuser. + type: str + choices: [ appuser, user ] + default: user + name: + description: + - The name of the user domain + type: str + aliases: [ domain_name, user_domain ] + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. type: str state: description: @@ -48,41 +58,56 @@ link: https://developer.cisco.com/docs/apic-mim-ref/ author: - Tim Cragg (@timcragg) -''' +- Sabari Jaganathan (@sajagana) +""" -EXAMPLES = r''' -- name: Add a new user domain +EXAMPLES = r""" +- name: Add a security domain to a aaa_user cisco.aci.aci_aaa_user_domain: host: apic username: admin password: SomeSecretPassword aaa_user: my_user - domain_name: my_domain + name: my_domain state: present delegate_to: localhost -- name: Remove user domain +- name: Remove a security domain from a aaa_user cisco.aci.aci_aaa_user_domain: host: apic username: admin password: SomeSecretPassword aaa_user: my_user - domain_name: my_domain + name: my_domain state: absent delegate_to: localhost -- name: Query a user domain +- name: Add list of security domains to a aaa_user + cisco.aci.aci_aaa_user_domain: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: my_user + name: "{{ item.name }}" + state: present + with_items: + - name: common + - name: all + - name: mgmt + delegate_to: localhost + +- name: Query a security domain from a aaa_user cisco.aci.aci_aaa_user_domain: host: apic username: admin password: SomeSecretPassword aaa_user: my_user - domain_name: my_domain + name: my_domain state: query delegate_to: localhost register: query_result -- name: Query all domains under a user +- name: Query all security domains of a aaa_user cisco.aci.aci_aaa_user_domain: host: apic username: admin @@ -90,175 +115,199 @@ aaa_user: my_user state: query delegate_to: localhost - register: query_result -''' + register: query_results -RETURN = r''' - current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] - error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } - raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' - sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } - previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] - proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } - filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only - method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST - response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) - status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 - url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json - ''' +- name: Query all security domains to user associations + cisco.aci.aci_aaa_user_domain: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_all_domains +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec +ACI_MAPPING = dict( + appuser=dict( + aci_class="aaaAppUser", + aci_mo="userext/appuser-", + ), + user=dict( + aci_class="aaaUser", + aci_mo="userext/user-", + ), +) + def main(): argument_spec = aci_argument_spec() argument_spec.update(aci_annotation_spec()) argument_spec.update(aci_owner_spec()) argument_spec.update( - aaa_user=dict(type='str', required=True), - domain_name=dict(type='str'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + aaa_user=dict(type="str", aliases=["user_name"]), + name=dict(type="str", aliases=["domain_name", "user_domain"]), + aaa_user_type=dict(type="str", default="user", choices=["appuser", "user"]), + name_alias=dict(type="str"), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_if=[ - ['state', 'absent', ['domain_name']], - ['state', 'present', ['domain_name']], + ["state", "absent", ["aaa_user", "name"]], + ["state", "present", ["aaa_user", "name"]], ], ) - aaa_user = module.params.get('aaa_user') - domain_name = module.params.get('domain_name') - state = module.params.get('state') + aaa_user = module.params.get("aaa_user") + name = module.params.get("name") + aaa_user_type = module.params.get("aaa_user_type") + name_alias = module.params.get("name_alias") + state = module.params.get("state") - child_classes = ['aaaUserRole'] + child_classes = ["aaaUserRole"] aci = ACIModule(module) aci.construct_url( root_class=dict( - aci_class='aaaUser', - aci_rn='userext/user-{0}'.format(aaa_user), + aci_class=ACI_MAPPING.get(aaa_user_type).get("aci_class"), + aci_rn="{0}{1}".format(ACI_MAPPING.get(aaa_user_type).get("aci_mo"), aaa_user), module_object=aaa_user, - target_filter={'name': aaa_user}, + target_filter={"name": aaa_user}, ), subclass_1=dict( - aci_class='aaaUserDomain', - aci_rn='userdomain-{0}'.format(domain_name), - module_object=domain_name, - target_filter={'name': domain_name}, + aci_class="aaaUserDomain", + aci_rn="userdomain-{0}".format(name), + module_object=name, + target_filter={"name": name}, ), child_classes=child_classes, ) - aci.get_existing() - if state == 'present': + if state == "present": aci.payload( - aci_class='aaaUserDomain', + aci_class="aaaUserDomain", class_config=dict( - name=domain_name + name=name, + nameAlias=name_alias, ), ) - aci.get_diff(aci_class='aaaUserDomain') + aci.get_diff(aci_class="aaaUserDomain") aci.post_config() - elif state == 'absent': + elif state == "absent": aci.delete_config() aci.exit_json() diff --git a/plugins/modules/aci_aaa_user_role.py b/plugins/modules/aci_aaa_user_role.py index df7f20a8e..3870db376 100644 --- a/plugins/modules/aci_aaa_user_role.py +++ b/plugins/modules/aci_aaa_user_role.py @@ -2,41 +2,52 @@ # -*- coding: utf-8 -*- # Copyright: (c) 2022, Tim Cragg (@timcragg) +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) # GNU General Public License v3.0+ (see LICENSE 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'} +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"} -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: aci_aaa_user_role -short_description: Manage AAA user role (aaaUserRole) objects. +short_description: Manage AAA user roles (aaa:UserRole) description: - Manage AAA User Role configuration on Cisco ACI fabrics. options: aaa_user: description: - - Name of an existing AAA user + - The name of the existing user to add roles and privileges type: str - required: yes + aliases: [ user_name ] + aaa_user_type: + description: + - Whether this is a normal user or an appuser. + type: str + choices: [ appuser, user ] + default: user domain_name: description: - - Name of the user domain + - The name of the security domain type: str - required: yes - role_name: + aliases: [ user_domain ] + name: description: - Name of the AAA role type: str - priv_type: + aliases: [ role_name, user_role ] + privilege_type: description: - Privilege for the role type: str + aliases: [ priv_type ] choices: [ read, write ] + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str state: description: - Use C(present) or C(absent) for adding or removing. @@ -58,45 +69,54 @@ link: https://developer.cisco.com/docs/apic-mim-ref/ author: - Tim Cragg (@timcragg) -''' +- Sabari Jaganathan (@sajagana) +""" -EXAMPLES = r''' -- name: Add a new user role +EXAMPLES = r""" +- name: Add a user role to a user security domain cisco.aci.aci_aaa_user_role: host: apic username: admin password: SomeSecretPassword aaa_user: my_user domain_name: my_domain - role_name: my_role - priv_type: read + name: my_role + privilege_type: read state: present delegate_to: localhost -- name: Remove user role +- name: Add list of user roles to a user domain cisco.aci.aci_aaa_user_role: host: apic username: admin password: SomeSecretPassword aaa_user: my_user domain_name: my_domain - role_name: my_role - state: absent + name: "{{ item.name }}" + privilege_type: "{{ item.privilege_type }}" + state: present + with_items: + - name: aaa + privilege_type: write + - name: access-admin + privilege_type: write + - name: ops + privilege_type: write delegate_to: localhost -- name: Query a user role +- name: Query a user role from a user security domain cisco.aci.aci_aaa_user_role: host: apic username: admin password: SomeSecretPassword aaa_user: my_user domain_name: my_domain - role_name: my_role + name: my_role state: query delegate_to: localhost register: query_result -- name: Query all roles within a user domain +- name: Query all user roles from a user security domain cisco.aci.aci_aaa_user_role: host: apic username: admin @@ -105,120 +125,161 @@ domain_name: my_domain state: query delegate_to: localhost - register: query_result -''' + register: query_results + +- name: Query all user roles from a user + cisco.aci.aci_aaa_user_role: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: my_user + state: query + delegate_to: localhost + register: query_all_roles_of_user + +- name: Query all user roles + cisco.aci.aci_aaa_user_role: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_all_user_roles + +- name: Remove user role from a user domain + cisco.aci.aci_aaa_user_role: + host: apic + username: admin + password: SomeSecretPassword + aaa_user: my_user + domain_name: my_domain + name: my_role + state: absent + delegate_to: localhost +""" -RETURN = r''' - current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] - error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } - raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' - sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } - previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] - proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } - filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only - method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST - response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) - status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 - url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json - ''' +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec +ACI_MAPPING = dict( + appuser=dict( + aci_class="aaaAppUser", + aci_mo="userext/appuser-", + ), + user=dict( + aci_class="aaaUser", + aci_mo="userext/user-", + ), +) + PRIV_TYPE_MAPPING = { - 'read': 'readPriv', - 'write': 'writePriv', + "read": "readPriv", + "write": "writePriv", } @@ -227,69 +288,74 @@ def main(): argument_spec.update(aci_annotation_spec()) argument_spec.update(aci_owner_spec()) argument_spec.update( - aaa_user=dict(type='str', required=True), - domain_name=dict(type='str', required=True), - role_name=dict(type='str'), - priv_type=dict(type='str', choices=['read', 'write']), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + aaa_user=dict(type="str", aliases=["user_name"]), + aaa_user_type=dict(type="str", default="user", choices=["appuser", "user"]), + domain_name=dict(type="str", aliases=["user_domain"]), + name=dict(type="str", aliases=["role_name", "user_role"]), + privilege_type=dict(type="str", aliases=["priv_type"], choices=["read", "write"]), + name_alias=dict(type="str"), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_if=[ - ['state', 'absent', ['role_name']], - ['state', 'present', ['role_name', 'priv_type']], + ["state", "absent", ["aaa_user", "domain_name", "name"]], + ["state", "present", ["aaa_user", "domain_name", "name"]], ], ) - aaa_user = module.params.get('aaa_user') - domain_name = module.params.get('domain_name') - role_name = module.params.get('role_name') - priv_type = module.params.get('priv_type') - state = module.params.get('state') + aaa_user = module.params.get("aaa_user") + aaa_user_type = module.params.get("aaa_user_type") + domain_name = module.params.get("domain_name") + name = module.params.get("name") + privilege_type = module.params.get("privilege_type") + name_alias = module.params.get("name_alias") + state = module.params.get("state") - if priv_type is not None: - priv_type = PRIV_TYPE_MAPPING[priv_type] + if privilege_type is not None: + privilege_type = PRIV_TYPE_MAPPING[privilege_type] aci = ACIModule(module) aci.construct_url( root_class=dict( - aci_class='aaaUser', - aci_rn='userext/user-{0}'.format(aaa_user), + aci_class=ACI_MAPPING.get(aaa_user_type).get("aci_class"), + aci_rn="{0}{1}".format(ACI_MAPPING.get(aaa_user_type).get("aci_mo"), aaa_user), module_object=aaa_user, - target_filter={'name': aaa_user}, + target_filter={"name": aaa_user}, ), subclass_1=dict( - aci_class='aaaUserDomain', - aci_rn='userdomain-{0}'.format(domain_name), + aci_class="aaaUserDomain", + aci_rn="userdomain-{0}".format(domain_name), module_object=domain_name, - target_filter={'name': domain_name}, + target_filter={"name": domain_name}, ), subclass_2=dict( - aci_class='aaaUserRole', - aci_rn='role-{0}'.format(role_name), - module_object=role_name, - target_filter={'name': role_name}, + aci_class="aaaUserRole", + aci_rn="role-{0}".format(name), + module_object=name, + target_filter={"name": name}, ), ) aci.get_existing() - if state == 'present': + if state == "present": aci.payload( - aci_class='aaaUserRole', + aci_class="aaaUserRole", class_config=dict( - name=role_name, - privType=priv_type + name=name, + privType=privilege_type, + nameAlias=name_alias, ), ) - aci.get_diff(aci_class='aaaUserRole') + aci.get_diff(aci_class="aaaUserRole") aci.post_config() - elif state == 'absent': + elif state == "absent": aci.delete_config() aci.exit_json() diff --git a/tests/integration/targets/aci_aaa_user_domain/tasks/main.yml b/tests/integration/targets/aci_aaa_user_domain/tasks/main.yml index 0368f57d8..ef47fe6c4 100644 --- a/tests/integration/targets/aci_aaa_user_domain/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_user_domain/tasks/main.yml @@ -1,5 +1,6 @@ # Test code for the ACI modules # Copyright: (c) 2022, Tim Cragg (timcragg) +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) # Copyright: (c) 2017, Dag Wieers (dagwieers) # # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -9,12 +10,13 @@ msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined +# SET VARS - name: Set vars set_fact: aci_info: &aci_info - host: "{{ aci_hostname }}" - username: "{{ aci_username }}" - password: "{{ aci_password }}" + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' @@ -28,129 +30,181 @@ state: absent # ADD USER -- name: Create user - cisco.aci.aci_aaa_user: +- name: Add a aaa_user + cisco.aci.aci_aaa_user: &user_present <<: *aci_info aaa_user: ansible aaa_password: 12!Ab:cD!34 - state: present + description: Ansible test user + email: ansible@ansible.lan + enabled: yes + expiration: never + expires: no + first_name: Ansible + last_name: User + phone: 1-234-555-678 + register: add_user # ADD AAA USER DOMAIN -- name: Add AAA user domain (check_mode) - cisco.aci.aci_aaa_user_domain: &domain_present +- name: Add user domain to aaa_user with check mode + cisco.aci.aci_aaa_user_domain: &cm_sec_domain_present <<: *aci_info + name: anstest-security-domain aaa_user: ansible - domain_name: mgmt state: present check_mode: yes - register: cm_add_user_domain + register: cm_sec_domain_present -- name: Add AAA user domain (normal mode) - cisco.aci.aci_aaa_user_domain: - <<: *domain_present - register: nm_add_user_domain +- name: Assertions check for add user domain to aaa_user with check mode + assert: + that: + - cm_sec_domain_present is changed + - cm_sec_domain_present.current | length == 0 + - cm_sec_domain_present.previous | length == 0 + - cm_sec_domain_present.sent.aaaUserDomain.attributes.name == 'anstest-security-domain' + +- name: Add user domain to aaa_user with normal mode + cisco.aci.aci_aaa_user_domain: &nm_sec_domain_present + <<: *cm_sec_domain_present + register: nm_sec_domain_present -- name: Add AAA user domain again(check mode) +- name: Assertions check for add user domain to aaa_user with normal mode + assert: + that: + - nm_sec_domain_present is changed + - nm_sec_domain_present.current | length == 1 + - nm_sec_domain_present.previous | length == 0 + - nm_sec_domain_present.current.0.aaaUserDomain.attributes.name == 'anstest-security-domain' + +- name: Add user domain to aaa_user with check mode - idempotency works cisco.aci.aci_aaa_user_domain: - <<: *domain_present + <<: *cm_sec_domain_present check_mode: yes - register: cm_add_user_domain_again + register: cm_idempotency_sec_domain_present + +- name: Idempotency assertions check for add user domain to aaa_user with check mode + assert: + that: + - cm_idempotency_sec_domain_present is not changed + - cm_idempotency_sec_domain_present.current | length == 1 + - cm_idempotency_sec_domain_present.previous | length == 1 + - cm_idempotency_sec_domain_present.current.0.aaaUserDomain.attributes.name == 'anstest-security-domain' + - cm_idempotency_sec_domain_present.previous.0.aaaUserDomain.attributes.name == 'anstest-security-domain' -- name: Add AAA user domain again (normal mode) +- name: Add user domain to aaa_user with normal mode - idempotency works cisco.aci.aci_aaa_user_domain: - <<: *domain_present - register: nm_add_user_domain_again + <<: *nm_sec_domain_present + register: idempotency_sec_domain_present -- name: Verify add user +- name: Idempotency assertions check for add user domain to aaa_user with normal mode assert: that: - - cm_add_user_domain is changed - - nm_add_user_domain is changed - - nm_add_user_domain.current.0.aaaUserDomain.attributes.dn == 'uni/userext/user-ansible/userdomain-mgmt' - - nm_add_user_domain.current.0.aaaUserDomain.attributes.name == 'mgmt' - - cm_add_user_domain_again is not changed - - nm_add_user_domain_again is not changed - - nm_add_user_domain_again.current.0.aaaUserDomain.attributes.dn == 'uni/userext/user-ansible/userdomain-mgmt' - - nm_add_user_domain_again.current.0.aaaUserDomain.attributes.name == 'mgmt' + - idempotency_sec_domain_present is not changed + - idempotency_sec_domain_present.current | length == 1 + - idempotency_sec_domain_present.previous | length == 1 + - idempotency_sec_domain_present.current.0.aaaUserDomain.attributes.name == 'anstest-security-domain' + - idempotency_sec_domain_present.previous.0.aaaUserDomain.attributes.name == 'anstest-security-domain' -# QUERY ALL USER DOMAINS -- name: Query all users (check_mode) - cisco.aci.aci_aaa_user_domain: &domain_query_all +- name: Add list of user domains to a aaa_user + cisco.aci.aci_aaa_user_domain: <<: *aci_info + name: "{{ item.name }}" + aaa_user: ansible + state: present + with_items: + - name: all + - name: mgmt + +# QUERY A USER DOMAIN +- name: Query a user domain from a aaa_user + cisco.aci.aci_aaa_user_domain: + <<: *aci_info + name: anstest-security-domain aaa_user: ansible state: query - check_mode: yes - register: cm_query_all_user_domains + register: query_anstest_sec_domain + +- name: Assertions check for query a user domain from a aaa_user + assert: + that: + - query_anstest_sec_domain is not changed + - query_anstest_sec_domain.current | length == 1 + - query_anstest_sec_domain.current.0.aaaUserDomain.attributes.name == 'anstest-security-domain' -- name: Query all users (normal mode) +# QUERY ALL USER DOMAINS +- name: Query all user domains from a aaa_user cisco.aci.aci_aaa_user_domain: - <<: *domain_query_all - register: nm_query_all_user_domains + <<: *aci_info + aaa_user: ansible + state: query + register: query_aaa_user_domains -- name: Verify query_all_user_domains +- name: Assertions check for query all user domains from a aaa_user assert: that: - - cm_query_all_user_domains is not changed - - nm_query_all_user_domains is not changed - -# QUERY OUR USER DOMAIN -- name: Query our domain (check_mode) - cisco.aci.aci_aaa_user_domain: &domain_query - <<: *domain_query_all - domain_name: common - check_mode: yes - register: cm_query_user_domain + - query_aaa_user_domains is not changed + - query_aaa_user_domains.current | length == 1 + - query_aaa_user_domains.current.0.aaaUser.children | length == 4 -- name: Query our user (normal mode) +- name: Query all user domains cisco.aci.aci_aaa_user_domain: - <<: *domain_query - register: nm_query_user_domain + <<: *aci_info + state: query + register: query_all_aaa_user_domains -- name: Verify query_user +- name: Assertions check for query all user domains assert: that: - - cm_query_user_domain is not changed - - nm_query_user_domain is not changed - - cm_query_user_domain == nm_query_user_domain + - query_all_aaa_user_domains is not changed + - query_all_aaa_user_domains.current | length >= 3 # REMOVE USER DOMAIN -- name: Remove user domain (check_mode) - cisco.aci.aci_aaa_user_domain: &domain_absent - <<: *aci_info - aaa_user: ansible - domain_name: mgmt +- name: Remove user domain from a aaa_user with check mode + cisco.aci.aci_aaa_user_domain: &cm_sec_domain_absent + <<: *nm_sec_domain_present state: absent check_mode: yes - register: cm_remove_user_domain + register: cm_sec_domain_absent -- name: Remove user domain (normal mode) - cisco.aci.aci_aaa_user_domain: - <<: *domain_absent - register: nm_remove_user_domain +- name: Assertions check for removing user domain from a aaa_user with check mode + assert: + that: + - cm_sec_domain_absent is changed + - cm_sec_domain_absent.current | length == 1 + - cm_sec_domain_absent.current.0.aaaUserDomain.attributes.name == 'anstest-security-domain' + - cm_sec_domain_absent.previous | length == 1 + - cm_sec_domain_absent.previous.0.aaaUserDomain.attributes.name == 'anstest-security-domain' + +- name: Remove user domain from a aaa_user with normal mode + cisco.aci.aci_aaa_user_domain: &nm_sec_domain_absent + <<: *cm_sec_domain_absent + state: absent + register: nm_sec_domain_absent -- name: Remove user domain again (check_mode) - cisco.aci.aci_aaa_user_domain: - <<: *domain_absent - check_mode: yes - register: cm_remove_user_domain_again +- name: Assertions check for removing user domain from a aaa_user with normal mode + assert: + that: + - nm_sec_domain_absent is changed + - nm_sec_domain_absent.current == [] + - nm_sec_domain_absent.previous | length == 1 + - nm_sec_domain_absent.previous.0.aaaUserDomain.attributes.name == 'anstest-security-domain' -- name: Remove user domain again (normal mode) +- name: Remove user domain from a aaa_user with normal mode - idempotency works cisco.aci.aci_aaa_user_domain: - <<: *domain_absent - register: nm_remove_user_domain_again + <<: *nm_sec_domain_absent + state: absent + check_mode: yes + register: idempotency_sec_domain_absent -- name: Verify remove_user +- name: Idempotency assertions check for removing user domain assert: that: - - cm_remove_user_domain is changed - - nm_remove_user_domain is changed - - nm_remove_user_domain.current == [] - - cm_remove_user_domain_again is not changed - - nm_remove_user_domain_again is not changed - -# CLEAN UP -- name: Remove user + - idempotency_sec_domain_absent is not changed + - idempotency_sec_domain_absent.current == [] + - idempotency_sec_domain_absent.previous == [] + +# Cleanup part +- name: Remove a aaa_user cisco.aci.aci_aaa_user: - <<: *aci_info - aaa_user: ansible + <<: *user_present state: absent diff --git a/tests/integration/targets/aci_aaa_user_role/tasks/main.yml b/tests/integration/targets/aci_aaa_user_role/tasks/main.yml index 3722cec59..11937880d 100644 --- a/tests/integration/targets/aci_aaa_user_role/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_user_role/tasks/main.yml @@ -1,5 +1,6 @@ # Test code for the ACI modules # Copyright: (c) 2022, Tim Cragg (timcragg) +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) # Copyright: (c) 2017, Dag Wieers (dagwieers) # # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -9,12 +10,13 @@ msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined +# SET VARS - name: Set vars set_fact: aci_info: &aci_info - host: "{{ aci_hostname }}" - username: "{{ aci_username }}" - password: "{{ aci_password }}" + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' @@ -28,167 +30,282 @@ state: absent # ADD USER -- name: Create AAA user - cisco.aci.aci_aaa_user: +- name: Add a aaa_user + cisco.aci.aci_aaa_user: &user_present <<: *aci_info aaa_user: ansible aaa_password: 12!Ab:cD!34 - state: present + description: Ansible test user + email: ansible@ansible.lan + enabled: yes + expiration: never + expires: no + first_name: Ansible + last_name: User + phone: 1-234-555-678 + register: add_user # ADD USER DOMAIN -- name: Add AAA user domain - cisco.aci.aci_aaa_user_domain: +- name: Add "anstest-security-domain" user domain to aaa_user + cisco.aci.aci_aaa_user_domain: &sec_domain_present + <<: *aci_info + name: anstest-security-domain + aaa_user: ansible + state: present + +- name: Add "anstest-security-domain-1" user domain to aaa_user + cisco.aci.aci_aaa_user_domain: &sec_domain1_present <<: *aci_info + name: anstest-security-domain-1 aaa_user: ansible - domain_name: mgmt state: present +# Test Part # ADD USER ROLE -- name: Add a new user role (Check Mode) - cisco.aci.aci_aaa_user_role: &role_present +- name: Add user role to "anstest-security-domain" user domain with check mode + cisco.aci.aci_aaa_user_role: &cm_user_role_present <<: *aci_info aaa_user: ansible - domain_name: mgmt - role_name: admin - priv_type: read + user_domain: anstest-security-domain + name: admin + privilege_type: write state: present check_mode: yes - register: cm_add_user_role + register: cm_user_role_present -- name: Add a new user role (Normal Mode) - cisco.aci.aci_aaa_user_role: - <<: *role_present - register: nm_add_user_role +- name: Assertions check for add user role to "anstest-security-domain" with check mode + assert: + that: + - cm_user_role_present is changed + - cm_user_role_present.current | length == 0 + - cm_user_role_present.previous | length == 0 + - cm_user_role_present.sent.aaaUserRole.attributes.name == 'admin' + - cm_user_role_present.sent.aaaUserRole.attributes.privType == 'writePriv' + +- name: Add user role to "anstest-security-domain" user domain with normal mode + cisco.aci.aci_aaa_user_role: &nm_user_role_present + <<: *cm_user_role_present + register: nm_user_role_present + +- name: Assertions check for add user role to "anstest-security-domain" with normal mode + assert: + that: + - nm_user_role_present is changed + - nm_user_role_present.current | length == 1 + - nm_user_role_present.previous | length == 0 + - nm_user_role_present.current.0.aaaUserRole.attributes.name == 'admin' + - nm_user_role_present.current.0.aaaUserRole.attributes.privType == 'writePriv' -- name: Add user role again (Check Mode) +- name: Add user role to "anstest-security-domain" user domain with check mode - idempotency works cisco.aci.aci_aaa_user_role: - <<: *role_present + <<: *nm_user_role_present check_mode: yes - register: cm_add_user_role_again + register: cm_idempotency_user_role_present + +- name: Idempotency assertions check for add user role to "anstest-security-domain" check mode + assert: + that: + - cm_idempotency_user_role_present is not changed + - cm_idempotency_user_role_present.previous | length == 1 + - cm_idempotency_user_role_present.current | length == 1 + - cm_idempotency_user_role_present.current.0.aaaUserRole.attributes.name == 'admin' + - cm_idempotency_user_role_present.current.0.aaaUserRole.attributes.privType == 'writePriv' + - cm_idempotency_user_role_present.previous.0.aaaUserRole.attributes.name == 'admin' + - cm_idempotency_user_role_present.previous.0.aaaUserRole.attributes.privType == 'writePriv' -- name: Add user role again (Normal Mode) +- name: Add user role to "anstest-security-domain" user domain with normal mode - idempotency works cisco.aci.aci_aaa_user_role: - <<: *role_present - register: nm_add_user_role_again + <<: *nm_user_role_present + register: idempotency_user_role_present -- name: Verify add user role +- name: Idempotency assertions check for add user role to "anstest-security-domain" assert: that: - - cm_add_user_role is changed - - nm_add_user_role is changed - - nm_add_user_role.current.0.aaaUserRole.attributes.dn == 'uni/userext/user-ansible/userdomain-mgmt/role-admin' - - nm_add_user_role.current.0.aaaUserRole.attributes.name == 'admin' - - nm_add_user_role.current.0.aaaUserRole.attributes.privType == 'readPriv' - - cm_add_user_role_again is not changed - - nm_add_user_role_again is not changed - - nm_add_user_role_again.current.0.aaaUserRole.attributes.dn == 'uni/userext/user-ansible/userdomain-mgmt/role-admin' - - nm_add_user_role_again.current.0.aaaUserRole.attributes.name == 'admin' - - nm_add_user_role_again.current.0.aaaUserRole.attributes.privType == 'readPriv' + - idempotency_user_role_present is not changed + - idempotency_user_role_present.previous | length == 1 + - idempotency_user_role_present.current | length == 1 + - idempotency_user_role_present.current.0.aaaUserRole.attributes.name == 'admin' + - idempotency_user_role_present.current.0.aaaUserRole.attributes.privType == 'writePriv' + - idempotency_user_role_present.previous.0.aaaUserRole.attributes.name == 'admin' + - idempotency_user_role_present.previous.0.aaaUserRole.attributes.privType == 'writePriv' + +- name: Add user role to "anstest-security-domain-1" user domain + cisco.aci.aci_aaa_user_role: + <<: *aci_info + aaa_user: ansible + user_domain: anstest-security-domain-1 + name: admin + state: present + register: user_role_present_sec_domain1 + +- name: Assertions check for add a user role to "anstest-security-domain-1" + assert: + that: + - user_role_present_sec_domain1 is changed + - user_role_present_sec_domain1.current | length == 1 + - user_role_present_sec_domain1.previous | length == 0 + - user_role_present_sec_domain1.current.0.aaaUserRole.attributes.name == 'admin' + - user_role_present_sec_domain1.current.0.aaaUserRole.attributes.privType == 'readPriv' # UPDATE USER ROLE -- name: Update user role (Check Mode) +- name: Update user role to "anstest-security-domain" user domain with check mode cisco.aci.aci_aaa_user_role: - <<: *role_present - priv_type: write + <<: *nm_user_role_present + privilege_type: read check_mode: yes - register: cm_update_user_role + register: cm_update_user_role_present -- name: Update user role (Normal Mode) +- name: Assertions check for update user role to "anstest-security-domain" with check mode + assert: + that: + - cm_update_user_role_present is changed + - cm_update_user_role_present.current | length == 1 + - cm_update_user_role_present.previous | length == 1 + - cm_update_user_role_present.sent.aaaUserRole.attributes.privType == 'readPriv' + +- name: Update user role to "anstest-security-domain" user domain with normal mode cisco.aci.aci_aaa_user_role: - <<: *role_present - priv_type: write - register: nm_update_user_role + <<: *nm_user_role_present + privilege_type: read + register: update_user_role_present -- name: Verify update user role +- name: Assertions check for update user role to "anstest-security-domain" with normal mode assert: that: - - cm_update_user_role is changed - - nm_update_user_role is changed - - nm_update_user_role.current.0.aaaUserRole.attributes.dn == 'uni/userext/user-ansible/userdomain-mgmt/role-admin' - - nm_update_user_role.current.0.aaaUserRole.attributes.name == 'admin' - - nm_update_user_role.current.0.aaaUserRole.attributes.privType == 'writePriv' + - update_user_role_present is changed + - update_user_role_present.current | length == 1 + - update_user_role_present.previous | length == 1 + - update_user_role_present.current.0.aaaUserRole.attributes.name == 'admin' + - update_user_role_present.current.0.aaaUserRole.attributes.privType == 'readPriv' + +- name: Add list of user roles to a user domain + cisco.aci.aci_aaa_user_role: + <<: *aci_info + aaa_user: ansible + user_domain: anstest-security-domain + name: "{{ item.name }}" + privilege_type: "{{ item.privilege_type }}" + state: present + with_items: + - name: aaa + privilege_type: write + - name: access-admin + privilege_type: write + - name: ops + privilege_type: write + +# QUERY USER ROLE +- name: Query a user role from "anstest-security-domain-1" user domain + cisco.aci.aci_aaa_user_role: + <<: *aci_info + user_domain: anstest-security-domain-1 + name: admin + state: query + register: query_user_role_from_sec_domain + +- name: Assertions check for query a user role from "anstest-security-domain-1" user domain + assert: + that: + - query_user_role_from_sec_domain is not changed + - query_user_role_from_sec_domain.current | length == 1 + - query_user_role_from_sec_domain.current.0.aaaUserDomain.children | length == 1 + - query_user_role_from_sec_domain.current.0.aaaUserDomain.children.0.aaaUserRole.attributes.name == 'admin' + - query_user_role_from_sec_domain.current.0.aaaUserDomain.children.0.aaaUserRole.attributes.privType == 'readPriv' # QUERY ALL USER ROLES -- name: Query all users (check mode) - cisco.aci.aci_aaa_user_role: &role_query_all +- name: Query all user roles from "anstest-security-domain" user domain + cisco.aci.aci_aaa_user_role: <<: *aci_info aaa_user: ansible - domain_name: mgmt + user_domain: anstest-security-domain state: query - check_mode: yes - register: cm_query_all_user_roles + register: query_all_user_roles_of_sec_domain + +- name: Assertions check for query all user roles from "anstest-security-domain" user domain + assert: + that: + - query_all_user_roles_of_sec_domain is not changed + - query_all_user_roles_of_sec_domain.current | length == 1 + - query_all_user_roles_of_sec_domain.current.0.aaaUserDomain.attributes.name == 'anstest-security-domain' + - query_all_user_roles_of_sec_domain.current.0.aaaUserDomain.children | length == 4 -- name: Query all users (normal mode) - cisco.aci.aci_aaa_user_domain: - <<: *role_query_all - register: nm_query_all_user_roles +- name: Query all user roles from a user + cisco.aci.aci_aaa_user_role: + <<: *aci_info + aaa_user: ansible + state: query + register: query_all_user_roles_from_a_user -- name: Verify query_all_user_roles +- name: Assertions check for query all user roles from a user assert: that: - - cm_query_all_user_roles is not changed - - nm_query_all_user_roles is not changed - -# QUERY OUR USER ROLE -- name: Query our role (check mode) - cisco.aci.aci_aaa_user_role: &role_query - <<: *role_query_all - role_name: admin - check_mode: yes - register: cm_query_user_role + - query_all_user_roles_from_a_user is not changed + - query_all_user_roles_from_a_user.current | length == 1 + - query_all_user_roles_from_a_user.current.0.aaaUser.attributes.name == 'ansible' + - query_all_user_roles_from_a_user.current.0.aaaUser.children | length == 3 # count of user domains including common -- name: Query our role (normal mode) +- name: Query all user roles cisco.aci.aci_aaa_user_role: - <<: *role_query - register: nm_query_user_role + <<: *aci_info + state: query + register: query_all_user_roles -- name: Verify query_user_role +- name: Assertions check for query all user roles assert: that: - - cm_query_user_role is not changed - - nm_query_user_role is not changed - - nm_query_user_role.current.0.aaaUserRole.attributes.dn == 'uni/userext/user-ansible/userdomain-mgmt/role-admin' - - nm_query_user_role.current.0.aaaUserRole.attributes.name == 'admin' - - nm_query_user_role.current.0.aaaUserRole.attributes.privType == 'writePriv' + - query_all_user_roles is not changed + - query_all_user_roles.current | length >= 6 # count of user roles including common sec domain user role # REMOVE USER ROLE -- name: Remove user role (check mode) - cisco.aci.aci_aaa_user_role: &role_absent +- name: Remove user role from a user domain with check mode + cisco.aci.aci_aaa_user_role: &cm_user_role_absent <<: *aci_info aaa_user: ansible - domain_name: mgmt - role_name: admin + user_domain: anstest-security-domain-1 + name: admin state: absent check_mode: yes - register: cm_remove_user_role + register: cm_user_role_absent -- name: Remove user role (normal mode) - cisco.aci.aci_aaa_user_role: - <<: *role_absent - register: nm_remove_user_role +- name: Assertions check for removing a user role from "anstest-security-domain-1" with check mode + assert: + that: + - cm_user_role_absent is changed + - cm_user_role_absent.current | length == 1 + - cm_user_role_absent.current.0.aaaUserRole.attributes.name == 'admin' + - cm_user_role_absent.current.0.aaaUserRole.attributes.privType == 'readPriv' + - cm_user_role_absent.previous | length == 1 + - cm_user_role_absent.previous.0.aaaUserRole.attributes.name == 'admin' + - cm_user_role_absent.previous.0.aaaUserRole.attributes.privType == 'readPriv' -- name: Remove user role again (check mode) - cisco.aci.aci_aaa_user_role: - <<: *role_absent - check_mode: yes - register: cm_remove_user_role_again +- name: Remove user role from a user domain with normal mode + cisco.aci.aci_aaa_user_role: &nm_user_role_absent + <<: *cm_user_role_absent + register: nm_user_role_absent -- name: Remove user role again (normal mode) +- name: Assertions check for removing a user role from "anstest-security-domain-1" with normal mode + assert: + that: + - nm_user_role_absent is changed + - nm_user_role_absent.current == [] + - nm_user_role_absent.previous | length == 1 + - nm_user_role_absent.previous.0.aaaUserRole.attributes.name == 'admin' + - nm_user_role_absent.previous.0.aaaUserRole.attributes.privType == 'readPriv' + +- name: Remove user role from a user domain with normal mode - idempotency works cisco.aci.aci_aaa_user_role: - <<: *role_absent - register: nm_remove_user_role_again + <<: *nm_user_role_absent + register: idempotency_user_role_absent -- name: Verify remove_user_role +- name: Idempotency assertions check for removing a user role from "anstest-security-domain-1" assert: that: - - cm_remove_user_role is changed - - nm_remove_user_role is changed - - nm_remove_user_role.current == [] - - cm_remove_user_role_again is not changed - - nm_remove_user_role_again is not changed - -- name: Remove user to clean environment for next test on ci - aci_aaa_user: - <<: *aci_info - aaa_user: ansible + - idempotency_user_role_absent is not changed + - idempotency_user_role_absent.current == [] + - idempotency_user_role_absent.previous == [] + +# Cleanup part +- name: Remove aaa_user "ansible" + cisco.aci.aci_aaa_user: + <<: *user_present state: absent