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

[PR #1059/a11e66a1 backport][stable-3] iam_user - stabilize for migration to amazon.aws #1120

Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
minor_changes:
- iam_user - add `user` value to return data structure to deprecate old `iam_user` (https://github.com/ansible-collections/community.aws/pull/1059).
bugfixes:
- iam_user - don't delete user login profile on check mode (https://github.com/ansible-collections/community.aws/pull/1059).
- iam_user_info - gracefully handle when no users are found (https://github.com/ansible-collections/community.aws/pull/1059).
64 changes: 49 additions & 15 deletions plugins/modules/iam_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,20 +161,20 @@
user_id:
description: the stable and unique string identifying the user
type: str
sample: AGPAIDBWE12NSFINE55TM
sample: "AGPAIDBWE12NSFINE55TM"
user_name:
description: the friendly name that identifies the user
type: str
sample: testuser1
sample: "testuser1"
path:
description: the path to the user
type: str
sample: /
sample: "/"
tags:
description: user tags
type: dict
returned: always
sample: '{"Env": "Prod"}'
sample: {"Env": "Prod"}
'''

try:
Expand Down Expand Up @@ -228,10 +228,6 @@ def convert_friendly_names_to_arns(connection, module, policy_names):


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')
Expand Down Expand Up @@ -263,6 +259,7 @@ def create_or_update_login_profile(connection, module):
try:
retval = connection.update_login_profile(**user_params)
except is_boto3_error_code('NoSuchEntity'):
# Login profile does not yet exist - create it
try:
retval = connection.create_login_profile(**user_params)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
Expand All @@ -274,14 +271,26 @@ def create_or_update_login_profile(connection, module):


def delete_login_profile(connection, module):

'''
Deletes a users login profile.
Parameters:
connection: IAM client
module: AWSModule
Returns:
(bool): True if login profile deleted, False if no login profile found to delete
'''
user_params = dict()
user_params['UserName'] = module.params.get('name')

try:
connection.delete_login_profile(**user_params)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to delete user login profile")
# User does not have login profile - nothing to delete
if not user_has_login_profile(connection, module, user_params['UserName']):
return False

if not module.check_mode:
try:
connection.delete_login_profile(**user_params)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Unable to delete user login profile")

return True

Expand Down Expand Up @@ -331,6 +340,9 @@ def create_or_update_user(connection, module):
update_result = update_user_tags(connection, module, params, user)

if module.params['update_password'] == "always" and module.params.get('password') is not None:
# Can't compare passwords, so just return changed on check mode runs
if module.check_mode:
module.exit_json(changed=True)
login_profile_result, login_profile_data = create_or_update_login_profile(connection, module)

if login_profile_data.get('LoginProfile', {}).get('PasswordResetRequired', False):
Expand Down Expand Up @@ -382,7 +394,7 @@ def create_or_update_user(connection, module):
# `LoginProfile` is only returned on `create_login_profile` method
user['user']['password_reset_required'] = login_profile_data.get('LoginProfile', {}).get('PasswordResetRequired', False)

module.exit_json(changed=changed, iam_user=user)
module.exit_json(changed=changed, iam_user=user, user=user['user'])


def destroy_user(connection, module):
Expand Down Expand Up @@ -412,7 +424,7 @@ def destroy_user(connection, module):
connection.delete_access_key(UserName=user_name, AccessKeyId=access_key["AccessKeyId"])

# Remove user's login profile (console password)
delete_user_login_profile(connection, module, user_name)
delete_login_profile(connection, module)

# Remove user's ssh public keys
ssh_public_keys = connection.list_ssh_public_keys(UserName=user_name)["SSHPublicKeys"]
Expand Down Expand Up @@ -495,6 +507,25 @@ def delete_user_login_profile(connection, module, user_name):
module.fail_json_aws(e, msg="Unable to delete login profile for user {0}".format(user_name))


def user_has_login_profile(connection, module, name):
'''
Returns whether or not given user has a login profile.
Parameters:
connection: IAM client
module: AWSModule
name (str): Username of user
Returns:
(bool): True if user had login profile, False if not
'''
try:
connection.get_login_profile(UserName=name)
except is_boto3_error_code('NoSuchEntity'):
return False
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Unable to get login profile for user {0}".format(name))
return True


def update_user_tags(connection, module, params, user):
user_name = params['UserName']
existing_tags = user['user']['tags']
Expand Down Expand Up @@ -543,6 +574,9 @@ def main():
mutually_exclusive=[['password', 'remove_password']],
)

module.deprecate("The 'iam_user' return key is deprecated and will be replaced by 'user'. Both values are returned for now.",
date='2024-05-01', collection_name='community.aws')

connection = module.client('iam')

state = module.params.get("state")
Expand Down
13 changes: 10 additions & 3 deletions plugins/modules/iam_user_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict

Expand Down Expand Up @@ -142,14 +143,18 @@ def list_iam_users(connection, module):
params['UserName'] = name
try:
iam_users.append(connection.get_user(**params)['User'])
except (ClientError, BotoCoreError) as e:
except is_boto3_error_code('NoSuchEntity'):
pass
except (ClientError, BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Couldn't get IAM user info for user %s" % name)

if group:
params['GroupName'] = group
try:
iam_users = list_iam_users_with_backoff(connection, 'get_group', **params)['Users']
except (ClientError, BotoCoreError) as e:
except is_boto3_error_code('NoSuchEntity'):
pass
except (ClientError, BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Couldn't get IAM user info for group %s" % group)
if name:
iam_users = [user for user in iam_users if user['UserName'] == name]
Expand All @@ -158,7 +163,9 @@ def list_iam_users(connection, module):
params['PathPrefix'] = path
try:
iam_users = list_iam_users_with_backoff(connection, 'list_users', **params)['Users']
except (ClientError, BotoCoreError) as e:
except is_boto3_error_code('NoSuchEntity'):
pass
except (ClientError, BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Couldn't get IAM user info for path %s" % path)
if name:
iam_users = [user for user in iam_users if user['UserName'] == name]
Expand Down
Loading