Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions pkg/connector/iam_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,170 @@ func (o *iamUserResourceType) CreateAccount(
IsCreateAccountResult: true,
}, nil, nil, nil
}

func (o *iamUserResourceType) Delete(ctx context.Context, resourceId *v2.ResourceId) (annotations.Annotations, error) {
var noSuchEntity *iamTypes.NoSuchEntityException
l := ctxzap.Extract(ctx)
if resourceId.ResourceType != resourceTypeIAMUser.Id {
return nil, fmt.Errorf("aws-connector: only IAM user resources can be deleted")
}
userName, err := iamUserNameFromARN(resourceId.Resource)
if err != nil {
return nil, err
}
awsStringUserName := awsSdk.String(userName)

// try to fetch the user, if not found then the user has already been deleted
user, err := o.iamClient.GetUser(ctx, &iam.GetUserInput{UserName: awsStringUserName})
if err != nil {
if errors.As(err, &noSuchEntity) {
l.Info("User not found, returning success for delete operation")
return nil, nil
}
return nil, fmt.Errorf("aws-connector: iam.GetUser failed: %w", err)
}

if user.User == nil {
return nil, fmt.Errorf("aws-connector: user not found")
}

// To delete a user through the API we'll need to manually delete information associated with it,
// which is a 10 step process (9 + delete itself).
// https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_remove.html#id_users_deleting_cli

// Permission needed: iam:DeleteLoginProfile
_, err = o.iamClient.DeleteLoginProfile(ctx, &iam.DeleteLoginProfileInput{UserName: awsStringUserName})
if err != nil {
if !errors.As(err, &noSuchEntity) {
return nil, fmt.Errorf("aws-connector: failed to delete login profile: %w", err)
}
l.Info("login profile not found, skipping")
}

// Delete all access keys
// Permission needed: iam:ListAccessKeys, iam:DeleteAccessKey

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these necessary permissions mentioned in the Readme file or in doc-info.md? This would also make it clear which permissions are required. The implementation is good and makes sense.

keys, err := o.iamClient.ListAccessKeys(ctx, &iam.ListAccessKeysInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list access keys: %w", err)
}

for _, key := range keys.AccessKeyMetadata {
_, err = o.iamClient.DeleteAccessKey(ctx, &iam.DeleteAccessKeyInput{UserName: awsStringUserName, AccessKeyId: awsSdk.String(awsSdk.ToString(key.AccessKeyId))})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to delete access key: %w", err)
}
}

// Delete all signing certificates
// Permission needed: iam:ListSigningCertificates, iam:DeleteSigningCertificate
certificates, err := o.iamClient.ListSigningCertificates(ctx, &iam.ListSigningCertificatesInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list signing certificates: %w", err)
}

for _, certificate := range certificates.Certificates {
_, err = o.iamClient.DeleteSigningCertificate(ctx, &iam.DeleteSigningCertificateInput{UserName: awsStringUserName, CertificateId: awsSdk.String(awsSdk.ToString(certificate.CertificateId))})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to delete signing certificate: %w", err)
}
}

// Delete all SSH public keys
// Permission needed: iam:ListSSHPublicKeys, iam:DeleteSSHPublicKey
sshKeys, err := o.iamClient.ListSSHPublicKeys(ctx, &iam.ListSSHPublicKeysInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list SSH public keys: %w", err)
}

for _, key := range sshKeys.SSHPublicKeys {
_, err = o.iamClient.DeleteSSHPublicKey(ctx, &iam.DeleteSSHPublicKeyInput{UserName: awsStringUserName, SSHPublicKeyId: awsSdk.String(awsSdk.ToString(key.SSHPublicKeyId))})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to delete SSH public key: %w", err)
}
}

// Delete all service specific credentials
// Permission needed: iam:ListServiceSpecificCredentials, iam:DeleteServiceSpecificCredential
ssCredentials, err := o.iamClient.ListServiceSpecificCredentials(ctx, &iam.ListServiceSpecificCredentialsInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list service specific credentials: %w", err)
}

for _, credential := range ssCredentials.ServiceSpecificCredentials {
_, err = o.iamClient.DeleteServiceSpecificCredential(
ctx,
&iam.DeleteServiceSpecificCredentialInput{
UserName: awsStringUserName,
ServiceSpecificCredentialId: awsSdk.String(awsSdk.ToString(credential.ServiceSpecificCredentialId)),
},
)
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to delete service specific credential: %w", err)
}
}

// If user has MFA, deactivate them
// Permission needed: iam:ListMFADevices, iam:DeactivateMFADevice
mfaDevices, err := o.iamClient.ListMFADevices(ctx, &iam.ListMFADevicesInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list MFA devices: %w", err)
}

for _, device := range mfaDevices.MFADevices {
_, err = o.iamClient.DeactivateMFADevice(ctx, &iam.DeactivateMFADeviceInput{UserName: awsStringUserName, SerialNumber: awsSdk.String(awsSdk.ToString(device.SerialNumber))})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to deactivate MFA device: %w", err)
}
}

// Delete users inline policies
// Permission needed: iam:ListUserPolicies, iam:DeleteUserPolicy
userPolicies, err := o.iamClient.ListUserPolicies(ctx, &iam.ListUserPoliciesInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list user policies: %w", err)
}

for _, policy := range userPolicies.PolicyNames {
_, err = o.iamClient.DeleteUserPolicy(ctx, &iam.DeleteUserPolicyInput{UserName: awsStringUserName, PolicyName: awsSdk.String(policy)})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to delete user policy: %w", err)
}
}

// List and detach all attached policies
// Permission needed: iam:ListAttachedUserPolicies, iam:DetachUserPolicy
attachedPolicies, err := o.iamClient.ListAttachedUserPolicies(ctx, &iam.ListAttachedUserPoliciesInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list attached user policies: %w", err)
}

for _, policy := range attachedPolicies.AttachedPolicies {
_, err = o.iamClient.DetachUserPolicy(ctx, &iam.DetachUserPolicyInput{UserName: awsStringUserName, PolicyArn: awsSdk.String(awsSdk.ToString(policy.PolicyArn))})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to detach user policy: %w", err)
}
}

// Remove the user from any IAM groups
// Permission needed: iam:ListGroupsForUser, iam:RemoveUserFromGroup
userGroups, err := o.iamClient.ListGroupsForUser(ctx, &iam.ListGroupsForUserInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to list groups for user: %w", err)
}

for _, group := range userGroups.Groups {
_, err = o.iamClient.RemoveUserFromGroup(ctx, &iam.RemoveUserFromGroupInput{UserName: awsStringUserName, GroupName: awsSdk.String(awsSdk.ToString(group.GroupName))})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to remove user from group: %w", err)
}
}

// Proceed to delete the user
// Permission needed: iam:DeleteUser
_, err = o.iamClient.DeleteUser(ctx, &iam.DeleteUserInput{UserName: awsStringUserName})
if err != nil {
return nil, fmt.Errorf("aws-connector: failed to delete user: %w", err)
}

return nil, nil
}