From e520c743e8457d438b44251779e4824de0181d20 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Fri, 6 Oct 2023 11:45:29 +0200 Subject: [PATCH] Revert "Migrate iam_role* modules and tests (#1760)" This reverts commit e109f32ce27de8613c9c57f7891c322c432cbb9e. --- changelogs/fragments/migrate_iam_role.yml | 7 - meta/runtime.yml | 4 +- plugins/modules/iam_role.py | 772 ------------------ plugins/modules/iam_role_info.py | 288 ------- tests/integration/targets/iam_role/aliases | 9 - .../targets/iam_role/defaults/main.yml | 5 - .../targets/iam_role/files/deny-all-a.json | 13 - .../targets/iam_role/files/deny-all-b.json | 13 - .../targets/iam_role/files/deny-all.json | 12 - .../targets/iam_role/files/deny-assume.json | 10 - .../targets/iam_role/meta/main.yml | 1 - .../iam_role/tasks/boundary_policy.yml | 86 -- .../iam_role/tasks/complex_role_creation.yml | 128 --- .../iam_role/tasks/creation_deletion.yml | 376 --------- .../iam_role/tasks/description_update.yml | 143 ---- .../iam_role/tasks/inline_policy_update.yml | 49 -- .../targets/iam_role/tasks/main.yml | 83 -- .../iam_role/tasks/max_session_update.yml | 66 -- .../iam_role/tasks/parameter_checks.yml | 82 -- .../targets/iam_role/tasks/policy_update.yml | 246 ------ .../targets/iam_role/tasks/role_removal.yml | 59 -- .../targets/iam_role/tasks/tags_update.yml | 328 -------- 22 files changed, 1 insertion(+), 2779 deletions(-) delete mode 100644 changelogs/fragments/migrate_iam_role.yml delete mode 100644 plugins/modules/iam_role.py delete mode 100644 plugins/modules/iam_role_info.py delete mode 100644 tests/integration/targets/iam_role/aliases delete mode 100644 tests/integration/targets/iam_role/defaults/main.yml delete mode 100644 tests/integration/targets/iam_role/files/deny-all-a.json delete mode 100644 tests/integration/targets/iam_role/files/deny-all-b.json delete mode 100644 tests/integration/targets/iam_role/files/deny-all.json delete mode 100644 tests/integration/targets/iam_role/files/deny-assume.json delete mode 100644 tests/integration/targets/iam_role/meta/main.yml delete mode 100644 tests/integration/targets/iam_role/tasks/boundary_policy.yml delete mode 100644 tests/integration/targets/iam_role/tasks/complex_role_creation.yml delete mode 100644 tests/integration/targets/iam_role/tasks/creation_deletion.yml delete mode 100644 tests/integration/targets/iam_role/tasks/description_update.yml delete mode 100644 tests/integration/targets/iam_role/tasks/inline_policy_update.yml delete mode 100644 tests/integration/targets/iam_role/tasks/main.yml delete mode 100644 tests/integration/targets/iam_role/tasks/max_session_update.yml delete mode 100644 tests/integration/targets/iam_role/tasks/parameter_checks.yml delete mode 100644 tests/integration/targets/iam_role/tasks/policy_update.yml delete mode 100644 tests/integration/targets/iam_role/tasks/role_removal.yml delete mode 100644 tests/integration/targets/iam_role/tasks/tags_update.yml diff --git a/changelogs/fragments/migrate_iam_role.yml b/changelogs/fragments/migrate_iam_role.yml deleted file mode 100644 index 9b8110ba7d8..00000000000 --- a/changelogs/fragments/migrate_iam_role.yml +++ /dev/null @@ -1,7 +0,0 @@ -major_changes: -- iam_role - The module has been migrated from the ``community.aws`` collection. Playbooks - using the Fully Qualified Collection Name for this module should be updated to use - ``amazon.aws.iam_role`` (https://github.com/ansible-collections/amazon.aws/pull/1760). -- iam_role_info - The module has been migrated from the ``community.aws`` collection. - Playbooks using the Fully Qualified Collection Name for this module should be updated - to use ``amazon.aws.iam_role_info`` (https://github.com/ansible-collections/amazon.aws/pull/1760). diff --git a/meta/runtime.yml b/meta/runtime.yml index da395988b6f..06d471fde1a 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -74,8 +74,6 @@ action_groups: - iam_managed_policy - iam_policy - iam_policy_info - - iam_role - - iam_role_info - iam_user - iam_user_info - kms_key @@ -147,4 +145,4 @@ plugin_routing: redirect: amazon.aws.ssm_parameter aws_secret: # Deprecation for this alias should not *start* prior to 2024-09-01 - redirect: amazon.aws.secretsmanager_secret \ No newline at end of file + redirect: amazon.aws.secretsmanager_secret diff --git a/plugins/modules/iam_role.py b/plugins/modules/iam_role.py deleted file mode 100644 index f373b2ef630..00000000000 --- a/plugins/modules/iam_role.py +++ /dev/null @@ -1,772 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: Contributors to the Ansible project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -DOCUMENTATION = r""" ---- -module: iam_role -version_added: 1.0.0 -version_added_collection: community.aws -short_description: Manage AWS IAM roles -description: - - Manage AWS IAM roles. -author: - - "Rob White (@wimnat)" -options: - path: - description: - - The path to the role. For more information about paths, see U(https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html). - default: "/" - type: str - name: - description: - - The name of the role to create. - required: true - type: str - description: - description: - - Provides a description of the role. - type: str - boundary: - description: - - The ARN of an IAM managed policy to use to restrict the permissions this role can pass on to IAM roles/users that it creates. - - Boundaries cannot be set on Instance Profiles, as such if this option is specified then I(create_instance_profile) must be C(false). - - This is intended for roles/users that have permissions to create new IAM objects. - - For more information on boundaries, see U(https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html). - aliases: [boundary_policy_arn] - type: str - assume_role_policy_document: - description: - - The trust relationship policy document that grants an entity permission to assume the role. - - This parameter is required when I(state=present). - type: json - managed_policies: - description: - - A list of managed policy ARNs, managed policy ARNs or friendly names. - - To remove all policies set I(purge_polices=true) and I(managed_policies=[None]). - - To embed an inline policy, use M(amazon.aws.iam_policy). - aliases: ['managed_policy'] - type: list - elements: str - max_session_duration: - description: - - The maximum duration (in seconds) of a session when assuming the role. - - Valid values are between 1 and 12 hours (3600 and 43200 seconds). - type: int - purge_policies: - description: - - When I(purge_policies=true) any managed policies not listed in I(managed_policies) will be detatched. - type: bool - aliases: ['purge_policy', 'purge_managed_policies'] - default: true - state: - description: - - Create or remove the IAM role. - default: present - choices: [ present, absent ] - type: str - create_instance_profile: - description: - - Creates an IAM instance profile along with the role. - default: true - type: bool - delete_instance_profile: - description: - - When I(delete_instance_profile=true) and I(state=absent) deleting a role will also delete the instance - profile created with the same I(name) as the role. - - Only applies when I(state=absent). - default: false - type: bool - wait_timeout: - description: - - How long (in seconds) to wait for creation / update to complete. - default: 120 - type: int - wait: - description: - - When I(wait=True) the module will wait for up to I(wait_timeout) seconds - for IAM role creation before returning. - default: True - type: bool -extends_documentation_fragment: - - amazon.aws.common.modules - - amazon.aws.region.modules - - amazon.aws.tags - - amazon.aws.boto3 -""" - -EXAMPLES = r""" -# Note: These examples do not set authentication details, see the AWS Guide for details. - -- name: Create a role with description and tags - amazon.aws.iam_role: - name: mynewrole - assume_role_policy_document: "{{ lookup('file','policy.json') }}" - description: This is My New Role - tags: - env: dev - -- name: "Create a role and attach a managed policy called 'PowerUserAccess'" - amazon.aws.iam_role: - name: mynewrole - assume_role_policy_document: "{{ lookup('file','policy.json') }}" - managed_policies: - - arn:aws:iam::aws:policy/PowerUserAccess - -- name: Keep the role created above but remove all managed policies - amazon.aws.iam_role: - name: mynewrole - assume_role_policy_document: "{{ lookup('file','policy.json') }}" - managed_policies: [] - -- name: Delete the role - amazon.aws.iam_role: - name: mynewrole - assume_role_policy_document: "{{ lookup('file', 'policy.json') }}" - state: absent - -""" -RETURN = r""" -iam_role: - description: dictionary containing the IAM Role data - returned: success - type: complex - contains: - path: - description: the path to the role - type: str - returned: always - sample: / - role_name: - description: the friendly name that identifies the role - type: str - returned: always - sample: myrole - role_id: - description: the stable and unique string identifying the role - type: str - returned: always - sample: ABCDEFF4EZ4ABCDEFV4ZC - arn: - description: the Amazon Resource Name (ARN) specifying the role - type: str - returned: always - sample: "arn:aws:iam::1234567890:role/mynewrole" - create_date: - description: the date and time, in ISO 8601 date-time format, when the role was created - type: str - returned: always - sample: "2016-08-14T04:36:28+00:00" - assume_role_policy_document: - description: - - the policy that grants an entity permission to assume the role - - | - note: the case of keys in this dictionary are currently converted from CamelCase to - snake_case. In a release after 2023-12-01 this behaviour will change - type: dict - returned: always - sample: { - 'statement': [ - { - 'action': 'sts:AssumeRole', - 'effect': 'Allow', - 'principal': { - 'service': 'ec2.amazonaws.com' - }, - 'sid': '' - } - ], - 'version': '2012-10-17' - } - assume_role_policy_document_raw: - description: the policy that grants an entity permission to assume the role - type: dict - returned: always - version_added: 5.3.0 - sample: { - 'Statement': [ - { - 'Action': 'sts:AssumeRole', - 'Effect': 'Allow', - 'Principal': { - 'Service': 'ec2.amazonaws.com' - }, - 'Sid': '' - } - ], - 'Version': '2012-10-17' - } - - attached_policies: - description: a list of dicts containing the name and ARN of the managed IAM policies attached to the role - type: list - returned: always - sample: [ - { - 'policy_arn': 'arn:aws:iam::aws:policy/PowerUserAccess', - 'policy_name': 'PowerUserAccess' - } - ] - tags: - description: role tags - type: dict - returned: always - sample: '{"Env": "Prod"}' -""" - -import json - -try: - import botocore -except ImportError: - pass # caught by AnsibleAWSModule - -from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict - -from ansible_collections.amazon.aws.plugins.module_utils.arn import validate_aws_arn -from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code -from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.policy import compare_policies -from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.tagging import ansible_dict_to_boto3_tag_list -from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict -from ansible_collections.amazon.aws.plugins.module_utils.tagging import compare_aws_tags - - -@AWSRetry.jittered_backoff() -def _list_policies(client): - paginator = client.get_paginator("list_policies") - return paginator.paginate().build_full_result()["Policies"] - - -def wait_iam_exists(module, client): - if module.check_mode: - return - if not module.params.get("wait"): - return - - role_name = module.params.get("name") - wait_timeout = module.params.get("wait_timeout") - - delay = min(wait_timeout, 5) - max_attempts = wait_timeout // delay - - try: - waiter = client.get_waiter("role_exists") - waiter.wait( - WaiterConfig={"Delay": delay, "MaxAttempts": max_attempts}, - RoleName=role_name, - ) - except botocore.exceptions.WaiterError as e: - module.fail_json_aws(e, msg="Timeout while waiting on IAM role creation") - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed while waiting on IAM role creation") - - -def convert_friendly_names_to_arns(module, client, policy_names): - if all(validate_aws_arn(policy, service="iam") for policy in policy_names if policy is not None): - return policy_names - - allpolicies = {} - policies = _list_policies(client) - - for policy in policies: - allpolicies[policy["PolicyName"]] = policy["Arn"] - allpolicies[policy["Arn"]] = policy["Arn"] - try: - return [allpolicies[policy] for policy in policy_names if policy is not None] - except KeyError as e: - module.fail_json_aws(e, msg="Couldn't find policy") - - -def attach_policies(module, client, policies_to_attach, role_name): - if module.check_mode and policies_to_attach: - return True - - changed = False - for policy_arn in policies_to_attach: - try: - client.attach_role_policy(RoleName=role_name, PolicyArn=policy_arn, aws_retry=True) - changed = True - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to attach policy {policy_arn} to role {role_name}") - return changed - - -def remove_policies(module, client, policies_to_remove, role_name): - if module.check_mode and policies_to_remove: - return True - - changed = False - for policy in policies_to_remove: - try: - client.detach_role_policy(RoleName=role_name, PolicyArn=policy, aws_retry=True) - changed = True - except is_boto3_error_code("NoSuchEntityException"): - pass - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg=f"Unable to detach policy {policy} from {role_name}") - return changed - - -def remove_inline_policies(module, client, role_name): - current_inline_policies = get_inline_policy_list(module, client, role_name) - for policy in current_inline_policies: - try: - client.delete_role_policy(RoleName=role_name, PolicyName=policy, aws_retry=True) - except is_boto3_error_code("NoSuchEntityException"): - pass - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg=f"Unable to delete policy {policy} embedded in {role_name}") - - -def generate_create_params(module): - params = dict() - params["Path"] = module.params.get("path") - params["RoleName"] = module.params.get("name") - params["AssumeRolePolicyDocument"] = module.params.get("assume_role_policy_document") - if module.params.get("description") is not None: - params["Description"] = module.params.get("description") - if module.params.get("max_session_duration") is not None: - params["MaxSessionDuration"] = module.params.get("max_session_duration") - if module.params.get("boundary") is not None: - params["PermissionsBoundary"] = module.params.get("boundary") - if module.params.get("tags") is not None: - params["Tags"] = ansible_dict_to_boto3_tag_list(module.params.get("tags")) - - return params - - -def create_basic_role(module, client): - """ - Perform the Role creation. - Assumes tests for the role existing have already been performed. - """ - if module.check_mode: - module.exit_json(changed=True) - - try: - params = generate_create_params(module) - role = client.create_role(aws_retry=True, **params) - # 'Description' is documented as key of the role returned by create_role - # but appears to be an AWS bug (the value is not returned using the AWS CLI either). - # Get the role after creating it. - role = get_role_with_backoff(module, client, params["RoleName"]) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Unable to create role") - - return role - - -def update_role_assumed_policy(module, client, role_name, target_assumed_policy, current_assumed_policy): - # Check Assumed Policy document - if target_assumed_policy is None or not compare_policies(current_assumed_policy, json.loads(target_assumed_policy)): - return False - - if module.check_mode: - return True - - try: - client.update_assume_role_policy(RoleName=role_name, PolicyDocument=target_assumed_policy, aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to update assume role policy for role {role_name}") - return True - - -def update_role_description(module, client, role_name, target_description, current_description): - # Check Description update - if target_description is None or current_description == target_description: - return False - - if module.check_mode: - return True - - try: - client.update_role(RoleName=role_name, Description=target_description, aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to update description for role {role_name}") - return True - - -def update_role_max_session_duration(module, client, role_name, target_duration, current_duration): - # Check MaxSessionDuration update - if target_duration is None or current_duration == target_duration: - return False - - if module.check_mode: - return True - - try: - client.update_role(RoleName=role_name, MaxSessionDuration=target_duration, aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to update maximum session duration for role {role_name}") - return True - - -def update_role_permissions_boundary( - module, client, role_name, target_permissions_boundary, current_permissions_boundary -): - # Check PermissionsBoundary - if target_permissions_boundary is None or target_permissions_boundary == current_permissions_boundary: - return False - - if module.check_mode: - return True - - if target_permissions_boundary == "": - try: - client.delete_role_permissions_boundary(RoleName=role_name, aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to remove permission boundary for role {role_name}") - else: - try: - client.put_role_permissions_boundary( - RoleName=role_name, PermissionsBoundary=target_permissions_boundary, aws_retry=True - ) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to update permission boundary for role {role_name}") - return True - - -def update_managed_policies(module, client, role_name, managed_policies, purge_policies): - # Check Managed Policies - if managed_policies is None: - return False - - # Get list of current attached managed policies - current_attached_policies = get_attached_policy_list(module, client, role_name) - current_attached_policies_arn_list = [policy["PolicyArn"] for policy in current_attached_policies] - - if len(managed_policies) == 1 and managed_policies[0] is None: - managed_policies = [] - - policies_to_remove = set(current_attached_policies_arn_list) - set(managed_policies) - policies_to_attach = set(managed_policies) - set(current_attached_policies_arn_list) - - changed = False - if purge_policies and policies_to_remove: - if module.check_mode: - return True - else: - changed |= remove_policies(module, client, policies_to_remove, role_name) - - if policies_to_attach: - if module.check_mode: - return True - else: - changed |= attach_policies(module, client, policies_to_attach, role_name) - - return changed - - -def create_or_update_role(module, client): - role_name = module.params.get("name") - assumed_policy = module.params.get("assume_role_policy_document") - create_instance_profile = module.params.get("create_instance_profile") - description = module.params.get("description") - duration = module.params.get("max_session_duration") - path = module.params.get("path") - permissions_boundary = module.params.get("boundary") - purge_tags = module.params.get("purge_tags") - tags = ansible_dict_to_boto3_tag_list(module.params.get("tags")) if module.params.get("tags") else None - purge_policies = module.params.get("purge_policies") - managed_policies = module.params.get("managed_policies") - if managed_policies: - # Attempt to list the policies early so we don't leave things behind if we can't find them. - managed_policies = convert_friendly_names_to_arns(module, client, managed_policies) - - changed = False - - # Get role - role = get_role(module, client, role_name) - - # If role is None, create it - if role is None: - role = create_basic_role(module, client) - - if not module.check_mode and module.params.get("wait"): - wait_iam_exists(module, client) - - changed = True - else: - # Role exists - get current attributes - current_assumed_policy = role.get("AssumeRolePolicyDocument") - current_description = role.get("Description") - current_duration = role.get("MaxSessionDuration") - current_permissions_boundary = role.get("PermissionsBoundary", {}).get("PermissionsBoundaryArn", "") - - # Update attributes - changed |= update_role_tags(module, client, role_name, tags, purge_tags) - changed |= update_role_assumed_policy(module, client, role_name, assumed_policy, current_assumed_policy) - changed |= update_role_description(module, client, role_name, description, current_description) - changed |= update_role_max_session_duration(module, client, role_name, duration, current_duration) - changed |= update_role_permissions_boundary( - module, client, role_name, permissions_boundary, current_permissions_boundary - ) - - if not module.check_mode and module.params.get("wait"): - wait_iam_exists(module, client) - - if create_instance_profile: - changed |= create_instance_profiles(module, client, role_name, path) - - if not module.check_mode and module.params.get("wait"): - wait_iam_exists(module, client) - - changed |= update_managed_policies(module, client, role_name, managed_policies, purge_policies) - wait_iam_exists(module, client) - - # Get the role again - role = get_role(module, client, role_name) - role["AttachedPolicies"] = get_attached_policy_list(module, client, role_name) - role["tags"] = get_role_tags(module, client) - - camel_role = camel_dict_to_snake_dict(role, ignore_list=["tags"]) - camel_role["assume_role_policy_document_raw"] = role.get("AssumeRolePolicyDocument", {}) - module.exit_json(changed=changed, iam_role=camel_role, **camel_role) - - -def create_instance_profiles(module, client, role_name, path): - # Fetch existing Profiles - try: - instance_profiles = client.list_instance_profiles_for_role(RoleName=role_name, aws_retry=True)[ - "InstanceProfiles" - ] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to list instance profiles for role {role_name}") - - # Profile already exists - if any(p["InstanceProfileName"] == role_name for p in instance_profiles): - return False - - if module.check_mode: - return True - - # Make sure an instance profile is created - try: - client.create_instance_profile(InstanceProfileName=role_name, Path=path, aws_retry=True) - except is_boto3_error_code("EntityAlreadyExists"): - # If the profile already exists, no problem, move on. - # Implies someone's changing things at the same time... - return False - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg=f"Unable to create instance profile for role {role_name}") - - # And attach the role to the profile - try: - client.add_role_to_instance_profile(InstanceProfileName=role_name, RoleName=role_name, aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to attach role {role_name} to instance profile {role_name}") - - return True - - -def remove_instance_profiles(module, client, role_name): - delete_profiles = module.params.get("delete_instance_profile") - - try: - instance_profiles = client.list_instance_profiles_for_role(aws_retry=True, RoleName=role_name)[ - "InstanceProfiles" - ] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to list instance profiles for role {role_name}") - - # Remove the role from the instance profile(s) - for profile in instance_profiles: - profile_name = profile["InstanceProfileName"] - try: - if not module.check_mode: - client.remove_role_from_instance_profile( - aws_retry=True, InstanceProfileName=profile_name, RoleName=role_name - ) - if profile_name == role_name: - if delete_profiles: - try: - client.delete_instance_profile(InstanceProfileName=profile_name, aws_retry=True) - except is_boto3_error_code("NoSuchEntityException"): - pass - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg=f"Unable to remove instance profile {profile_name}") - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to remove role {role_name} from instance profile {profile_name}") - - -def destroy_role(module, client): - role_name = module.params.get("name") - role = get_role(module, client, role_name) - - if role is None: - module.exit_json(changed=False) - - if not module.check_mode: - # Before we try to delete the role we need to remove any - # - attached instance profiles - # - attached managed policies - # - embedded inline policies - remove_instance_profiles(module, client, role_name) - update_managed_policies(module, client, role_name, [], True) - remove_inline_policies(module, client, role_name) - try: - client.delete_role(aws_retry=True, RoleName=role_name) - except is_boto3_error_code("NoSuchEntityException"): - module.exit_json(changed=False) - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Unable to delete role") - - module.exit_json(changed=True) - - -def get_role_with_backoff(module, client, name): - try: - return AWSRetry.jittered_backoff(catch_extra_error_codes=["NoSuchEntity"])(client.get_role)(RoleName=name)[ - "Role" - ] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to get role {name}") - - -def get_role(module, client, name): - try: - return client.get_role(RoleName=name, aws_retry=True)["Role"] - except is_boto3_error_code("NoSuchEntity"): - return None - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg=f"Unable to get role {name}") - - -def get_attached_policy_list(module, client, name): - try: - return client.list_attached_role_policies(RoleName=name, aws_retry=True)["AttachedPolicies"] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to list attached policies for role {name}") - - -def get_inline_policy_list(module, client, name): - try: - return client.list_role_policies(RoleName=name, aws_retry=True)["PolicyNames"] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to list attached policies for role {name}") - - -def get_role_tags(module, client): - role_name = module.params.get("name") - try: - return boto3_tag_list_to_ansible_dict(client.list_role_tags(RoleName=role_name, aws_retry=True)["Tags"]) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to list tags for role {role_name}") - - -def update_role_tags(module, client, role_name, new_tags, purge_tags): - if new_tags is None: - return False - new_tags = boto3_tag_list_to_ansible_dict(new_tags) - - try: - existing_tags = boto3_tag_list_to_ansible_dict( - client.list_role_tags(RoleName=role_name, aws_retry=True)["Tags"] - ) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError, KeyError): - existing_tags = {} - - tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, new_tags, purge_tags=purge_tags) - - if not module.check_mode: - try: - if tags_to_remove: - client.untag_role(RoleName=role_name, TagKeys=tags_to_remove, aws_retry=True) - if tags_to_add: - client.tag_role(RoleName=role_name, Tags=ansible_dict_to_boto3_tag_list(tags_to_add), aws_retry=True) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Unable to set tags for role {role_name}") - - changed = bool(tags_to_add) or bool(tags_to_remove) - return changed - - -def main(): - argument_spec = dict( - name=dict(type="str", required=True), - path=dict(type="str", default="/"), - assume_role_policy_document=dict(type="json"), - managed_policies=dict(type="list", aliases=["managed_policy"], elements="str"), - max_session_duration=dict(type="int"), - state=dict(type="str", choices=["present", "absent"], default="present"), - description=dict(type="str"), - boundary=dict(type="str", aliases=["boundary_policy_arn"]), - create_instance_profile=dict(type="bool", default=True), - delete_instance_profile=dict(type="bool", default=False), - purge_policies=dict(default=True, type="bool", aliases=["purge_policy", "purge_managed_policies"]), - tags=dict(type="dict", aliases=["resource_tags"]), - purge_tags=dict(type="bool", default=True), - wait=dict(type="bool", default=True), - wait_timeout=dict(default=120, type="int"), - ) - - module = AnsibleAWSModule( - argument_spec=argument_spec, - required_if=[("state", "present", ["assume_role_policy_document"])], - supports_check_mode=True, - ) - - module.deprecate( - "All return values other than iam_role and changed have been deprecated and " - "will be removed in a release after 2023-12-01.", - date="2023-12-01", - collection_name="amazon.aws", - ) - - module.deprecate( - "In a release after 2023-12-01 the contents of iam_role.assume_role_policy_document " - "will no longer be converted from CamelCase to snake_case. The " - "iam_role.assume_role_policy_document_raw return value already returns the " - "policy document in this future format.", - date="2023-12-01", - collection_name="amazon.aws", - ) - - if module.params.get("boundary"): - if module.params.get("create_instance_profile"): - module.fail_json(msg="When using a boundary policy, `create_instance_profile` must be set to `false`.") - if not validate_aws_arn(module.params.get("boundary"), service="iam"): - module.fail_json(msg="Boundary policy must be an ARN") - if module.params.get("max_session_duration"): - max_session_duration = module.params.get("max_session_duration") - if max_session_duration < 3600 or max_session_duration > 43200: - module.fail_json(msg="max_session_duration must be between 1 and 12 hours (3600 and 43200 seconds)") - if module.params.get("path"): - path = module.params.get("path") - if not path.endswith("/") or not path.startswith("/"): - module.fail_json(msg="path must begin and end with /") - - client = module.client("iam", retry_decorator=AWSRetry.jittered_backoff()) - - state = module.params.get("state") - - if state == "present": - create_or_update_role(module, client) - elif state == "absent": - destroy_role(module, client) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/iam_role_info.py b/plugins/modules/iam_role_info.py deleted file mode 100644 index b56a35a3e9c..00000000000 --- a/plugins/modules/iam_role_info.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -DOCUMENTATION = r""" ---- -module: iam_role_info -version_added: 1.0.0 -version_added_collection: community.aws -short_description: Gather information on IAM roles -description: - - Gathers information about IAM roles. -author: - - "Will Thames (@willthames)" -options: - name: - description: - - Name of a role to search for. - - Mutually exclusive with I(path_prefix). - aliases: - - role_name - type: str - path_prefix: - description: - - Prefix of role to restrict IAM role search for. - - Mutually exclusive with I(name). - type: str -extends_documentation_fragment: - - amazon.aws.common.modules - - amazon.aws.region.modules - - amazon.aws.boto3 -""" - -EXAMPLES = r""" -- name: find all existing IAM roles - amazon.aws.iam_role_info: - register: result - -- name: describe a single role - amazon.aws.iam_role_info: - name: MyIAMRole - -- name: describe all roles matching a path prefix - amazon.aws.iam_role_info: - path_prefix: /application/path -""" - -RETURN = r""" -iam_roles: - description: List of IAM roles - returned: always - type: complex - contains: - arn: - description: Amazon Resource Name for IAM role. - returned: always - type: str - sample: arn:aws:iam::123456789012:role/AnsibleTestRole - assume_role_policy_document: - description: - - The policy that grants an entity permission to assume the role - - | - Note: the case of keys in this dictionary are currently converted from CamelCase to - snake_case. In a release after 2023-12-01 this behaviour will change. - returned: always - type: dict - assume_role_policy_document_raw: - description: The policy document describing what can assume the role. - returned: always - type: dict - version_added: 5.3.0 - create_date: - description: Date IAM role was created. - returned: always - type: str - sample: '2017-10-23T00:05:08+00:00' - inline_policies: - description: List of names of inline policies. - returned: always - type: list - sample: [] - managed_policies: - description: List of attached managed policies. - returned: always - type: complex - contains: - policy_arn: - description: Amazon Resource Name for the policy. - returned: always - type: str - sample: arn:aws:iam::123456789012:policy/AnsibleTestEC2Policy - policy_name: - description: Name of managed policy. - returned: always - type: str - sample: AnsibleTestEC2Policy - instance_profiles: - description: List of attached instance profiles. - returned: always - type: complex - contains: - arn: - description: Amazon Resource Name for the instance profile. - returned: always - type: str - sample: arn:aws:iam::123456789012:instance-profile/AnsibleTestEC2Policy - create_date: - description: Date instance profile was created. - returned: always - type: str - sample: '2017-10-23T00:05:08+00:00' - instance_profile_id: - description: Amazon Identifier for the instance profile. - returned: always - type: str - sample: AROAII7ABCD123456EFGH - instance_profile_name: - description: Name of instance profile. - returned: always - type: str - sample: AnsibleTestEC2Policy - path: - description: Path of instance profile. - returned: always - type: str - sample: / - roles: - description: List of roles associated with this instance profile. - returned: always - type: list - sample: [] - path: - description: Path of role. - returned: always - type: str - sample: / - role_id: - description: Amazon Identifier for the role. - returned: always - type: str - sample: AROAII7ABCD123456EFGH - role_name: - description: Name of the role. - returned: always - type: str - sample: AnsibleTestRole - tags: - description: Role tags. - type: dict - returned: always - sample: '{"Env": "Prod"}' -""" - -try: - import botocore -except ImportError: - pass # caught by AnsibleAWSModule - -from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict - -from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code -from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict - - -@AWSRetry.jittered_backoff() -def list_iam_roles_with_backoff(client, **kwargs): - paginator = client.get_paginator("list_roles") - return paginator.paginate(**kwargs).build_full_result() - - -@AWSRetry.jittered_backoff() -def list_iam_role_policies_with_backoff(client, role_name): - paginator = client.get_paginator("list_role_policies") - return paginator.paginate(RoleName=role_name).build_full_result()["PolicyNames"] - - -@AWSRetry.jittered_backoff() -def list_iam_attached_role_policies_with_backoff(client, role_name): - paginator = client.get_paginator("list_attached_role_policies") - return paginator.paginate(RoleName=role_name).build_full_result()["AttachedPolicies"] - - -@AWSRetry.jittered_backoff() -def list_iam_instance_profiles_for_role_with_backoff(client, role_name): - paginator = client.get_paginator("list_instance_profiles_for_role") - return paginator.paginate(RoleName=role_name).build_full_result()["InstanceProfiles"] - - -def describe_iam_role(module, client, role): - name = role["RoleName"] - try: - role["InlinePolicies"] = list_iam_role_policies_with_backoff(client, name) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Couldn't get inline policies for role {name}") - try: - role["ManagedPolicies"] = list_iam_attached_role_policies_with_backoff(client, name) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Couldn't get managed policies for role {name}") - try: - role["InstanceProfiles"] = list_iam_instance_profiles_for_role_with_backoff(client, name) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg=f"Couldn't get instance profiles for role {name}") - try: - role["tags"] = boto3_tag_list_to_ansible_dict(role["Tags"]) - del role["Tags"] - except KeyError: - role["tags"] = {} - return role - - -def describe_iam_roles(module, client): - name = module.params["name"] - path_prefix = module.params["path_prefix"] - if name: - try: - roles = [client.get_role(RoleName=name, aws_retry=True)["Role"]] - except is_boto3_error_code("NoSuchEntity"): - return [] - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg=f"Couldn't get IAM role {name}") - else: - params = dict() - if path_prefix: - if not path_prefix.startswith("/"): - path_prefix = "/" + path_prefix - if not path_prefix.endswith("/"): - path_prefix = path_prefix + "/" - params["PathPrefix"] = path_prefix - try: - roles = list_iam_roles_with_backoff(client, **params)["Roles"] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't list IAM roles") - return [normalize_role(describe_iam_role(module, client, role)) for role in roles] - - -def normalize_profile(profile): - new_profile = camel_dict_to_snake_dict(profile) - if profile.get("Roles"): - profile["roles"] = [normalize_role(role) for role in profile.get("Roles")] - return new_profile - - -def normalize_role(role): - new_role = camel_dict_to_snake_dict(role, ignore_list=["tags"]) - new_role["assume_role_policy_document_raw"] = role.get("AssumeRolePolicyDocument") - if role.get("InstanceProfiles"): - role["instance_profiles"] = [normalize_profile(profile) for profile in role.get("InstanceProfiles")] - return new_role - - -def main(): - """ - Module action handler - """ - argument_spec = dict( - name=dict(aliases=["role_name"]), - path_prefix=dict(), - ) - - module = AnsibleAWSModule( - argument_spec=argument_spec, - supports_check_mode=True, - mutually_exclusive=[["name", "path_prefix"]], - ) - - client = module.client("iam", retry_decorator=AWSRetry.jittered_backoff()) - - module.deprecate( - "In a release after 2023-12-01 the contents of assume_role_policy_document " - "will no longer be converted from CamelCase to snake_case. The " - ".assume_role_policy_document_raw return value already returns the " - "policy document in this future format.", - date="2023-12-01", - collection_name="amazon.aws", - ) - - module.exit_json(changed=False, iam_roles=describe_iam_roles(module, client)) - - -if __name__ == "__main__": - main() diff --git a/tests/integration/targets/iam_role/aliases b/tests/integration/targets/iam_role/aliases deleted file mode 100644 index 483c861158c..00000000000 --- a/tests/integration/targets/iam_role/aliases +++ /dev/null @@ -1,9 +0,0 @@ -# reason: missing-policy -# It should be possible to test iam_role by limiting which policies can be -# attached to the roles. -# Careful review is needed prior to adding this to the main CI. -unsupported - -cloud/aws - -iam_role_info diff --git a/tests/integration/targets/iam_role/defaults/main.yml b/tests/integration/targets/iam_role/defaults/main.yml deleted file mode 100644 index e83a563990b..00000000000 --- a/tests/integration/targets/iam_role/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -test_role: '{{ resource_prefix }}-role' -test_path: /{{ resource_prefix }}/ -safe_managed_policy: AWSDenyAll -custom_policy_name: '{{ resource_prefix }}-denyall' -boundary_policy: arn:aws:iam::aws:policy/AWSDenyAll diff --git a/tests/integration/targets/iam_role/files/deny-all-a.json b/tests/integration/targets/iam_role/files/deny-all-a.json deleted file mode 100644 index ae62fd1975d..00000000000 --- a/tests/integration/targets/iam_role/files/deny-all-a.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "*" - ], - "Effect": "Deny", - "Resource": "*", - "Sid": "DenyA" - } - ] -} diff --git a/tests/integration/targets/iam_role/files/deny-all-b.json b/tests/integration/targets/iam_role/files/deny-all-b.json deleted file mode 100644 index 3a4704a46ab..00000000000 --- a/tests/integration/targets/iam_role/files/deny-all-b.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "*" - ], - "Effect": "Deny", - "Resource": "*", - "Sid": "DenyB" - } - ] -} diff --git a/tests/integration/targets/iam_role/files/deny-all.json b/tests/integration/targets/iam_role/files/deny-all.json deleted file mode 100644 index 3d324b9b9c6..00000000000 --- a/tests/integration/targets/iam_role/files/deny-all.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "*" - ], - "Effect": "Deny", - "Resource": "*" - } - ] -} diff --git a/tests/integration/targets/iam_role/files/deny-assume.json b/tests/integration/targets/iam_role/files/deny-assume.json deleted file mode 100644 index 73e87715862..00000000000 --- a/tests/integration/targets/iam_role/files/deny-assume.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Principal": { "Service": "ec2.amazonaws.com" }, - "Effect": "Deny" - } - ] -} diff --git a/tests/integration/targets/iam_role/meta/main.yml b/tests/integration/targets/iam_role/meta/main.yml deleted file mode 100644 index 32cf5dda7ed..00000000000 --- a/tests/integration/targets/iam_role/meta/main.yml +++ /dev/null @@ -1 +0,0 @@ -dependencies: [] diff --git a/tests/integration/targets/iam_role/tasks/boundary_policy.yml b/tests/integration/targets/iam_role/tasks/boundary_policy.yml deleted file mode 100644 index 818b701f1ef..00000000000 --- a/tests/integration/targets/iam_role/tasks/boundary_policy.yml +++ /dev/null @@ -1,86 +0,0 @@ -- name: Create minimal role with no boundary policy - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - -- name: Configure Boundary Policy (CHECK MODE) - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - boundary: '{{ boundary_policy }}' - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Configure Boundary Policy - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - boundary: '{{ boundary_policy }}' - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - -- name: Configure Boundary Policy (no change) - check mode - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - boundary: '{{ boundary_policy }}' - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Configure Boundary Policy (no change) - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - boundary: '{{ boundary_policy }}' - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after adding boundary policy - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - '"description" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 0 - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 3600 - - role_info.iam_roles[0].path == '/' - - role_info.iam_roles[0].permissions_boundary.permissions_boundary_arn == boundary_policy - - role_info.iam_roles[0].permissions_boundary.permissions_boundary_type == 'Policy' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - -- name: Remove IAM Role - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - register: iam_role -- assert: - that: - - iam_role is changed diff --git a/tests/integration/targets/iam_role/tasks/complex_role_creation.yml b/tests/integration/targets/iam_role/tasks/complex_role_creation.yml deleted file mode 100644 index 59db5d156fe..00000000000 --- a/tests/integration/targets/iam_role/tasks/complex_role_creation.yml +++ /dev/null @@ -1,128 +0,0 @@ -- name: Complex IAM Role (CHECK MODE) - iam_role: - name: '{{ test_role }}' - assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' - boundary: '{{ boundary_policy }}' - create_instance_profile: no - description: Ansible Test Role {{ resource_prefix }} - managed_policy: - - '{{ safe_managed_policy }}' - - '{{ custom_policy_name }}' - max_session_duration: 43200 - path: '{{ test_path }}' - tags: - TagA: ValueA - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: iam_role_info after Complex Role creation in check_mode - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 0 - -- name: Complex IAM Role - iam_role: - name: '{{ test_role }}' - assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' - boundary: '{{ boundary_policy }}' - create_instance_profile: no - description: Ansible Test Role {{ resource_prefix }} - managed_policy: - - '{{ safe_managed_policy }}' - - '{{ custom_policy_name }}' - max_session_duration: 43200 - path: '{{ test_path }}' - tags: - TagA: ValueA - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.arn.startswith("arn") - - iam_role.iam_role.arn.endswith("role" + test_path + test_role ) - # Would be nice to test the contents... - - '"assume_role_policy_document" in iam_role.iam_role' - - iam_role.iam_role.attached_policies | length == 2 - - iam_role.iam_role.max_session_duration == 43200 - - iam_role.iam_role.path == test_path - - iam_role.iam_role.role_name == test_role - - '"create_date" in iam_role.iam_role' - - '"role_id" in iam_role.iam_role' - -- name: Complex IAM role (no change) - check mode - iam_role: - name: '{{ test_role }}' - assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' - boundary: '{{ boundary_policy }}' - create_instance_profile: no - description: Ansible Test Role {{ resource_prefix }} - managed_policy: - - '{{ safe_managed_policy }}' - - '{{ custom_policy_name }}' - max_session_duration: 43200 - path: '{{ test_path }}' - tags: - TagA: ValueA - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Complex IAM role (no change) - iam_role: - name: '{{ test_role }}' - assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' - boundary: '{{ boundary_policy }}' - create_instance_profile: no - description: Ansible Test Role {{ resource_prefix }} - managed_policy: - - '{{ safe_managed_policy }}' - - '{{ custom_policy_name }}' - max_session_duration: 43200 - path: '{{ test_path }}' - tags: - TagA: ValueA - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after Role creation - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role" + test_path + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 0 - - role_info.iam_roles[0].managed_policies | length == 2 - - safe_managed_policy in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == test_path - - role_info.iam_roles[0].permissions_boundary.permissions_boundary_arn == boundary_policy - - role_info.iam_roles[0].permissions_boundary.permissions_boundary_type == 'Policy' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - '"TagA" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagA == "ValueA" diff --git a/tests/integration/targets/iam_role/tasks/creation_deletion.yml b/tests/integration/targets/iam_role/tasks/creation_deletion.yml deleted file mode 100644 index 166c7f1236e..00000000000 --- a/tests/integration/targets/iam_role/tasks/creation_deletion.yml +++ /dev/null @@ -1,376 +0,0 @@ -- name: Try running some rapid fire create/delete tests - block: - - name: Minimal IAM Role without instance profile (rapid) - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role - - name: Minimal IAM Role without instance profile (rapid) - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role_again - - assert: - that: - - iam_role is changed - - iam_role_again is not changed - - - name: Remove IAM Role (rapid) - iam_role: - state: absent - name: '{{ test_role }}' - register: iam_role - - name: Remove IAM Role (rapid) - iam_role: - state: absent - name: '{{ test_role }}' - register: iam_role_again - - assert: - that: - - iam_role is changed - - iam_role_again is not changed - - - name: Minimal IAM Role without instance profile (rapid) - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role - - name: Remove IAM Role (rapid) - iam_role: - state: absent - name: '{{ test_role }}' - register: iam_role_again - - assert: - that: - - iam_role is changed - - iam_role_again is changed - -# =================================================================== -# Role Creation -# (without Instance profile) -- name: iam_role_info before Role creation (no args) - iam_role_info: - register: role_info -- assert: - that: - - role_info is succeeded - -- name: iam_role_info before Role creation (search for test role) - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 0 - -- name: Minimal IAM Role (CHECK MODE) - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is changed - -- name: iam_role_info after Role creation in check_mode - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 0 - -- name: Minimal IAM Role without instance profile - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.arn.startswith("arn") - - iam_role.iam_role.arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in iam_role.iam_role' - - '"assume_role_policy_document_raw" in iam_role.iam_role' - - iam_role.iam_role.assume_role_policy_document_raw == assume_deny_policy - - iam_role.iam_role.attached_policies | length == 0 - - iam_role.iam_role.max_session_duration == 3600 - - iam_role.iam_role.path == '/' - - iam_role.iam_role.role_name == test_role - - '"create_date" in iam_role.iam_role' - - '"role_id" in iam_role.iam_role' - -- name: Minimal IAM Role without instance profile (no change) - check mode - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Minimal IAM Role without instance profile (no change) - iam_role: - name: '{{ test_role }}' - create_instance_profile: no - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after Role creation - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"assume_role_policy_document_raw" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - '"description" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].assume_role_policy_document_raw == assume_deny_policy - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 0 - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 3600 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 0 - -- name: Remove IAM Role - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: iam_role_info after Role deletion - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 0 - -# ------------------------------------------------------------------------------------------ - -# (with path) -- name: Minimal IAM Role with path (CHECK MODE) - iam_role: - name: '{{ test_role }}' - path: '{{ test_path }}' - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is changed - -- name: Minimal IAM Role with path - iam_role: - name: '{{ test_role }}' - path: '{{ test_path }}' - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.arn.startswith("arn") - - iam_role.iam_role.arn.endswith("role" + test_path + test_role ) - # Would be nice to test the contents... - - '"assume_role_policy_document" in iam_role.iam_role' - - iam_role.iam_role.attached_policies | length == 0 - - iam_role.iam_role.max_session_duration == 3600 - - iam_role.iam_role.path == '{{ test_path }}' - - iam_role.iam_role.role_name == test_role - - '"create_date" in iam_role.iam_role' - - '"role_id" in iam_role.iam_role' - -- name: Minimal IAM Role with path (no change) - check mode - iam_role: - name: '{{ test_role }}' - path: '{{ test_path }}' - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Minimal IAM Role with path (no change) - iam_role: - name: '{{ test_role }}' - path: '{{ test_path }}' - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after Role creation - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role" + test_path + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - '"description" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile" - + test_path + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 3600 - - role_info.iam_roles[0].path == '{{ test_path }}' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 0 - -- name: iam_role_info after Role creation (searching a path) - iam_role_info: - path_prefix: '{{ test_path }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role" + test_path + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - '"description" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile" - + test_path + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 3600 - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].path == '{{ test_path }}' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 0 - -- name: Remove IAM Role - iam_role: - state: absent - name: '{{ test_role }}' - path: '{{ test_path }}' - delete_instance_profile: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: iam_role_info after Role deletion - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 0 - -# ------------------------------------------------------------------------------------------ - -# (with Instance profile) -- name: Minimal IAM Role with instance profile - check mode - iam_role: - name: '{{ test_role }}' - create_instance_profile: yes - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is changed - -- name: Minimal IAM Role with instance profile - iam_role: - name: '{{ test_role }}' - create_instance_profile: yes - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.arn.startswith("arn") - - iam_role.iam_role.arn.endswith("role/" + test_role ) - # Would be nice to test the contents... - - '"assume_role_policy_document" in iam_role.iam_role' - - iam_role.iam_role.attached_policies | length == 0 - - iam_role.iam_role.max_session_duration == 3600 - - iam_role.iam_role.path == '/' - - iam_role.iam_role.role_name == test_role - - '"create_date" in iam_role.iam_role' - - '"role_id" in iam_role.iam_role' - -- name: Minimal IAM Role wth instance profile (no change) - check mode - iam_role: - name: '{{ test_role }}' - create_instance_profile: yes - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Minimal IAM Role wth instance profile (no change) - iam_role: - name: '{{ test_role }}' - create_instance_profile: yes - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after Role creation - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - '"description" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 3600 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 0 diff --git a/tests/integration/targets/iam_role/tasks/description_update.yml b/tests/integration/targets/iam_role/tasks/description_update.yml deleted file mode 100644 index 198104134fb..00000000000 --- a/tests/integration/targets/iam_role/tasks/description_update.yml +++ /dev/null @@ -1,143 +0,0 @@ -- name: Add Description (CHECK MODE) - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role {{ resource_prefix }} - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Add Description - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role {{ resource_prefix }} - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.description == 'Ansible Test Role {{ resource_prefix }}' - -- name: Add Description (no change) - check mode - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role {{ resource_prefix }} - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Add Description (no change) - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role {{ resource_prefix }} - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.description == 'Ansible Test Role {{ resource_prefix }}' - -- name: iam_role_info after adding Description - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 0 - -# ------------------------------------------------------------------------------------------ - -- name: Update Description (CHECK MODE) - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role (updated) {{ resource_prefix }} - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Update Description - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role (updated) {{ resource_prefix }} - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.description == 'Ansible Test Role (updated) {{ resource_prefix - }}' - -- name: Update Description (no change) - check mode - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role (updated) {{ resource_prefix }} - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Update Description (no change) - iam_role: - name: '{{ test_role }}' - description: Ansible Test Role (updated) {{ resource_prefix }} - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.description == 'Ansible Test Role (updated) {{ resource_prefix - }}' - -- name: iam_role_info after updating Description - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 0 diff --git a/tests/integration/targets/iam_role/tasks/inline_policy_update.yml b/tests/integration/targets/iam_role/tasks/inline_policy_update.yml deleted file mode 100644 index 3c82196dd8d..00000000000 --- a/tests/integration/targets/iam_role/tasks/inline_policy_update.yml +++ /dev/null @@ -1,49 +0,0 @@ -- name: Attach inline policy a - iam_policy: - state: present - iam_type: role - iam_name: '{{ test_role }}' - policy_name: inline-policy-a - policy_json: '{{ lookup("file", "deny-all-a.json") }}' -- name: Attach inline policy b - iam_policy: - state: present - iam_type: role - iam_name: '{{ test_role }}' - policy_name: inline-policy-b - policy_json: '{{ lookup("file", "deny-all-b.json") }}' -- name: iam_role_info after attaching inline policies (using iam_policy) - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 2 - - '"inline-policy-a" in role_info.iam_roles[0].inline_policies' - - '"inline-policy-b" in role_info.iam_roles[0].inline_policies' - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 1 - - safe_managed_policy not in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 1 - - '"TagB" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagB == "ValueB" diff --git a/tests/integration/targets/iam_role/tasks/main.yml b/tests/integration/targets/iam_role/tasks/main.yml deleted file mode 100644 index a787d5cfac1..00000000000 --- a/tests/integration/targets/iam_role/tasks/main.yml +++ /dev/null @@ -1,83 +0,0 @@ -# Tests for iam_role and iam_role_info -# -# Tests: -# - Minimal Role creation -# - Role deletion -# - Fetching a specific role -# - Creating roles w/ and w/o instance profiles -# - Creating roles w/ a path -# - Updating Max Session Duration -# - Updating Description -# - Managing list of managed policies -# - Managing list of inline policies (for testing _info) -# - Managing boundary policy -# -# Notes: -# - Only tests *documented* return values ( RESULT.iam_role ) -# - There are some known timing issues with boto3 returning before actions -# complete in the case of problems with "changed" status it's worth enabling -# the standard_pauses and paranoid_pauses options as a first step in debugging - - -- name: Setup AWS connection info - module_defaults: - group/aws: - access_key: '{{ aws_access_key }}' - secret_key: '{{ aws_secret_key }}' - session_token: '{{ security_token | default(omit) }}' - region: '{{ aws_region }}' - iam_role: - assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' - collections: - - community.general - block: - - set_fact: - assume_deny_policy: '{{ lookup("file", "deny-assume.json") | from_json }}' - - include_tasks: parameter_checks.yml - - name: Create Safe IAM Managed Policy - iam_managed_policy: - state: present - policy_name: '{{ custom_policy_name }}' - policy_description: A safe (deny-all) managed policy - policy: "{{ lookup('file', 'deny-all.json') }}" - register: create_managed_policy - - assert: - that: - - create_managed_policy is succeeded - - # =================================================================== - # Rapid Role Creation and deletion - - include_tasks: creation_deletion.yml - - include_tasks: max_session_update.yml - - include_tasks: description_update.yml - - include_tasks: tags_update.yml - - include_tasks: policy_update.yml - - include_tasks: inline_policy_update.yml - - include_tasks: role_removal.yml - - include_tasks: boundary_policy.yml - - include_tasks: complex_role_creation.yml - always: - # =================================================================== - # Cleanup - - - name: Remove IAM Role - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - ignore_errors: true - - name: Remove IAM Role (with path) - iam_role: - state: absent - name: '{{ test_role }}' - path: '{{ test_path }}' - delete_instance_profile: yes - ignore_errors: true - - name: iam_role_info after Role deletion - iam_role_info: - name: '{{ test_role }}' - ignore_errors: true - - name: Remove test managed policy - iam_managed_policy: - state: absent - policy_name: '{{ custom_policy_name }}' diff --git a/tests/integration/targets/iam_role/tasks/max_session_update.yml b/tests/integration/targets/iam_role/tasks/max_session_update.yml deleted file mode 100644 index a850de70264..00000000000 --- a/tests/integration/targets/iam_role/tasks/max_session_update.yml +++ /dev/null @@ -1,66 +0,0 @@ -- name: Update Max Session Duration (CHECK MODE) - iam_role: - name: '{{ test_role }}' - max_session_duration: 43200 - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Update Max Session Duration - iam_role: - name: '{{ test_role }}' - max_session_duration: 43200 - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.max_session_duration == 43200 - -- name: Update Max Session Duration (no change) - iam_role: - name: '{{ test_role }}' - max_session_duration: 43200 - register: iam_role -- assert: - that: - - iam_role is not changed - -- name: Update Max Session Duration (no change) - check mode - iam_role: - name: '{{ test_role }}' - max_session_duration: 43200 - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: iam_role_info after updating Max Session Duration - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - '"description" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 0 diff --git a/tests/integration/targets/iam_role/tasks/parameter_checks.yml b/tests/integration/targets/iam_role/tasks/parameter_checks.yml deleted file mode 100644 index 74d3294b1c5..00000000000 --- a/tests/integration/targets/iam_role/tasks/parameter_checks.yml +++ /dev/null @@ -1,82 +0,0 @@ -# Parameter Checks -- name: Friendly message when creating an instance profile and adding a boundary profile - iam_role: - name: '{{ test_role }}' - boundary: '{{ boundary_policy }}' - register: iam_role - ignore_errors: yes -- assert: - that: - - iam_role is failed - - '"boundary policy" in iam_role.msg' - - '"create_instance_profile" in iam_role.msg' - - '"false" in iam_role.msg' - -- name: Friendly message when boundary profile is not an ARN - iam_role: - name: '{{ test_role }}' - boundary: AWSDenyAll - create_instance_profile: no - register: iam_role - ignore_errors: yes -- assert: - that: - - iam_role is failed - - '"Boundary policy" in iam_role.msg' - - '"ARN" in iam_role.msg' - -- name: Friendly message when "present" without assume_role_policy_document - module_defaults: {iam_role: {}} - iam_role: - name: '{{ test_role }}' - register: iam_role - ignore_errors: yes -- assert: - that: - - iam_role is failed - - iam_role.msg.startswith("state is present but all of the following are missing") - - '"assume_role_policy_document" in iam_role.msg' - -- name: Maximum Session Duration needs to be between 1 and 12 hours - iam_role: - name: '{{ test_role }}' - max_session_duration: 3599 - register: iam_role - ignore_errors: yes -- assert: - that: - - iam_role is failed - - '"max_session_duration must be between" in iam_role.msg' - -- name: Maximum Session Duration needs to be between 1 and 12 hours - iam_role: - name: '{{ test_role }}' - max_session_duration: 43201 - register: iam_role - ignore_errors: yes -- assert: - that: - - iam_role is failed - - '"max_session_duration must be between" in iam_role.msg' - -- name: Role Paths must start with / - iam_role: - name: '{{ test_role }}' - path: test/ - register: iam_role - ignore_errors: yes -- assert: - that: - - iam_role is failed - - '"path must begin and end with /" in iam_role.msg' - -- name: Role Paths must end with / - iam_role: - name: '{{ test_role }}' - path: /test - register: iam_role - ignore_errors: yes -- assert: - that: - - iam_role is failed - - '"path must begin and end with /" in iam_role.msg' diff --git a/tests/integration/targets/iam_role/tasks/policy_update.yml b/tests/integration/targets/iam_role/tasks/policy_update.yml deleted file mode 100644 index ab16ea81f39..00000000000 --- a/tests/integration/targets/iam_role/tasks/policy_update.yml +++ /dev/null @@ -1,246 +0,0 @@ -- name: Add Managed Policy (CHECK MODE) - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ safe_managed_policy }}' - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Add Managed Policy - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ safe_managed_policy }}' - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - -- name: Add Managed Policy (no change) - check mode - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ safe_managed_policy }}' - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Add Managed Policy (no change) - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ safe_managed_policy }}' - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after adding Managed Policy - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 1 - - safe_managed_policy in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - custom_policy_name not in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 1 - - '"TagB" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagB == "ValueB" - -# ------------------------------------------------------------------------------------------ - -- name: Update Managed Policy without purge (CHECK MODE) - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ custom_policy_name }}' - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Update Managed Policy without purge - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ custom_policy_name }}' - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - -- name: Update Managed Policy without purge (no change) - check mode - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ custom_policy_name }}' - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Update Managed Policy without purge (no change) - iam_role: - name: '{{ test_role }}' - purge_policies: no - managed_policy: - - '{{ custom_policy_name }}' - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after updating Managed Policy without purge - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 2 - - safe_managed_policy in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 1 - - '"TagB" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagB == "ValueB" - -# ------------------------------------------------------------------------------------------ - -# Managed Policies are purged by default -- name: Update Managed Policy with purge (CHECK MODE) - iam_role: - name: '{{ test_role }}' - managed_policy: - - '{{ custom_policy_name }}' - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Update Managed Policy with purge - iam_role: - name: '{{ test_role }}' - managed_policy: - - '{{ custom_policy_name }}' - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - -- name: Update Managed Policy with purge (no change) - check mode - iam_role: - name: '{{ test_role }}' - managed_policy: - - '{{ custom_policy_name }}' - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Update Managed Policy with purge (no change) - iam_role: - name: '{{ test_role }}' - managed_policy: - - '{{ custom_policy_name }}' - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - -- name: iam_role_info after updating Managed Policy with purge - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 1 - - safe_managed_policy not in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - custom_policy_name in ( role_info | community.general.json_query("iam_roles[*].managed_policies[*].policy_name") - | list | flatten ) - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 1 - - '"TagB" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagB == "ValueB" diff --git a/tests/integration/targets/iam_role/tasks/role_removal.yml b/tests/integration/targets/iam_role/tasks/role_removal.yml deleted file mode 100644 index 7450fb9685c..00000000000 --- a/tests/integration/targets/iam_role/tasks/role_removal.yml +++ /dev/null @@ -1,59 +0,0 @@ -- name: Remove IAM Role (CHECK MODE) - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: iam_role_info after deleting role in check mode - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - -- name: Remove IAM Role - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: iam_role_info after deleting role - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 0 - -- name: Remove IAM Role (should be gone already) - check mode - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Remove IAM Role (should be gone already) - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - register: iam_role -- assert: - that: - - iam_role is not changed diff --git a/tests/integration/targets/iam_role/tasks/tags_update.yml b/tests/integration/targets/iam_role/tasks/tags_update.yml deleted file mode 100644 index b68013212dd..00000000000 --- a/tests/integration/targets/iam_role/tasks/tags_update.yml +++ /dev/null @@ -1,328 +0,0 @@ -- name: Add Tag (CHECK MODE) - iam_role: - name: '{{ test_role }}' - tags: - TagA: ValueA - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Add Tag - iam_role: - name: '{{ test_role }}' - tags: - TagA: ValueA - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - iam_role.iam_role.tags | length == 1 - - '"TagA" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagA == "ValueA" - -- name: Add Tag (no change) - check mode - iam_role: - name: '{{ test_role }}' - tags: - TagA: ValueA - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Add Tag (no change) - iam_role: - name: '{{ test_role }}' - tags: - TagA: ValueA - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - - '"TagA" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagA == "ValueA" - -- name: iam_role_info after adding Tags - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 1 - - '"TagA" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagA == "ValueA" - -# ------------------------------------------------------------------------------------------ - -- name: Update Tag (CHECK MODE) - iam_role: - name: '{{ test_role }}' - tags: - TagA: AValue - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Update Tag - iam_role: - name: '{{ test_role }}' - tags: - TagA: AValue - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - '"TagA" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagA == "AValue" - -- name: Update Tag (no change) - check mode - iam_role: - name: '{{ test_role }}' - tags: - TagA: AValue - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Update Tag (no change) - iam_role: - name: '{{ test_role }}' - tags: - TagA: AValue - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - - '"TagA" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagA == "AValue" - -- name: iam_role_info after updating Tag - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 1 - - '"TagA" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagA == "AValue" - -# ------------------------------------------------------------------------------------------ - -- name: Add second Tag without purge (CHECK MODE) - iam_role: - name: '{{ test_role }}' - purge_tags: no - tags: - TagB: ValueB - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Add second Tag without purge - iam_role: - name: '{{ test_role }}' - purge_tags: no - tags: - TagB: ValueB - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - '"TagB" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagB == "ValueB" - -- name: Add second Tag without purge (no change) - check mode - iam_role: - name: '{{ test_role }}' - purge_tags: no - tags: - TagB: ValueB - register: iam_role - check_mode: yes -- assert: - that: - - iam_role is not changed - -- name: Add second Tag without purge (no change) - iam_role: - name: '{{ test_role }}' - purge_tags: no - tags: - TagB: ValueB - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - - '"TagB" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagB == "ValueB" - -- name: iam_role_info after adding second Tag without purge - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 2 - - '"TagA" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagA == "AValue" - - '"TagB" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagB == "ValueB" - -# ------------------------------------------------------------------------------------------ - -- name: Purge first tag (CHECK MODE) - iam_role: - name: '{{ test_role }}' - purge_tags: yes - tags: - TagB: ValueB - check_mode: yes - register: iam_role -- assert: - that: - - iam_role is changed - -- name: Purge first tag - iam_role: - name: '{{ test_role }}' - purge_tags: yes - tags: - TagB: ValueB - register: iam_role -- assert: - that: - - iam_role is changed - - iam_role.iam_role.role_name == test_role - - '"TagB" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagB == "ValueB" - -- name: Purge first tag (no change) - check mode - iam_role: - name: '{{ test_role }}' - purge_tags: yes - tags: - TagB: ValueB - register: iam_role -- assert: - that: - - iam_role is not changed - -- name: Purge first tag (no change) - iam_role: - name: '{{ test_role }}' - purge_tags: yes - tags: - TagB: ValueB - register: iam_role -- assert: - that: - - iam_role is not changed - - iam_role.iam_role.role_name == test_role - - '"TagB" in iam_role.iam_role.tags' - - iam_role.iam_role.tags.TagB == "ValueB" - -- name: iam_role_info after purging first Tag - iam_role_info: - name: '{{ test_role }}' - register: role_info -- assert: - that: - - role_info is succeeded - - role_info.iam_roles | length == 1 - - role_info.iam_roles[0].arn.startswith("arn") - - role_info.iam_roles[0].arn.endswith("role/" + test_role ) - - '"assume_role_policy_document" in role_info.iam_roles[0]' - - '"create_date" in role_info.iam_roles[0]' - - role_info.iam_roles[0].description == "Ansible Test Role (updated) {{ resource_prefix - }}" - - role_info.iam_roles[0].inline_policies | length == 0 - - role_info.iam_roles[0].instance_profiles | length == 1 - - role_info.iam_roles[0].instance_profiles[0].instance_profile_name == test_role - - role_info.iam_roles[0].instance_profiles[0].arn.startswith("arn") - - role_info.iam_roles[0].instance_profiles[0].arn.endswith("instance-profile/" - + test_role) - - role_info.iam_roles[0].managed_policies | length == 0 - - role_info.iam_roles[0].max_session_duration == 43200 - - role_info.iam_roles[0].path == '/' - - '"permissions_boundary" not in role_info.iam_roles[0]' - - role_info.iam_roles[0].role_id == iam_role.iam_role.role_id - - role_info.iam_roles[0].role_name == test_role - - role_info.iam_roles[0].tags | length == 1 - - '"TagA" not in role_info.iam_roles[0].tags' - - '"TagB" in role_info.iam_roles[0].tags' - - role_info.iam_roles[0].tags.TagB == "ValueB"