Rewrite iam_policy using boto3 (#63924)

* reworked iam_policy

* Deprecate policy_document option

* deprecate defaulting skip_duplicates to true

* No longer explicitly catch ParamValidationError.

ParamValidationErrror is already caught by ClientError

* Work with complex policy objects rather than json documents

comparisons can better cope with the special cases (eg True vs "True" )

* Enable check_mode tests and fix related 'changed' bug

* changelog

* doc cleanup based on review
pull/65134/head
Mark Chappell 5 years ago committed by Jill R
parent 426e37ea92
commit f1311d3e98

@ -0,0 +1,5 @@
minor_changes:
- "iam_policy - The iam_policy module has been migrated from boto to boto3."
deprecated_features:
- "iam_policy - The ``policy_document`` will be removed in Ansible 2.14. To maintain the existing behavior use the ``policy_json`` option and read the file with the ``lookup`` plugin."
- "iam_policy - The default value of ``skip_duplicates`` will change in Ansible 2.14 from ``true`` to ``false``."

@ -68,11 +68,15 @@ The following functionality will be removed in Ansible 2.14. Please update updat
* :ref:`ec2_key <ec2_key_module>`: the ``wait`` option will be removed. It has had no effect since Ansible 2.5. * :ref:`ec2_key <ec2_key_module>`: the ``wait`` option will be removed. It has had no effect since Ansible 2.5.
* :ref:`ec2_key <ec2_key_module>`: the ``wait_timeout`` option will be removed. It has had no effect since Ansible 2.5. * :ref:`ec2_key <ec2_key_module>`: the ``wait_timeout`` option will be removed. It has had no effect since Ansible 2.5.
* :ref:`ec2_lc <ec2_lc_module>`: the ``associate_public_ip_address`` option will be removed. It has always been ignored by the module. * :ref:`ec2_lc <ec2_lc_module>`: the ``associate_public_ip_address`` option will be removed. It has always been ignored by the module.
* :ref:`iam_policy <iam_policy_module>`: the ``policy_document`` option will be removed. To maintain the existing behavior use the ``policy_json`` option and read the file with the ``lookup`` plugin.
The following functionality will change in Ansible 2.14. Please update update your playbooks accordingly. The following functionality will change in Ansible 2.14. Please update update your playbooks accordingly.
* The :ref:`docker_container <docker_container_module>` module has a new option, ``container_default_behavior``, whose default value will change from ``compatibility`` to ``no_defaults``. Set to an explicit value to avoid deprecation warnings. * The :ref:`docker_container <docker_container_module>` module has a new option, ``container_default_behavior``, whose default value will change from ``compatibility`` to ``no_defaults``. Set to an explicit value to avoid deprecation warnings.
* :ref:`iam_policy <iam_policy_module>`: the default value for the ``skip_duplicates`` option will change from ``true`` to ``false``. To maintain the existing behavior explicitly set it to ``true``.
The following modules will be removed in Ansible 2.14. Please update your playbooks accordingly. The following modules will be removed in Ansible 2.14. Please update your playbooks accordingly.

@ -5,7 +5,6 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'community'} 'supported_by': 'community'}
@ -40,6 +39,8 @@ options:
description: description:
- The path to the properly json formatted policy file. - The path to the properly json formatted policy file.
- Mutually exclusive with I(policy_json). - Mutually exclusive with I(policy_json).
- This option has been deprecated and will be removed in 2.14. The existing behavior can be
reproduced by using the I(policy_json) option and reading the file using the lookup plugin.
type: str type: str
policy_json: policy_json:
description: description:
@ -50,19 +51,20 @@ options:
state: state:
description: description:
- Whether to create or delete the IAM policy. - Whether to create or delete the IAM policy.
required: true
choices: [ "present", "absent"] choices: [ "present", "absent"]
default: present default: present
type: str type: str
skip_duplicates: skip_duplicates:
description: description:
- By default the module looks for any policies that match the document you pass in, if there is a match it will not make a new policy object with - When I(skip_duplicates=true) the module looks for any policies that match the document you pass in. If there is a match it will not make
the same rules. You can override this by specifying false which would allow for two policy objects with different names but same rules. a new policy object with the same rules.
default: True - The current default is C(true). However, this behavior can be confusing and as such the default will change to C(false) in 2.14. To maintain
the existing behavior explicitly set I(skip_duplicates=true).
type: bool type: bool
author: author:
- Jonathan I. Davila (@defionscode) - "Jonathan I. Davila (@defionscode)"
- "Dennis Podkovyrin (@sbj-ss)"
extends_documentation_fragment: extends_documentation_fragment:
- aws - aws
- ec2 - ec2
@ -115,238 +117,230 @@ EXAMPLES = '''
import json import json
try: try:
import boto from botocore.exceptions import BotoCoreError, ClientError
import boto.iam
import boto.ec2
HAS_BOTO = True
except ImportError: except ImportError:
HAS_BOTO = False pass
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import connect_to_aws, ec2_argument_spec, get_aws_connection_info, boto_exception from ansible.module_utils.ec2 import compare_policies
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.module_utils.six.moves import urllib
def user_action(module, iam, name, policy_name, skip, pdoc, state): class PolicyError(Exception):
policy_match = False pass
changed = False
try:
current_policies = [cp for cp in iam.get_all_user_policies(name).
list_user_policies_result.
policy_names]
matching_policies = []
for pol in current_policies:
'''
urllib is needed here because boto returns url encoded strings instead
'''
if urllib.parse.unquote(iam.get_user_policy(name, pol).
get_user_policy_result.policy_document) == pdoc:
policy_match = True
matching_policies.append(pol)
if state == 'present':
# If policy document does not already exist (either it's changed class Policy:
# or the policy is not present) or if we're not skipping dupes then
# make the put call. Note that the put call does a create or update. def __init__(self, client, name, policy_name, policy_document, policy_json, skip_duplicates, state, check_mode):
if not policy_match or (not skip and policy_name not in matching_policies): self.client = client
changed = True self.name = name
iam.put_user_policy(name, policy_name, pdoc) self.policy_name = policy_name
elif state == 'absent': self.policy_document = policy_document
try: self.policy_json = policy_json
iam.delete_user_policy(name, policy_name) self.skip_duplicates = skip_duplicates
changed = True self.state = state
except boto.exception.BotoServerError as err: self.check_mode = check_mode
error_msg = boto_exception(err) self.changed = False
if 'cannot be found.' in error_msg:
changed = False @staticmethod
module.exit_json(changed=changed, msg="%s policy is already absent" % policy_name) def _iam_type():
return ''
updated_policies = [cp for cp in iam.get_all_user_policies(name).
list_user_policies_result. def _list(self, name):
policy_names] return {}
except boto.exception.BotoServerError as err:
error_msg = boto_exception(err) def list(self):
module.fail_json(changed=changed, msg=error_msg) return self._list(self.name).get('PolicyNames', [])
return changed, name, updated_policies def _get(self, name, policy_name):
return '{}'
def role_action(module, iam, name, policy_name, skip, pdoc, state): def get(self, policy_name):
policy_match = False return self._get(self.name, policy_name)['PolicyDocument']
changed = False
try: def _put(self, name, policy_name, policy_doc):
current_policies = [cp for cp in iam.list_role_policies(name). pass
list_role_policies_result.
policy_names] def put(self, policy_doc):
except boto.exception.BotoServerError as e: if not self.check_mode:
if e.error_code == "NoSuchEntity": self._put(self.name, self.policy_name, json.dumps(policy_doc, sort_keys=True))
# Role doesn't exist so it's safe to assume the policy doesn't either self.changed = True
module.exit_json(changed=False, msg="No such role, policy will be skipped.")
def _delete(self, name, policy_name):
pass
def delete(self):
if self.policy_name not in self.list():
self.changed = False
return
self.changed = True
if not self.check_mode:
self._delete(self.name, self.policy_name)
def get_policy_text(self):
try:
if self.policy_document is not None:
return self.get_policy_from_document()
if self.policy_json is not None:
return self.get_policy_from_json()
except json.JSONDecodeError as e:
raise PolicyError('Failed to decode the policy as valid JSON: %s' % str(e))
return None
def get_policy_from_document(self):
try:
with open(self.policy_document, 'r') as json_data:
pdoc = json.load(json_data)
json_data.close()
except IOError as e:
if e.errno == 2:
raise PolicyError('policy_document {0:!r} does not exist'.format(self.policy_document))
raise
return pdoc
def get_policy_from_json(self):
if isinstance(self.policy_json, string_types):
pdoc = json.loads(self.policy_json)
else: else:
module.fail_json(msg=e.message) pdoc = self.policy_json
return pdoc
try: def create(self):
matching_policies = [] matching_policies = []
for pol in current_policies: policy_doc = self.get_policy_text()
if urllib.parse.unquote(iam.get_role_policy(name, pol). policy_match = False
get_role_policy_result.policy_document) == pdoc: for pol in self.list():
policy_match = True if not compare_policies(self.get(pol), policy_doc):
matching_policies.append(pol) matching_policies.append(pol)
if state == 'present':
# If policy document does not already exist (either it's changed
# or the policy is not present) or if we're not skipping dupes then
# make the put call. Note that the put call does a create or update.
if not policy_match or (not skip and policy_name not in matching_policies):
changed = True
iam.put_role_policy(name, policy_name, pdoc)
elif state == 'absent':
try:
iam.delete_role_policy(name, policy_name)
changed = True
except boto.exception.BotoServerError as err:
error_msg = boto_exception(err)
if 'cannot be found.' in error_msg:
changed = False
module.exit_json(changed=changed,
msg="%s policy is already absent" % policy_name)
else:
module.fail_json(msg=err.message)
updated_policies = [cp for cp in iam.list_role_policies(name).
list_role_policies_result.
policy_names]
except boto.exception.BotoServerError as err:
error_msg = boto_exception(err)
module.fail_json(changed=changed, msg=error_msg)
return changed, name, updated_policies
def group_action(module, iam, name, policy_name, skip, pdoc, state):
policy_match = False
changed = False
msg = ''
try:
current_policies = [cp for cp in iam.get_all_group_policies(name).
list_group_policies_result.
policy_names]
matching_policies = []
for pol in current_policies:
if urllib.parse.unquote(iam.get_group_policy(name, pol).
get_group_policy_result.policy_document) == pdoc:
policy_match = True policy_match = True
matching_policies.append(pol)
msg = ("The policy document you specified already exists " if (self.policy_name not in matching_policies) and not (self.skip_duplicates and policy_match):
"under the name %s." % pol) self.put(policy_doc)
if state == 'present':
# If policy document does not already exist (either it's changed def run(self):
# or the policy is not present) or if we're not skipping dupes then if self.state == 'present':
# make the put call. Note that the put call does a create or update. self.create()
if not policy_match or (not skip and policy_name not in matching_policies): elif self.state == 'absent':
changed = True self.delete()
iam.put_group_policy(name, policy_name, pdoc) return {
elif state == 'absent': 'changed': self.changed,
try: self._iam_type() + '_name': self.name,
iam.delete_group_policy(name, policy_name) 'policies': self.list()
changed = True }
except boto.exception.BotoServerError as err:
error_msg = boto_exception(err)
if 'cannot be found.' in error_msg: class UserPolicy(Policy):
changed = False
module.exit_json(changed=changed, @staticmethod
msg="%s policy is already absent" % policy_name) def _iam_type():
return 'user'
updated_policies = [cp for cp in iam.get_all_group_policies(name).
list_group_policies_result. def _list(self, name):
policy_names] return self.client.list_user_policies(UserName=name)
except boto.exception.BotoServerError as err:
error_msg = boto_exception(err) def _get(self, name, policy_name):
module.fail_json(changed=changed, msg=error_msg) return self.client.get_user_policy(UserName=name, PolicyName=policy_name)
return changed, name, updated_policies, msg def _put(self, name, policy_name, policy_doc):
return self.client.put_user_policy(UserName=name, PolicyName=policy_name, PolicyDocument=policy_doc)
def _delete(self, name, policy_name):
return self.client.delete_user_policy(UserName=name, PolicyName=policy_name)
class RolePolicy(Policy):
@staticmethod
def _iam_type():
return 'role'
def _list(self, name):
return self.client.list_role_policies(RoleName=name)
def _get(self, name, policy_name):
return self.client.get_role_policy(RoleName=name, PolicyName=policy_name)
def _put(self, name, policy_name, policy_doc):
return self.client.put_role_policy(RoleName=name, PolicyName=policy_name, PolicyDocument=policy_doc)
def _delete(self, name, policy_name):
return self.client.delete_role_policy(RoleName=name, PolicyName=policy_name)
class GroupPolicy(Policy):
@staticmethod
def _iam_type():
return 'group'
def _list(self, name):
return self.client.list_group_policies(GroupName=name)
def _get(self, name, policy_name):
return self.client.get_group_policy(GroupName=name, PolicyName=policy_name)
def _put(self, name, policy_name, policy_doc):
return self.client.put_group_policy(GroupName=name, PolicyName=policy_name, PolicyDocument=policy_doc)
def _delete(self, name, policy_name):
return self.client.delete_group_policy(GroupName=name, PolicyName=policy_name)
def main(): def main():
argument_spec = ec2_argument_spec() argument_spec = dict(
argument_spec.update(dict(
iam_type=dict(required=True, choices=['user', 'group', 'role']), iam_type=dict(required=True, choices=['user', 'group', 'role']),
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
iam_name=dict(default=None, required=False), iam_name=dict(default=None, required=False),
policy_name=dict(required=True), policy_name=dict(required=True),
policy_document=dict(default=None, required=False), policy_document=dict(default=None, required=False),
policy_json=dict(type='json', default=None, required=False), policy_json=dict(type='json', default=None, required=False),
skip_duplicates=dict(type='bool', default=True, required=False) skip_duplicates=dict(type='bool', default=None, required=False)
))
module = AnsibleModule(
argument_spec=argument_spec,
) )
mutually_exclusive = [['policy_document', 'policy_json']]
if not HAS_BOTO:
module.fail_json(msg='boto required for this module') module = AnsibleAWSModule(argument_spec=argument_spec, mutually_exclusive=mutually_exclusive, supports_check_mode=True)
iam_type = module.params.get('iam_type').lower() skip_duplicates = module.params.get('skip_duplicates')
state = module.params.get('state')
name = module.params.get('iam_name') if (skip_duplicates is None):
policy_name = module.params.get('policy_name') module.deprecate('The skip_duplicates behaviour has caused confusion and'
skip = module.params.get('skip_duplicates') ' will be disabled by default in Ansible 2.14',
version='2.14')
policy_document = module.params.get('policy_document') skip_duplicates = True
if policy_document is not None and module.params.get('policy_json') is not None:
module.fail_json(msg='Only one of "policy_document" or "policy_json" may be set') if module.params.get('policy_document'):
module.deprecate('The policy_document option has been deprecated and'
if policy_document is not None: ' will be removed in Ansible 2.14',
try: version='2.14')
with open(policy_document, 'r') as json_data:
pdoc = json.dumps(json.load(json_data)) args = dict(
json_data.close() client=module.client('iam'),
except IOError as e: name=module.params.get('iam_name'),
if e.errno == 2: policy_name=module.params.get('policy_name'),
module.fail_json( policy_document=module.params.get('policy_document'),
msg='policy_document {0:!r} does not exist'.format(policy_document)) policy_json=module.params.get('policy_json'),
else: skip_duplicates=skip_duplicates,
raise state=module.params.get('state'),
elif module.params.get('policy_json') is not None: check_mode=module.check_mode,
pdoc = module.params.get('policy_json') )
# if its a string, assume it is already JSON iam_type = module.params.get('iam_type')
if not isinstance(pdoc, string_types):
try:
pdoc = json.dumps(pdoc)
except Exception as e:
module.fail_json(msg='Failed to convert the policy into valid JSON: %s' % str(e))
else:
pdoc = None
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)
try: try:
if region: if iam_type == 'user':
iam = connect_to_aws(boto.iam, region, **aws_connect_kwargs) policy = UserPolicy(**args)
else: elif iam_type == 'role':
iam = boto.iam.connection.IAMConnection(**aws_connect_kwargs) policy = RolePolicy(**args)
except boto.exception.NoAuthHandlerFound as e: elif iam_type == 'group':
policy = GroupPolicy(**args)
module.exit_json(**(policy.run()))
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e)
except PolicyError as e:
module.fail_json(msg=str(e)) module.fail_json(msg=str(e))
changed = False
if iam_type == 'user':
changed, user_name, current_policies = user_action(module, iam, name,
policy_name, skip, pdoc,
state)
module.exit_json(changed=changed, user_name=name, policies=current_policies)
elif iam_type == 'role':
changed, role_name, current_policies = role_action(module, iam, name,
policy_name, skip, pdoc,
state)
module.exit_json(changed=changed, role_name=name, policies=current_policies)
elif iam_type == 'group':
changed, group_name, current_policies, msg = group_action(module, iam, name,
policy_name, skip, pdoc,
state)
module.exit_json(changed=changed, group_name=name, policies=current_policies, msg=msg)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -30,21 +30,21 @@
- iam_policy_info is succeeded - iam_policy_info is succeeded
# ============================================================ # ============================================================
#- name: 'Create policy using document for {{ iam_type }} (check mode)' - name: 'Create policy using document for {{ iam_type }} (check mode)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_a }}' policy_name: '{{ iam_policy_name_a }}'
# policy_document: '{{ tmpdir.path }}/no_access.json' policy_document: '{{ tmpdir.path }}/no_access.json'
# skip_duplicates: yes skip_duplicates: yes
# register: result register: result
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result is changed - result is changed
- name: 'Create policy using document for {{ iam_type }}' - name: 'Create policy using document for {{ iam_type }}'
iam_policy: iam_policy:
@ -103,29 +103,29 @@
- '"Id" not in iam_policy_info.policies[0].policy_document' - '"Id" not in iam_policy_info.policies[0].policy_document'
# ============================================================ # ============================================================
#- name: 'Create policy using document for {{ iam_type }} (check mode) (skip_duplicates)' - name: 'Create policy using document for {{ iam_type }} (check mode) (skip_duplicates)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_b }}' policy_name: '{{ iam_policy_name_b }}'
# policy_document: '{{ tmpdir.path }}/no_access.json' policy_document: '{{ tmpdir.path }}/no_access.json'
# skip_duplicates: yes skip_duplicates: yes
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_b }}' policy_name: '{{ iam_policy_name_b }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result is not changed - result is not changed
# - iam_policy_info.all_policy_names | length == 1 - iam_policy_info.all_policy_names | length == 1
# - '"policies" not in iam_policy_info' - '"policies" not in iam_policy_info'
# - iam_policy_name_b not in iam_policy_info.all_policy_names - iam_policy_name_b not in iam_policy_info.all_policy_names
- name: 'Create policy using document for {{ iam_type }} (skip_duplicates)' - name: 'Create policy using document for {{ iam_type }} (skip_duplicates)'
iam_policy: iam_policy:
@ -154,30 +154,30 @@
- iam_policy_info.all_policy_names | length == 1 - iam_policy_info.all_policy_names | length == 1
- iam_policy_name_b not in iam_policy_info.all_policy_names - iam_policy_name_b not in iam_policy_info.all_policy_names
#- name: 'Create policy using document for {{ iam_type }} (check mode) (skip_duplicates = no)' - name: 'Create policy using document for {{ iam_type }} (check mode) (skip_duplicates = no)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_b }}' policy_name: '{{ iam_policy_name_b }}'
# policy_document: '{{ tmpdir.path }}/no_access.json' policy_document: '{{ tmpdir.path }}/no_access.json'
# skip_duplicates: no skip_duplicates: no
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_b }}' policy_name: '{{ iam_policy_name_b }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result.changed == True - result.changed == True
# - '"policies" not in iam_policy_info' - '"policies" not in iam_policy_info'
# - iam_policy_info.all_policy_names | length == 1 - iam_policy_info.all_policy_names | length == 1
# - iam_policy_name_a in iam_policy_info.all_policy_names - iam_policy_name_a in iam_policy_info.all_policy_names
# - iam_policy_name_b not in iam_policy_info.all_policy_names - iam_policy_name_b not in iam_policy_info.all_policy_names
- name: 'Create policy using document for {{ iam_type }} (skip_duplicates = no)' - name: 'Create policy using document for {{ iam_type }} (skip_duplicates = no)'
iam_policy: iam_policy:
@ -238,31 +238,31 @@
- '"Id" not in iam_policy_info.policies[0].policy_document' - '"Id" not in iam_policy_info.policies[0].policy_document'
# ============================================================ # ============================================================
#- name: 'Create policy using json for {{ iam_type }} (check mode)' - name: 'Create policy using json for {{ iam_type }} (check mode)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_c }}' policy_name: '{{ iam_policy_name_c }}'
# policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_id.json") }}' policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_id.json") }}'
# skip_duplicates: yes skip_duplicates: yes
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_c }}' policy_name: '{{ iam_policy_name_c }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result is changed - result is changed
# - '"policies" not in iam_policy_info' - '"policies" not in iam_policy_info'
# - iam_policy_info.all_policy_names | length == 2 - iam_policy_info.all_policy_names | length == 2
# - iam_policy_name_c not in iam_policy_info.all_policy_names - iam_policy_name_c not in iam_policy_info.all_policy_names
# - iam_policy_name_a in iam_policy_info.all_policy_names - iam_policy_name_a in iam_policy_info.all_policy_names
# - iam_policy_name_b in iam_policy_info.all_policy_names - iam_policy_name_b in iam_policy_info.all_policy_names
- name: 'Create policy using json for {{ iam_type }}' - name: 'Create policy using json for {{ iam_type }}'
iam_policy: iam_policy:
@ -324,32 +324,32 @@
- iam_policy_info.policies[0].policy_document.Id == 'MyId' - iam_policy_info.policies[0].policy_document.Id == 'MyId'
# ============================================================ # ============================================================
#- name: 'Create policy using json for {{ iam_type }} (check mode) (skip_duplicates)' - name: 'Create policy using json for {{ iam_type }} (check mode) (skip_duplicates)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_id.json") }}' policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_id.json") }}'
# skip_duplicates: yes skip_duplicates: yes
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result is not changed - result is not changed
# - iam_policy_name_a in iam_policy_info.all_policy_names - iam_policy_name_a in iam_policy_info.all_policy_names
# - iam_policy_name_b in iam_policy_info.all_policy_names - iam_policy_name_b in iam_policy_info.all_policy_names
# - iam_policy_name_c in iam_policy_info.all_policy_names - iam_policy_name_c in iam_policy_info.all_policy_names
# - iam_policy_name_d not in iam_policy_info.all_policy_names - iam_policy_name_d not in iam_policy_info.all_policy_names
# - iam_policy_info.all_policy_names | length == 3 - iam_policy_info.all_policy_names | length == 3
# - '"policies" not in iam_policy_info' - '"policies" not in iam_policy_info'
- name: 'Create policy using json for {{ iam_type }} (skip_duplicates)' - name: 'Create policy using json for {{ iam_type }} (skip_duplicates)'
iam_policy: iam_policy:
@ -380,26 +380,26 @@
- iam_policy_info.all_policy_names | length == 3 - iam_policy_info.all_policy_names | length == 3
- '"policies" not in iam_policy_info' - '"policies" not in iam_policy_info'
#- name: 'Create policy using json for {{ iam_type }} (check mode) (skip_duplicates = no)' - name: 'Create policy using json for {{ iam_type }} (check mode) (skip_duplicates = no)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_id.json") }}' policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_id.json") }}'
# skip_duplicates: no skip_duplicates: no
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result.changed == True - result.changed == True
- name: 'Create policy using json for {{ iam_type }} (skip_duplicates = no)' - name: 'Create policy using json for {{ iam_type }} (skip_duplicates = no)'
iam_policy: iam_policy:
@ -490,28 +490,28 @@
- iam_policy_name_d in (iam_policy_info.policies | json_query('[?policy_document.Id == `MyId`].policy_name') | list) - iam_policy_name_d in (iam_policy_info.policies | json_query('[?policy_document.Id == `MyId`].policy_name') | list)
# ============================================================ # ============================================================
#- name: 'Update policy using document for {{ iam_type }} (check mode) (skip_duplicates)' - name: 'Update policy using document for {{ iam_type }} (check mode) (skip_duplicates)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_a }}' policy_name: '{{ iam_policy_name_a }}'
# policy_document: '{{ tmpdir.path }}/no_access_with_id.json' policy_document: '{{ tmpdir.path }}/no_access_with_id.json'
# skip_duplicates: yes skip_duplicates: yes
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_a }}' policy_name: '{{ iam_policy_name_a }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result is not changed - result is not changed
# - iam_policy_info.policies[0].policy_name == iam_policy_name_a - iam_policy_info.policies[0].policy_name == iam_policy_name_a
# - '"Id" not in iam_policy_info.policies[0].policy_document' - '"Id" not in iam_policy_info.policies[0].policy_document'
- name: 'Update policy using document for {{ iam_type }} (skip_duplicates)' - name: 'Update policy using document for {{ iam_type }} (skip_duplicates)'
iam_policy: iam_policy:
@ -539,29 +539,29 @@
- iam_policy_info.policies[0].policy_name == iam_policy_name_a - iam_policy_info.policies[0].policy_name == iam_policy_name_a
- '"Id" not in iam_policy_info.policies[0].policy_document' - '"Id" not in iam_policy_info.policies[0].policy_document'
#- name: 'Update policy using document for {{ iam_type }} (check mode) (skip_duplicates = no)' - name: 'Update policy using document for {{ iam_type }} (check mode) (skip_duplicates = no)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_a }}' policy_name: '{{ iam_policy_name_a }}'
# policy_document: '{{ tmpdir.path }}/no_access_with_id.json' policy_document: '{{ tmpdir.path }}/no_access_with_id.json'
# skip_duplicates: no skip_duplicates: no
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_a }}' policy_name: '{{ iam_policy_name_a }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be updated for {{ iam_type }}' - name: 'Assert policy would be updated for {{ iam_type }}'
# assert: assert:
# that: that:
# - result.changed == True - result.changed == True
# - iam_policy_info.all_policy_names | length == 4 - iam_policy_info.all_policy_names | length == 4
# - iam_policy_info.policies[0].policy_name == iam_policy_name_a - iam_policy_info.policies[0].policy_name == iam_policy_name_a
# - '"Id" not in iam_policy_info.policies[0].policy_document' - '"Id" not in iam_policy_info.policies[0].policy_document'
- name: 'Update policy using document for {{ iam_type }} (skip_duplicates = no)' - name: 'Update policy using document for {{ iam_type }} (skip_duplicates = no)'
iam_policy: iam_policy:
@ -638,28 +638,28 @@
# ============================================================ # ============================================================
# Update C with no_access.json # Update C with no_access.json
# Delete C # Delete C
#
#- name: 'Update policy using json for {{ iam_type }} (check mode) (skip_duplicates)' - name: 'Update policy using json for {{ iam_type }} (check mode) (skip_duplicates)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_c }}' policy_name: '{{ iam_policy_name_c }}'
# policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access.json") }}' policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access.json") }}'
# skip_duplicates: yes skip_duplicates: yes
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_c }}' policy_name: '{{ iam_policy_name_c }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be added for {{ iam_type }}' - name: 'Assert policy would be added for {{ iam_type }}'
# assert: assert:
# that: that:
# - result is not changed - result is not changed
# - iam_policy_info.policies[0].policy_document.Id == 'MyId' - iam_policy_info.policies[0].policy_document.Id == 'MyId'
- name: 'Update policy using json for {{ iam_type }} (skip_duplicates)' - name: 'Update policy using json for {{ iam_type }} (skip_duplicates)'
iam_policy: iam_policy:
@ -685,27 +685,27 @@
- result[iam_object_key] == iam_name - result[iam_object_key] == iam_name
- iam_policy_info.policies[0].policy_document.Id == 'MyId' - iam_policy_info.policies[0].policy_document.Id == 'MyId'
#- name: 'Update policy using json for {{ iam_type }} (check mode) (skip_duplicates = no)' - name: 'Update policy using json for {{ iam_type }} (check mode) (skip_duplicates = no)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_c }}' policy_name: '{{ iam_policy_name_c }}'
# policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access.json") }}' policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access.json") }}'
# skip_duplicates: no skip_duplicates: no
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_c }}' policy_name: '{{ iam_policy_name_c }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be updated for {{ iam_type }}' - name: 'Assert policy would be updated for {{ iam_type }}'
# assert: assert:
# that: that:
# - result.changed == True - result.changed == True
# - iam_policy_info.policies[0].policy_document.Id == 'MyId' - iam_policy_info.policies[0].policy_document.Id == 'MyId'
- name: 'Update policy using json for {{ iam_type }} (skip_duplicates = no)' - name: 'Update policy using json for {{ iam_type }} (skip_duplicates = no)'
iam_policy: iam_policy:
@ -780,26 +780,26 @@
- iam_policy_name_c not in iam_policy_info.all_policy_names - iam_policy_name_c not in iam_policy_info.all_policy_names
# ============================================================ # ============================================================
#- name: 'Update policy using document for {{ iam_type }} (check mode)' - name: 'Update policy using document for {{ iam_type }} (check mode)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_b }}' policy_name: '{{ iam_policy_name_b }}'
# policy_document: '{{ tmpdir.path }}/no_access_with_second_id.json' policy_document: '{{ tmpdir.path }}/no_access_with_second_id.json'
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_b }}' policy_name: '{{ iam_policy_name_b }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be updated for {{ iam_type }}' - name: 'Assert policy would be updated for {{ iam_type }}'
# assert: assert:
# that: that:
# - result.changed == True - result.changed == True
# - '"Id" not in iam_policy_info.policies[0].policy_document' - '"Id" not in iam_policy_info.policies[0].policy_document'
- name: 'Update policy using document for {{ iam_type }}' - name: 'Update policy using document for {{ iam_type }}'
iam_policy: iam_policy:
@ -872,26 +872,26 @@
- iam_policy_name_b not in iam_policy_info.all_policy_names - iam_policy_name_b not in iam_policy_info.all_policy_names
# ============================================================ # ============================================================
#- name: 'Update policy using json for {{ iam_type }} (check mode)' - name: 'Update policy using json for {{ iam_type }} (check mode)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: present state: present
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_second_id.json") }}' policy_json: '{{ lookup("file", "{{ tmpdir.path }}/no_access_with_second_id.json") }}'
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert policy would be updated for {{ iam_type }}' - name: 'Assert policy would be updated for {{ iam_type }}'
# assert: assert:
# that: that:
# - result.changed == True - result.changed == True
# - iam_policy_info.policies[0].policy_document.Id == 'MyId' - iam_policy_info.policies[0].policy_document.Id == 'MyId'
- name: 'Update policy using json for {{ iam_type }}' - name: 'Update policy using json for {{ iam_type }}'
iam_policy: iam_policy:
@ -941,30 +941,30 @@
- iam_policy_info.policies[0].policy_document.Id == 'MyOtherId' - iam_policy_info.policies[0].policy_document.Id == 'MyOtherId'
# ============================================================ # ============================================================
#- name: 'Delete policy D (check_mode)' - name: 'Delete policy D (check_mode)'
# check_mode: yes check_mode: yes
# iam_policy: iam_policy:
# state: absent state: absent
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# register: result register: result
#- iam_policy_info: - iam_policy_info:
# iam_type: '{{ iam_type }}' iam_type: '{{ iam_type }}'
# iam_name: '{{ iam_name }}' iam_name: '{{ iam_name }}'
# policy_name: '{{ iam_policy_name_d }}' policy_name: '{{ iam_policy_name_d }}'
# register: iam_policy_info register: iam_policy_info
#- name: 'Assert not deleted' - name: 'Assert not deleted'
# assert: assert:
# that: that:
# - result is changed - result is changed
# - result.policies | length == 1 - result.policies | length == 1
# - iam_policy_name_d in result.policies - iam_policy_name_d in result.policies
# - result[iam_object_key] == iam_name - result[iam_object_key] == iam_name
# - iam_policy_info.all_policy_names | length == 1 - iam_policy_info.all_policy_names | length == 1
# - iam_policy_name_d in iam_policy_info.all_policy_names - iam_policy_name_d in iam_policy_info.all_policy_names
# - iam_policy_info.policies[0].policy_document.Id == 'MyOtherId' - iam_policy_info.policies[0].policy_document.Id == 'MyOtherId'
- name: 'Delete policy D' - name: 'Delete policy D'
iam_policy: iam_policy:
@ -1009,6 +1009,27 @@
- '"policies" not in iam_policy_info' - '"policies" not in iam_policy_info'
- iam_policy_info.all_policy_names | length == 0 - iam_policy_info.all_policy_names | length == 0
- name: 'Delete policy D (check_mode) (test idempotency)'
check_mode: yes
iam_policy:
state: absent
iam_type: '{{ iam_type }}'
iam_name: '{{ iam_name }}'
policy_name: '{{ iam_policy_name_d }}'
register: result
- iam_policy_info:
iam_type: '{{ iam_type }}'
iam_name: '{{ iam_name }}'
policy_name: '{{ iam_policy_name_d }}'
register: iam_policy_info
- name: 'Assert deleted'
assert:
that:
- result is not changed
- '"policies" not in iam_policy_info'
- iam_policy_info.all_policy_names | length == 0
always: always:
# ============================================================ # ============================================================
- name: 'Delete policy A for {{ iam_type }}' - name: 'Delete policy A for {{ iam_type }}'

Loading…
Cancel
Save