Skip to content

Commit

Permalink
Add instance profile deletion if role is only one attached.
Browse files Browse the repository at this point in the history
  • Loading branch information
jefchien committed Dec 7, 2023
1 parent a691080 commit 3c3788d
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 3 deletions.
55 changes: 54 additions & 1 deletion tool/clean/clean_iam_roles/clean_iam_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var (
"cwa-integ-assume-role",
"cwagent-eks-Worker-Role",
"cwagent-integ-test-task-role",
"cwagent-integ-test-task-execution-role",
"cwagent-operator-eks-Worker-Role",
"cwagent-operator-helm-integ-Worker-Role",
}
Expand All @@ -36,6 +37,9 @@ type iamClient interface {
DeleteRole(ctx context.Context, input *iam.DeleteRoleInput, optFns ...func(*iam.Options)) (*iam.DeleteRoleOutput, error)
ListAttachedRolePolicies(ctx context.Context, input *iam.ListAttachedRolePoliciesInput, optFns ...func(*iam.Options)) (*iam.ListAttachedRolePoliciesOutput, error)
DetachRolePolicy(ctx context.Context, input *iam.DetachRolePolicyInput, optFns ...func(*iam.Options)) (*iam.DetachRolePolicyOutput, error)
ListInstanceProfilesForRole(ctx context.Context, input *iam.ListInstanceProfilesForRoleInput, optFns ...func(*iam.Options)) (*iam.ListInstanceProfilesForRoleOutput, error)
RemoveRoleFromInstanceProfile(ctx context.Context, input *iam.RemoveRoleFromInstanceProfileInput, optFns ...func(*iam.Options)) (*iam.RemoveRoleFromInstanceProfileOutput, error)
DeleteInstanceProfile(ctx context.Context, input *iam.DeleteInstanceProfileInput, optFns ...func(*iam.Options)) (*iam.DeleteInstanceProfileOutput, error)
}

func main() {
Expand Down Expand Up @@ -93,7 +97,11 @@ func deleteRole(ctx context.Context, client iamClient, role types.Role) error {
if err := detachPolicies(ctx, client, role); err != nil {
return err
}
if err := deleteProfiles(ctx, client, role); err != nil {
return err
}
if _, err := client.DeleteRole(ctx, &iam.DeleteRoleInput{RoleName: role.RoleName}); err != nil {
log.Printf("Failed to delete role (%q): %v", *role.RoleName, err)
return err
}
log.Printf("Deleted role (%q) successfully", *role.RoleName)
Expand All @@ -108,8 +116,31 @@ func detachPolicies(ctx context.Context, client iamClient, role types.Role) erro
return err
}
for _, policy := range output.AttachedPolicies {
log.Printf("Trying to detach policy (%q) from role (%q)", *policy.PolicyName, *role.RoleName)
if _, err = client.DetachRolePolicy(ctx, &iam.DetachRolePolicyInput{PolicyArn: policy.PolicyArn, RoleName: role.RoleName}); err != nil {
return fmt.Errorf("unable to detach policy (%q) from role (%q): %w", *policy.PolicyName, *role.RoleName, err)
log.Printf("Failed to detach policy (%q): %v", *policy.PolicyName, err)
return err
}
log.Printf("Detached policy (%q) from role (%q) successfully", *policy.PolicyName, *role.RoleName)
}
if output.Marker == nil {
break
}
marker = output.Marker
}
return nil
}

func deleteProfiles(ctx context.Context, client iamClient, role types.Role) error {
var marker *string
for {
output, err := client.ListInstanceProfilesForRole(ctx, &iam.ListInstanceProfilesForRoleInput{RoleName: role.RoleName, Marker: marker})
if err != nil {
return err
}
for _, profile := range output.InstanceProfiles {
if err = deleteProfile(ctx, client, role, profile); err != nil {
return err
}
}
if output.Marker == nil {
Expand All @@ -120,6 +151,28 @@ func detachPolicies(ctx context.Context, client iamClient, role types.Role) erro
return nil
}

func deleteProfile(ctx context.Context, client iamClient, role types.Role, profile types.InstanceProfile) error {
log.Printf("Trying to remove role (%q) from instance profile (%q)", *role.RoleName, *profile.InstanceProfileName)
_, err := client.RemoveRoleFromInstanceProfile(ctx, &iam.RemoveRoleFromInstanceProfileInput{
InstanceProfileName: profile.InstanceProfileName,
RoleName: role.RoleName,
})
if err != nil {
return err
}
log.Printf("Removed role (%q) from instance profile (%q) successfully", *role.RoleName, *profile.InstanceProfileName)
// profile's only role is about to be deleted, so delete it too
if len(profile.Roles) == 1 {
log.Printf("Trying to delete instance profile (%q) attached to role (%q)", *profile.InstanceProfileName, *role.RoleName)
if _, err = client.DeleteInstanceProfile(ctx, &iam.DeleteInstanceProfileInput{InstanceProfileName: profile.InstanceProfileName}); err != nil {
log.Printf("Failed to delete instance profile (%q): %v", *profile.InstanceProfileName, err)
return err
}
log.Printf("Deleted instance profile (%q) successfully", *profile.InstanceProfileName)
}
return nil
}

func hasPrefix(roleName string) bool {
for _, prefix := range roleNamePrefixes {
if strings.HasPrefix(roleName, prefix) {
Expand Down
35 changes: 33 additions & 2 deletions tool/clean/clean_iam_roles/clean_iam_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ func (m *mockIamClient) DetachRolePolicy(ctx context.Context, input *iam.DetachR
return args.Get(0).(*iam.DetachRolePolicyOutput), args.Error(1)
}

func (m *mockIamClient) ListInstanceProfilesForRole(ctx context.Context, input *iam.ListInstanceProfilesForRoleInput, optFns ...func(*iam.Options)) (*iam.ListInstanceProfilesForRoleOutput, error) {
args := m.Called(ctx, input, optFns)
return args.Get(0).(*iam.ListInstanceProfilesForRoleOutput), args.Error(1)
}

func (m *mockIamClient) RemoveRoleFromInstanceProfile(ctx context.Context, input *iam.RemoveRoleFromInstanceProfileInput, optFns ...func(*iam.Options)) (*iam.RemoveRoleFromInstanceProfileOutput, error) {
args := m.Called(ctx, input, optFns)
return args.Get(0).(*iam.RemoveRoleFromInstanceProfileOutput), args.Error(1)
}

func (m *mockIamClient) DeleteInstanceProfile(ctx context.Context, input *iam.DeleteInstanceProfileInput, optFns ...func(*iam.Options)) (*iam.DeleteInstanceProfileOutput, error) {
args := m.Called(ctx, input, optFns)
return args.Get(0).(*iam.DeleteInstanceProfileOutput), args.Error(1)
}

func TestDeleteRoles(t *testing.T) {
expirationDate := getExpirationDate()

Expand Down Expand Up @@ -83,16 +98,32 @@ func TestDeleteRoles(t *testing.T) {
PolicyName: aws.String("policy-name"),
},
}
testProfile := types.InstanceProfile{
InstanceProfileName: aws.String("instance-profile-name"),
Roles: []types.Role{expiredRole},
}

client := &mockIamClient{}
client.On("ListRoles", ctx, &iam.ListRolesInput{}, mock.Anything).Return(&iam.ListRolesOutput{Roles: testRoles}, nil)
client.On("GetRole", ctx, &iam.GetRoleInput{RoleName: aws.String(expiredTestRoleName)}, mock.Anything).Return(&iam.GetRoleOutput{Role: &expiredRole}, nil)
client.On("GetRole", ctx, &iam.GetRoleInput{RoleName: aws.String(activeTestRoleName)}, mock.Anything).Return(&iam.GetRoleOutput{Role: &activeRole}, nil)
client.On("DeleteRole", ctx, mock.Anything, mock.Anything).Return(&iam.DeleteRoleOutput{}, nil)
client.On("ListAttachedRolePolicies", ctx, mock.Anything, mock.Anything).Return(&iam.ListAttachedRolePoliciesOutput{AttachedPolicies: testPolicies}, nil)
client.On("ListAttachedRolePolicies", ctx, mock.Anything, mock.Anything).Return(&iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: testPolicies,
}, nil)
client.On("DetachRolePolicy", ctx, mock.Anything, mock.Anything).Return(&iam.DetachRolePolicyOutput{}, nil)
client.On("ListInstanceProfilesForRole", ctx, &iam.ListInstanceProfilesForRoleInput{
RoleName: aws.String(expiredTestRoleName),
}, mock.Anything).Return(&iam.ListInstanceProfilesForRoleOutput{InstanceProfiles: []types.InstanceProfile{testProfile}}, nil)
client.On("RemoveRoleFromInstanceProfile", ctx, &iam.RemoveRoleFromInstanceProfileInput{
RoleName: aws.String(expiredTestRoleName),
InstanceProfileName: testProfile.InstanceProfileName,
}, mock.Anything).Return(&iam.RemoveRoleFromInstanceProfileOutput{}, nil)
client.On("DeleteInstanceProfile", ctx, &iam.DeleteInstanceProfileInput{
InstanceProfileName: testProfile.InstanceProfileName,
}, mock.Anything).Return(&iam.DeleteInstanceProfileOutput{}, nil)
assert.NoError(t, deleteRoles(ctx, client, expirationDate))
assert.Len(t, client.Calls, 6)
assert.Len(t, client.Calls, 9)
for _, call := range client.Calls {
switch call.Method {
case "DeleteRole":
Expand Down

0 comments on commit 3c3788d

Please sign in to comment.