diff --git a/internal/service/iam/user.go b/internal/service/iam/user.go index 8bdb58d2374f..f1efd677ed48 100644 --- a/internal/service/iam/user.go +++ b/internal/service/iam/user.go @@ -109,7 +109,7 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("failed creating IAM User (%s): %s", name, err) + return fmt.Errorf("failed creating IAM User (%s): %w", name, err) } d.SetId(aws.StringValue(createResp.User.UserName)) @@ -226,7 +226,7 @@ func resourceUserUpdate(d *schema.ResourceData, meta interface{}) error { d.SetId("") return nil } - return fmt.Errorf("Error updating IAM User %s: %s", d.Id(), err) + return fmt.Errorf("Error updating IAM User %s: %w", d.Id(), err) } d.SetId(nn.(string)) @@ -241,7 +241,7 @@ func resourceUserUpdate(d *schema.ResourceData, meta interface{}) error { } _, err := conn.PutUserPermissionsBoundary(input) if err != nil { - return fmt.Errorf("error updating IAM User permissions boundary: %s", err) + return fmt.Errorf("error updating IAM User permissions boundary: %w", err) } } else { input := &iam.DeleteUserPermissionsBoundaryInput{ @@ -249,7 +249,7 @@ func resourceUserUpdate(d *schema.ResourceData, meta interface{}) error { } _, err := conn.DeleteUserPermissionsBoundary(input) if err != nil { - return fmt.Errorf("error deleting IAM User permissions boundary: %s", err) + return fmt.Errorf("error deleting IAM User permissions boundary: %w", err) } } } @@ -284,27 +284,31 @@ func resourceUserDelete(d *schema.ResourceData, meta interface{}) error { // All access keys, MFA devices and login profile for the user must be removed if d.Get("force_destroy").(bool) { if err := DeleteUserAccessKeys(conn, d.Id()); err != nil { - return fmt.Errorf("error removing IAM User (%s) access keys: %s", d.Id(), err) + return fmt.Errorf("error removing IAM User (%s) access keys: %w", d.Id(), err) } if err := DeleteUserSSHKeys(conn, d.Id()); err != nil { - return fmt.Errorf("error removing IAM User (%s) SSH keys: %s", d.Id(), err) + return fmt.Errorf("error removing IAM User (%s) SSH keys: %w", d.Id(), err) } if err := DeleteUserVirtualMFADevices(conn, d.Id()); err != nil { - return fmt.Errorf("error removing IAM User (%s) Virtual MFA devices: %s", d.Id(), err) + return fmt.Errorf("error removing IAM User (%s) Virtual MFA devices: %w", d.Id(), err) } if err := DeactivateUserMFADevices(conn, d.Id()); err != nil { - return fmt.Errorf("error removing IAM User (%s) MFA devices: %s", d.Id(), err) + return fmt.Errorf("error removing IAM User (%s) MFA devices: %w", d.Id(), err) } if err := DeleteUserLoginProfile(conn, d.Id()); err != nil { - return fmt.Errorf("error removing IAM User (%s) login profile: %s", d.Id(), err) + return fmt.Errorf("error removing IAM User (%s) login profile: %w", d.Id(), err) } if err := deleteUserSigningCertificates(conn, d.Id()); err != nil { - return fmt.Errorf("error removing IAM User (%s) signing certificate: %s", d.Id(), err) + return fmt.Errorf("error removing IAM User (%s) signing certificate: %w", d.Id(), err) + } + + if err := DeleteServiceSpecificCredentials(conn, d.Id()); err != nil { + return fmt.Errorf("error removing IAM User (%s) Service Specific Credentials: %w", d.Id(), err) } } @@ -320,7 +324,7 @@ func resourceUserDelete(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("Error deleting IAM User %s: %s", d.Id(), err) + return fmt.Errorf("Error deleting IAM User %s: %w", d.Id(), err) } return nil @@ -367,7 +371,7 @@ func DeleteUserSSHKeys(svc *iam.IAM, username string) error { } err = svc.ListSSHPublicKeysPages(listSSHPublicKeys, pageOfListSSHPublicKeys) if err != nil { - return fmt.Errorf("Error removing public SSH keys of user %s: %s", username, err) + return fmt.Errorf("Error removing public SSH keys of user %s: %w", username, err) } for _, k := range publicKeys { _, err := svc.DeleteSSHPublicKey(&iam.DeleteSSHPublicKeyInput{ @@ -375,7 +379,7 @@ func DeleteUserSSHKeys(svc *iam.IAM, username string) error { SSHPublicKeyId: aws.String(k), }) if err != nil { - return fmt.Errorf("Error deleting public SSH key %s: %s", k, err) + return fmt.Errorf("Error deleting public SSH key %s: %w", k, err) } } @@ -400,7 +404,7 @@ func DeleteUserVirtualMFADevices(svc *iam.IAM, username string) error { } err = svc.ListVirtualMFADevicesPages(listVirtualMFADevices, pageOfVirtualMFADevices) if err != nil { - return fmt.Errorf("Error removing Virtual MFA devices of user %s: %s", username, err) + return fmt.Errorf("Error removing Virtual MFA devices of user %s: %w", username, err) } for _, m := range VirtualMFADevices { _, err := svc.DeactivateMFADevice(&iam.DeactivateMFADeviceInput{ @@ -408,13 +412,13 @@ func DeleteUserVirtualMFADevices(svc *iam.IAM, username string) error { SerialNumber: aws.String(m), }) if err != nil { - return fmt.Errorf("Error deactivating Virtual MFA device %s: %s", m, err) + return fmt.Errorf("Error deactivating Virtual MFA device %s: %w", m, err) } _, err = svc.DeleteVirtualMFADevice(&iam.DeleteVirtualMFADeviceInput{ SerialNumber: aws.String(m), }) if err != nil { - return fmt.Errorf("Error deleting Virtual MFA device %s: %s", m, err) + return fmt.Errorf("Error deleting Virtual MFA device %s: %w", m, err) } } @@ -436,7 +440,7 @@ func DeactivateUserMFADevices(svc *iam.IAM, username string) error { } err = svc.ListMFADevicesPages(listMFADevices, pageOfMFADevices) if err != nil { - return fmt.Errorf("Error removing MFA devices of user %s: %s", username, err) + return fmt.Errorf("Error removing MFA devices of user %s: %w", username, err) } for _, m := range MFADevices { _, err := svc.DeactivateMFADevice(&iam.DeactivateMFADeviceInput{ @@ -444,7 +448,7 @@ func DeactivateUserMFADevices(svc *iam.IAM, username string) error { SerialNumber: aws.String(m), }) if err != nil { - return fmt.Errorf("Error deactivating MFA device %s: %s", m, err) + return fmt.Errorf("Error deactivating MFA device %s: %w", m, err) } } @@ -474,7 +478,7 @@ func DeleteUserLoginProfile(svc *iam.IAM, username string) error { _, err = svc.DeleteLoginProfile(input) } if err != nil { - return fmt.Errorf("Error deleting Account Login Profile: %s", err) + return fmt.Errorf("Error deleting Account Login Profile: %w", err) } return nil @@ -494,7 +498,7 @@ func DeleteUserAccessKeys(svc *iam.IAM, username string) error { } err = svc.ListAccessKeysPages(listAccessKeys, pageOfAccessKeys) if err != nil { - return fmt.Errorf("Error removing access keys of user %s: %s", username, err) + return fmt.Errorf("Error removing access keys of user %s: %w", username, err) } for _, k := range accessKeys { _, err := svc.DeleteAccessKey(&iam.DeleteAccessKeyInput{ @@ -502,7 +506,7 @@ func DeleteUserAccessKeys(svc *iam.IAM, username string) error { AccessKeyId: aws.String(k), }) if err != nil { - return fmt.Errorf("Error deleting access key %s: %s", k, err) + return fmt.Errorf("Error deleting access key %s: %w", k, err) } } @@ -523,7 +527,7 @@ func deleteUserSigningCertificates(svc *iam.IAM, userName string) error { return !lastPage }) if err != nil { - return fmt.Errorf("Error removing signing certificates of user %s: %s", userName, err) + return fmt.Errorf("Error removing signing certificates of user %s: %w", userName, err) } for _, c := range certificateIDList { @@ -532,7 +536,29 @@ func deleteUserSigningCertificates(svc *iam.IAM, userName string) error { UserName: aws.String(userName), }) if err != nil { - return fmt.Errorf("Error deleting signing certificate %s: %s", c, err) + return fmt.Errorf("Error deleting signing certificate %s: %w", c, err) + } + } + + return nil +} + +func DeleteServiceSpecificCredentials(svc *iam.IAM, username string) error { + input := &iam.ListServiceSpecificCredentialsInput{ + UserName: aws.String(username), + } + + output, err := svc.ListServiceSpecificCredentials(input) + if err != nil { + return fmt.Errorf("Error listing Service Specific Credentials of user %s: %w", username, err) + } + for _, m := range output.ServiceSpecificCredentials { + _, err := svc.DeleteServiceSpecificCredential(&iam.DeleteServiceSpecificCredentialInput{ + UserName: aws.String(username), + ServiceSpecificCredentialId: m.ServiceSpecificCredentialId, + }) + if err != nil { + return fmt.Errorf("Error deleting Service Specific Credentials %s: %w", m, err) } } diff --git a/internal/service/iam/user_test.go b/internal/service/iam/user_test.go index 1edded11d062..7355f7c07640 100644 --- a/internal/service/iam/user_test.go +++ b/internal/service/iam/user_test.go @@ -201,6 +201,35 @@ func TestAccIAMUser_ForceDestroy_sshKey(t *testing.T) { }) } +func TestAccIAMUser_ForceDestroy_serviceSpecificCred(t *testing.T) { + var user iam.GetUserOutput + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_user.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccUserForceDestroyConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserExists(resourceName, &user), + testAccCheckUserServiceSpecificCredential(&user), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_destroy"}, + }, + }, + }) +} + func TestAccIAMUser_ForceDestroy_signingCertificate(t *testing.T) { var user iam.GetUserOutput @@ -621,6 +650,26 @@ func testAccCheckUserUploadsSSHKey(getUserOutput *iam.GetUserOutput) resource.Te } } +// Creates an IAM User Service Specific Credential outside of Terraform to verify that it is deleted when `force_destroy` is set +func testAccCheckUserServiceSpecificCredential(getUserOutput *iam.GetUserOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + + conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn + + input := &iam.CreateServiceSpecificCredentialInput{ + UserName: getUserOutput.User.UserName, + ServiceName: aws.String("codecommit.amazonaws.com"), + } + + _, err := conn.CreateServiceSpecificCredential(input) + if err != nil { + return fmt.Errorf("error uploading IAM User (%s) Service Specifc Credential: %w", aws.StringValue(getUserOutput.User.UserName), err) + } + + return nil + } +} + func testAccCheckUserUploadSigningCertificate(getUserOutput *iam.GetUserOutput) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn