diff --git a/plugins/modules/iam_role.py b/plugins/modules/iam_role.py index 948358b184e..7ca0d8c4fbb 100644 --- a/plugins/modules/iam_role.py +++ b/plugins/modules/iam_role.py @@ -87,6 +87,17 @@ - Remove tags not listed in I(tags) when tags is specified. default: true 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.aws - amazon.aws.ec2 @@ -215,16 +226,40 @@ def compare_assume_role_policy_doc(current_policy_doc, new_policy_doc): @AWSRetry.jittered_backoff() -def _list_policies(connection): - paginator = connection.get_paginator('list_policies') +def _list_policies(): + paginator = client.get_paginator('list_policies') return paginator.paginate().build_full_result()['Policies'] -def convert_friendly_names_to_arns(connection, module, policy_names): +def wait_iam_exists(): + 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(policy_names): if not any(not policy.startswith('arn:') for policy in policy_names): return policy_names allpolicies = {} - policies = _list_policies(connection) + policies = _list_policies() for policy in policies: allpolicies[policy['PolicyName']] = policy['Arn'] @@ -235,31 +270,31 @@ def convert_friendly_names_to_arns(connection, module, policy_names): module.fail_json_aws(e, msg="Couldn't find policy") -def attach_policies(connection, module, policies_to_attach, params): +def attach_policies(policies_to_attach, params): changed = False for policy_arn in policies_to_attach: try: if not module.check_mode: - connection.attach_role_policy(RoleName=params['RoleName'], PolicyArn=policy_arn, aws_retry=True) + client.attach_role_policy(RoleName=params['RoleName'], PolicyArn=policy_arn, aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to attach policy {0} to role {1}".format(policy_arn, params['RoleName'])) changed = True return changed -def remove_policies(connection, module, policies_to_remove, params): +def remove_policies(policies_to_remove, params): changed = False for policy in policies_to_remove: try: if not module.check_mode: - connection.detach_role_policy(RoleName=params['RoleName'], PolicyArn=policy, aws_retry=True) + client.detach_role_policy(RoleName=params['RoleName'], PolicyArn=policy, aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to detach policy {0} from {1}".format(policy, params['RoleName'])) changed = True return changed -def generate_create_params(module): +def generate_create_params(): params = dict() params['Path'] = module.params.get('path') params['RoleName'] = module.params.get('name') @@ -276,7 +311,7 @@ def generate_create_params(module): return params -def create_basic_role(connection, module, params): +def create_basic_role(params): """ Perform the Role creation. Assumes tests for the role existing have already been performed. @@ -284,11 +319,11 @@ def create_basic_role(connection, module, params): try: if not module.check_mode: - role = connection.create_role(aws_retry=True, **params) + 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(connection, module, params['RoleName']) + role = get_role_with_backoff(params['RoleName']) else: role = {'MadeInCheckMode': True} role['AssumeRolePolicyDocument'] = json.loads(params['AssumeRolePolicyDocument']) @@ -298,7 +333,7 @@ def create_basic_role(connection, module, params): return role -def update_role_assumed_policy(connection, module, params, role): +def update_role_assumed_policy(params, role): # Check Assumed Policy document if compare_assume_role_policy_doc(role['AssumeRolePolicyDocument'], params['AssumeRolePolicyDocument']): return False @@ -307,7 +342,7 @@ def update_role_assumed_policy(connection, module, params, role): return True try: - connection.update_assume_role_policy( + client.update_assume_role_policy( RoleName=params['RoleName'], PolicyDocument=json.dumps(json.loads(params['AssumeRolePolicyDocument'])), aws_retry=True) @@ -316,7 +351,7 @@ def update_role_assumed_policy(connection, module, params, role): return True -def update_role_description(connection, module, params, role): +def update_role_description(params, role): # Check Description update if params.get('Description') is None: return False @@ -327,13 +362,13 @@ def update_role_description(connection, module, params, role): return True try: - connection.update_role(RoleName=params['RoleName'], Description=params['Description'], aws_retry=True) + client.update_role(RoleName=params['RoleName'], Description=params['Description'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update description for role {0}".format(params['RoleName'])) return True -def update_role_max_session_duration(connection, module, params, role): +def update_role_max_session_duration(params, role): # Check MaxSessionDuration update if params.get('MaxSessionDuration') is None: return False @@ -344,13 +379,13 @@ def update_role_max_session_duration(connection, module, params, role): return True try: - connection.update_role(RoleName=params['RoleName'], MaxSessionDuration=params['MaxSessionDuration'], aws_retry=True) + client.update_role(RoleName=params['RoleName'], MaxSessionDuration=params['MaxSessionDuration'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update maximum session duration for role {0}".format(params['RoleName'])) return True -def update_role_permissions_boundary(connection, module, params, role): +def update_role_permissions_boundary(params, role): # Check PermissionsBoundary if params.get('PermissionsBoundary') is None: return False @@ -362,18 +397,18 @@ def update_role_permissions_boundary(connection, module, params, role): if params.get('PermissionsBoundary') == '': try: - connection.delete_role_permissions_boundary(RoleName=params['RoleName'], aws_retry=True) + client.delete_role_permissions_boundary(RoleName=params['RoleName'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to remove permission boundary for role {0}".format(params['RoleName'])) else: try: - connection.put_role_permissions_boundary(RoleName=params['RoleName'], PermissionsBoundary=params['PermissionsBoundary'], aws_retry=True) + client.put_role_permissions_boundary(RoleName=params['RoleName'], PermissionsBoundary=params['PermissionsBoundary'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to update permission boundary for role {0}".format(params['RoleName'])) return True -def update_managed_policies(connection, module, params, role, managed_policies, purge_policies): +def update_managed_policies(params, role, managed_policies, purge_policies): # Check Managed Policies if managed_policies is None: return False @@ -384,7 +419,7 @@ def update_managed_policies(connection, module, params, role, managed_policies, return True # Get list of current attached managed policies - current_attached_policies = get_attached_policy_list(connection, module, params['RoleName']) + current_attached_policies = get_attached_policy_list(params['RoleName']) current_attached_policies_arn_list = [policy['PolicyArn'] for policy in current_attached_policies] if len(managed_policies) == 1 and managed_policies[0] is None: @@ -396,16 +431,16 @@ def update_managed_policies(connection, module, params, role, managed_policies, changed = False if purge_policies: - changed |= remove_policies(connection, module, policies_to_remove, params) + changed |= remove_policies(policies_to_remove, params) - changed |= attach_policies(connection, module, policies_to_attach, params) + changed |= attach_policies(policies_to_attach, params) return changed -def create_or_update_role(connection, module): +def create_or_update_role(): - params = generate_create_params(module) + params = generate_create_params() role_name = params['RoleName'] create_instance_profile = module.params.get('create_instance_profile') purge_policies = module.params.get('purge_policies') @@ -414,48 +449,59 @@ def create_or_update_role(connection, module): 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(connection, module, managed_policies) + managed_policies = convert_friendly_names_to_arns(managed_policies) changed = False # Get role - role = get_role(connection, module, role_name) + role = get_role(role_name) # If role is None, create it if role is None: - role = create_basic_role(connection, module, params) + role = create_basic_role(params) + + if not module.check_mode and module.params.get('wait'): + wait_iam_exists() + changed = True else: - changed |= update_role_tags(connection, module, params, role) - changed |= update_role_assumed_policy(connection, module, params, role) - changed |= update_role_description(connection, module, params, role) - changed |= update_role_max_session_duration(connection, module, params, role) - changed |= update_role_permissions_boundary(connection, module, params, role) + changed |= update_role_tags(params, role) + changed |= update_role_assumed_policy(params, role) + changed |= update_role_description(params, role) + changed |= update_role_max_session_duration(params, role) + changed |= update_role_permissions_boundary(params, role) + + if not module.check_mode and module.params.get('wait'): + wait_iam_exists() if create_instance_profile: - changed |= create_instance_profiles(connection, module, params, role) + changed |= create_instance_profiles(params, role) - changed |= update_managed_policies(connection, module, params, role, managed_policies, purge_policies) + if not module.check_mode and module.params.get('wait'): + wait_iam_exists() + + changed |= update_managed_policies(params, role, managed_policies, purge_policies) + wait_iam_exists() # Get the role again if not role.get('MadeInCheckMode', False): - role = get_role(connection, module, params['RoleName']) - role['AttachedPolicies'] = get_attached_policy_list(connection, module, params['RoleName']) - role['tags'] = get_role_tags(connection, module) + role = get_role(params['RoleName']) + role['AttachedPolicies'] = get_attached_policy_list(params['RoleName']) + role['tags'] = get_role_tags() module.exit_json( changed=changed, iam_role=camel_dict_to_snake_dict(role, ignore_list=['tags']), **camel_dict_to_snake_dict(role, ignore_list=['tags'])) -def create_instance_profiles(connection, module, params, role): +def create_instance_profiles(params, role): if role.get('MadeInCheckMode', False): return False # Fetch existing Profiles try: - instance_profiles = connection.list_instance_profiles_for_role(RoleName=params['RoleName'], aws_retry=True)['InstanceProfiles'] + instance_profiles = client.list_instance_profiles_for_role(RoleName=params['RoleName'], aws_retry=True)['InstanceProfiles'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to list instance profiles for role {0}".format(params['RoleName'])) @@ -468,7 +514,7 @@ def create_instance_profiles(connection, module, params, role): # Make sure an instance profile is created try: - connection.create_instance_profile(InstanceProfileName=params['RoleName'], Path=params['Path'], aws_retry=True) + client.create_instance_profile(InstanceProfileName=params['RoleName'], Path=params['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... @@ -478,19 +524,19 @@ def create_instance_profiles(connection, module, params, role): # And attach the role to the profile try: - connection.add_role_to_instance_profile(InstanceProfileName=params['RoleName'], RoleName=params['RoleName'], aws_retry=True) + client.add_role_to_instance_profile(InstanceProfileName=params['RoleName'], RoleName=params['RoleName'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to attach role {0} to instance profile {0}".format(params['RoleName'])) return True -def remove_instance_profiles(connection, module, role_params, role): +def remove_instance_profiles(role_params, role): role_name = module.params.get('name') delete_profiles = module.params.get("delete_instance_profile") try: - instance_profiles = connection.list_instance_profiles_for_role(aws_retry=True, **role_params)['InstanceProfiles'] + instance_profiles = client.list_instance_profiles_for_role(aws_retry=True, **role_params)['InstanceProfiles'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to list instance profiles for role {0}".format(role_name)) @@ -499,21 +545,21 @@ def remove_instance_profiles(connection, module, role_params, role): profile_name = profile['InstanceProfileName'] try: if not module.check_mode: - connection.remove_role_from_instance_profile(aws_retry=True, InstanceProfileName=profile_name, **role_params) + client.remove_role_from_instance_profile(aws_retry=True, InstanceProfileName=profile_name, **role_params) if profile_name == role_name: if delete_profiles: try: - connection.delete_instance_profile(InstanceProfileName=profile_name, aws_retry=True) + client.delete_instance_profile(InstanceProfileName=profile_name, aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to remove instance profile {0}".format(profile_name)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to remove role {0} from instance profile {1}".format(role_name, profile_name)) -def destroy_role(connection, module): +def destroy_role(): role_name = module.params.get('name') - role = get_role(connection, module, role_name) + role = get_role(role_name) role_params = dict() role_params['RoleName'] = role_name boundary_params = dict(role_params) @@ -526,51 +572,51 @@ def destroy_role(connection, module): # - attached instance profiles # - attached managed policies # - permissions boundary - remove_instance_profiles(connection, module, role_params, role) - update_managed_policies(connection, module, role_params, role, [], True) - update_role_permissions_boundary(connection, module, boundary_params, role) + remove_instance_profiles(role_params, role) + update_managed_policies(role_params, role, [], True) + update_role_permissions_boundary(boundary_params, role) try: if not module.check_mode: - connection.delete_role(aws_retry=True, **role_params) + client.delete_role(aws_retry=True, **role_params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Unable to delete role") module.exit_json(changed=True) -def get_role_with_backoff(connection, module, name): +def get_role_with_backoff(name): try: - return AWSRetry.jittered_backoff(catch_extra_error_codes=['NoSuchEntity'])(connection.get_role)(RoleName=name)['Role'] + 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="Unable to get role {0}".format(name)) -def get_role(connection, module, name): +def get_role(name): try: - return connection.get_role(RoleName=name, aws_retry=True)['Role'] + 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="Unable to get role {0}".format(name)) -def get_attached_policy_list(connection, module, name): +def get_attached_policy_list(name): try: - return connection.list_attached_role_policies(RoleName=name, aws_retry=True)['AttachedPolicies'] + 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="Unable to list attached policies for role {0}".format(name)) -def get_role_tags(connection, module): +def get_role_tags(): role_name = module.params.get('name') try: - return boto3_tag_list_to_ansible_dict(connection.list_role_tags(RoleName=role_name, aws_retry=True)['Tags']) + 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="Unable to list tags for role {0}".format(role_name)) -def update_role_tags(connection, module, params, role): +def update_role_tags(params, role): new_tags = params.get('Tags') if new_tags is None: return False @@ -580,7 +626,7 @@ def update_role_tags(connection, module, params, role): purge_tags = module.params.get('purge_tags') try: - existing_tags = boto3_tag_list_to_ansible_dict(connection.list_role_tags(RoleName=role_name, aws_retry=True)['Tags']) + 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 = {} @@ -589,9 +635,9 @@ def update_role_tags(connection, module, params, role): if not module.check_mode: try: if tags_to_remove: - connection.untag_role(RoleName=role_name, TagKeys=tags_to_remove, aws_retry=True) + client.untag_role(RoleName=role_name, TagKeys=tags_to_remove, aws_retry=True) if tags_to_add: - connection.tag_role(RoleName=role_name, Tags=ansible_dict_to_boto3_tag_list(tags_to_add), aws_retry=True) + 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='Unable to set tags for role %s' % role_name) @@ -601,6 +647,9 @@ def update_role_tags(connection, module, params, role): def main(): + global module + global client + argument_spec = dict( name=dict(type='str', required=True), path=dict(type='str', default="/"), @@ -615,6 +664,8 @@ def main(): purge_policies=dict(type='bool', aliases=['purge_policy', 'purge_managed_policies']), tags=dict(type='dict'), 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'])], @@ -638,14 +689,14 @@ def main(): if not path.endswith('/') or not path.startswith('/'): module.fail_json(msg="path must begin and end with /") - connection = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) + client = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) state = module.params.get("state") if state == 'present': - create_or_update_role(connection, module) + create_or_update_role() else: - destroy_role(connection, module) + destroy_role() if __name__ == '__main__': diff --git a/tests/integration/targets/iam_role/defaults/main.yml b/tests/integration/targets/iam_role/defaults/main.yml index 46db605072e..d496c421636 100644 --- a/tests/integration/targets/iam_role/defaults/main.yml +++ b/tests/integration/targets/iam_role/defaults/main.yml @@ -4,5 +4,3 @@ test_path: '/{{ resource_prefix }}/' safe_managed_policy: 'AWSDenyAll' custom_policy_name: '{{ resource_prefix }}-denyall' boundary_policy: 'arn:aws:iam::aws:policy/AWSDenyAll' -paranoid_pauses: no -standard_pauses: no diff --git a/tests/integration/targets/iam_role/tasks/boundary_policy.yml b/tests/integration/targets/iam_role/tasks/boundary_policy.yml new file mode 100644 index 00000000000..9f4684d150d --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/boundary_policy.yml @@ -0,0 +1,82 @@ +--- +- 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)" + 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 \ No newline at end of file diff --git a/tests/integration/targets/iam_role/tasks/complex_role_creation.yml b/tests/integration/targets/iam_role/tasks/complex_role_creation.yml new file mode 100644 index 00000000000..93a7b7d8342 --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/complex_role_creation.yml @@ -0,0 +1,110 @@ +--- +- 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)" + 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 new file mode 100644 index 00000000000..88fc79f977c --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/creation_deletion.yml @@ -0,0 +1,353 @@ +--- +- 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 + check_mode: yes + register: iam_role + +- 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 )' + # 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 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]' + - '"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 == '/' + - '"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)" + 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 }}" + # If we don't delete the existing profile it'll be reused (with the path) + # by the test below. + 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" + 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)" + 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 \ No newline at end of file diff --git a/tests/integration/targets/iam_role/tasks/description_update.yml b/tests/integration/targets/iam_role/tasks/description_update.yml new file mode 100644 index 00000000000..d4ee520147f --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/description_update.yml @@ -0,0 +1,124 @@ +--- +- 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)" + 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)" + 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 new file mode 100644 index 00000000000..7fb5eef3e37 --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/inline_policy_update.yml @@ -0,0 +1,63 @@ +--- +- 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" + +# XXX iam_role fails to remove inline policies before deleting the role +- name: "Detach inline policy a" + iam_policy: + state: absent + iam_type: "role" + iam_name: "{{ test_role }}" + policy_name: "inline-policy-a" + +- name: "Detach inline policy b" + iam_policy: + state: absent + iam_type: "role" + iam_name: "{{ test_role }}" + policy_name: "inline-policy-b" diff --git a/tests/integration/targets/iam_role/tasks/main.yml b/tests/integration/targets/iam_role/tasks/main.yml index 34c17af3369..b4132c60c2a 100644 --- a/tests/integration/targets/iam_role/tasks/main.yml +++ b/tests/integration/targets/iam_role/tasks/main.yml @@ -22,1500 +22,115 @@ # Possible Bugs: # - Fails to delete role if inline policies not removed first -- name: 'Setup AWS connection info' +- name: "Setup AWS connection info" module_defaults: group/aws: - aws_access_key: '{{ aws_access_key }}' - aws_secret_key: '{{ aws_secret_key }}' - security_token: '{{ security_token | default(omit) }}' - region: '{{ aws_region }}' + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" iam_role: assume_role_policy_document: '{{ lookup("file", "deny-assume.json") }}' collections: - amazon.aws + - community.general block: - # =================================================================== - # 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' + # =================================================================== + # Parameter Checks + - include_tasks: parameter_checks.yml + + # =================================================================== + # Supplemental resource pre-creation + - 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 - - 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' - - # =================================================================== - # Supplemental resource pre-creation - - 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 - - name: Try running some rapid fire create/delete tests - # We've previously seen issues with iam_role returning before creation's - # actually complete, if we think the issue's gone, let's try creating and - # deleting things in quick succession - when: not (standard_pauses | bool) - 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 - check_mode: yes - register: iam_role - - assert: - that: - - iam_role is changed - # Pause this first time, just in case we actually created something... - - name: Short pause for role creation to finish - pause: - seconds: 10 - when: standard_pauses | bool - - - 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 )' - # 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: Short pause for role creation to finish - pause: - seconds: 10 - when: standard_pauses | bool - - - 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]' - - '"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 == '/' - - '"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: Short pause for role removal to finish - pause: - seconds: 10 - when: paranoid_pauses | bool - - - 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: Short pause for role creation to finish - pause: - seconds: 10 - when: standard_pauses | bool - - - 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 }}' - # If we don't delete the existing profile it'll be reused (with the path) - # by the test below. - delete_instance_profile: yes - register: iam_role - - assert: - that: - - iam_role is changed - - name: Short pause for role removal to finish - pause: - seconds: 10 - when: paranoid_pauses | bool - - - 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' - 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: Short pause for role creation to finish - pause: - seconds: 10 - when: standard_pauses | bool - - - 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 - - # =================================================================== - # Max Session Duration Manipulation - - - 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 - - iam_role.iam_role.role_name == test_role - - - 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 - - # =================================================================== - # Description Manipulation - - - 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)' - 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)' - 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 - - - # =================================================================== - # Tag Manipulation - - - 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" + - create_managed_policy is succeeded - - 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)' - 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)' - 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)' - 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" - - - # =================================================================== - # Policy Manipulation - - - 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)' - 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)' - 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 + # =================================================================== + # Rapid Role Creation and deletion + - include_tasks: creation_deletion.yml - - 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" - - # =================================================================== - # Inline Policy (test _info behaviour) - - # XXX Not sure if it's a bug in Ansible or a "quirk" of AWS, but these two - # policies need to have at least different Sids or the second doesn't show - # up... - - - 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" - - # XXX iam_role fails to remove inline policies before deleting the role - - name: 'Detach inline policy a' - iam_policy: - state: absent - iam_type: 'role' - iam_name: '{{ test_role }}' - policy_name: 'inline-policy-a' - - - name: 'Detach inline policy b' - iam_policy: - state: absent - iam_type: 'role' - iam_name: '{{ test_role }}' - policy_name: 'inline-policy-b' - - # =================================================================== - # Role Removal - - 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: 'Short pause for role removal to finish' - pause: - seconds: 10 - when: paranoid_pauses | bool + # =================================================================== + # Max Session Duration Manipulation + - include_tasks: max_session_update.yml - - 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 + # =================================================================== + # Description Manipulation + - include_tasks: description_update.yml - - 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: 'Short pause for role removal to finish' - pause: - seconds: 10 - when: paranoid_pauses | bool + # =================================================================== + # Tag Manipulation + - include_tasks: tags_update.yml - - 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 + # =================================================================== + # Policy Manipulation + - include_tasks: policy_update.yml - - 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 - - name: 'Short pause for role removal to finish' - pause: - seconds: 10 - when: paranoid_pauses | bool + # =================================================================== + # Inline Policy (test _info behavior) + - include_tasks: inline_policy_update.yml - # =================================================================== - # Boundary Policy (requires create_instance_profile: no) - - 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 + # =================================================================== + # Role Removal + - include_tasks: role_removal.yml - - 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 + # =================================================================== + # Boundary Policy (requires create_instance_profile: no) + - include_tasks: boundary_policy.yml - - 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)' - 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 - - name: Short pause for role removal to finish - pause: - seconds: 10 - when: paranoid_pauses | bool - - # =================================================================== - # Complex role Creation - - 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: Short pause for role creation to finish - pause: - seconds: 10 - when: standard_pauses | bool - - - 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: Short pause for role creation to finish - pause: - seconds: 10 - when: standard_pauses | bool - - - 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" + # =================================================================== + # Complex role Creation + - include_tasks: complex_role_creation.yml always: - # =================================================================== - # Cleanup - - # XXX iam_role fails to remove inline policies before deleting the role - - name: 'Detach inline policy a' - iam_policy: - state: absent - iam_type: 'role' - iam_name: '{{ test_role }}' - policy_name: 'inline-policy-a' - ignore_errors: true + # =================================================================== + # Cleanup - - name: 'Detach inline policy b' - iam_policy: - state: absent - iam_type: 'role' - iam_name: '{{ test_role }}' - policy_name: 'inline-policy-b' - ignore_errors: true - - - name: 'Remove IAM Role' - iam_role: - state: absent - name: '{{ test_role }}' - delete_instance_profile: yes - ignore_errors: true + # XXX iam_role fails to remove inline policies before deleting the role + - name: "Detach inline policy a" + iam_policy: + state: absent + iam_type: "role" + iam_name: "{{ test_role }}" + policy_name: "inline-policy-a" + 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: "Detach inline policy b" + iam_policy: + state: absent + iam_type: "role" + iam_name: "{{ test_role }}" + policy_name: "inline-policy-b" + ignore_errors: true - - name: 'iam_role_info after Role deletion' - iam_role_info: - name: '{{ test_role }}' - ignore_errors: true + - name: "Remove IAM Role" + iam_role: + state: absent + name: "{{ test_role }}" + delete_instance_profile: yes + ignore_errors: true - - name: 'Remove test managed policy' - iam_managed_policy: - state: absent - policy_name: '{{ custom_policy_name }}' + - 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 new file mode 100644 index 00000000000..cbe64df684e --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/max_session_update.yml @@ -0,0 +1,61 @@ +--- +- 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 + - iam_role.iam_role.role_name == test_role + +- 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 new file mode 100644 index 00000000000..57df5436afc --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/parameter_checks.yml @@ -0,0 +1,90 @@ +--- +# 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 new file mode 100644 index 00000000000..a34f2a0ad30 --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/policy_update.yml @@ -0,0 +1,208 @@ +--- +- 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)" + 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)" + 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)" + 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 new file mode 100644 index 00000000000..1b8d10710a9 --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/role_removal.yml @@ -0,0 +1,53 @@ +--- +- 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)" + 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 new file mode 100644 index 00000000000..617fb9a1331 --- /dev/null +++ b/tests/integration/targets/iam_role/tasks/tags_update.yml @@ -0,0 +1,286 @@ +--- +- 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)" + 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)" + 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)" + 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)" + 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"