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
This commit is contained in:
Mark Chappell 2019-11-21 00:59:02 +01:00 committed by Jill R
parent 426e37ea92
commit f1311d3e98
4 changed files with 501 additions and 477 deletions

View file

@ -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``."

View file

@ -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_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:`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 :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.

View file

@ -5,7 +5,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
@ -40,6 +39,8 @@ options:
description:
- The path to the properly json formatted policy file.
- 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
policy_json:
description:
@ -50,19 +51,20 @@ options:
state:
description:
- Whether to create or delete the IAM policy.
required: true
choices: [ "present", "absent"]
default: present
type: str
skip_duplicates:
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
the same rules. You can override this by specifying false which would allow for two policy objects with different names but same rules.
default: True
- 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
a new policy object with the same rules.
- 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
author:
- Jonathan I. Davila (@defionscode)
- "Jonathan I. Davila (@defionscode)"
- "Dennis Podkovyrin (@sbj-ss)"
extends_documentation_fragment:
- aws
- ec2
@ -115,238 +117,230 @@ EXAMPLES = '''
import json
try:
import boto
import boto.iam
import boto.ec2
HAS_BOTO = True
from botocore.exceptions import BotoCoreError, ClientError
except ImportError:
HAS_BOTO = False
pass
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ec2 import connect_to_aws, ec2_argument_spec, get_aws_connection_info, boto_exception
from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import compare_policies
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):
policy_match = False
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
# 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_user_policy(name, policy_name, pdoc)
elif state == 'absent':
try:
iam.delete_user_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)
updated_policies = [cp for cp in iam.get_all_user_policies(name).
list_user_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
class PolicyError(Exception):
pass
def role_action(module, iam, name, policy_name, skip, pdoc, state):
policy_match = False
changed = False
try:
current_policies = [cp for cp in iam.list_role_policies(name).
list_role_policies_result.
policy_names]
except boto.exception.BotoServerError as e:
if e.error_code == "NoSuchEntity":
# Role doesn't exist so it's safe to assume the policy doesn't either
module.exit_json(changed=False, msg="No such role, policy will be skipped.")
class Policy:
def __init__(self, client, name, policy_name, policy_document, policy_json, skip_duplicates, state, check_mode):
self.client = client
self.name = name
self.policy_name = policy_name
self.policy_document = policy_document
self.policy_json = policy_json
self.skip_duplicates = skip_duplicates
self.state = state
self.check_mode = check_mode
self.changed = False
@staticmethod
def _iam_type():
return ''
def _list(self, name):
return {}
def list(self):
return self._list(self.name).get('PolicyNames', [])
def _get(self, name, policy_name):
return '{}'
def get(self, policy_name):
return self._get(self.name, policy_name)['PolicyDocument']
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
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:
module.fail_json(msg=e.message)
pdoc = self.policy_json
return pdoc
try:
def create(self):
matching_policies = []
for pol in current_policies:
if urllib.parse.unquote(iam.get_role_policy(name, pol).
get_role_policy_result.policy_document) == pdoc:
policy_match = True
policy_doc = self.get_policy_text()
policy_match = False
for pol in self.list():
if not compare_policies(self.get(pol), policy_doc):
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
matching_policies.append(pol)
msg = ("The policy document you specified already exists "
"under the name %s." % 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_group_policy(name, policy_name, pdoc)
elif state == 'absent':
try:
iam.delete_group_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)
updated_policies = [cp for cp in iam.get_all_group_policies(name).
list_group_policies_result.
policy_names]
except boto.exception.BotoServerError as err:
error_msg = boto_exception(err)
module.fail_json(changed=changed, msg=error_msg)
if (self.policy_name not in matching_policies) and not (self.skip_duplicates and policy_match):
self.put(policy_doc)
return changed, name, updated_policies, msg
def run(self):
if self.state == 'present':
self.create()
elif self.state == 'absent':
self.delete()
return {
'changed': self.changed,
self._iam_type() + '_name': self.name,
'policies': self.list()
}
class UserPolicy(Policy):
@staticmethod
def _iam_type():
return 'user'
def _list(self, name):
return self.client.list_user_policies(UserName=name)
def _get(self, name, policy_name):
return self.client.get_user_policy(UserName=name, PolicyName=policy_name)
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():
argument_spec = ec2_argument_spec()
argument_spec.update(dict(
argument_spec = dict(
iam_type=dict(required=True, choices=['user', 'group', 'role']),
state=dict(default='present', choices=['present', 'absent']),
iam_name=dict(default=None, required=False),
policy_name=dict(required=True),
policy_document=dict(default=None, required=False),
policy_json=dict(type='json', default=None, required=False),
skip_duplicates=dict(type='bool', default=True, required=False)
))
module = AnsibleModule(
argument_spec=argument_spec,
skip_duplicates=dict(type='bool', default=None, required=False)
)
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()
state = module.params.get('state')
name = module.params.get('iam_name')
policy_name = module.params.get('policy_name')
skip = module.params.get('skip_duplicates')
skip_duplicates = module.params.get('skip_duplicates')
policy_document = module.params.get('policy_document')
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 (skip_duplicates is None):
module.deprecate('The skip_duplicates behaviour has caused confusion and'
' will be disabled by default in Ansible 2.14',
version='2.14')
skip_duplicates = True
if policy_document is not None:
try:
with open(policy_document, 'r') as json_data:
pdoc = json.dumps(json.load(json_data))
json_data.close()
except IOError as e:
if e.errno == 2:
module.fail_json(
msg='policy_document {0:!r} does not exist'.format(policy_document))
else:
raise
elif module.params.get('policy_json') is not None:
pdoc = module.params.get('policy_json')
# if its a string, assume it is already JSON
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
if module.params.get('policy_document'):
module.deprecate('The policy_document option has been deprecated and'
' will be removed in Ansible 2.14',
version='2.14')
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)
args = dict(
client=module.client('iam'),
name=module.params.get('iam_name'),
policy_name=module.params.get('policy_name'),
policy_document=module.params.get('policy_document'),
policy_json=module.params.get('policy_json'),
skip_duplicates=skip_duplicates,
state=module.params.get('state'),
check_mode=module.check_mode,
)
iam_type = module.params.get('iam_type')
try:
if region:
iam = connect_to_aws(boto.iam, region, **aws_connect_kwargs)
else:
iam = boto.iam.connection.IAMConnection(**aws_connect_kwargs)
except boto.exception.NoAuthHandlerFound as e:
if iam_type == 'user':
policy = UserPolicy(**args)
elif iam_type == 'role':
policy = RolePolicy(**args)
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))
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__':
main()

View file

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