From 62dfa2ad118b1f5720bb7b3406147a138e10cf04 Mon Sep 17 00:00:00 2001 From: Rob White Date: Thu, 22 Dec 2016 23:22:40 +1100 Subject: [PATCH] Add helper function so that IAM policies can be compared for equality and update s3_bucket to take advantage of helper function --- lib/ansible/module_utils/ec2.py | 47 +++++++++++++++++-- .../modules/cloud/amazon/GUIDELINES.md | 36 +++++++++++++- lib/ansible/modules/cloud/amazon/s3_bucket.py | 2 +- 3 files changed, 79 insertions(+), 6 deletions(-) mode change 100644 => 100755 lib/ansible/module_utils/ec2.py mode change 100644 => 100755 lib/ansible/modules/cloud/amazon/GUIDELINES.md mode change 100644 => 100755 lib/ansible/modules/cloud/amazon/s3_bucket.py diff --git a/lib/ansible/module_utils/ec2.py b/lib/ansible/module_utils/ec2.py old mode 100644 new mode 100755 index 8db34bfba5f..e8afab6751d --- a/lib/ansible/module_utils/ec2.py +++ b/lib/ansible/module_utils/ec2.py @@ -327,7 +327,6 @@ def camel_dict_to_snake_dict(camel_dict): return all_cap_re.sub(r'\1_\2', s1).lower() - def value_is_list(camel_list): checked_list = [] @@ -341,7 +340,6 @@ def camel_dict_to_snake_dict(camel_dict): return checked_list - snake_dict = {} for k, v in camel_dict.items(): if isinstance(v, dict): @@ -464,7 +462,6 @@ def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id else: return sg.name - def get_sg_id(sg, boto3): if boto3: @@ -472,7 +469,6 @@ def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id else: return sg.id - sec_group_id_list = [] if isinstance(sec_group_list, string_types): @@ -514,3 +510,46 @@ def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id return sec_group_id_list + +def sort_json_policy_dict(policy_dict): + + """ Sort any lists in an IAM JSON policy so that comparison of two policies with identical values but + different orders will return true + Args: + policy_dict (dict): Dict representing IAM JSON policy. + Basic Usage: + >>> my_iam_policy = {'Principle': {'AWS':["31","7","14","101"]} + >>> sort_json_policy_dict(my_iam_policy) + Returns: + Dict: Will return a copy of the policy as a Dict but any List will be sorted + { + 'Principle': { + 'AWS': [ '7', '14', '31', '101' ] + } + } + """ + + def value_is_list(my_list): + + checked_list = [] + for item in my_list: + if isinstance(item, dict): + checked_list.append(sort_json_policy_dict(item)) + elif isinstance(item, list): + checked_list.append(value_is_list(item)) + else: + checked_list.append(item) + + checked_list.sort() + return checked_list + + ordered_policy_dict = {} + for key, value in policy_dict.items(): + if isinstance(value, dict): + ordered_policy_dict[key] = sort_json_policy_dict(value) + elif isinstance(value, list): + ordered_policy_dict[key] = value_is_list(value) + else: + ordered_policy_dict[key] = value + + return ordered_policy_dict diff --git a/lib/ansible/modules/cloud/amazon/GUIDELINES.md b/lib/ansible/modules/cloud/amazon/GUIDELINES.md old mode 100644 new mode 100755 index b8ca836b79a..45a88635c04 --- a/lib/ansible/modules/cloud/amazon/GUIDELINES.md +++ b/lib/ansible/modules/cloud/amazon/GUIDELINES.md @@ -233,6 +233,34 @@ result = connection.aws_call() module.exit_json(changed=True, **camel_dict_to_snake_dict(result)) ``` +### Dealing with IAM JSON policy + +If your module accepts IAM JSON policies then set the type to 'json' in the module spec. For example" + +```python +argument_spec.update( + dict( + policy=dict(required=False, default=None, type='json'), + ) +) +``` + +Note that AWS is unlikely to return the policy in the same order that is was submitted. Therefore, a helper +function has been created to order policies before comparison. + +```python +# Get the policy from AWS +current_policy = aws_object.get_policy() + +# Compare the user submitted policy to the current policy but sort them first +if sort_json_policy_dict(user_policy) == sort_json_policy_dict(current_policy): + # Nothing to do + pass +else: + # Update the policy + aws_object.set_policy(user_policy) +``` + ### Helper functions Along with the connection functions in Ansible ec2.py module_utils, there are some other useful functions detailed below. @@ -261,4 +289,10 @@ Opposite of above. Converts an Ansible dict to a boto3 tag list of dicts. Pass this function a list of security group names or combination of security group names and IDs and this function will return a list of IDs. You should also pass the VPC ID if known because security group names are not necessarily unique -across VPCs. \ No newline at end of file +across VPCs. + +### sort_json_policy_dict + +Pass any JSON policy dict to this function in order to sort any list contained therein. This is useful +because AWS rarely return lists in the same order that they were submitted so without this function, comparison +of identical policies returns false. \ No newline at end of file diff --git a/lib/ansible/modules/cloud/amazon/s3_bucket.py b/lib/ansible/modules/cloud/amazon/s3_bucket.py old mode 100644 new mode 100755 index 37b3ffcc7ee..05c6a8527a0 --- a/lib/ansible/modules/cloud/amazon/s3_bucket.py +++ b/lib/ansible/modules/cloud/amazon/s3_bucket.py @@ -213,7 +213,7 @@ def _create_or_update_bucket(connection, module, location): # only show changed if there was already a policy changed = bool(current_policy) - elif current_policy != policy: + elif sort_json_policy_dict(current_policy) != sort_json_policy_dict(policy): try: bucket.set_policy(json.dumps(policy)) changed = True