diff --git a/lib/ansible/modules/cloud/amazon/iam_user.py b/lib/ansible/modules/cloud/amazon/iam_user.py index f5d319405e3..7ab90c6e0ac 100644 --- a/lib/ansible/modules/cloud/amazon/iam_user.py +++ b/lib/ansible/modules/cloud/amazon/iam_user.py @@ -99,14 +99,14 @@ user: ''' from ansible.module_utils._text import to_native -from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.aws.core import AnsibleAWSModule from ansible.module_utils.ec2 import camel_dict_to_snake_dict, ec2_argument_spec, get_aws_connection_info, boto3_conn from ansible.module_utils.ec2 import HAS_BOTO3 import traceback try: - from botocore.exceptions import ClientError, ParamValidationError + from botocore.exceptions import ClientError, ParamValidationError, BotoCoreError except ImportError: pass # caught by imported HAS_BOTO3 @@ -227,38 +227,72 @@ def create_or_update_user(connection, module): def destroy_user(connection, module): - params = dict() - params['UserName'] = module.params.get('name') + user_name = module.params.get('name') - if get_user(connection, module, params['UserName']): - # Check mode means we would remove this user - if module.check_mode: - module.exit_json(changed=True) + user = get_user(connection, module, user_name) + # User is not present + if not user: + module.exit_json(changed=False) - # Remove any attached policies otherwise deletion fails - try: - for policy in get_attached_policy_list(connection, module, params['UserName']): - connection.detach_user_policy(UserName=params['UserName'], PolicyArn=policy['PolicyArn']) - except ClientError as e: - module.fail_json(msg="Unable to detach policy {0} from user {1}: {2}".format( - policy['PolicyArn'], params['UserName'], to_native(e)), - exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) - except ParamValidationError as e: - module.fail_json(msg="Unable to detach policy {0} from user {1}: {2}".format( - policy['PolicyArn'], params['UserName'], to_native(e)), - exception=traceback.format_exc()) + # Check mode means we would remove this user + if module.check_mode: + module.exit_json(changed=True) - try: - connection.delete_user(**params) - except ClientError as e: - module.fail_json(msg="Unable to delete user {0}: {1}".format(params['UserName'], to_native(e)), - exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) - except ParamValidationError as e: - module.fail_json(msg="Unable to delete user {0}: {1}".format(params['UserName'], to_native(e)), - exception=traceback.format_exc()) + # Remove any attached policies otherwise deletion fails + try: + for policy in get_attached_policy_list(connection, module, user_name): + connection.detach_user_policy(UserName=user_name, PolicyArn=policy['PolicyArn']) + except (ClientError, BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to delete user {0}".format(user_name)) - else: - module.exit_json(changed=False) + try: + # Remove user's access keys + access_keys = connection.list_access_keys(UserName=user_name)["AccessKeyMetadata"] + for access_key in access_keys: + 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) + + # Remove user's ssh public keys + ssh_public_keys = connection.list_ssh_public_keys(UserName=user_name)["SSHPublicKeys"] + for ssh_public_key in ssh_public_keys: + connection.delete_ssh_public_key(UserName=user_name, SSHPublicKeyId=ssh_public_key["SSHPublicKeyId"]) + + # Remove user's service specific credentials + service_credentials = connection.list_service_specific_credentials(UserName=user_name)["ServiceSpecificCredentials"] + for service_specific_credential in service_credentials: + connection.delete_service_specific_credential( + UserName=user_name, + ServiceSpecificCredentialId=service_specific_credential["ServiceSpecificCredentialId"] + ) + + # Remove user's signing certificates + signing_certificates = connection.list_signing_certificates(UserName=user_name)["Certificates"] + for signing_certificate in signing_certificates: + connection.delete_signing_certificate( + UserName=user_name, + CertificateId=signing_certificate["CertificateId"] + ) + + # Remove user's MFA devices + mfa_devices = connection.list_mfa_devices(UserName=user_name)["MFADevices"] + for mfa_device in mfa_devices: + connection.deactivate_mfa_device(UserName=user_name, SerialNumber=mfa_device["SerialNumber"]) + + # Remove user's inline policies + inline_policies = connection.list_user_policies(UserName=user_name)["PolicyNames"] + for policy_name in inline_policies: + connection.delete_user_policy(UserName=user_name, PolicyName=policy_name) + + # Remove user's group membership + user_groups = connection.list_groups_for_user(UserName=user_name)["Groups"] + for group in user_groups: + connection.remove_user_from_group(UserName=user_name, GroupName=group["GroupName"]) + + connection.delete_user(UserName=user_name) + except (ClientError, BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to delete user {0}".format(user_name)) module.exit_json(changed=True) @@ -286,8 +320,18 @@ def get_attached_policy_list(connection, module, name): if e.response['Error']['Code'] == 'NoSuchEntity': return None else: - module.fail_json(msg="Unable to get policies for user {0}: {1}".format(name, to_native(e)), - **camel_dict_to_snake_dict(e.response)) + module.fail_json_aws(e, msg="Unable to get policies for user {0}".format(name)) + + +def delete_user_login_profile(connection, module, user_name): + + try: + return connection.delete_login_profile(UserName=user_name) + except ClientError as e: + if e.response["Error"]["Code"] == "NoSuchEntity": + return None + else: + module.fail_json_aws(e, msg="Unable to delete login profile for user {0}".format(user_name)) def main(): @@ -302,7 +346,7 @@ def main(): ) ) - module = AnsibleModule( + module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True )