Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor IAM Groups #534

Merged
merged 1 commit into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -2022,7 +2022,7 @@ func GetAllResources(targetRegions []string, excludeAfter time.Time, resourceTyp
}
if IsNukeable(iamGroups.ResourceName(), resourceTypes) {
start := time.Now()
groupNames, err := getAllIamGroups(session, excludeAfter, configObj)
groupNames, err := iamGroups.getAll(configObj)
if err != nil {
return nil, errors.WithStackTrace(err)
}
Expand Down
68 changes: 23 additions & 45 deletions aws/iam_group.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
package aws

import (
"github.com/gruntwork-io/cloud-nuke/report"
"github.com/gruntwork-io/cloud-nuke/telemetry"
commonTelemetry "github.com/gruntwork-io/go-commons/telemetry"
"sync"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/cloud-nuke/logging"
"github.com/gruntwork-io/cloud-nuke/report"
"github.com/gruntwork-io/cloud-nuke/telemetry"
commonTelemetry "github.com/gruntwork-io/go-commons/telemetry"
"github.com/gruntwork-io/gruntwork-cli/errors"
"github.com/hashicorp/go-multierror"
"sync"
)

func getAllIamGroups(session *session.Session, excludeAfter time.Time, configObj config.Config) ([]*string, error) {
svc := iam.New(session)

func (ig IAMGroups) getAll(configObj config.Config) ([]*string, error) {
var allIamGroups []*string
err := svc.ListGroupsPages(
err := ig.Client.ListGroupsPages(
&iam.ListGroupsInput{},
func(page *iam.ListGroupsOutput, lastPage bool) bool {
for _, iamGroup := range page.Groups {
if shouldIncludeIamGroup(iamGroup, excludeAfter, configObj) {
if configObj.IAMGroups.ShouldInclude(config.ResourceValue{
Time: iamGroup.CreateDate,
Name: iamGroup.GroupName,
}) {
allIamGroups = append(allIamGroups, iamGroup.GroupName)
}
}
Expand All @@ -34,13 +32,12 @@ func getAllIamGroups(session *session.Session, excludeAfter time.Time, configObj
if err != nil {
return nil, errors.WithStackTrace(err)
}

return allIamGroups, nil
}

// nukeAllIamGroups - delete all IAM groups. Caller is responsible for pagination (no more than 100/request)
func nukeAllIamGroups(session *session.Session, groupNames []*string) error {
svc := iam.New(session)

// nukeAll - delete all IAM groups. Caller is responsible for pagination (no more than 100/request)
func (ig IAMGroups) nukeAll(groupNames []*string) error {
if len(groupNames) == 0 {
logging.Logger.Debug("No IAM Groups to nuke")
return nil
Expand All @@ -59,7 +56,7 @@ func nukeAllIamGroups(session *session.Session, groupNames []*string) error {
errChans := make([]chan error, len(groupNames))
for i, groupName := range groupNames {
errChans[i] = make(chan error, 1)
go deleteIamGroupAsync(wg, errChans[i], svc, groupName)
go ig.deleteAsync(wg, errChans[i], groupName)
}
wg.Wait()

Expand All @@ -71,9 +68,7 @@ func nukeAllIamGroups(session *session.Session, groupNames []*string) error {
logging.Logger.Errorf("[Failed] %s", err)
telemetry.TrackEvent(commonTelemetry.EventContext{
EventName: "Error Nuking IAM Group",
}, map[string]interface{}{
"region": *session.Config.Region,
})
}, map[string]interface{}{})
}
}
finalErr := allErrs.ErrorOrNil()
Expand All @@ -85,29 +80,29 @@ func nukeAllIamGroups(session *session.Session, groupNames []*string) error {
}

// deleteIamGroup - removes an IAM group from AWS, designed to run as a goroutine
func deleteIamGroupAsync(wg *sync.WaitGroup, errChan chan error, svc *iam.IAM, groupName *string) {
func (ig IAMGroups) deleteAsync(wg *sync.WaitGroup, errChan chan error, groupName *string) {
defer wg.Done()
var multierr *multierror.Error

//Remove any users from the group
getGroupInput := &iam.GetGroupInput{
GroupName: groupName,
}
grp, err := svc.GetGroup(getGroupInput)
grp, err := ig.Client.GetGroup(getGroupInput)
for _, user := range grp.Users {
unlinkUserInput := &iam.RemoveUserFromGroupInput{
UserName: user.UserName,
GroupName: groupName,
}
_, err := svc.RemoveUserFromGroup(unlinkUserInput)
_, err := ig.Client.RemoveUserFromGroup(unlinkUserInput)
if err != nil {
multierr = multierror.Append(multierr, err)
}
}

//Detach any policies on the group
allPolicies := []*string{}
err = svc.ListAttachedGroupPoliciesPages(&iam.ListAttachedGroupPoliciesInput{GroupName: groupName},
err = ig.Client.ListAttachedGroupPoliciesPages(&iam.ListAttachedGroupPoliciesInput{GroupName: groupName},
func(page *iam.ListAttachedGroupPoliciesOutput, lastPage bool) bool {
for _, iamPolicy := range page.AttachedPolicies {
allPolicies = append(allPolicies, iamPolicy.PolicyArn)
Expand All @@ -121,12 +116,12 @@ func deleteIamGroupAsync(wg *sync.WaitGroup, errChan chan error, svc *iam.IAM, g
GroupName: groupName,
PolicyArn: policy,
}
_, err = svc.DetachGroupPolicy(unlinkPolicyInput)
_, err = ig.Client.DetachGroupPolicy(unlinkPolicyInput)
}

// Detach any inline policies on the group
allInlinePolicyNames := []*string{}
err = svc.ListGroupPoliciesPages(&iam.ListGroupPoliciesInput{GroupName: groupName},
err = ig.Client.ListGroupPoliciesPages(&iam.ListGroupPoliciesInput{GroupName: groupName},
func(page *iam.ListGroupPoliciesOutput, lastPage bool) bool {
logging.Logger.Info("ListGroupPolicies response page: ", page)
for _, policyName := range page.PolicyNames {
Expand All @@ -138,14 +133,14 @@ func deleteIamGroupAsync(wg *sync.WaitGroup, errChan chan error, svc *iam.IAM, g

logging.Logger.Info("inline policies: ", allInlinePolicyNames)
for _, policyName := range allInlinePolicyNames {
_, err = svc.DeleteGroupPolicy(&iam.DeleteGroupPolicyInput{
_, err = ig.Client.DeleteGroupPolicy(&iam.DeleteGroupPolicyInput{
GroupName: groupName,
PolicyName: policyName,
})
}

//Delete the group
_, err = svc.DeleteGroup(&iam.DeleteGroupInput{
_, err = ig.Client.DeleteGroup(&iam.DeleteGroupInput{
GroupName: groupName,
})
if err != nil {
Expand All @@ -164,23 +159,6 @@ func deleteIamGroupAsync(wg *sync.WaitGroup, errChan chan error, svc *iam.IAM, g
errChan <- multierr.ErrorOrNil()
}

// check if iam group should be included based on config rules (RegExp and Exclude After)
func shouldIncludeIamGroup(iamGroup *iam.Group, excludeAfter time.Time, configObj config.Config) bool {
if iamGroup == nil {
return false
}

if excludeAfter.Before(aws.TimeValue(iamGroup.CreateDate)) {
return false
}

return config.ShouldInclude(
aws.StringValue(iamGroup.GroupName),
configObj.IAMGroups.IncludeRule.NamesRegExp,
configObj.IAMGroups.ExcludeRule.NamesRegExp,
)
}

// TooManyIamGroupErr Custom Errors
type TooManyIamGroupErr struct{}

Expand Down
Loading