Fix iam_password_policy integration tests (#60930)

* iam_password_policy: (integration tests) Use module defaults for AWS connection details

* iam_password_policy: (integration tests) Ensure the policy is removed when tests fail

* iam_password_policy: (integration tests) Add regression test for #59102

* iam_password_policy: Only return changed when the policy changes.

* iam_password_policy: PasswordReusePrevention must be omitted to remove/set to 0

* #60930 add changelog

* Update hacking AWS security policy to allow testing of Password Policy Management
This commit is contained in:
Mark Chappell 2019-08-22 15:25:25 +02:00 committed by Will Thames
parent 35ed1fbe8d
commit 70777020c4
4 changed files with 129 additions and 82 deletions

View file

@ -0,0 +1,3 @@
bugfixes:
- iam_password_policy now only returns changed when the policy changes
- iam_password_policy no longer throws errors when you don't set pw_reuse_prevent

View file

@ -128,6 +128,16 @@
"iam:GetServerCertificate" "iam:GetServerCertificate"
], ],
"Resource": "*" "Resource": "*"
},
{
"Sid": "AllowAccessToManagePasswordPolicy",
"Effect": "Allow",
"Action": [
"iam:GetAccountPasswordPolicy",
"iam:DeleteAccountPasswordPolicy",
"iam:UpdateAccountPasswordPolicy"
],
"Resource": "*"
} }
] ]
} }

View file

@ -109,7 +109,6 @@ from ansible.module_utils.ec2 import camel_dict_to_snake_dict, boto3_tag_list_to
class IAMConnection(object): class IAMConnection(object):
def __init__(self, module): def __init__(self, module):
try: try:
self.connection = module.resource('iam') self.connection = module.resource('iam')
@ -117,6 +116,17 @@ class IAMConnection(object):
except Exception as e: except Exception as e:
module.fail_json(msg="Failed to connect to AWS: %s" % str(e)) module.fail_json(msg="Failed to connect to AWS: %s" % str(e))
def policy_to_dict(self, policy):
policy_attributes = [
'allow_users_to_change_password', 'expire_passwords', 'hard_expiry',
'max_password_age', 'minimum_password_length', 'password_reuse_prevention',
'require_lowercase_characters', 'require_numbers', 'require_symbols', 'require_uppercase_characters'
]
ret = {}
for attr in policy_attributes:
ret[attr] = getattr(policy, attr)
return ret
def update_password_policy(self, module, policy): def update_password_policy(self, module, policy):
min_pw_length = module.params.get('min_pw_length') min_pw_length = module.params.get('min_pw_length')
require_symbols = module.params.get('require_symbols') require_symbols = module.params.get('require_symbols')
@ -135,18 +145,27 @@ class IAMConnection(object):
RequireUppercaseCharacters=require_uppercase, RequireUppercaseCharacters=require_uppercase,
RequireLowercaseCharacters=require_lowercase, RequireLowercaseCharacters=require_lowercase,
AllowUsersToChangePassword=allow_pw_change, AllowUsersToChangePassword=allow_pw_change,
PasswordReusePrevention=pw_reuse_prevent,
HardExpiry=pw_expire HardExpiry=pw_expire
) )
if pw_reuse_prevent:
update_parameters.update(PasswordReusePrevention=pw_reuse_prevent)
if pw_max_age: if pw_max_age:
update_parameters.update(MaxPasswordAge=pw_max_age) update_parameters.update(MaxPasswordAge=pw_max_age)
try:
original_policy = self.policy_to_dict(policy)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
original_policy = {}
try: try:
results = policy.update(**update_parameters) results = policy.update(**update_parameters)
policy.reload() policy.reload()
updated_policy = self.policy_to_dict(policy)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
self.module.fail_json_aws(e, msg="Couldn't update IAM Password Policy") self.module.fail_json_aws(e, msg="Couldn't update IAM Password Policy")
return camel_dict_to_snake_dict(results)
changed = (original_policy != updated_policy)
return (changed, updated_policy, camel_dict_to_snake_dict(results))
def delete_password_policy(self, policy): def delete_password_policy(self, policy):
try: try:
@ -182,8 +201,8 @@ def main():
state = module.params.get('state') state = module.params.get('state')
if state == 'present': if state == 'present':
update_result = resource.update_password_policy(module, policy) (changed, new_policy, update_result) = resource.update_password_policy(module, policy)
module.exit_json(changed=True, task_status={'IAM': update_result}) module.exit_json(changed=changed, task_status={'IAM': update_result}, policy=new_policy)
if state == 'absent': if state == 'absent':
delete_result = resource.delete_password_policy(policy) delete_result = resource.delete_password_policy(policy)

View file

@ -1,15 +1,12 @@
- name: set connection information for all tasks - module_defaults:
set_fact: group/aws:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}" aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}" aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}" security_token: "{{ security_token | default(omit) }}"
region: "{{ aws_region }}" region: "{{ aws_region }}"
no_log: true block:
- name: set iam password policy
- name: set iam password policy
iam_password_policy: iam_password_policy:
<<: *aws_connection_info
state: present state: present
min_pw_length: 8 min_pw_length: 8
require_symbols: false require_symbols: false
@ -22,14 +19,13 @@
pw_expire: false pw_expire: false
register: result register: result
- name: assert that changes were made - name: assert that changes were made
assert: assert:
that: that:
- result.changed - result.changed
- name: verify iam password policy has been created - name: verify iam password policy has been created
iam_password_policy: iam_password_policy:
<<: *aws_connection_info
state: present state: present
min_pw_length: 8 min_pw_length: 8
require_symbols: false require_symbols: false
@ -42,14 +38,13 @@
pw_expire: false pw_expire: false
register: result register: result
- name: assert that no changes were made - name: assert that no changes were made
assert: assert:
that: that:
- not result.changed - not result.changed
- name: update iam password policy - name: update iam password policy with different settings
iam_password_policy: iam_password_policy:
<<: *aws_connection_info
state: present state: present
min_pw_length: 15 min_pw_length: 15
require_symbols: true require_symbols: true
@ -62,29 +57,49 @@
pw_expire: true pw_expire: true
register: result register: result
- name: assert that updates were made - name: assert that updates were made
assert: assert:
that: that:
- result.changed - result.changed
- name: remove iam password policy # Test for regression of #59102
- name: update iam password policy without expiry
iam_password_policy: iam_password_policy:
<<: *aws_connection_info state: present
state: absent min_pw_length: 15
require_symbols: true
require_numbers: true
require_uppercase: true
require_lowercase: true
allow_pw_change: true
register: result register: result
- name: assert password policy has been removed - name: assert that changes were made
assert: assert:
that: that:
- result.changed - result.changed
- name: verify password policy has been removed - name: remove iam password policy
iam_password_policy: iam_password_policy:
<<: *aws_connection_info
state: absent state: absent
register: result register: result
- name: assert no changes were made - name: assert password policy has been removed
assert:
that:
- result.changed
- name: verify password policy has been removed
iam_password_policy:
state: absent
register: result
- name: assert no changes were made
assert: assert:
that: that:
- not result.changed - not result.changed
always:
- name: remove iam password policy
iam_password_policy:
state: absent
register: result