Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iam_user password management support #822

Merged
merged 9 commits into from
Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/fragments/822-add-password-support-iam_user.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- iam_user - add password management support bringing parity with `iam` module (https://github.com/ansible-collections/community.aws/pull/822).
- iam_user - add boto3 waiter for iam user creation (https://github.com/ansible-collections/community.aws/pull/822).
93 changes: 90 additions & 3 deletions plugins/modules/iam_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,28 @@
version_added: 1.0.0
short_description: Manage AWS IAM users
description:
- Manage AWS IAM users.
- A module to manage AWS IAM users.
- The module does not manage groups that users belong to, groups memberships can be managed using `iam_group`.
author: Josh Souza (@joshsouza)
options:
name:
description:
- The name of the user to create.
required: true
type: str
password:
description:
- The password to apply to the user.
required: false
type: str
update_password:
default: always
choices: ['always', 'on_create']
description:
- When to update user passwords.
- I(update_password=always) will ensure the password is set to I(password).
- I(update_password=on_create) will only set the password for newly created users.
type: str
managed_policies:
description:
- A list of managed policy ARNs or friendly names to attach to the user.
Expand All @@ -36,7 +50,7 @@
type: str
purge_policies:
description:
- When I(purge_policies=true) any managed policies not listed in I(managed_policies) will be detatched.
- When I(purge_policies=true) any managed policies not listed in I(managed_policies) will be detached.
required: false
default: false
type: bool
Expand All @@ -53,6 +67,17 @@
default: true
type: bool
version_added: 2.1.0
wait:
description:
- When I(wait=True) the module will wait for up to I(wait_timeout) seconds
for IAM user creation before returning.
default: True
type: bool
wait_timeout:
description:
- How long (in seconds) to wait for creation / updates to complete.
default: 120
type: int
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
Expand Down Expand Up @@ -179,15 +204,60 @@ def convert_friendly_names_to_arns(connection, module, policy_names):
module.fail_json(msg="Couldn't find policy: " + str(e))


def wait_iam_exists(connection, module):
if module.check_mode:
return
if not module.params.get('wait'):
return

user_name = module.params.get('name')
wait_timeout = module.params.get('wait_timeout')

delay = min(wait_timeout, 5)
max_attempts = wait_timeout // delay

try:
waiter = connection.get_waiter('user_exists')
waiter.wait(
WaiterConfig={'Delay': delay, 'MaxAttempts': max_attempts},
UserName=user_name,
)
except botocore.exceptions.WaiterError as e:
module.fail_json_aws(e, msg='Timeout while waiting on IAM user creation')
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Failed while waiting on IAM user creation')


def create_or_update_login_profile(connection, module):

# Apply new password / update password for the user
user_params = dict()
user_params['UserName'] = module.params.get('name')
user_params['Password'] = module.params.get('password')

try:
connection.update_login_profile(**user_params)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
try:
connection.create_login_profile(**user_params)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to create / update user login profile")
marknet15 marked this conversation as resolved.
Show resolved Hide resolved

return True


def create_or_update_user(connection, module):

params = dict()
params['UserName'] = module.params.get('name')
managed_policies = module.params.get('managed_policies')
purge_policies = module.params.get('purge_policies')

if module.params.get('tags') is not None:
params["Tags"] = ansible_dict_to_boto3_tag_list(module.params.get('tags'))

changed = False

if managed_policies:
managed_policies = convert_friendly_names_to_arns(connection, module, managed_policies)

Expand All @@ -205,8 +275,21 @@ def create_or_update_user(connection, module):
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to create user")

# Wait for user to be fully available before continuing
if module.params.get('wait'):
wait_iam_exists(connection, module)

if module.params.get('password') is not None:
create_or_update_login_profile(connection, module)
else:
changed = update_user_tags(connection, module, params, user)
login_profile_result = None
update_result = update_user_tags(connection, module, params, user)

if module.params['update_password'] == "always" and module.params.get('password') is not None:
login_profile_result = create_or_update_login_profile(connection, module)

changed = bool(update_result) or bool(login_profile_result)

# Manage managed policies
current_attached_policies = get_attached_policy_list(connection, module, params['UserName'])
Expand Down Expand Up @@ -388,11 +471,15 @@ def main():

argument_spec = dict(
name=dict(required=True, type='str'),
password=dict(type='str', no_log=True),
update_password=dict(default='always', choices=['always', 'on_create'], no_log=False),
managed_policies=dict(default=[], type='list', aliases=['managed_policy'], elements='str'),
state=dict(choices=['present', 'absent'], required=True),
purge_policies=dict(default=False, type='bool', aliases=['purge_policy', 'purge_managed_policies']),
tags=dict(type='dict'),
purge_tags=dict(type='bool', default=True),
wait=dict(type='bool', default=True),
wait_timeout=dict(default=120, type='int'),
)

module = AnsibleAWSModule(
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/targets/iam_user/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
test_group: '{{ resource_prefix }}-group'
test_path: '/'
test_user: '{{ test_users[0] }}'
test_password: ATotallySecureUncrackablePassword1!
test_new_password: ATotallyNewSecureUncrackablePassword1!
test_users:
- '{{ resource_prefix }}-user-a'
- '{{ resource_prefix }}-user-b'
Loading