diff --git a/iamy/aws.go b/iamy/aws.go index 7305854..79ccdfb 100644 --- a/iamy/aws.go +++ b/iamy/aws.go @@ -1,8 +1,10 @@ package iamy import ( + "fmt" "log" "regexp" + "strings" "sync" "github.com/aws/aws-sdk-go/aws" @@ -194,10 +196,11 @@ func (a *AwsFetcher) marshalRoleDescriptionAsync(roleName string, target *string func (a *AwsFetcher) populateInstanceProfileData(resp *iam.ListInstanceProfilesOutput) error { for _, profileResp := range resp.InstanceProfiles { - if cfnResourceRegexp.MatchString(*profileResp.InstanceProfileName) { - log.Printf("Skipping CloudFormation generated instance profile %s", *profileResp.InstanceProfileName) + if ok, err := isSkippableManagedResource(*profileResp.InstanceProfileName); ok { + log.Printf(err) continue } + profile := InstanceProfile{iamService: iamService{ Name: *profileResp.InstanceProfileName, Path: *profileResp.Path, @@ -213,8 +216,8 @@ func (a *AwsFetcher) populateInstanceProfileData(resp *iam.ListInstanceProfilesO func (a *AwsFetcher) populateIamData(resp *iam.GetAccountAuthorizationDetailsOutput) error { for _, userResp := range resp.UserDetailList { - if cfnResourceRegexp.MatchString(*userResp.UserName) { - log.Printf("Skipping CloudFormation generated user %s", *userResp.UserName) + if ok, err := isSkippableManagedResource(*userResp.UserName); ok { + log.Printf(err) continue } @@ -243,8 +246,8 @@ func (a *AwsFetcher) populateIamData(resp *iam.GetAccountAuthorizationDetailsOut } for _, groupResp := range resp.GroupDetailList { - if cfnResourceRegexp.MatchString(*groupResp.GroupName) { - log.Printf("Skipping CloudFormation generated group %s", *groupResp.GroupName) + if ok, err := isSkippableManagedResource(*groupResp.GroupName); ok { + log.Printf(err) continue } @@ -264,8 +267,8 @@ func (a *AwsFetcher) populateIamData(resp *iam.GetAccountAuthorizationDetailsOut } for _, roleResp := range resp.RoleDetailList { - if cfnResourceRegexp.MatchString(*roleResp.RoleName) { - log.Printf("Skipping CloudFormation generated role %s", *roleResp.RoleName) + if ok, err := isSkippableManagedResource(*roleResp.RoleName); ok { + log.Printf(err) continue } @@ -294,8 +297,8 @@ func (a *AwsFetcher) populateIamData(resp *iam.GetAccountAuthorizationDetailsOut } for _, policyResp := range resp.Policies { - if cfnResourceRegexp.MatchString(*policyResp.PolicyName) { - log.Printf("Skipping CloudFormation generated policy %s", *policyResp.PolicyName) + if ok, err := isSkippableManagedResource(*policyResp.PolicyName); ok { + log.Printf(err) continue } @@ -379,3 +382,22 @@ func (a *AwsFetcher) getAccount() (*Account, error) { return &acct, nil } + +// isSkippableResource takes the resource identifier as a string and +// checks it against known resources that we shouldn't need to manage as +// it will already be managed by another process (such as Cloudformation +// roles). +// +// Returns a boolean of whether it can be skipped and a string of the +// reasoning why it was skipped. +func isSkippableManagedResource(resourceIdentifier string) (bool, string) { + if cfnResourceRegexp.MatchString(resourceIdentifier) { + return true, fmt.Sprintf("CloudFormation generated resource %s", resourceIdentifier) + } + + if strings.Contains(resourceIdentifier, "AWSServiceRole") || strings.Contains(resourceIdentifier, "aws-service-role") { + return true, fmt.Sprintf("AWS Service role generated resource %s", resourceIdentifier) + } + + return false, "" +} diff --git a/iamy/aws_test.go b/iamy/aws_test.go new file mode 100644 index 0000000..62e27c2 --- /dev/null +++ b/iamy/aws_test.go @@ -0,0 +1,47 @@ +package iamy + +import ( + "testing" +) + +func TestIsSkippableManagedResource(t *testing.T) { + skippables := []string{ + "myalias-123/iam/role/aws-service-role/spot.amazonaws.com/AWSServiceRoleForEC2Spot.yaml", + "AWSServiceRoleTest", + "my-example-role-ABCDEFGH1234567", + } + + nonSkippables := []string{ + "myalias-123/iam/user/foo/billy.blogs.yaml", + "myalias-123/s3/my-bucket.yaml", + "myalias-123/iam/instance-profile/example.yaml", + } + + for _, name := range skippables { + t.Run(name, func(t *testing.T) { + + skipped, err := isSkippableManagedResource(name) + if skipped == false { + t.Errorf("expected %s to be skipped but got false", name) + } + + if err == "" { + t.Errorf("expected %s to output an error message but it was empty", name) + } + }) + } + + for _, name := range nonSkippables { + t.Run(name, func(t *testing.T) { + + skipped, err := isSkippableManagedResource(name) + if skipped == true { + t.Errorf("expected %s to not be skipped but got true", name) + } + + if err != "" { + t.Errorf("expected %s to not output an error message but got: %s", name, err) + } + }) + } +}