Skip to content

Commit

Permalink
Refactor: moved all delete behaviours into seperate functions
Browse files Browse the repository at this point in the history
This makes the force delete flow more obvious and allows for easier reordering or parallelisation of delete behaviours
  • Loading branch information
Rick Burgess committed Nov 4, 2018
1 parent 51191b6 commit e4631a3
Showing 1 changed file with 124 additions and 81 deletions.
205 changes: 124 additions & 81 deletions aws/resource_aws_iam_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,97 +205,25 @@ func resourceAwsIamUserDelete(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) {
var accessKeys []string
listAccessKeys := &iam.ListAccessKeysInput{
UserName: aws.String(d.Id()),
}
pageOfAccessKeys := func(page *iam.ListAccessKeysOutput, lastPage bool) (shouldContinue bool) {
for _, k := range page.AccessKeyMetadata {
accessKeys = append(accessKeys, *k.AccessKeyId)
}
return !lastPage
}
err = iamconn.ListAccessKeysPages(listAccessKeys, pageOfAccessKeys)

err = deleteAwsIamUserAccessKeys(iamconn, d.Id())
if err != nil {
return fmt.Errorf("Error removing access keys of user %s: %s", d.Id(), err)
}
for _, k := range accessKeys {
_, err := iamconn.DeleteAccessKey(&iam.DeleteAccessKeyInput{
UserName: aws.String(d.Id()),
AccessKeyId: aws.String(k),
})
if err != nil {
return fmt.Errorf("Error deleting access key %s: %s", k, err)
}
return err
}

var publicKeys []string
listSSHPublicKeys := &iam.ListSSHPublicKeysInput{
UserName: aws.String(d.Id()),
}
pageOfListSSHPublicKeys := func(page *iam.ListSSHPublicKeysOutput, lastPage bool) (shouldContinue bool) {
for _, k := range page.SSHPublicKeys {
publicKeys = append(publicKeys, *k.SSHPublicKeyId)
}
return !lastPage
}
err = iamconn.ListSSHPublicKeysPages(listSSHPublicKeys, pageOfListSSHPublicKeys)
err = deleteAwsIamUserSSHKeys(iamconn, d.Id())
if err != nil {
return fmt.Errorf("Error removing public SSH keys of user %s: %s", d.Id(), err)
}
for _, k := range publicKeys {
_, err := iamconn.DeleteSSHPublicKey(&iam.DeleteSSHPublicKeyInput{
UserName: aws.String(d.Id()),
SSHPublicKeyId: aws.String(k),
})
if err != nil {
return fmt.Errorf("Error deleting public SSH key %s: %s", k, err)
}
return err
}

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)
err = deleteAwsIamUserMFADevices(iamconn, d.Id())
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)
}
return err
}

err = resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err = iamconn.DeleteLoginProfile(&iam.DeleteLoginProfileInput{
UserName: aws.String(d.Id()),
})
if err != nil {
if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
return nil
}
// EntityTemporarilyUnmodifiable: Login Profile for User XXX cannot be modified while login profile is being created.
if isAWSErr(err, iam.ErrCodeEntityTemporarilyUnmodifiableException, "") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})

err = deleteAwsIamUserLoginProfile(iamconn, d.Id())
if err != nil {
return fmt.Errorf("Error deleting Account Login Profile: %s", err)
return err
}
}

Expand Down Expand Up @@ -324,3 +252,118 @@ func validateAwsIamUserName(v interface{}, k string) (ws []string, errors []erro
}
return
}

func deleteAwsIamUserSSHKeys(svc *iam.IAM, username string) error {
var publicKeys []string
var err error

listSSHPublicKeys := &iam.ListSSHPublicKeysInput{
UserName: aws.String(username),
}
pageOfListSSHPublicKeys := func(page *iam.ListSSHPublicKeysOutput, lastPage bool) (shouldContinue bool) {
for _, k := range page.SSHPublicKeys {
publicKeys = append(publicKeys, *k.SSHPublicKeyId)
}
return !lastPage
}
err = svc.ListSSHPublicKeysPages(listSSHPublicKeys, pageOfListSSHPublicKeys)
if err != nil {
return fmt.Errorf("Error removing public SSH keys of user %s: %s", username, err)
}
for _, k := range publicKeys {
_, err := svc.DeleteSSHPublicKey(&iam.DeleteSSHPublicKeyInput{
UserName: aws.String(username),
SSHPublicKeyId: aws.String(k),
})
if err != nil {
return fmt.Errorf("Error deleting public SSH key %s: %s", k, err)
}
}

return nil
}

func deleteAwsIamUserMFADevices(svc *iam.IAM, username string) error {
var MFADevices []string
var err error

listMFADevices := &iam.ListMFADevicesInput{
UserName: aws.String(username),
}
pageOfMFADevices := func(page *iam.ListMFADevicesOutput, lastPage bool) (shouldContinue bool) {
for _, m := range page.MFADevices {
MFADevices = append(MFADevices, *m.SerialNumber)
}
return !lastPage
}
err = svc.ListMFADevicesPages(listMFADevices, pageOfMFADevices)
if err != nil {
return fmt.Errorf("Error removing MFA devices of user %s: %s", username, err)
}
for _, m := range MFADevices {
_, err := svc.DeactivateMFADevice(&iam.DeactivateMFADeviceInput{
UserName: aws.String(username),
SerialNumber: aws.String(m),
})
if err != nil {
return fmt.Errorf("Error deactivating MFA device %s: %s", m, err)
}
}

return nil
}

func deleteAwsIamUserLoginProfile(svc *iam.IAM, username string) error {
var err error
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err = svc.DeleteLoginProfile(&iam.DeleteLoginProfileInput{
UserName: aws.String(username),
})
if err != nil {
if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
return nil
}
// EntityTemporarilyUnmodifiable: Login Profile for User XXX cannot be modified while login profile is being created.
if isAWSErr(err, iam.ErrCodeEntityTemporarilyUnmodifiableException, "") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})

if err != nil {
return fmt.Errorf("Error deleting Account Login Profile: %s", err)
}

return nil
}

func deleteAwsIamUserAccessKeys(svc *iam.IAM, username string) error {
var accessKeys []string
var err error
listAccessKeys := &iam.ListAccessKeysInput{
UserName: aws.String(username),
}
pageOfAccessKeys := func(page *iam.ListAccessKeysOutput, lastPage bool) (shouldContinue bool) {
for _, k := range page.AccessKeyMetadata {
accessKeys = append(accessKeys, *k.AccessKeyId)
}
return !lastPage
}
err = svc.ListAccessKeysPages(listAccessKeys, pageOfAccessKeys)
if err != nil {
return fmt.Errorf("Error removing access keys of user %s: %s", username, err)
}
for _, k := range accessKeys {
_, err := svc.DeleteAccessKey(&iam.DeleteAccessKeyInput{
UserName: aws.String(username),
AccessKeyId: aws.String(k),
})
if err != nil {
return fmt.Errorf("Error deleting access key %s: %s", k, err)
}
}

return nil
}

0 comments on commit e4631a3

Please sign in to comment.