From 28083abdcf229aa502f19f45d186793bc9e65381 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Kuester Date: Sun, 24 May 2020 13:41:40 +0200 Subject: [PATCH] Handle aws_iam_policy attachments --- README.md | 2 +- go.mod | 2 +- go.sum | 2 + pkg/resource/list.go | 42 +++++++++++- pkg/resource/select.go | 51 --------------- test/iam_policy_test.go | 72 ++++++++++++++++++++ test/test-fixtures/iam-policy/main.tf | 76 ++++++++++++++++++++++ test/test-fixtures/iam-policy/outputs.tf | 7 ++ test/test-fixtures/iam-policy/variables.tf | 11 ++++ 9 files changed, 209 insertions(+), 56 deletions(-) create mode 100644 test/iam_policy_test.go create mode 100644 test/test-fixtures/iam-policy/main.tf create mode 100644 test/test-fixtures/iam-policy/outputs.tf create mode 100644 test/test-fixtures/iam-policy/variables.tf diff --git a/README.md b/README.md index b98fc8331..d1a8d9ed9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- AWSweeper + AWSweeper

AWSweeper

A tool for cleaning your AWS account

diff --git a/go.mod b/go.mod index f558b1f10..5bbd0b389 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/golang/mock v1.4.0 github.com/gruntwork-io/terratest v0.24.2 github.com/hashicorp/terraform v0.12.24 - github.com/jckuester/awsls v0.0.0-20200523195606-04f54b8ca1c6 + github.com/jckuester/awsls v0.0.0-20200524112109-93c2a4665746 github.com/jckuester/terradozer v0.0.0-20200523195146-e66de6fa55f3 github.com/onsi/gomega v1.9.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 814314fc1..194edf3b9 100644 --- a/go.sum +++ b/go.sum @@ -353,6 +353,8 @@ github.com/jckuester/awsls v0.0.0-20200523105025-fe25c60a9fba h1:fbk5OV5nnwtkElu github.com/jckuester/awsls v0.0.0-20200523105025-fe25c60a9fba/go.mod h1:6dqF/j6Ar6b10DmHSV0CRYgKP2jwN8VUsHoFT3szFfA= github.com/jckuester/awsls v0.0.0-20200523195606-04f54b8ca1c6 h1:Rbcyj5lTyRRE4QgkJVncOgLbn6ehT6Yp5SdjCprr6FA= github.com/jckuester/awsls v0.0.0-20200523195606-04f54b8ca1c6/go.mod h1:hbj1nD8zXLBoZffzkV7WXUmMGarGPKkFeke90U9eehs= +github.com/jckuester/awsls v0.0.0-20200524112109-93c2a4665746 h1:CCO3Lc/ofOzEwH5WIZpndhoixKwNQRNVPKczVT4IS9w= +github.com/jckuester/awsls v0.0.0-20200524112109-93c2a4665746/go.mod h1:hbj1nD8zXLBoZffzkV7WXUmMGarGPKkFeke90U9eehs= github.com/jckuester/terradozer v0.0.0-20200505071321-36ef87ab4394 h1:7LmuH4Cm81Qsi0trqbnOmZ75xemFDhmzin0OoNL7aM4= github.com/jckuester/terradozer v0.0.0-20200505071321-36ef87ab4394/go.mod h1:KYrRcPbIiXgcRp7hG9fOBdAyvDnUx2aA9cjTqvKa4cI= github.com/jckuester/terradozer v0.0.0-20200522202131-ed33ac929141 h1:BowqT+JEJsqjPRWcTH7EOEEJuJlhKoh80MKZouM3pkE= diff --git a/pkg/resource/list.go b/pkg/resource/list.go index 6af722989..b5459b3d5 100644 --- a/pkg/resource/list.go +++ b/pkg/resource/list.go @@ -57,14 +57,19 @@ func List(filter *Filter, client *AWS, awsClient *awsls.Client, switch rType { case "aws_iam_user": - policyAttachments := getAttachedUserPolicies(filteredRes, client, provider) - print(policyAttachments, outputType) + attachedPolicies := getAttachedUserPolicies(filteredRes, client, provider) + print(attachedPolicies, outputType) inlinePolicies := getInlineUserPolicies(filteredRes, client, provider) print(inlinePolicies, outputType) - filteredRes = append(filteredRes, policyAttachments...) + filteredRes = append(filteredRes, attachedPolicies...) filteredRes = append(filteredRes, inlinePolicies...) + case "aws_iam_policy": + policyAttachments := getPolicyAttachments(filteredRes, provider) + print(policyAttachments, outputType) + + filteredRes = append(filteredRes, policyAttachments...) } for _, r := range filteredRes { @@ -142,7 +147,38 @@ func getInlineUserPolicies(users []awsls.Resource, client *AWS, result = append(result, r) } + } + + return result +} + +func getPolicyAttachments(policies []awsls.Resource, provider *provider.TerraformProvider) []awsls.Resource { + var result []awsls.Resource + + for _, policy := range policies { + arn, err := awslsRes.GetAttribute("arn", &policy) + if err != nil { + fmt.Fprint(os.Stderr, color.RedString("Error: %s\n", err)) + continue + } + + r := awsls.Resource{ + Type: "aws_iam_policy_attachment", + // Note: ID is only set for pretty printing (could be also left empty) + ID: policy.ID, + } + + r.Resource = terradozerRes.New(r.Type, r.ID, map[string]cty.Value{ + "policy_arn": cty.StringVal(arn), + }, provider) + + err = r.UpdateState() + if err != nil { + fmt.Fprint(os.Stderr, color.RedString("Error: %s\n", err)) + continue + } + result = append(result, r) } return result diff --git a/pkg/resource/select.go b/pkg/resource/select.go index 418362b71..ee1d392db 100644 --- a/pkg/resource/select.go +++ b/pkg/resource/select.go @@ -7,7 +7,6 @@ import ( "github.com/apex/log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/efs" - "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/kms" awsls "github.com/jckuester/awsls/aws" "github.com/zclconf/go-cty/cty" @@ -35,8 +34,6 @@ func (f Filter) Apply(resType string, res []awsls.Resource, raw interface{}, aws switch resType { case EfsFileSystem: return f.efsFileSystemFilter(res, raw, aws) - case "aws_iam_policy": - return f.iamPolicyFilter(res, raw, aws) case KmsKey: return f.kmsKeysFilter(res, aws) case KmsAlias: @@ -118,54 +115,6 @@ func (f Filter) efsFileSystemFilter(res []awsls.Resource, raw interface{}, c *AW return result } -func (f Filter) iamPolicyFilter(res []awsls.Resource, raw interface{}, c *AWS) []awsls.Resource { - var result []awsls.Resource - - for _, r := range res { - if f.Match(r) { - es, err := c.ListEntitiesForPolicy(&iam.ListEntitiesForPolicyInput{ - PolicyArn: &r.ID, - }) - if err != nil { - log.Fatal(err.Error()) - } - - var roles []string - var users []string - var groups []string - - for _, u := range es.PolicyUsers { - users = append(users, *u.UserName) - } - for _, g := range es.PolicyGroups { - groups = append(groups, *g.GroupName) - } - for _, r := range es.PolicyRoles { - roles = append(roles, *r.RoleName) - } - - result = append(result, awsls.Resource{ - Type: "aws_iam_policy_attachment", - ID: "none", - /* - Attrs: map[string]string{ - "policy_arn": r.ID, - "name": *raw.([]*iam.Policy)[i].PolicyName, - "users": strings.Join(users, "."), - "roles": strings.Join(roles, "."), - "groups": strings.Join(groups, "."), - }, - - */ - }) - result = append(result, r) - } - } - // policy attachments are not resources - // what happens here, is that policy is detached from groups, users and roles - return result -} - func (f Filter) kmsKeysFilter(res []awsls.Resource, c *AWS) []awsls.Resource { var result []awsls.Resource diff --git a/test/iam_policy_test.go b/test/iam_policy_test.go new file mode 100644 index 000000000..056c263ea --- /dev/null +++ b/test/iam_policy_test.go @@ -0,0 +1,72 @@ +package test + +import ( + "fmt" + "os" + "testing" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAcc_IamPolicy_DeleteByID(t *testing.T) { + if testing.Short() { + t.Skip("Skipping acceptance test.") + } + + env := InitEnv(t) + + terraformDir := "./test-fixtures/iam-policy" + + terraformOptions := getTerraformOptions(terraformDir, env) + + defer terraform.Destroy(t, terraformOptions) + + terraform.InitAndApply(t, terraformOptions) + + arn := terraform.Output(t, terraformOptions, "arn") + assertIamPolicyExists(t, env, arn) + + id := terraform.Output(t, terraformOptions, "id") + writeConfigID(t, terraformDir, "aws_iam_policy", id) + + defer os.Remove(terraformDir + "/config.yml") + + logBuffer, err := runBinary(t, terraformDir, "YES\n") + require.NoError(t, err) + + assertIamPolicyDeleted(t, env, arn) + + fmt.Println(logBuffer) +} + +func assertIamPolicyExists(t *testing.T, env EnvVars, arn string) { + assert.True(t, iamPolicyExists(t, env, arn)) +} + +func assertIamPolicyDeleted(t *testing.T, env EnvVars, arn string) { + assert.False(t, iamPolicyExists(t, env, arn)) +} + +func iamPolicyExists(t *testing.T, env EnvVars, arn string) bool { + opts := &iam.GetPolicyInput{ + PolicyArn: &arn, + } + + _, err := env.AWSClient.IAMAPI.GetPolicy(opts) + if err != nil { + ec2err, ok := err.(awserr.Error) + if !ok { + t.Fatal() + } + if ec2err.Code() == "NoSuchEntity" { + return false + } + t.Fatal(err) + } + + return true +} diff --git a/test/test-fixtures/iam-policy/main.tf b/test/test-fixtures/iam-policy/main.tf new file mode 100644 index 000000000..50fb584ca --- /dev/null +++ b/test/test-fixtures/iam-policy/main.tf @@ -0,0 +1,76 @@ +provider "aws" { + version = "~> 2.0" + + profile = var.profile + region = var.region +} + +terraform { + # The configuration for this backend will be filled in by Terragrunt + backend "s3" { + } +} + +resource "aws_iam_user" "test" { + name = "awsweeper-test-acc" + + tags = { + awsweeper = "test-acc" + } +} + +resource "aws_iam_role" "test" { + name = "awsweeper-test-acc" + + assume_role_policy = <