Skip to content

Commit

Permalink
iam_policy - add support for diff mode (ansible-collections#1375)
Browse files Browse the repository at this point in the history
iam_policy - add support for diff mode

SUMMARY
fixes: ansible-collections#560

Adds support for diff mode
renames policies to policy_names so that in future we can return the policies (outside of the diff) too.
Attempts to handle AccessDenied more cleanly

ISSUE TYPE

Feature Pull Request

COMPONENT NAME
iam_policy
ADDITIONAL INFORMATION

Reviewed-by: Mark Chappell <None>
Reviewed-by: Markus Bergholz <git@osuv.de>
  • Loading branch information
tremble authored Jul 29, 2022
1 parent 6a85335 commit 1ac84c3
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 18 deletions.
5 changes: 5 additions & 0 deletions changelogs/fragments/560-iam_policy-diff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
minor_changes:
- iam_policy - added support for ``--diff`` mode (https://github.com/ansible-collections/community.aws/issues/560).
- iam_policy - attempts to continue when read requests are denied by IAM policy (https://github.com/ansible-collections/community.aws/pull/1375).
deprecated_features:
- iam_policy - the ``policies`` return value has been renamed ``policy_names`` and will be removed in a release after 2024-08-01, both values are currently returned (https://github.com/ansible-collections/community.aws/pull/1375).
74 changes: 56 additions & 18 deletions plugins/modules/iam_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
version_added: 1.0.0
short_description: Manage inline IAM policies for users, groups, and roles
description:
- Allows uploading or removing inline IAM policies for IAM users, groups or roles.
- To administer managed policies please see M(community.aws.iam_user), M(community.aws.iam_role),
M(community.aws.iam_group) and M(community.aws.iam_managed_policy)
- Allows uploading or removing inline IAM policies for IAM users, groups or roles.
- To administer managed policies please see M(community.aws.iam_user), M(community.aws.iam_role),
M(community.aws.iam_group) and M(community.aws.iam_managed_policy)
options:
iam_type:
description:
Expand All @@ -35,7 +35,6 @@
policy_json:
description:
- A properly json formatted policy as string.
- See U(https://github.com/ansible/ansible/issues/7005#issuecomment-42894813) on how to use it properly.
type: json
state:
description:
Expand All @@ -54,9 +53,8 @@
- "Jonathan I. Davila (@defionscode)"
- "Dennis Podkovyrin (@sbj-ss)"
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
- amazon.aws.aws
- amazon.aws.ec2
'''

EXAMPLES = '''
Expand Down Expand Up @@ -94,7 +92,7 @@
'''
RETURN = '''
policies:
policy_names:
description: A list of names of the inline policies embedded in the specified IAM resource (user, group, or role).
returned: always
type: list
Expand All @@ -112,6 +110,7 @@
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 compare_policies
from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code


class PolicyError(Exception):
Expand All @@ -130,6 +129,9 @@ def __init__(self, client, name, policy_name, policy_json, skip_duplicates, stat
self.check_mode = check_mode
self.changed = False

self.original_policies = self.get_all_policies().copy()
self.updated_policies = {}

@staticmethod
def _iam_type():
return ''
Expand All @@ -138,33 +140,48 @@ def _list(self, name):
return {}

def list(self):
return self._list(self.name).get('PolicyNames', [])
try:
return self._list(self.name).get('PolicyNames', [])
except is_boto3_error_code('AccessDenied'):
return []

def _get(self, name, policy_name):
return '{}'

def get(self, policy_name):
return self._get(self.name, policy_name)['PolicyDocument']
try:
return self._get(self.name, policy_name)['PolicyDocument']
except is_boto3_error_code('AccessDenied'):
return {}

def _put(self, name, policy_name, policy_doc):
pass

def put(self, policy_doc):
if not self.check_mode:
self._put(self.name, self.policy_name, json.dumps(policy_doc, sort_keys=True))
self.changed = True

if self.check_mode:
return

self._put(self.name, self.policy_name, json.dumps(policy_doc, sort_keys=True))

def _delete(self, name, policy_name):
pass

def delete(self):
self.updated_policies = self.original_policies.copy()

if self.policy_name not in self.list():
self.changed = False
return

if not self.check_mode:
self._delete(self.name, self.policy_name)
self.changed = True
self.updated_policies.pop(self.policy_name, None)

if self.check_mode:
return

self._delete(self.name, self.policy_name)

def get_policy_text(self):
try:
Expand All @@ -181,17 +198,30 @@ def get_policy_from_json(self):
pdoc = self.policy_json
return pdoc

def get_all_policies(self):
policies = {}
for pol in self.list():
policies[pol] = self.get(pol)
return policies

def create(self):
matching_policies = []
policy_doc = self.get_policy_text()
policy_match = False
for pol in self.list():
if not compare_policies(self.get(pol), policy_doc):
if not compare_policies(self.original_policies[pol], policy_doc):
matching_policies.append(pol)
policy_match = True

if (self.policy_name not in matching_policies) and not (self.skip_duplicates and policy_match):
self.put(policy_doc)
self.updated_policies = self.original_policies.copy()

if self.policy_name in matching_policies:
return
if self.skip_duplicates and policy_match:
return

self.put(policy_doc)
self.updated_policies[self.policy_name] = policy_doc

def run(self):
if self.state == 'present':
Expand All @@ -201,7 +231,12 @@ def run(self):
return {
'changed': self.changed,
self._iam_type() + '_name': self.name,
'policies': self.list()
'policies': self.list(),
'policy_names': self.list(),
'diff': dict(
before=self.original_policies,
after=self.updated_policies,
),
}


Expand Down Expand Up @@ -300,6 +335,9 @@ def main():
elif iam_type == 'group':
policy = GroupPolicy(**args)

module.deprecate("The 'policies' return key is deprecated and will be replaced by 'policy_names'. Both values are returned for now.",
date='2024-08-01', collection_name='community.aws')

module.exit_json(**(policy.run()))
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e)
Expand Down

0 comments on commit 1ac84c3

Please sign in to comment.