From 2a5e1d400ddd77fe680dcafc0bcf22c20b4fc37b Mon Sep 17 00:00:00 2001 From: Richard Henning Date: Mon, 21 Nov 2016 03:17:27 -0500 Subject: [PATCH] provider/aws: Remove IAM user's MFA devices with `force_destroy` #5908 (#10262) When `force_destroy` was specifed on an `aws_iam_user` resource, only IAM access keys and the login profile were destroyed. If a multi-factor auth device had been activated for that user, deletion would fail as follows: ``` * aws_iam_user.testuser1: Error deleting IAM User testuser1: DeleteConflict: Cannot delete entity, must delete MFA device first. status code: 409, request id: aa41b1b7-ac4d-11e6-bb3f-3b4c7a310c65 ``` This commit iterates over any of the user's MFA devices and deactivates them before deleting the user. It follows a pattern similar to that used to remove users' IAM access keys before deletion. ``` $ make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSUser_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/11/20 17:09:00 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSUser_ -timeout 120m === RUN TestAccAWSUser_importBasic --- PASS: TestAccAWSUser_importBasic (5.70s) === RUN TestAccAWSUser_basic --- PASS: TestAccAWSUser_basic (11.12s) PASS ok github.com/rhenning/terraform/builtin/providers/aws 20.840s ``` --- .../providers/aws/resource_aws_iam_user.go | 28 +++++++++++++++++-- .../providers/aws/r/iam_user.html.markdown | 4 +-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_iam_user.go b/builtin/providers/aws/resource_aws_iam_user.go index 82b502f906..e2ebdd7361 100644 --- a/builtin/providers/aws/resource_aws_iam_user.go +++ b/builtin/providers/aws/resource_aws_iam_user.go @@ -54,7 +54,7 @@ func resourceAwsIamUser() *schema.Resource { Type: schema.TypeBool, Optional: true, Default: false, - Description: "Delete user even if it has non-Terraform-managed IAM access keys and login profile", + Description: "Delete user even if it has non-Terraform-managed IAM access keys, login profile or MFA devices", }, }, } @@ -167,7 +167,7 @@ func resourceAwsIamUserDelete(d *schema.ResourceData, meta interface{}) error { } } - // All access keys and login profile for the user must be removed + // All access keys, MFA devices and login profile for the user must be removed if d.Get("force_destroy").(bool) { var accessKeys []string listAccessKeys := &iam.ListAccessKeysInput{ @@ -193,6 +193,30 @@ func resourceAwsIamUserDelete(d *schema.ResourceData, meta interface{}) error { } } + var MFADevices []string + listMFADevices := &iam.ListMFADevicesInput{ + UserName: aws.String(d.Id()), + } + pageOfMFADevices := func(page *iam.ListMFADevicesOutput, lastPage bool) (shouldContinue bool) { + for _, m := range page.MFADevices { + MFADevices = append(MFADevices, *m.SerialNumber) + } + return !lastPage + } + err = iamconn.ListMFADevicesPages(listMFADevices, pageOfMFADevices) + if err != nil { + return fmt.Errorf("Error removing MFA devices of user %s: %s", d.Id(), err) + } + for _, m := range MFADevices { + _, err := iamconn.DeactivateMFADevice(&iam.DeactivateMFADeviceInput{ + UserName: aws.String(d.Id()), + SerialNumber: aws.String(m), + }) + if err != nil { + return fmt.Errorf("Error deactivating MFA device %s: %s", m, err) + } + } + _, err = iamconn.DeleteLoginProfile(&iam.DeleteLoginProfileInput{ UserName: aws.String(d.Id()), }) diff --git a/website/source/docs/providers/aws/r/iam_user.html.markdown b/website/source/docs/providers/aws/r/iam_user.html.markdown index 69d54e62e2..77de19b530 100644 --- a/website/source/docs/providers/aws/r/iam_user.html.markdown +++ b/website/source/docs/providers/aws/r/iam_user.html.markdown @@ -48,8 +48,8 @@ The following arguments are supported: * `name` - (Required) The user's name. The name must consist of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: `=,.@-_.`. User names are not distinguished by case. For example, you cannot create users named both "TESTUSER" and "testuser". * `path` - (Optional, default "/") Path in which to create the user. -* `force_destroy` - (Optional, default false) When destroying this user, destroy - even if it has non-Terraform-managed IAM access keys and login profile. Without `force_destroy` +* `force_destroy` - (Optional, default false) When destroying this user, destroy even if it + has non-Terraform-managed IAM access keys, login profile or MFA devices. Without `force_destroy` a user with non-Terraform-managed access keys and login profile will fail to be destroyed. ## Attributes Reference