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
```
This commit is contained in:
Richard Henning 2016-11-21 03:17:27 -05:00 committed by Paul Stack
parent e7d59ab245
commit 2a5e1d400d
2 changed files with 28 additions and 4 deletions

View File

@ -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()),
})

View File

@ -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