From 048fee2ed662f39af17bb0163bcc2d51d938e86d Mon Sep 17 00:00:00 2001 From: jillr Date: Mon, 2 Mar 2020 19:25:18 +0000 Subject: [PATCH 01/31] Initial commit This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/eb75681585a23ea79e642b86a0f8e64e0f40a6d7 --- plugins/modules/iam_managed_policy.py | 384 ++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100644 plugins/modules/iam_managed_policy.py diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py new file mode 100644 index 00000000000..3b8d4736aef --- /dev/null +++ b/plugins/modules/iam_managed_policy.py @@ -0,0 +1,384 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['stableinterface'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: iam_managed_policy +short_description: Manage User Managed IAM policies +description: + - Allows creating and removing managed IAM policies +options: + policy_name: + description: + - The name of the managed policy. + required: True + type: str + policy_description: + description: + - A helpful description of this policy, this value is immutable and only set when creating a new policy. + default: '' + type: str + policy: + description: + - A properly json formatted policy + type: json + make_default: + description: + - Make this revision the default revision. + default: True + type: bool + only_version: + description: + - Remove all other non default revisions, if this is used with C(make_default) it will result in all other versions of this policy being deleted. + type: bool + default: false + state: + description: + - Should this managed policy be present or absent. Set to absent to detach all entities from this policy and remove it if found. + default: present + choices: [ "present", "absent" ] + type: str + fail_on_delete: + description: + - The I(fail_on_delete) option does nothing and will be removed in Ansible 2.14. + type: bool + +author: "Dan Kozlowski (@dkhenry)" +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.ec2 + +requirements: + - boto3 + - botocore +''' + +EXAMPLES = ''' +# Create Policy ex nihilo +- name: Create IAM Managed Policy + iam_managed_policy: + policy_name: "ManagedPolicy" + policy_description: "A Helpful managed policy" + policy: "{{ lookup('template', 'managed_policy.json.j2') }}" + state: present + +# Update a policy with a new default version +- name: Create IAM Managed Policy + iam_managed_policy: + policy_name: "ManagedPolicy" + policy: "{{ lookup('file', 'managed_policy_update.json') }}" + state: present + +# Update a policy with a new non default version +- name: Create IAM Managed Policy + iam_managed_policy: + policy_name: "ManagedPolicy" + policy: "{{ lookup('file', 'managed_policy_update.json') }}" + make_default: false + state: present + +# Update a policy and make it the only version and the default version +- name: Create IAM Managed Policy + iam_managed_policy: + policy_name: "ManagedPolicy" + policy: "{ 'Version': '2012-10-17', 'Statement':[{'Effect': 'Allow','Action': '*','Resource': '*'}]}" + only_version: true + state: present + +# Remove a policy +- name: Create IAM Managed Policy + iam_managed_policy: + policy_name: "ManagedPolicy" + state: absent +''' + +RETURN = ''' +policy: + description: Returns the policy json structure, when state == absent this will return the value of the removed policy. + returned: success + type: str + sample: '{ + "arn": "arn:aws:iam::aws:policy/AdministratorAccess " + "attachment_count": 0, + "create_date": "2017-03-01T15:42:55.981000+00:00", + "default_version_id": "v1", + "is_attachable": true, + "path": "/", + "policy_id": "ANPALM4KLDMTFXGOOJIHL", + "policy_name": "AdministratorAccess", + "update_date": "2017-03-01T15:42:55.981000+00:00" + }' +''' + +import json +import traceback + +try: + import botocore +except ImportError: + pass # caught by imported HAS_BOTO3 + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (boto3_conn, get_aws_connection_info, ec2_argument_spec, AWSRetry, + camel_dict_to_snake_dict, HAS_BOTO3, compare_policies) +from ansible.module_utils._text import to_native + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def list_policies_with_backoff(iam): + paginator = iam.get_paginator('list_policies') + return paginator.paginate(Scope='Local').build_full_result() + + +def get_policy_by_name(module, iam, name): + try: + response = list_policies_with_backoff(iam) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't list policies: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + for policy in response['Policies']: + if policy['PolicyName'] == name: + return policy + return None + + +def delete_oldest_non_default_version(module, iam, policy): + try: + versions = [v for v in iam.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] + if not v['IsDefaultVersion']] + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't list policy versions: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + versions.sort(key=lambda v: v['CreateDate'], reverse=True) + for v in versions[-1:]: + try: + iam.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't delete policy version: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + + +# This needs to return policy_version, changed +def get_or_create_policy_version(module, iam, policy, policy_document): + try: + versions = iam.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't list policy versions: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + for v in versions: + try: + document = iam.get_policy_version(PolicyArn=policy['Arn'], + VersionId=v['VersionId'])['PolicyVersion']['Document'] + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't get policy version %s: %s" % (v['VersionId'], str(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + # If the current policy matches the existing one + if not compare_policies(document, json.loads(to_native(policy_document))): + return v, False + + # No existing version so create one + # There is a service limit (typically 5) of policy versions. + # + # Rather than assume that it is 5, we'll try to create the policy + # and if that doesn't work, delete the oldest non default policy version + # and try again. + try: + version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + return version, True + except botocore.exceptions.ClientError as e: + if e.response['Error']['Code'] == 'LimitExceeded': + delete_oldest_non_default_version(module, iam, policy) + try: + version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + return version, True + except botocore.exceptions.ClientError as second_e: + e = second_e + # Handle both when the exception isn't LimitExceeded or + # the second attempt still failed + module.fail_json(msg="Couldn't create policy version: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + + +def set_if_default(module, iam, policy, policy_version, is_default): + if is_default and not policy_version['IsDefaultVersion']: + try: + iam.set_default_policy_version(PolicyArn=policy['Arn'], VersionId=policy_version['VersionId']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't set default policy version: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + return True + return False + + +def set_if_only(module, iam, policy, policy_version, is_only): + if is_only: + try: + versions = [v for v in iam.list_policy_versions(PolicyArn=policy['Arn'])[ + 'Versions'] if not v['IsDefaultVersion']] + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't list policy versions: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + for v in versions: + try: + iam.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't delete policy version: %s" % str(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + return len(versions) > 0 + return False + + +def detach_all_entities(module, iam, policy, **kwargs): + try: + entities = iam.list_entities_for_policy(PolicyArn=policy['Arn'], **kwargs) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't detach list entities for policy %s: %s" % (policy['PolicyName'], str(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + + for g in entities['PolicyGroups']: + try: + iam.detach_group_policy(PolicyArn=policy['Arn'], GroupName=g['GroupName']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't detach group policy %s: %s" % (g['GroupName'], str(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + for u in entities['PolicyUsers']: + try: + iam.detach_user_policy(PolicyArn=policy['Arn'], UserName=u['UserName']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't detach user policy %s: %s" % (u['UserName'], str(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + for r in entities['PolicyRoles']: + try: + iam.detach_role_policy(PolicyArn=policy['Arn'], RoleName=r['RoleName']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't detach role policy %s: %s" % (r['RoleName'], str(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + if entities['IsTruncated']: + detach_all_entities(module, iam, policy, marker=entities['Marker']) + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + policy_name=dict(required=True), + policy_description=dict(default=''), + policy=dict(type='json'), + make_default=dict(type='bool', default=True), + only_version=dict(type='bool', default=False), + fail_on_delete=dict(type='bool', removed_in_version='2.14'), + state=dict(default='present', choices=['present', 'absent']), + )) + + module = AnsibleModule( + argument_spec=argument_spec, + required_if=[['state', 'present', ['policy']]] + ) + + if not HAS_BOTO3: + module.fail_json(msg='boto3 is required for this module') + + name = module.params.get('policy_name') + description = module.params.get('policy_description') + state = module.params.get('state') + default = module.params.get('make_default') + only = module.params.get('only_version') + + policy = None + + if module.params.get('policy') is not None: + policy = json.dumps(json.loads(module.params.get('policy'))) + + try: + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + iam = boto3_conn(module, conn_type='client', resource='iam', + region=region, endpoint=ec2_url, **aws_connect_kwargs) + except (botocore.exceptions.NoCredentialsError, botocore.exceptions.ProfileNotFound) as e: + module.fail_json(msg="Can't authorize connection. Check your credentials and profile.", + exceptions=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) + + p = get_policy_by_name(module, iam, name) + if state == 'present': + if p is None: + # No Policy so just create one + try: + rvalue = iam.create_policy(PolicyName=name, Path='/', + PolicyDocument=policy, Description=description) + except Exception as e: + module.fail_json(msg="Couldn't create policy %s: %s" % (name, to_native(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + + module.exit_json(changed=True, policy=camel_dict_to_snake_dict(rvalue['Policy'])) + else: + policy_version, changed = get_or_create_policy_version(module, iam, p, policy) + changed = set_if_default(module, iam, p, policy_version, default) or changed + changed = set_if_only(module, iam, p, policy_version, only) or changed + # If anything has changed we needto refresh the policy + if changed: + try: + p = iam.get_policy(PolicyArn=p['Arn'])['Policy'] + except Exception as e: + module.fail_json(msg="Couldn't get policy: %s" % to_native(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + + module.exit_json(changed=changed, policy=camel_dict_to_snake_dict(p)) + else: + # Check for existing policy + if p: + # Detach policy + detach_all_entities(module, iam, p) + # Delete Versions + try: + versions = iam.list_policy_versions(PolicyArn=p['Arn'])['Versions'] + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't list policy versions: %s" % to_native(e), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + for v in versions: + if not v['IsDefaultVersion']: + try: + iam.delete_policy_version(PolicyArn=p['Arn'], VersionId=v['VersionId']) + except botocore.exceptions.ClientError as e: + module.fail_json(msg="Couldn't delete policy version %s: %s" % + (v['VersionId'], to_native(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + # Delete policy + try: + iam.delete_policy(PolicyArn=p['Arn']) + except Exception as e: + module.fail_json(msg="Couldn't delete policy %s: %s" % (p['PolicyName'], to_native(e)), + exception=traceback.format_exc(), + **camel_dict_to_snake_dict(e.response)) + # This is the one case where we will return the old policy + module.exit_json(changed=True, policy=camel_dict_to_snake_dict(p)) + else: + module.exit_json(changed=False, policy=None) +# end main + + +if __name__ == '__main__': + main() From 8e0466c288ce6bc2784955ea9d6284b21e63d204 Mon Sep 17 00:00:00 2001 From: jillr Date: Tue, 3 Mar 2020 19:43:21 +0000 Subject: [PATCH 02/31] migration test cleanup This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/13b104b912784bb31a0bff23eed4c27b0f5e0283 --- plugins/modules/iam_managed_policy.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 3b8d4736aef..fd393359f24 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -128,8 +128,14 @@ pass # caught by imported HAS_BOTO3 from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (boto3_conn, get_aws_connection_info, ec2_argument_spec, AWSRetry, - camel_dict_to_snake_dict, HAS_BOTO3, compare_policies) +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (boto3_conn, + get_aws_connection_info, + ec2_argument_spec, + AWSRetry, + camel_dict_to_snake_dict, + HAS_BOTO3, + compare_policies, + ) from ansible.module_utils._text import to_native From 5884aa6eb61cfaf63b3ecc84a6fdcb37d0de859a Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:39:40 -0700 Subject: [PATCH 03/31] Rename collection (#12) * Rename core collection Rename references to ansible.amazon to amazon.aws. * Rename community.amazon to community.aws Fix pep8 line lengths for rewritten amazon.aws imports * Missed a path in shippable.sh * Dependency repos moved This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/235c5db571cc45db5839476c94356c9b91e1f228 --- plugins/modules/iam_managed_policy.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index fd393359f24..435d2dcf800 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -54,8 +54,8 @@ author: "Dan Kozlowski (@dkhenry)" extends_documentation_fragment: -- ansible.amazon.aws -- ansible.amazon.ec2 +- amazon.aws.aws +- amazon.aws.ec2 requirements: - boto3 @@ -128,14 +128,14 @@ pass # caught by imported HAS_BOTO3 from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (boto3_conn, - get_aws_connection_info, - ec2_argument_spec, - AWSRetry, - camel_dict_to_snake_dict, - HAS_BOTO3, - compare_policies, - ) +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (boto3_conn, + get_aws_connection_info, + ec2_argument_spec, + AWSRetry, + camel_dict_to_snake_dict, + HAS_BOTO3, + compare_policies, + ) from ansible.module_utils._text import to_native From afe65d5bdca40b642cde2f4cf54552310defc5e2 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Tue, 19 May 2020 16:06:12 -0700 Subject: [PATCH 04/31] Remove METADATA and cleanup galaxy.yml (#70) * Remove ANSIBLE_METADATA entirely, see ansible/ansible/pull/69454. Remove `license` field from galaxy.yml, in favor of `license_file`. This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/05672a64e2362cc2d865b5af6a57da6bc3cd08e3 --- plugins/modules/iam_managed_policy.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 435d2dcf800..0631a243dd0 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -6,10 +6,6 @@ __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['stableinterface'], - 'supported_by': 'community'} - DOCUMENTATION = ''' --- module: iam_managed_policy From e68fa69a65a3fc18067f39401289dcdb42b1ae06 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Tue, 16 Jun 2020 11:23:52 -0700 Subject: [PATCH 05/31] Collections related fixes for CI (#96) * Update module deprecations Switch version to `removed_at_date` * Don't install amazon.aws from galaxy We've been using galaxy to install amazon.aws in shippable, but that doesn't really work if we aren't publising faster. Get that collection from git so it is most up to date. * We need to declare python test deps now * missed a python dep This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/7cd211e9383db26bc2aa4cc06e657cf60ed0acc0 --- plugins/modules/iam_managed_policy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 0631a243dd0..06e31a906d5 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -45,7 +45,7 @@ type: str fail_on_delete: description: - - The I(fail_on_delete) option does nothing and will be removed in Ansible 2.14. + - The I(fail_on_delete) option does nothing and will be removed after 2022-06-01 type: bool author: "Dan Kozlowski (@dkhenry)" @@ -289,7 +289,7 @@ def main(): policy=dict(type='json'), make_default=dict(type='bool', default=True), only_version=dict(type='bool', default=False), - fail_on_delete=dict(type='bool', removed_in_version='2.14'), + fail_on_delete=dict(type='bool', removed_at_date='2022-06-01', removed_from_collection='community.aws'), state=dict(default='present', choices=['present', 'absent']), )) From 5ab2ca21d445aef68e7985564fd398cb795232eb Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Wed, 17 Jun 2020 01:24:54 +0530 Subject: [PATCH 06/31] Update Examples with FQCN (#67) Updated module examples with FQCN Signed-off-by: Abhijeet Kasurde This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/98173aefbbceed7fc0d9db62687b73f96a55a999 --- plugins/modules/iam_managed_policy.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 06e31a906d5..0abe10faf5d 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -61,7 +61,7 @@ EXAMPLES = ''' # Create Policy ex nihilo - name: Create IAM Managed Policy - iam_managed_policy: + community.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy_description: "A Helpful managed policy" policy: "{{ lookup('template', 'managed_policy.json.j2') }}" @@ -69,14 +69,14 @@ # Update a policy with a new default version - name: Create IAM Managed Policy - iam_managed_policy: + community.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: "{{ lookup('file', 'managed_policy_update.json') }}" state: present # Update a policy with a new non default version - name: Create IAM Managed Policy - iam_managed_policy: + community.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: "{{ lookup('file', 'managed_policy_update.json') }}" make_default: false @@ -84,7 +84,7 @@ # Update a policy and make it the only version and the default version - name: Create IAM Managed Policy - iam_managed_policy: + community.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: "{ 'Version': '2012-10-17', 'Statement':[{'Effect': 'Allow','Action': '*','Resource': '*'}]}" only_version: true @@ -92,7 +92,7 @@ # Remove a policy - name: Create IAM Managed Policy - iam_managed_policy: + community.aws.iam_managed_policy: policy_name: "ManagedPolicy" state: absent ''' From f2f9f30a76508a075cdfefd41f5967b9ba1753bf Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Wed, 17 Jun 2020 09:31:32 -0700 Subject: [PATCH 07/31] Update docs (#99) * Update docs Remove .git from repo url so links in readme will generate correctly Add required ansible version Run latest version of add_docs.py Add version_added string to modules * galaxy.yml was missing authors This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/96ee268e5267f5b12c3d59892bc1279f75aa3135 --- plugins/modules/iam_managed_policy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 0abe10faf5d..3e5f14a7ddb 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -9,6 +9,7 @@ DOCUMENTATION = ''' --- module: iam_managed_policy +version_added: 1.0.0 short_description: Manage User Managed IAM policies description: - Allows creating and removing managed IAM policies From 06cb45d5efa81e03718d17f763c9882281c29952 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 5 Aug 2020 11:08:43 +0200 Subject: [PATCH 08/31] iam_managed_policy: fix json in documentation (#178) This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/2365fddf37d42ddf47cfaaf41baca737290a4f64 --- plugins/modules/iam_managed_policy.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 3e5f14a7ddb..3b1adece098 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -79,7 +79,12 @@ - name: Create IAM Managed Policy community.aws.iam_managed_policy: policy_name: "ManagedPolicy" - policy: "{{ lookup('file', 'managed_policy_update.json') }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: "logs:CreateLogGroup" + Resource: "*" make_default: false state: present @@ -87,7 +92,15 @@ - name: Create IAM Managed Policy community.aws.iam_managed_policy: policy_name: "ManagedPolicy" - policy: "{ 'Version': '2012-10-17', 'Statement':[{'Effect': 'Allow','Action': '*','Resource': '*'}]}" + policy: | + { + "Version": "2012-10-17", + "Statement":[{ + "Effect": "Allow", + "Action": "logs:PutRetentionPolicy", + "Resource": "*" + }] + } only_version: true state: present From 134e48a1a9c40f3b08f3381811673989917a2f98 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 12 Aug 2020 13:06:35 +0200 Subject: [PATCH 09/31] Bulk migration to AnsibleAWSModule (#173) * Update comments to reference AnsibleAWSModule rather than AnsibleModule * Bulk re-order imports and split onto one from import per-line. * Add AnsibleAWSModule imports * Migrate boto 2 based modules to AnsibleAWSModule * Move boto3-only modules over to AnsibleAWSModule * Remove extra ec2_argument_spec calls - not needed now we're using AnsibleAWSModule * Remove most HAS_BOTO3 code, it's handled by AnsibleAWSModule * Handle missing Boto 2 consistently (HAS_BOTO) * Remove AnsibleModule imports * Changelog fragment This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/818c6d2faa046974a9bdfa9346122d11e5bef3b1 --- plugins/modules/iam_managed_policy.py | 32 +++++++++++---------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 3b1adece098..552b93b1b23 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -135,19 +135,17 @@ try: import botocore except ImportError: - pass # caught by imported HAS_BOTO3 - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (boto3_conn, - get_aws_connection_info, - ec2_argument_spec, - AWSRetry, - camel_dict_to_snake_dict, - HAS_BOTO3, - compare_policies, - ) + pass # Handled by AnsibleAWSModule + from ansible.module_utils._text import to_native +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) def list_policies_with_backoff(iam): @@ -296,8 +294,7 @@ def detach_all_entities(module, iam, policy, **kwargs): def main(): - argument_spec = ec2_argument_spec() - argument_spec.update(dict( + argument_spec = dict( policy_name=dict(required=True), policy_description=dict(default=''), policy=dict(type='json'), @@ -305,16 +302,13 @@ def main(): only_version=dict(type='bool', default=False), fail_on_delete=dict(type='bool', removed_at_date='2022-06-01', removed_from_collection='community.aws'), state=dict(default='present', choices=['present', 'absent']), - )) + ) - module = AnsibleModule( + module = AnsibleAWSModule( argument_spec=argument_spec, - required_if=[['state', 'present', ['policy']]] + required_if=[['state', 'present', ['policy']]], ) - if not HAS_BOTO3: - module.fail_json(msg='boto3 is required for this module') - name = module.params.get('policy_name') description = module.params.get('policy_description') state = module.params.get('state') From c2326274406d67ac48e155b845bdb29072869181 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 26 Aug 2020 11:35:32 +0200 Subject: [PATCH 10/31] Cleanup: Bulk Migration from boto3_conn to module.client() (#188) * Migrate from boto3_conn to module.client * Simplify error handling when creating connections * Simplify Region handling * Remove unused imports * Changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/6bdf00d2198927bdaa119ae76ddd379a8b6eeb3d --- plugins/modules/iam_managed_policy.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 552b93b1b23..a0b7c3c48af 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -140,8 +140,6 @@ from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies @@ -321,12 +319,9 @@ def main(): policy = json.dumps(json.loads(module.params.get('policy'))) try: - region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) - iam = boto3_conn(module, conn_type='client', resource='iam', - region=region, endpoint=ec2_url, **aws_connect_kwargs) - except (botocore.exceptions.NoCredentialsError, botocore.exceptions.ProfileNotFound) as e: - module.fail_json(msg="Can't authorize connection. Check your credentials and profile.", - exceptions=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) + iam = module.client('iam') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS') p = get_policy_by_name(module, iam, name) if state == 'present': From 789d02c80fcde7979904fcca59e9e91ddbf02dfb Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 27 Jan 2021 09:17:44 +0100 Subject: [PATCH 11/31] Bulk migration to fail_json_aws (#361) * Split imports and sort * Move camel_dict_to_snake_dict imports to ansible.module_utils.common.dict_transformations * Cleanup unused imports * Bulk migration to fail_json_aws * Changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/6c883156d250d3ed926a21dbd619b2b138246c5d --- plugins/modules/iam_managed_policy.py | 114 +++++++++----------------- 1 file changed, 39 insertions(+), 75 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index a0b7c3c48af..aa668498ad1 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -130,7 +130,6 @@ ''' import json -import traceback try: import botocore @@ -138,10 +137,10 @@ pass # Handled by AnsibleAWSModule from ansible.module_utils._text import to_native +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies @@ -154,10 +153,8 @@ def list_policies_with_backoff(iam): def get_policy_by_name(module, iam, name): try: response = list_policies_with_backoff(iam) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't list policies: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't list policies") for policy in response['Policies']: if policy['PolicyName'] == name: return policy @@ -168,36 +165,28 @@ def delete_oldest_non_default_version(module, iam, policy): try: versions = [v for v in iam.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] if not v['IsDefaultVersion']] - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't list policy versions: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't list policy versions") versions.sort(key=lambda v: v['CreateDate'], reverse=True) for v in versions[-1:]: try: iam.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't delete policy version: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't delete policy version") # This needs to return policy_version, changed def get_or_create_policy_version(module, iam, policy, policy_document): try: versions = iam.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't list policy versions: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't list policy versions") for v in versions: try: document = iam.get_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId'])['PolicyVersion']['Document'] - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't get policy version %s: %s" % (v['VersionId'], str(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't get policy version {0}".format(v['VersionId'])) # If the current policy matches the existing one if not compare_policies(document, json.loads(to_native(policy_document))): return v, False @@ -217,23 +206,19 @@ def get_or_create_policy_version(module, iam, policy, policy_document): try: version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] return version, True - except botocore.exceptions.ClientError as second_e: + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as second_e: e = second_e # Handle both when the exception isn't LimitExceeded or # the second attempt still failed - module.fail_json(msg="Couldn't create policy version: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + module.fail_json_aws(e, msg="Couldn't create policy version") def set_if_default(module, iam, policy, policy_version, is_default): if is_default and not policy_version['IsDefaultVersion']: try: iam.set_default_policy_version(PolicyArn=policy['Arn'], VersionId=policy_version['VersionId']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't set default policy version: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't set default policy version") return True return False @@ -243,17 +228,13 @@ def set_if_only(module, iam, policy, policy_version, is_only): try: versions = [v for v in iam.list_policy_versions(PolicyArn=policy['Arn'])[ 'Versions'] if not v['IsDefaultVersion']] - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't list policy versions: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't list policy versions") for v in versions: try: iam.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't delete policy version: %s" % str(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't delete policy version") return len(versions) > 0 return False @@ -261,32 +242,24 @@ def set_if_only(module, iam, policy, policy_version, is_only): def detach_all_entities(module, iam, policy, **kwargs): try: entities = iam.list_entities_for_policy(PolicyArn=policy['Arn'], **kwargs) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't detach list entities for policy %s: %s" % (policy['PolicyName'], str(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't detach list entities for policy {0}".format(policy['PolicyName'])) for g in entities['PolicyGroups']: try: iam.detach_group_policy(PolicyArn=policy['Arn'], GroupName=g['GroupName']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't detach group policy %s: %s" % (g['GroupName'], str(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't detach group policy {0}".format(g['GroupName'])) for u in entities['PolicyUsers']: try: iam.detach_user_policy(PolicyArn=policy['Arn'], UserName=u['UserName']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't detach user policy %s: %s" % (u['UserName'], str(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't detach user policy {0}".format(u['UserName'])) for r in entities['PolicyRoles']: try: iam.detach_role_policy(PolicyArn=policy['Arn'], RoleName=r['RoleName']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't detach role policy %s: %s" % (r['RoleName'], str(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't detach role policy {0}".format(r['RoleName'])) if entities['IsTruncated']: detach_all_entities(module, iam, policy, marker=entities['Marker']) @@ -330,10 +303,8 @@ def main(): try: rvalue = iam.create_policy(PolicyName=name, Path='/', PolicyDocument=policy, Description=description) - except Exception as e: - module.fail_json(msg="Couldn't create policy %s: %s" % (name, to_native(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't create policy {0}".format(name)) module.exit_json(changed=True, policy=camel_dict_to_snake_dict(rvalue['Policy'])) else: @@ -344,10 +315,8 @@ def main(): if changed: try: p = iam.get_policy(PolicyArn=p['Arn'])['Policy'] - except Exception as e: - module.fail_json(msg="Couldn't get policy: %s" % to_native(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json(msg="Couldn't get policy") module.exit_json(changed=changed, policy=camel_dict_to_snake_dict(p)) else: @@ -358,26 +327,21 @@ def main(): # Delete Versions try: versions = iam.list_policy_versions(PolicyArn=p['Arn'])['Versions'] - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't list policy versions: %s" % to_native(e), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't list policy versions") for v in versions: if not v['IsDefaultVersion']: try: iam.delete_policy_version(PolicyArn=p['Arn'], VersionId=v['VersionId']) - except botocore.exceptions.ClientError as e: - module.fail_json(msg="Couldn't delete policy version %s: %s" % - (v['VersionId'], to_native(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws( + e, msg="Couldn't delete policy version {0}".format(v['VersionId'])) # Delete policy try: iam.delete_policy(PolicyArn=p['Arn']) - except Exception as e: - module.fail_json(msg="Couldn't delete policy %s: %s" % (p['PolicyName'], to_native(e)), - exception=traceback.format_exc(), - **camel_dict_to_snake_dict(e.response)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't delete policy {0}".format(p['PolicyName'])) + # This is the one case where we will return the old policy module.exit_json(changed=True, policy=camel_dict_to_snake_dict(p)) else: From ee7818f02a0eac919877bfcc24aaf8052c35c2c1 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 5 Feb 2021 09:43:09 +0100 Subject: [PATCH 12/31] Cleanup - use is_boto3_error_(message|code) (#268) * Reorder imports * Make use of is_boto3_error_message * Mass-migration over to is_boto3_error_code * Remove unused imports * unused vars in exception * Improve consistency around catching BotoCoreError and ClientError * Remove unused imports * Remove unused 'PolicyError' from iam_policy_info * Avoid catching botocore.exceptions.ClientError when we only want some error codes * Import camel_dict_to_snake_dict/snake_dict_to_camel_dict from ansible.module_utils.common.dict_transformations This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/4cf52ef67682fd4f9ed2a707adfbafffe7b88f15 --- plugins/modules/iam_managed_policy.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index aa668498ad1..f0fa588c44e 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -140,6 +140,7 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies @@ -200,16 +201,14 @@ def get_or_create_policy_version(module, iam, policy, policy_document): try: version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] return version, True - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] == 'LimitExceeded': - delete_oldest_non_default_version(module, iam, policy) - try: - version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] - return version, True - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as second_e: - e = second_e - # Handle both when the exception isn't LimitExceeded or - # the second attempt still failed + except is_boto3_error_code('LimitExceeded'): + delete_oldest_non_default_version(module, iam, policy) + try: + version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + return version, True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as second_e: + module.fail_json_aws(second_e, msg="Couldn't create policy version") + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Couldn't create policy version") From f30365de51b1d26569a0fd57c4db09459bcc716d Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 6 May 2021 21:01:46 +0200 Subject: [PATCH 13/31] Update the default module requirements from python 2.6/boto to python 3.6/boto3 This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/c097c55293be0834a2b9d394733ec28965d142d7 --- plugins/modules/iam_managed_policy.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index f0fa588c44e..a56e76d037f 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -53,10 +53,6 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - -requirements: - - boto3 - - botocore ''' EXAMPLES = ''' From f5447d8f37c473e008fb4a1f3befe001d6d3b7d3 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 18 Oct 2021 08:30:22 +0200 Subject: [PATCH 14/31] Bulk update AWSRetry.backoff to AWSRetry.jittered_backoff (#764) Bulk update AWSRetry.backoff to AWSRetry.jittered_backoff SUMMARY CloudRetry.backoff has been deprecated in favour of CloudRetry{exponential,jittered}_backoff bulk update AWSRetry.backoff usage. ISSUE TYPE Feature Pull Request COMPONENT NAME plugins/modules/aws_config_delivery_channel.py plugins/modules/aws_direct_connect_confirm_connection.py plugins/modules/aws_direct_connect_connection.py plugins/modules/aws_direct_connect_link_aggregation_group.py plugins/modules/aws_direct_connect_virtual_interface.py plugins/modules/aws_inspector_target.py plugins/modules/aws_kms.py plugins/modules/aws_kms_info.py plugins/modules/cloudformation_stack_set.py plugins/modules/dms_endpoint.py plugins/modules/dms_replication_subnet_group.py plugins/modules/ec2_asg.py plugins/modules/ec2_elb_info.py plugins/modules/ecs_service_info.py plugins/modules/iam_managed_policy.py plugins/modules/iam_saml_federation.py plugins/modules/rds.py ADDITIONAL INFORMATION Reviewed-by: None Reviewed-by: None This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/22a63708068d0d603ebc228f2e6673ac878e82bd --- plugins/modules/iam_managed_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index a56e76d037f..d6cdd33525e 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -141,7 +141,7 @@ from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies -@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) def list_policies_with_backoff(iam): paginator = iam.get_paginator('list_policies') return paginator.paginate(Scope='Local').build_full_result() From 685674fb1b41de8c6fccc5a3e1e0a95452331f6b Mon Sep 17 00:00:00 2001 From: Priyadarshini Chettiar <45838555+priyadarshu@users.noreply.github.com> Date: Thu, 3 Feb 2022 02:22:27 +0530 Subject: [PATCH 15/31] Update the name attribute value in the examples (#918) Update the name attribute value in the examples SUMMARY Problem- All the examples had same name key value irrespective of different purposes of the tasks Action taken - Made changes in the name of the tasks under examples Corrected it with relevant name key value to the comments of the task ISSUE TYPE Docs Pull Request COMPONENT NAME ADDITIONAL INFORMATION Reviewed-by: Joseph Torcasso Reviewed-by: Markus Bergholz This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/0e08a409083947386482ff8423e85c4226a40dba --- plugins/modules/iam_managed_policy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index d6cdd33525e..2b33d711e71 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -65,14 +65,14 @@ state: present # Update a policy with a new default version -- name: Create IAM Managed Policy +- name: Update an IAM Managed Policy with new default version community.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: "{{ lookup('file', 'managed_policy_update.json') }}" state: present # Update a policy with a new non default version -- name: Create IAM Managed Policy +- name: Update an IAM Managed Policy with a non default version community.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: @@ -85,7 +85,7 @@ state: present # Update a policy and make it the only version and the default version -- name: Create IAM Managed Policy +- name: Update an IAM Managed Policy with default version as the only version community.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: | @@ -101,7 +101,7 @@ state: present # Remove a policy -- name: Create IAM Managed Policy +- name: Remove an existing IAM Managed Policy community.aws.iam_managed_policy: policy_name: "ManagedPolicy" state: absent From fb2f10d476d0686c55b7883f94e1adbf6e82d1ce Mon Sep 17 00:00:00 2001 From: Mark Woolley Date: Thu, 10 Feb 2022 12:26:27 +0000 Subject: [PATCH 16/31] Refactor iam_managed_policy module and add integration tests (#893) Refactor iam_managed_policy module and add integration tests SUMMARY Refactor iam_managed_policy module to: Improve AWS retry backoff logic Add check_mode support Fix module exit on updates to policies when no changes are present Other changes: Add disabled integration tests ISSUE TYPE Bugfix Pull Request COMPONENT NAME iam_managed_policy ADDITIONAL INFORMATION Backoff logic only partially covered the module, and it didn't support check_mode or have any integration tests. Due to the nature of the IAM based modules the tests are intentionally disabled but have been run locally: ansible-test integration iam_managed_policy --allow-unsupported --docker PLAY RECAP ********************************************************************* testhost : ok=20 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 AWS ACTIONS: ['iam:CreatePolicy', 'iam:CreatePolicyVersion', 'iam:DeletePolicy', 'iam:DeletePolicyVersion', 'iam:GetPolicy', 'iam:GetPolicyVersion', 'iam:ListEntitiesForPolicy', 'iam:ListPolicies', 'iam:ListPolicyVersions', 'iam:SetDefaultPolicyVersion'] Reviewed-by: Alina Buzachis Reviewed-by: Markus Bergholz This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/44daa2ded8dc9f1dab0f7a4643176fe668a2a89c --- plugins/modules/iam_managed_policy.py | 201 ++++++++++-------- .../targets/iam_managed_policy/aliases | 6 + .../iam_managed_policy/defaults/main.yml | 2 + .../targets/iam_managed_policy/tasks/main.yml | 160 ++++++++++++++ 4 files changed, 282 insertions(+), 87 deletions(-) create mode 100644 tests/integration/targets/iam_managed_policy/aliases create mode 100644 tests/integration/targets/iam_managed_policy/defaults/main.yml create mode 100644 tests/integration/targets/iam_managed_policy/tasks/main.yml diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 2b33d711e71..403b4720d50 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -6,7 +6,7 @@ __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: iam_managed_policy version_added: 1.0.0 @@ -55,7 +55,7 @@ - amazon.aws.ec2 ''' -EXAMPLES = ''' +EXAMPLES = r''' # Create Policy ex nihilo - name: Create IAM Managed Policy community.aws.iam_managed_policy: @@ -107,11 +107,12 @@ state: absent ''' -RETURN = ''' +RETURN = r''' policy: description: Returns the policy json structure, when state == absent this will return the value of the removed policy. returned: success - type: str + type: complex + contains: {} sample: '{ "arn": "arn:aws:iam::aws:policy/AdministratorAccess " "attachment_count": 0, @@ -142,14 +143,14 @@ @AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def list_policies_with_backoff(iam): - paginator = iam.get_paginator('list_policies') +def list_policies_with_backoff(): + paginator = client.get_paginator('list_policies') return paginator.paginate(Scope='Local').build_full_result() -def get_policy_by_name(module, iam, name): +def get_policy_by_name(name): try: - response = list_policies_with_backoff(iam) + response = list_policies_with_backoff() except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policies") for policy in response['Policies']: @@ -158,32 +159,36 @@ def get_policy_by_name(module, iam, name): return None -def delete_oldest_non_default_version(module, iam, policy): +def delete_oldest_non_default_version(policy): try: - versions = [v for v in iam.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] + versions = [v for v in client.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] if not v['IsDefaultVersion']] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policy versions") versions.sort(key=lambda v: v['CreateDate'], reverse=True) for v in versions[-1:]: try: - iam.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) + client.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete policy version") # This needs to return policy_version, changed -def get_or_create_policy_version(module, iam, policy, policy_document): +def get_or_create_policy_version(policy, policy_document): try: - versions = iam.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] + versions = client.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policy versions") + for v in versions: try: - document = iam.get_policy_version(PolicyArn=policy['Arn'], - VersionId=v['VersionId'])['PolicyVersion']['Document'] + document = client.get_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId'])['PolicyVersion']['Document'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get policy version {0}".format(v['VersionId'])) + + if module.check_mode and compare_policies(document, json.loads(to_native(policy_document))): + return v, True + # If the current policy matches the existing one if not compare_policies(document, json.loads(to_native(policy_document))): return v, False @@ -195,12 +200,12 @@ def get_or_create_policy_version(module, iam, policy, policy_document): # and if that doesn't work, delete the oldest non default policy version # and try again. try: - version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + version = client.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] return version, True except is_boto3_error_code('LimitExceeded'): - delete_oldest_non_default_version(module, iam, policy) + delete_oldest_non_default_version(policy) try: - version = iam.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + version = client.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] return version, True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as second_e: module.fail_json_aws(second_e, msg="Couldn't create policy version") @@ -208,58 +213,132 @@ def get_or_create_policy_version(module, iam, policy, policy_document): module.fail_json_aws(e, msg="Couldn't create policy version") -def set_if_default(module, iam, policy, policy_version, is_default): +def set_if_default(policy, policy_version, is_default): if is_default and not policy_version['IsDefaultVersion']: try: - iam.set_default_policy_version(PolicyArn=policy['Arn'], VersionId=policy_version['VersionId']) + client.set_default_policy_version(PolicyArn=policy['Arn'], VersionId=policy_version['VersionId']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't set default policy version") return True return False -def set_if_only(module, iam, policy, policy_version, is_only): +def set_if_only(policy, policy_version, is_only): if is_only: try: - versions = [v for v in iam.list_policy_versions(PolicyArn=policy['Arn'])[ + versions = [v for v in client.list_policy_versions(PolicyArn=policy['Arn'])[ 'Versions'] if not v['IsDefaultVersion']] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policy versions") for v in versions: try: - iam.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) + client.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete policy version") return len(versions) > 0 return False -def detach_all_entities(module, iam, policy, **kwargs): +def detach_all_entities(policy, **kwargs): try: - entities = iam.list_entities_for_policy(PolicyArn=policy['Arn'], **kwargs) + entities = client.list_entities_for_policy(PolicyArn=policy['Arn'], **kwargs) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't detach list entities for policy {0}".format(policy['PolicyName'])) for g in entities['PolicyGroups']: try: - iam.detach_group_policy(PolicyArn=policy['Arn'], GroupName=g['GroupName']) + client.detach_group_policy(PolicyArn=policy['Arn'], GroupName=g['GroupName']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't detach group policy {0}".format(g['GroupName'])) for u in entities['PolicyUsers']: try: - iam.detach_user_policy(PolicyArn=policy['Arn'], UserName=u['UserName']) + client.detach_user_policy(PolicyArn=policy['Arn'], UserName=u['UserName']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't detach user policy {0}".format(u['UserName'])) for r in entities['PolicyRoles']: try: - iam.detach_role_policy(PolicyArn=policy['Arn'], RoleName=r['RoleName']) + client.detach_role_policy(PolicyArn=policy['Arn'], RoleName=r['RoleName']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't detach role policy {0}".format(r['RoleName'])) if entities['IsTruncated']: - detach_all_entities(module, iam, policy, marker=entities['Marker']) + detach_all_entities(policy, marker=entities['Marker']) + + +def create_or_update_policy(existing_policy): + name = module.params.get('policy_name') + description = module.params.get('policy_description') + default = module.params.get('make_default') + only = module.params.get('only_version') + + policy = None + + if module.params.get('policy') is not None: + policy = json.dumps(json.loads(module.params.get('policy'))) + + if existing_policy is None: + if module.check_mode: + module.exit_json(changed=True) + + # Create policy when none already exists + try: + rvalue = client.create_policy(PolicyName=name, Path='/', PolicyDocument=policy, Description=description) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't create policy {0}".format(name)) + + module.exit_json(changed=True, policy=camel_dict_to_snake_dict(rvalue['Policy'])) + else: + policy_version, changed = get_or_create_policy_version(existing_policy, policy) + changed = set_if_default(existing_policy, policy_version, default) or changed + changed = set_if_only(existing_policy, policy_version, only) or changed + + # If anything has changed we need to refresh the policy + if changed: + try: + updated_policy = client.get_policy(PolicyArn=existing_policy['Arn'])['Policy'] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json(msg="Couldn't get policy") + + module.exit_json(changed=changed, policy=camel_dict_to_snake_dict(updated_policy)) + else: + module.exit_json(changed=changed, policy=camel_dict_to_snake_dict(existing_policy)) + + +def delete_policy(existing_policy): + # Check for existing policy + if existing_policy: + if module.check_mode: + module.exit_json(changed=True) + + # Detach policy + detach_all_entities(existing_policy) + # Delete Versions + try: + versions = client.list_policy_versions(PolicyArn=existing_policy['Arn'])['Versions'] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't list policy versions") + for v in versions: + if not v['IsDefaultVersion']: + try: + client.delete_policy_version(PolicyArn=existing_policy['Arn'], VersionId=v['VersionId']) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws( + e, msg="Couldn't delete policy version {0}".format(v['VersionId'])) + # Delete policy + try: + client.delete_policy(PolicyArn=existing_policy['Arn']) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't delete policy {0}".format(existing_policy['PolicyName'])) + + # This is the one case where we will return the old policy + module.exit_json(changed=True, policy=camel_dict_to_snake_dict(existing_policy)) + else: + module.exit_json(changed=False, policy=None) def main(): + global module + global client + argument_spec = dict( policy_name=dict(required=True), policy_description=dict(default=''), @@ -273,75 +352,23 @@ def main(): module = AnsibleAWSModule( argument_spec=argument_spec, required_if=[['state', 'present', ['policy']]], + supports_check_mode=True ) name = module.params.get('policy_name') - description = module.params.get('policy_description') state = module.params.get('state') - default = module.params.get('make_default') - only = module.params.get('only_version') - - policy = None - - if module.params.get('policy') is not None: - policy = json.dumps(json.loads(module.params.get('policy'))) try: - iam = module.client('iam') + client = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Failed to connect to AWS') - p = get_policy_by_name(module, iam, name) - if state == 'present': - if p is None: - # No Policy so just create one - try: - rvalue = iam.create_policy(PolicyName=name, Path='/', - PolicyDocument=policy, Description=description) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't create policy {0}".format(name)) - - module.exit_json(changed=True, policy=camel_dict_to_snake_dict(rvalue['Policy'])) - else: - policy_version, changed = get_or_create_policy_version(module, iam, p, policy) - changed = set_if_default(module, iam, p, policy_version, default) or changed - changed = set_if_only(module, iam, p, policy_version, only) or changed - # If anything has changed we needto refresh the policy - if changed: - try: - p = iam.get_policy(PolicyArn=p['Arn'])['Policy'] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json(msg="Couldn't get policy") + existing_policy = get_policy_by_name(name) - module.exit_json(changed=changed, policy=camel_dict_to_snake_dict(p)) + if state == 'present': + create_or_update_policy(existing_policy) else: - # Check for existing policy - if p: - # Detach policy - detach_all_entities(module, iam, p) - # Delete Versions - try: - versions = iam.list_policy_versions(PolicyArn=p['Arn'])['Versions'] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't list policy versions") - for v in versions: - if not v['IsDefaultVersion']: - try: - iam.delete_policy_version(PolicyArn=p['Arn'], VersionId=v['VersionId']) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws( - e, msg="Couldn't delete policy version {0}".format(v['VersionId'])) - # Delete policy - try: - iam.delete_policy(PolicyArn=p['Arn']) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't delete policy {0}".format(p['PolicyName'])) - - # This is the one case where we will return the old policy - module.exit_json(changed=True, policy=camel_dict_to_snake_dict(p)) - else: - module.exit_json(changed=False, policy=None) -# end main + delete_policy(existing_policy) if __name__ == '__main__': diff --git a/tests/integration/targets/iam_managed_policy/aliases b/tests/integration/targets/iam_managed_policy/aliases new file mode 100644 index 00000000000..839bd014bd7 --- /dev/null +++ b/tests/integration/targets/iam_managed_policy/aliases @@ -0,0 +1,6 @@ +# reason: missing-policy +# It's not possible to control what permissions are granted to a policy. +# This makes securely testing iam_policy very difficult +unsupported + +cloud/aws diff --git a/tests/integration/targets/iam_managed_policy/defaults/main.yml b/tests/integration/targets/iam_managed_policy/defaults/main.yml new file mode 100644 index 00000000000..a6edcacefae --- /dev/null +++ b/tests/integration/targets/iam_managed_policy/defaults/main.yml @@ -0,0 +1,2 @@ +--- +policy_name: "{{ resource_prefix }}-policy" diff --git a/tests/integration/targets/iam_managed_policy/tasks/main.yml b/tests/integration/targets/iam_managed_policy/tasks/main.yml new file mode 100644 index 00000000000..f17b7cad096 --- /dev/null +++ b/tests/integration/targets/iam_managed_policy/tasks/main.yml @@ -0,0 +1,160 @@ +--- +- name: "Run integration tests for IAM managed policy" + 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 }}" + collections: + - amazon.aws + block: + ## Test policy creation + - name: Create IAM managed policy - check mode + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:CreateLogGroup" + Resource: "*" + state: present + register: result + check_mode: yes + + - name: Create IAM managed policy - check mode + assert: + that: + - result.changed + + - name: Create IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:CreateLogGroup" + Resource: "*" + state: present + register: result + + - name: Create IAM managed policy + assert: + that: + - result.changed + - result.policy.policy_name == policy_name + + - name: Create IAM managed policy - idempotency check + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:CreateLogGroup" + Resource: "*" + state: present + register: result + + - name: Create IAM managed policy - idempotency check + assert: + that: + - not result.changed + + ## Test policy update + - name: Update IAM managed policy - check mode + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:Describe*" + Resource: "*" + state: present + register: result + check_mode: yes + + - name: Update IAM managed policy - check mode + assert: + that: + - result.changed + + - name: Update IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:Describe*" + Resource: "*" + state: present + register: result + + - name: Update IAM managed policy + assert: + that: + - result.changed + - result.policy.policy_name == policy_name + + - name: Update IAM managed policy - idempotency check + iam_managed_policy: + policy_name: "{{ policy_name }}" + policy: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Action: "logs:Describe*" + Resource: "*" + state: present + register: result + + - name: Update IAM managed policy - idempotency check + assert: + that: + - not result.changed + + ## Test policy deletion + - name: Delete IAM managed policy - check mode + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + register: result + check_mode: yes + + - name: Delete IAM managed policy - check mode + assert: + that: + - result.changed + + - name: Delete IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + register: result + + - name: Delete IAM managed policy + assert: + that: + - result.changed + + - name: Delete IAM managed policy - idempotency check + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + register: result + + - name: Delete IAM managed policy - idempotency check + assert: + that: + - not result.changed + + always: + - name: Delete IAM managed policy + iam_managed_policy: + policy_name: "{{ policy_name }}" + state: absent + ignore_errors: yes From 3513388d07a20f3e2a820bb0b1088f1d159df4f6 Mon Sep 17 00:00:00 2001 From: greenflowers <46848026+greenflowers@users.noreply.github.com> Date: Thu, 24 Feb 2022 10:10:37 +0000 Subject: [PATCH 17/31] Remove string of iam_managed_policy module docs (#952) Remove string of iam_managed_policy module docs SUMMARY Remove "ex nihilo" docs of iam_managed_policy module ISSUE TYPE Docs Pull Request COMPONENT NAME iam_managed_policy Reviewed-by: Alina Buzachis Reviewed-by: Mark Chappell This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/82ad0843fd36e730add89296d0f5df65634fcf75 --- plugins/modules/iam_managed_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 403b4720d50..4c02054db21 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -56,7 +56,7 @@ ''' EXAMPLES = r''' -# Create Policy ex nihilo +# Create a policy - name: Create IAM Managed Policy community.aws.iam_managed_policy: policy_name: "ManagedPolicy" From f42e08b69bd6ff190a00c1dd1505a8157f6ea39c Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 22 Apr 2022 12:20:04 +0200 Subject: [PATCH 18/31] For consistency - add empty dependencies file to targets with no current meta data (#1090) For consistency - add empty dependencies file to targets with no current meta data SUMMARY For consistency - add empty dependencies file to targets with no current meta data ISSUE TYPE Feature Pull Request COMPONENT NAME tests/integration/targets ADDITIONAL INFORMATION Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/96385803df04cfa34c62d1ab19be21b12f593af6 --- tests/integration/targets/iam_managed_policy/meta/main.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/integration/targets/iam_managed_policy/meta/main.yml diff --git a/tests/integration/targets/iam_managed_policy/meta/main.yml b/tests/integration/targets/iam_managed_policy/meta/main.yml new file mode 100644 index 00000000000..32cf5dda7ed --- /dev/null +++ b/tests/integration/targets/iam_managed_policy/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] From db5500b94ecae64ce49c015100293cc51421b181 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 26 May 2022 09:44:49 +0200 Subject: [PATCH 19/31] iam_managed_policy - remove unused fail_on_delete parameter (#1168) iam_managed_policy - remove unused fail_on_delete parameter SUMMARY remove unused fail_on_delete parameter ISSUE TYPE Feature Pull Request COMPONENT NAME iam_managed_policy ADDITIONAL INFORMATION ansible/ansible#63961 Reviewed-by: Markus Bergholz This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/7095617d88788a2a89721ee3faff137b9aef6770 --- plugins/modules/iam_managed_policy.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 4c02054db21..3e30c4a667c 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -44,10 +44,6 @@ default: present choices: [ "present", "absent" ] type: str - fail_on_delete: - description: - - The I(fail_on_delete) option does nothing and will be removed after 2022-06-01 - type: bool author: "Dan Kozlowski (@dkhenry)" extends_documentation_fragment: @@ -345,7 +341,6 @@ def main(): policy=dict(type='json'), make_default=dict(type='bool', default=True), only_version=dict(type='bool', default=False), - fail_on_delete=dict(type='bool', removed_at_date='2022-06-01', removed_from_collection='community.aws'), state=dict(default='present', choices=['present', 'absent']), ) From 2e2f040fe6b2bdbff7889d72897fa9107531e0fa Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 28 Sep 2022 13:40:43 +0200 Subject: [PATCH 20/31] Make example AWS UUIDS follow a specific pattern (#1539) Make example AWS UUIDS follow a specific pattern SUMMARY Various AWS IAM resources have UUID which follow a specific pattern. Similarly AWS accounts are all 12 digit numbers (text aliases in a couple of cases). To minimize the risk of accidental data leaks use a consistent Account ID in examples (123456789012), and a specific format for the UUIDS: (AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)12345EXAMPLE54321 While this does nothing about historic data, having consistency makes it easier to prevent future leaks. Note: We should follow this up with an update to the developer docs, however I'd like to get this in prior to 5.0.0 ISSUE TYPE Docs Pull Request COMPONENT NAME plugins/modules/acm_certificate_info.py plugins/modules/application_autoscaling_policy.py plugins/modules/autoscaling_launch_config.py plugins/modules/autoscaling_launch_config_info.py plugins/modules/codecommit_repository.py plugins/modules/directconnect_link_aggregation_group.py plugins/modules/dms_endpoint.py plugins/modules/dynamodb_table.py plugins/modules/ec2_transit_gateway_info.py plugins/modules/ec2_transit_gateway_vpc_attachment.py plugins/modules/ec2_transit_gateway_vpc_attachment_info.py plugins/modules/ec2_vpc_peer.py plugins/modules/ec2_vpc_peering_info.py plugins/modules/ec2_vpc_vpn_info.py plugins/modules/ecs_cluster.py plugins/modules/ecs_ecr.py plugins/modules/ecs_service.py plugins/modules/ecs_service_info.py plugins/modules/ecs_task.py plugins/modules/efs.py plugins/modules/efs_info.py plugins/modules/eks_cluster.py plugins/modules/elasticache_subnet_group.py plugins/modules/elb_network_lb.py plugins/modules/elb_target_group.py plugins/modules/elb_target_group_info.py plugins/modules/elb_target_info.py plugins/modules/iam_group.py plugins/modules/iam_managed_policy.py plugins/modules/iam_mfa_device_info.py plugins/modules/iam_server_certificate_info.py plugins/modules/lightsail.py plugins/modules/lightsail_static_ip.py plugins/modules/msk_cluster.py plugins/modules/s3_bucket_notification.py plugins/modules/sns_topic.py plugins/modules/sns_topic_info.py plugins/modules/sqs_queue.py plugins/modules/stepfunctions_state_machine.py plugins/modules/stepfunctions_state_machine_execution.py plugins/modules/storagegateway_info.py plugins/modules/wafv2_web_acl.py ADDITIONAL INFORMATION While the 'secret' nature of these UUIDs is debatable (they're closer to user names than passwords), deliberately mangling them makes it easier for InfoSec teams to spot when their secret counterparts may have been leaked in combination with a real 'public' part. This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/94764225332c869eefa574a8948da680bb668407 --- plugins/modules/iam_managed_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 3e30c4a667c..4b3dbfebda4 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -116,7 +116,7 @@ "default_version_id": "v1", "is_attachable": true, "path": "/", - "policy_id": "ANPALM4KLDMTFXGOOJIHL", + "policy_id": "ANPA1245EXAMPLE54321", "policy_name": "AdministratorAccess", "update_date": "2017-03-01T15:42:55.981000+00:00" }' From 610c0752a5e046250ea16108c120689b3fd465a3 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Wed, 5 Oct 2022 17:04:40 +0200 Subject: [PATCH 21/31] Update extends_documentation_fragment with amazon.aws.boto3 (#1459) Update extends_documentation_fragment with amazon.aws.boto3 Depends-On: ansible/ansible-zuul-jobs#1654 SUMMARY As per ansible-collections/amazon.aws#985 add amazon.aws.boto3. ISSUE TYPE Docs Pull Request COMPONENT NAME several Reviewed-by: Jill R Reviewed-by: Mark Chappell Reviewed-by: Markus Bergholz This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/bd3c03fcba0848f593b86309740fa73e986a9646 --- plugins/modules/iam_managed_policy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 4b3dbfebda4..f86f019d536 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -49,6 +49,7 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 +- amazon.aws.boto3 ''' EXAMPLES = r''' From b98ffc9aeed02e6224baecf8b426af33bd9cc451 Mon Sep 17 00:00:00 2001 From: Bikouo Aubin <79859644+abikouo@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:22:13 +0100 Subject: [PATCH 22/31] Ansible User-Agent identification for community.aws (#1632) Ansible User-Agent identification for community.aws SUMMARY The value will be similar to this APN/1.0 Ansible/2.14.1 community.aws/6.0.0-dev0 ISSUE TYPE Feature Pull Request Reviewed-by: Mark Chappell Reviewed-by: Bikouo Aubin Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/a8cbce24071bcc62fe4594c38aff1baf18bd2862 --- plugins/modules/iam_managed_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index f86f019d536..eabf03b23d7 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -133,7 +133,7 @@ from ansible.module_utils._text import to_native from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies From 8c62ca31f6a646e3fb5fab8a2c865ee1940417e7 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 8 Mar 2023 12:07:26 +0100 Subject: [PATCH 23/31] Cleanup headers and imports (#1738) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup headers and imports SUMMARY Mass update of imports, docs fragments and file headers Many of the amazon.aws module_utils and docs fragments got moved about, update community.aws to reflect this. Consistently apply the comment headers as documented at https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_documenting.html#python-shebang-utf-8-coding ISSUE TYPE Docs Pull Request Feature Pull Request COMPONENT NAME ADDITIONAL INFORMATION Header cleanup based upon: https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_documenting.html#python-shebang-utf-8-coding Begin your Ansible module with #!/usr/bin/python - this “shebang” allows ansible_python_interpreter to work. Follow the shebang immediately with # -*- coding: utf-8 -*- to clarify that the file is UTF-8 encoded. and https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_documenting.html#copyright-and-license After the shebang and UTF-8 coding, add a copyright line with the original copyright holder and a license declaration. The license declaration should be ONLY one line, not the full GPL prefix. ... Additions to the module (for instance, rewrites) are not permitted to add additional copyright lines other than the default copyright statement if missing: Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/a4f20bf114bfab19b1c84c4ecf42efd5614ab80c --- plugins/modules/iam_managed_policy.py | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index eabf03b23d7..f590fcf9d64 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -1,18 +1,16 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- + # Copyright: Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: iam_managed_policy version_added: 1.0.0 short_description: Manage User Managed IAM policies description: - - Allows creating and removing managed IAM policies + - Allows creating and removing managed IAM policies options: policy_name: description: @@ -45,14 +43,15 @@ choices: [ "present", "absent" ] type: str -author: "Dan Kozlowski (@dkhenry)" +author: + - "Dan Kozlowski (@dkhenry)" extends_documentation_fragment: -- amazon.aws.aws -- amazon.aws.ec2 -- amazon.aws.boto3 -''' + - amazon.aws.common.modules + - amazon.aws.region.modules + - amazon.aws.boto3 +""" -EXAMPLES = r''' +EXAMPLES = r""" # Create a policy - name: Create IAM Managed Policy community.aws.iam_managed_policy: @@ -102,9 +101,9 @@ community.aws.iam_managed_policy: policy_name: "ManagedPolicy" state: absent -''' +""" -RETURN = r''' +RETURN = r""" policy: description: Returns the policy json structure, when state == absent this will return the value of the removed policy. returned: success @@ -121,7 +120,7 @@ "policy_name": "AdministratorAccess", "update_date": "2017-03-01T15:42:55.981000+00:00" }' -''' +""" import json @@ -133,10 +132,11 @@ from ansible.module_utils._text import to_native from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.policy import compare_policies +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry + from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies @AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) From c036f8904b34a46231dc604e996cd05a441edbe1 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 26 Apr 2023 19:26:07 +0200 Subject: [PATCH 24/31] Big Black PR (#1784) * Black prep * Black * changelog * Fix pylint unused-import in tests * Split SSM connection plugin changes * disable glue tests - bucket's missing * Disable s3_logging and s3_sync tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/2c4575c248776c65d66b06cd60fa09b0dae1cd6f --- plugins/modules/iam_managed_policy.py | 132 ++++++++++++++------------ 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index f590fcf9d64..0f6189ca454 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -141,8 +141,8 @@ @AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) def list_policies_with_backoff(): - paginator = client.get_paginator('list_policies') - return paginator.paginate(Scope='Local').build_full_result() + paginator = client.get_paginator("list_policies") + return paginator.paginate(Scope="Local").build_full_result() def get_policy_by_name(name): @@ -150,22 +150,23 @@ def get_policy_by_name(name): response = list_policies_with_backoff() except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policies") - for policy in response['Policies']: - if policy['PolicyName'] == name: + for policy in response["Policies"]: + if policy["PolicyName"] == name: return policy return None def delete_oldest_non_default_version(policy): try: - versions = [v for v in client.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] - if not v['IsDefaultVersion']] + versions = [ + v for v in client.list_policy_versions(PolicyArn=policy["Arn"])["Versions"] if not v["IsDefaultVersion"] + ] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policy versions") - versions.sort(key=lambda v: v['CreateDate'], reverse=True) + versions.sort(key=lambda v: v["CreateDate"], reverse=True) for v in versions[-1:]: try: - client.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) + client.delete_policy_version(PolicyArn=policy["Arn"], VersionId=v["VersionId"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete policy version") @@ -173,15 +174,17 @@ def delete_oldest_non_default_version(policy): # This needs to return policy_version, changed def get_or_create_policy_version(policy, policy_document): try: - versions = client.list_policy_versions(PolicyArn=policy['Arn'])['Versions'] + versions = client.list_policy_versions(PolicyArn=policy["Arn"])["Versions"] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policy versions") for v in versions: try: - document = client.get_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId'])['PolicyVersion']['Document'] + document = client.get_policy_version(PolicyArn=policy["Arn"], VersionId=v["VersionId"])["PolicyVersion"][ + "Document" + ] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't get policy version {0}".format(v['VersionId'])) + module.fail_json_aws(e, msg="Couldn't get policy version {0}".format(v["VersionId"])) if module.check_mode and compare_policies(document, json.loads(to_native(policy_document))): return v, True @@ -197,23 +200,28 @@ def get_or_create_policy_version(policy, policy_document): # and if that doesn't work, delete the oldest non default policy version # and try again. try: - version = client.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + version = client.create_policy_version(PolicyArn=policy["Arn"], PolicyDocument=policy_document)["PolicyVersion"] return version, True - except is_boto3_error_code('LimitExceeded'): + except is_boto3_error_code("LimitExceeded"): delete_oldest_non_default_version(policy) try: - version = client.create_policy_version(PolicyArn=policy['Arn'], PolicyDocument=policy_document)['PolicyVersion'] + version = client.create_policy_version(PolicyArn=policy["Arn"], PolicyDocument=policy_document)[ + "PolicyVersion" + ] return version, True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as second_e: module.fail_json_aws(second_e, msg="Couldn't create policy version") - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + except ( + botocore.exceptions.ClientError, + botocore.exceptions.BotoCoreError, + ) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Couldn't create policy version") def set_if_default(policy, policy_version, is_default): - if is_default and not policy_version['IsDefaultVersion']: + if is_default and not policy_version["IsDefaultVersion"]: try: - client.set_default_policy_version(PolicyArn=policy['Arn'], VersionId=policy_version['VersionId']) + client.set_default_policy_version(PolicyArn=policy["Arn"], VersionId=policy_version["VersionId"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't set default policy version") return True @@ -223,13 +231,14 @@ def set_if_default(policy, policy_version, is_default): def set_if_only(policy, policy_version, is_only): if is_only: try: - versions = [v for v in client.list_policy_versions(PolicyArn=policy['Arn'])[ - 'Versions'] if not v['IsDefaultVersion']] + versions = [ + v for v in client.list_policy_versions(PolicyArn=policy["Arn"])["Versions"] if not v["IsDefaultVersion"] + ] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policy versions") for v in versions: try: - client.delete_policy_version(PolicyArn=policy['Arn'], VersionId=v['VersionId']) + client.delete_policy_version(PolicyArn=policy["Arn"], VersionId=v["VersionId"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete policy version") return len(versions) > 0 @@ -238,39 +247,39 @@ def set_if_only(policy, policy_version, is_only): def detach_all_entities(policy, **kwargs): try: - entities = client.list_entities_for_policy(PolicyArn=policy['Arn'], **kwargs) + entities = client.list_entities_for_policy(PolicyArn=policy["Arn"], **kwargs) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach list entities for policy {0}".format(policy['PolicyName'])) + module.fail_json_aws(e, msg="Couldn't detach list entities for policy {0}".format(policy["PolicyName"])) - for g in entities['PolicyGroups']: + for g in entities["PolicyGroups"]: try: - client.detach_group_policy(PolicyArn=policy['Arn'], GroupName=g['GroupName']) + client.detach_group_policy(PolicyArn=policy["Arn"], GroupName=g["GroupName"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach group policy {0}".format(g['GroupName'])) - for u in entities['PolicyUsers']: + module.fail_json_aws(e, msg="Couldn't detach group policy {0}".format(g["GroupName"])) + for u in entities["PolicyUsers"]: try: - client.detach_user_policy(PolicyArn=policy['Arn'], UserName=u['UserName']) + client.detach_user_policy(PolicyArn=policy["Arn"], UserName=u["UserName"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach user policy {0}".format(u['UserName'])) - for r in entities['PolicyRoles']: + module.fail_json_aws(e, msg="Couldn't detach user policy {0}".format(u["UserName"])) + for r in entities["PolicyRoles"]: try: - client.detach_role_policy(PolicyArn=policy['Arn'], RoleName=r['RoleName']) + client.detach_role_policy(PolicyArn=policy["Arn"], RoleName=r["RoleName"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach role policy {0}".format(r['RoleName'])) - if entities['IsTruncated']: - detach_all_entities(policy, marker=entities['Marker']) + module.fail_json_aws(e, msg="Couldn't detach role policy {0}".format(r["RoleName"])) + if entities["IsTruncated"]: + detach_all_entities(policy, marker=entities["Marker"]) def create_or_update_policy(existing_policy): - name = module.params.get('policy_name') - description = module.params.get('policy_description') - default = module.params.get('make_default') - only = module.params.get('only_version') + name = module.params.get("policy_name") + description = module.params.get("policy_description") + default = module.params.get("make_default") + only = module.params.get("only_version") policy = None - if module.params.get('policy') is not None: - policy = json.dumps(json.loads(module.params.get('policy'))) + if module.params.get("policy") is not None: + policy = json.dumps(json.loads(module.params.get("policy"))) if existing_policy is None: if module.check_mode: @@ -278,11 +287,11 @@ def create_or_update_policy(existing_policy): # Create policy when none already exists try: - rvalue = client.create_policy(PolicyName=name, Path='/', PolicyDocument=policy, Description=description) + rvalue = client.create_policy(PolicyName=name, Path="/", PolicyDocument=policy, Description=description) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't create policy {0}".format(name)) - module.exit_json(changed=True, policy=camel_dict_to_snake_dict(rvalue['Policy'])) + module.exit_json(changed=True, policy=camel_dict_to_snake_dict(rvalue["Policy"])) else: policy_version, changed = get_or_create_policy_version(existing_policy, policy) changed = set_if_default(existing_policy, policy_version, default) or changed @@ -291,7 +300,7 @@ def create_or_update_policy(existing_policy): # If anything has changed we need to refresh the policy if changed: try: - updated_policy = client.get_policy(PolicyArn=existing_policy['Arn'])['Policy'] + updated_policy = client.get_policy(PolicyArn=existing_policy["Arn"])["Policy"] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json(msg="Couldn't get policy") @@ -310,21 +319,20 @@ def delete_policy(existing_policy): detach_all_entities(existing_policy) # Delete Versions try: - versions = client.list_policy_versions(PolicyArn=existing_policy['Arn'])['Versions'] + versions = client.list_policy_versions(PolicyArn=existing_policy["Arn"])["Versions"] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't list policy versions") for v in versions: - if not v['IsDefaultVersion']: + if not v["IsDefaultVersion"]: try: - client.delete_policy_version(PolicyArn=existing_policy['Arn'], VersionId=v['VersionId']) + client.delete_policy_version(PolicyArn=existing_policy["Arn"], VersionId=v["VersionId"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws( - e, msg="Couldn't delete policy version {0}".format(v['VersionId'])) + module.fail_json_aws(e, msg="Couldn't delete policy version {0}".format(v["VersionId"])) # Delete policy try: - client.delete_policy(PolicyArn=existing_policy['Arn']) + client.delete_policy(PolicyArn=existing_policy["Arn"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't delete policy {0}".format(existing_policy['PolicyName'])) + module.fail_json_aws(e, msg="Couldn't delete policy {0}".format(existing_policy["PolicyName"])) # This is the one case where we will return the old policy module.exit_json(changed=True, policy=camel_dict_to_snake_dict(existing_policy)) @@ -338,34 +346,34 @@ def main(): argument_spec = dict( policy_name=dict(required=True), - policy_description=dict(default=''), - policy=dict(type='json'), - make_default=dict(type='bool', default=True), - only_version=dict(type='bool', default=False), - state=dict(default='present', choices=['present', 'absent']), + policy_description=dict(default=""), + policy=dict(type="json"), + make_default=dict(type="bool", default=True), + only_version=dict(type="bool", default=False), + state=dict(default="present", choices=["present", "absent"]), ) module = AnsibleAWSModule( argument_spec=argument_spec, - required_if=[['state', 'present', ['policy']]], - supports_check_mode=True + required_if=[["state", "present", ["policy"]]], + supports_check_mode=True, ) - name = module.params.get('policy_name') - state = module.params.get('state') + name = module.params.get("policy_name") + state = module.params.get("state") try: - client = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) + client = module.client("iam", retry_decorator=AWSRetry.jittered_backoff()) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Failed to connect to AWS') + module.fail_json_aws(e, msg="Failed to connect to AWS") existing_policy = get_policy_by_name(name) - if state == 'present': + if state == "present": create_or_update_policy(existing_policy) else: delete_policy(existing_policy) -if __name__ == '__main__': +if __name__ == "__main__": main() From a07a40d0f358c7c3e96241352a6e5b28786365db Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 8 May 2023 19:21:22 +0200 Subject: [PATCH 25/31] Bulk migration to Python 3.6 f-strings (#1810) Bulk migration to Python 3.6 f-strings SUMMARY We've dropped support for Python <3.6, bulk migrate to fstrings and perform some general string cleanup A combination of black --preview flynt some manual cleanup ISSUE TYPE Feature Pull Request COMPONENT NAME plugins/ tests/ ADDITIONAL INFORMATION Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/de338210dc1b0bb2eecee1dc16e073163b2d1df7 --- plugins/modules/iam_managed_policy.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 0f6189ca454..cc7fd8450e5 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -184,7 +184,7 @@ def get_or_create_policy_version(policy, policy_document): "Document" ] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't get policy version {0}".format(v["VersionId"])) + module.fail_json_aws(e, msg=f"Couldn't get policy version {v['VersionId']}") if module.check_mode and compare_policies(document, json.loads(to_native(policy_document))): return v, True @@ -249,23 +249,23 @@ def detach_all_entities(policy, **kwargs): try: entities = client.list_entities_for_policy(PolicyArn=policy["Arn"], **kwargs) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach list entities for policy {0}".format(policy["PolicyName"])) + module.fail_json_aws(e, msg=f"Couldn't detach list entities for policy {policy['PolicyName']}") for g in entities["PolicyGroups"]: try: client.detach_group_policy(PolicyArn=policy["Arn"], GroupName=g["GroupName"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach group policy {0}".format(g["GroupName"])) + module.fail_json_aws(e, msg=f"Couldn't detach group policy {g['GroupName']}") for u in entities["PolicyUsers"]: try: client.detach_user_policy(PolicyArn=policy["Arn"], UserName=u["UserName"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach user policy {0}".format(u["UserName"])) + module.fail_json_aws(e, msg=f"Couldn't detach user policy {u['UserName']}") for r in entities["PolicyRoles"]: try: client.detach_role_policy(PolicyArn=policy["Arn"], RoleName=r["RoleName"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't detach role policy {0}".format(r["RoleName"])) + module.fail_json_aws(e, msg=f"Couldn't detach role policy {r['RoleName']}") if entities["IsTruncated"]: detach_all_entities(policy, marker=entities["Marker"]) @@ -289,7 +289,7 @@ def create_or_update_policy(existing_policy): try: rvalue = client.create_policy(PolicyName=name, Path="/", PolicyDocument=policy, Description=description) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't create policy {0}".format(name)) + module.fail_json_aws(e, msg=f"Couldn't create policy {name}") module.exit_json(changed=True, policy=camel_dict_to_snake_dict(rvalue["Policy"])) else: @@ -327,12 +327,12 @@ def delete_policy(existing_policy): try: client.delete_policy_version(PolicyArn=existing_policy["Arn"], VersionId=v["VersionId"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't delete policy version {0}".format(v["VersionId"])) + module.fail_json_aws(e, msg=f"Couldn't delete policy version {v['VersionId']}") # Delete policy try: client.delete_policy(PolicyArn=existing_policy["Arn"]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't delete policy {0}".format(existing_policy["PolicyName"])) + module.fail_json_aws(e, msg=f"Couldn't delete policy {existing_policy['PolicyName']}") # This is the one case where we will return the old policy module.exit_json(changed=True, policy=camel_dict_to_snake_dict(existing_policy)) From 99f6d3f4e4c4ea42a5d1bf09b6c6a383fe87f62c Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 31 Aug 2023 17:58:59 +0200 Subject: [PATCH 26/31] Mass update of docs and tests (credentials/session tokens) (#1921) Mass update of docs and tests (credentials/session tokens) SUMMARY We had a cleanup of credentials/session parameters which included a batch of deprecations and renames. Ensure that all of our tests and docs are using the 'canonical' names ISSUE TYPE Docs Pull Request COMPONENT NAME plugins/modules/batch_compute_environment.py plugins/modules/cloudformation_exports_info.py plugins/modules/ec2_vpc_vpn.py plugins/modules/elasticache.py plugins/modules/elasticache_parameter_group.py plugins/modules/elasticache_snapshot.py plugins/modules/ses_rule_set.py plugins/modules/sts_assume_role.py plugins/modules/sts_session_token.py tests/integration ADDITIONAL INFORMATION See also ansible-collections/amazon.aws#1172 ansible-collections/amazon.aws#1714 Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/4a5b50e9b9c0d6ca1a1f802f3b03d4f503c16885 --- tests/integration/targets/iam_managed_policy/tasks/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/targets/iam_managed_policy/tasks/main.yml b/tests/integration/targets/iam_managed_policy/tasks/main.yml index f17b7cad096..9aba195eee7 100644 --- a/tests/integration/targets/iam_managed_policy/tasks/main.yml +++ b/tests/integration/targets/iam_managed_policy/tasks/main.yml @@ -2,9 +2,9 @@ - name: "Run integration tests for IAM managed policy" module_defaults: group/aws: - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" - security_token: "{{ security_token | default(omit) }}" + access_key: "{{ aws_access_key }}" + secret_key: "{{ aws_secret_key }}" + session_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" collections: - amazon.aws From c1949a87cbddd255317518e4402f8fbbefa46d76 Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Fri, 22 Sep 2023 11:55:37 -0700 Subject: [PATCH 27/31] Update runtime --- meta/runtime.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/meta/runtime.yml b/meta/runtime.yml index c627df5be2b..07469d07d38 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -70,6 +70,7 @@ action_groups: - execute_lambda - iam_instance_profile - iam_instance_profile_info + - iam_managed_policy - iam_policy - iam_policy_info - iam_user From aab27e94f2c7f7d98f1b05ce6e31d7a36817b06c Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Fri, 22 Sep 2023 12:05:59 -0700 Subject: [PATCH 28/31] promote iam_managed_policy --- .../fragments/migrate_iam_managed_policy.yml | 4 ++++ meta/runtime.yml | 14 ++++++++++++++ plugins/modules/iam_managed_policy.py | 10 +++++----- plugins/modules/rds_instance.py | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 changelogs/fragments/migrate_iam_managed_policy.yml diff --git a/changelogs/fragments/migrate_iam_managed_policy.yml b/changelogs/fragments/migrate_iam_managed_policy.yml new file mode 100644 index 00000000000..dcfd2914f51 --- /dev/null +++ b/changelogs/fragments/migrate_iam_managed_policy.yml @@ -0,0 +1,4 @@ +major_changes: +- iam_managed_policy - The module has been migrated from the ``community.aws`` collection. + Playbooks using the Fully Qualified Collection Name for this module should be updated + to use ``amazon.aws.iam_managed_policy``. diff --git a/meta/runtime.yml b/meta/runtime.yml index 07469d07d38..bd0c5d44e08 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -9,10 +9,17 @@ action_groups: - aws_s3 - backup_plan - backup_plan_info +<<<<<<< HEAD - backup_tag - backup_tag_info - backup_selection - backup_selection_info +======= + - backup_selection + - backup_selection_info + - backup_tag + - backup_tag_info +>>>>>>> e8209bfb (promote iam_managed_policy) - backup_vault - backup_vault_info - cloudformation @@ -20,12 +27,19 @@ action_groups: - cloudtrail - cloudtrail_info - cloudwatch_metric_alarm +<<<<<<< HEAD +======= + - cloudwatch_metric_alarm_info +>>>>>>> e8209bfb (promote iam_managed_policy) - cloudwatchevent_rule - cloudwatchevent_rule - cloudwatchlogs_log_group - cloudwatchlogs_log_group_info - cloudwatchlogs_log_group_metric_filter +<<<<<<< HEAD - cloudwatch_metric_alarm_info +======= +>>>>>>> e8209bfb (promote iam_managed_policy) - ec2_ami - ec2_ami_info - ec2_eip diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index cc7fd8450e5..3f8fd2368bd 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -54,7 +54,7 @@ EXAMPLES = r""" # Create a policy - name: Create IAM Managed Policy - community.aws.iam_managed_policy: + amazon.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy_description: "A Helpful managed policy" policy: "{{ lookup('template', 'managed_policy.json.j2') }}" @@ -62,14 +62,14 @@ # Update a policy with a new default version - name: Update an IAM Managed Policy with new default version - community.aws.iam_managed_policy: + amazon.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: "{{ lookup('file', 'managed_policy_update.json') }}" state: present # Update a policy with a new non default version - name: Update an IAM Managed Policy with a non default version - community.aws.iam_managed_policy: + amazon.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: Version: "2012-10-17" @@ -82,7 +82,7 @@ # Update a policy and make it the only version and the default version - name: Update an IAM Managed Policy with default version as the only version - community.aws.iam_managed_policy: + amazon.aws.iam_managed_policy: policy_name: "ManagedPolicy" policy: | { @@ -98,7 +98,7 @@ # Remove a policy - name: Remove an existing IAM Managed Policy - community.aws.iam_managed_policy: + amazon.aws.iam_managed_policy: policy_name: "ManagedPolicy" state: absent """ diff --git a/plugins/modules/rds_instance.py b/plugins/modules/rds_instance.py index b5d7d8bd414..613f5f2d51d 100644 --- a/plugins/modules/rds_instance.py +++ b/plugins/modules/rds_instance.py @@ -500,7 +500,7 @@ # Add IAM role to db instance - name: Create IAM policy - community.aws.iam_managed_policy: + amazon.aws.iam_managed_policy: policy_name: "my-policy" policy: "{{ lookup('file','files/policy.json') }}" state: present From aca0aaf44e9b0365dadc56287501b06093f8e6ca Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Fri, 22 Sep 2023 12:14:54 -0700 Subject: [PATCH 29/31] minor fixes --- changelogs/fragments/migrate_iam_managed_policy.yml | 2 +- plugins/modules/iam_managed_policy.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changelogs/fragments/migrate_iam_managed_policy.yml b/changelogs/fragments/migrate_iam_managed_policy.yml index dcfd2914f51..fcbccde3900 100644 --- a/changelogs/fragments/migrate_iam_managed_policy.yml +++ b/changelogs/fragments/migrate_iam_managed_policy.yml @@ -1,4 +1,4 @@ major_changes: - iam_managed_policy - The module has been migrated from the ``community.aws`` collection. Playbooks using the Fully Qualified Collection Name for this module should be updated - to use ``amazon.aws.iam_managed_policy``. + to use ``amazon.aws.iam_managed_policy`` (https://github.com/ansible-collections/amazon.aws/pull/1762). diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index 3f8fd2368bd..c3aa5ef597b 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -8,6 +8,7 @@ --- module: iam_managed_policy version_added: 1.0.0 +version_added_collection: community.aws short_description: Manage User Managed IAM policies description: - Allows creating and removing managed IAM policies From b6b21713d755079e668f6044e63b674a9d206ade Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Fri, 22 Sep 2023 12:43:31 -0700 Subject: [PATCH 30/31] update AnsibleAWSModule import --- meta/runtime.yml | 14 -------------- plugins/modules/iam_managed_policy.py | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/meta/runtime.yml b/meta/runtime.yml index bd0c5d44e08..07469d07d38 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -9,17 +9,10 @@ action_groups: - aws_s3 - backup_plan - backup_plan_info -<<<<<<< HEAD - backup_tag - backup_tag_info - backup_selection - backup_selection_info -======= - - backup_selection - - backup_selection_info - - backup_tag - - backup_tag_info ->>>>>>> e8209bfb (promote iam_managed_policy) - backup_vault - backup_vault_info - cloudformation @@ -27,19 +20,12 @@ action_groups: - cloudtrail - cloudtrail_info - cloudwatch_metric_alarm -<<<<<<< HEAD -======= - - cloudwatch_metric_alarm_info ->>>>>>> e8209bfb (promote iam_managed_policy) - cloudwatchevent_rule - cloudwatchevent_rule - cloudwatchlogs_log_group - cloudwatchlogs_log_group_info - cloudwatchlogs_log_group_metric_filter -<<<<<<< HEAD - cloudwatch_metric_alarm_info -======= ->>>>>>> e8209bfb (promote iam_managed_policy) - ec2_ami - ec2_ami_info - ec2_eip diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index c3aa5ef597b..f1d8e55f9ce 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -137,7 +137,7 @@ from ansible_collections.amazon.aws.plugins.module_utils.policy import compare_policies from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry -from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule @AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) From ee50124df0030d885879a5389d3c401813ee9c7b Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Fri, 6 Oct 2023 14:21:01 +0200 Subject: [PATCH 31/31] Apply isort Signed-off-by: Alina Buzachis --- plugins/modules/iam_managed_policy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/modules/iam_managed_policy.py b/plugins/modules/iam_managed_policy.py index f1d8e55f9ce..2cc7d8da95f 100644 --- a/plugins/modules/iam_managed_policy.py +++ b/plugins/modules/iam_managed_policy.py @@ -134,11 +134,10 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.policy import compare_policies from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule - @AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) def list_policies_with_backoff():