From ab47eb11b3d5e78fa5da09771e7bf14ff72aff75 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 9 Dec 2024 17:27:09 -0700 Subject: [PATCH 01/87] fix(gamelift): skip unsupported regions --- resources/gamelift-build.go | 12 +++++++++++- resources/gamelift-fleet.go | 12 +++++++++++- resources/gamelift-mm-config.go | 12 +++++++++++- resources/gamelift-mm-rule.go | 12 +++++++++++- resources/gamelift-queue.go | 12 +++++++++++- resources/gamelift.go | 23 +++++++++++++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 resources/gamelift.go diff --git a/resources/gamelift-build.go b/resources/gamelift-build.go index 57808692..2e69b3ba 100644 --- a/resources/gamelift-build.go +++ b/resources/gamelift-build.go @@ -24,12 +24,22 @@ func init() { }) } -type GameLiftBuildLister struct{} +type GameLiftBuildLister struct { + GameLift +} func (l *GameLiftBuildLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) var resources []resource.Resource + if !l.IsSupportedRegion(opts.Region.Name) { + opts.Logger. + WithField("resource", GameLiftBuildResource). + WithField("region", opts.Region.Name). + Debug("region not supported") + return resources, nil + } + svc := gamelift.New(opts.Session) params := &gamelift.ListBuildsInput{} diff --git a/resources/gamelift-fleet.go b/resources/gamelift-fleet.go index b4c52f18..90b3507f 100644 --- a/resources/gamelift-fleet.go +++ b/resources/gamelift-fleet.go @@ -23,12 +23,22 @@ func init() { }) } -type GameLiftFleetLister struct{} +type GameLiftFleetLister struct { + GameLift +} func (l *GameLiftFleetLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) var resources []resource.Resource + if !l.IsSupportedRegion(opts.Region.Name) { + opts.Logger. + WithField("resource", GameLiftFleetResource). + WithField("region", opts.Region.Name). + Debug("region not supported") + return resources, nil + } + svc := gamelift.New(opts.Session) params := &gamelift.ListFleetsInput{} diff --git a/resources/gamelift-mm-config.go b/resources/gamelift-mm-config.go index 797f2fcf..9ed9b66c 100644 --- a/resources/gamelift-mm-config.go +++ b/resources/gamelift-mm-config.go @@ -25,12 +25,22 @@ func init() { }) } -type GameLiftMatchmakingConfigurationLister struct{} +type GameLiftMatchmakingConfigurationLister struct { + GameLift +} func (l *GameLiftMatchmakingConfigurationLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) var resources []resource.Resource + if !l.IsSupportedRegion(opts.Region.Name) { + opts.Logger. + WithField("resource", GameLiftMatchmakingConfigurationResource). + WithField("region", opts.Region.Name). + Debug("region not supported") + return resources, nil + } + svc := gamelift.New(opts.Session) params := &gamelift.DescribeMatchmakingConfigurationsInput{} diff --git a/resources/gamelift-mm-rule.go b/resources/gamelift-mm-rule.go index 81fabee0..e2cf5787 100644 --- a/resources/gamelift-mm-rule.go +++ b/resources/gamelift-mm-rule.go @@ -24,12 +24,22 @@ func init() { }) } -type GameLiftMatchmakingRuleSetLister struct{} +type GameLiftMatchmakingRuleSetLister struct { + GameLift +} func (l *GameLiftMatchmakingRuleSetLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) var resources []resource.Resource + if !l.IsSupportedRegion(opts.Region.Name) { + opts.Logger. + WithField("resource", GameLiftMatchmakingRuleSetResource). + WithField("region", opts.Region.Name). + Debug("region not supported") + return resources, nil + } + svc := gamelift.New(opts.Session) params := &gamelift.DescribeMatchmakingRuleSetsInput{} diff --git a/resources/gamelift-queue.go b/resources/gamelift-queue.go index 00f39188..74f82bb7 100644 --- a/resources/gamelift-queue.go +++ b/resources/gamelift-queue.go @@ -23,12 +23,22 @@ func init() { }) } -type GameLiftQueueLister struct{} +type GameLiftQueueLister struct { + GameLift +} func (l *GameLiftQueueLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) var resources []resource.Resource + if !l.IsSupportedRegion(opts.Region.Name) { + opts.Logger. + WithField("resource", GameLiftQueueResource). + WithField("region", opts.Region.Name). + Debug("region not supported") + return resources, nil + } + svc := gamelift.New(opts.Session) params := &gamelift.DescribeGameSessionQueuesInput{} diff --git a/resources/gamelift.go b/resources/gamelift.go new file mode 100644 index 00000000..8503c331 --- /dev/null +++ b/resources/gamelift.go @@ -0,0 +1,23 @@ +package resources + +import "slices" + +type GameLift struct{} + +func (g *GameLift) IsSupportedRegion(region string) bool { + // ref: https://docs.aws.amazon.com/gamelift/latest/developerguide/gamelift-regions.html + // there are fewer unsupported, so doing the inverse + // unsupported are regions that only support "Remote location for multi-location fleets" + // note: we do not currently filter down to the local zone + unsupportedRegions := []string{ + "af-south-1", + "ap-east-1", + "ap-northeast-3", + "eu-north-1", + "eu-south-1", + "eu-west-3", + "me-south-1", + } + + return !slices.Contains(unsupportedRegions, region) +} From 7406289483cc39d172974b17204ffc11e65c2867 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:26:33 +0000 Subject: [PATCH 02/87] fix(deps): update module github.com/ekristen/libnuke to v0.22.0 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 415f789c..cd60d0de 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 github.com/aws/smithy-go v1.22.1 - github.com/ekristen/libnuke v0.21.8 + github.com/ekristen/libnuke v0.22.0 github.com/fatih/color v1.18.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index aefda508..76269efa 100644 --- a/go.sum +++ b/go.sum @@ -124,6 +124,8 @@ github.com/ekristen/libnuke v0.21.4 h1:krEZZIAITp/begn/ltNfp9xJ7WznUI/tnEPCIFmHV github.com/ekristen/libnuke v0.21.4/go.mod h1:FgzH3PKJqk/31Lty+B3MFwjWc7fz+NpGylVtgzIDFII= github.com/ekristen/libnuke v0.21.8 h1:JyMyMUbh/ti8r1wjXe5B8h8xhPibFN2tu9zVlkn1vyw= github.com/ekristen/libnuke v0.21.8/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.0 h1:x5ZxadNiH0DZJOtdxVDKZaaBymojdi65Pi+k3ITfv7M= +github.com/ekristen/libnuke v0.22.0/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= From 33df674ff43767e1adeab6d3e9e078d93b2c7825 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 12:38:07 -0700 Subject: [PATCH 03/87] fix(ec2-tgw): filter out gateways not owned by account --- resources/ec2-tgw.go | 47 ++++++++++++++++++++++----------------- resources/ec2-tgw_test.go | 28 +++++++++++++++++++++++ 2 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 resources/ec2-tgw_test.go diff --git a/resources/ec2-tgw.go b/resources/ec2-tgw.go index 5d34be67..eada36ec 100644 --- a/resources/ec2-tgw.go +++ b/resources/ec2-tgw.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/gotidy/ptr" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/ekristen/libnuke/pkg/registry" @@ -45,8 +47,11 @@ func (l *EC2TGWLister) List(_ context.Context, o interface{}) ([]resource.Resour for _, tgw := range resp.TransitGateways { resources = append(resources, &EC2TGW{ - svc: svc, - tgw: tgw, + svc: svc, + ID: tgw.TransitGatewayId, + OwnerID: tgw.OwnerId, + Tags: tgw.Tags, + accountID: opts.AccountID, }) } @@ -63,16 +68,21 @@ func (l *EC2TGWLister) List(_ context.Context, o interface{}) ([]resource.Resour } type EC2TGW struct { - svc *ec2.EC2 - tgw *ec2.TransitGateway + svc *ec2.EC2 + ID *string `description:"The ID of the transit gateway."` + OwnerID *string `property:"name=OwnerId" description:"The ID of the AWS account that owns the transit gateway."` + State *string `description:"The state of the transit gateway."` + Tags []*ec2.Tag `description:"The tags associated with the transit gateway."` + + accountID *string } -func (e *EC2TGW) Remove(_ context.Context) error { +func (r *EC2TGW) Remove(_ context.Context) error { params := &ec2.DeleteTransitGatewayInput{ - TransitGatewayId: e.tgw.TransitGatewayId, + TransitGatewayId: r.ID, } - _, err := e.svc.DeleteTransitGateway(params) + _, err := r.svc.DeleteTransitGateway(params) if err != nil { return err } @@ -80,26 +90,21 @@ func (e *EC2TGW) Remove(_ context.Context) error { return nil } -func (e *EC2TGW) Filter() error { - if *e.tgw.State == awsutil.StateDeleted { +func (r *EC2TGW) Filter() error { + if ptr.ToString(r.State) == awsutil.StateDeleted { return fmt.Errorf("already deleted") } + if ptr.ToString(r.OwnerID) != ptr.ToString(r.accountID) { + return fmt.Errorf("not owned by account") + } return nil } -func (e *EC2TGW) Properties() types.Properties { - properties := types.NewProperties() - for _, tagValue := range e.tgw.Tags { - properties.SetTag(tagValue.Key, tagValue.Value) - } - properties. - Set("ID", e.tgw.TransitGatewayId). - Set("OwnerId", e.tgw.OwnerId) - - return properties +func (r *EC2TGW) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } -func (e *EC2TGW) String() string { - return *e.tgw.TransitGatewayId +func (r *EC2TGW) String() string { + return *r.ID } diff --git a/resources/ec2-tgw_test.go b/resources/ec2-tgw_test.go new file mode 100644 index 00000000..de51f4f4 --- /dev/null +++ b/resources/ec2-tgw_test.go @@ -0,0 +1,28 @@ +package resources + +import ( + "testing" + + "github.com/gotidy/ptr" + "github.com/stretchr/testify/assert" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +func Test_EC2TGW_Properties(t *testing.T) { + tgw := &EC2TGW{ + ID: ptr.String("tgw-1234567890abcdef0"), + OwnerID: ptr.String("123456789012"), + Tags: []*ec2.Tag{ + { + Key: ptr.String("TestTag"), + Value: ptr.String("test-tgw"), + }, + }, + } + + assert.Equal(t, "tgw-1234567890abcdef0", tgw.Properties().Get("ID")) + assert.Equal(t, "123456789012", tgw.Properties().Get("OwnerId")) + assert.Equal(t, "test-tgw", tgw.Properties().Get("tag:TestTag")) + assert.Equal(t, "tgw-1234567890abcdef0", tgw.String()) +} From 5bfaf103bdf099567e4e31d8f10d5d59ee990eb3 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 17:19:40 -0700 Subject: [PATCH 04/87] refactor(sfn-state-machine): standardization --- .../{sfn-statemachines.go => sfn-state-machine.go} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename resources/{sfn-statemachines.go => sfn-state-machine.go} (85%) diff --git a/resources/sfn-statemachines.go b/resources/sfn-state-machine.go similarity index 85% rename from resources/sfn-statemachines.go rename to resources/sfn-state-machine.go index ca38e0bd..0522e5ec 100644 --- a/resources/sfn-statemachines.go +++ b/resources/sfn-state-machine.go @@ -63,14 +63,14 @@ type SFNStateMachine struct { ARN *string } -func (f *SFNStateMachine) Remove(_ context.Context) error { - _, err := f.svc.DeleteStateMachine(&sfn.DeleteStateMachineInput{ - StateMachineArn: f.ARN, +func (r *SFNStateMachine) Remove(_ context.Context) error { + _, err := r.svc.DeleteStateMachine(&sfn.DeleteStateMachineInput{ + StateMachineArn: r.ARN, }) return err } -func (f *SFNStateMachine) String() string { - return *f.ARN +func (r *SFNStateMachine) String() string { + return *r.ARN } From 01d94f8b62bf548647451be2642b22c207f1f391 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 17:22:55 -0700 Subject: [PATCH 05/87] feat(sfn-state-machine): new properties, tags, name, type, creationdate --- resources/sfn-state-machine.go | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/resources/sfn-state-machine.go b/resources/sfn-state-machine.go index 0522e5ec..a6cf1458 100644 --- a/resources/sfn-state-machine.go +++ b/resources/sfn-state-machine.go @@ -2,6 +2,8 @@ package resources import ( "context" + "github.com/ekristen/libnuke/pkg/types" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sfn" @@ -42,9 +44,23 @@ func (l *SFNStateMachineLister) List(_ context.Context, o interface{}) ([]resour } for _, stateMachine := range output.StateMachines { + var resourceTags []*sfn.Tag + tags, err := svc.ListTagsForResource(&sfn.ListTagsForResourceInput{ + ResourceArn: stateMachine.StateMachineArn, + }) + if err != nil { + opts.Logger.WithError(err).Error("unable to list state machine tags") + } else { + resourceTags = tags.Tags + } + resources = append(resources, &SFNStateMachine{ - svc: svc, - ARN: stateMachine.StateMachineArn, + svc: svc, + ARN: stateMachine.StateMachineArn, + Name: stateMachine.Name, + Type: stateMachine.Type, + CreationDate: stateMachine.CreationDate, + Tags: resourceTags, }) } @@ -59,8 +75,12 @@ func (l *SFNStateMachineLister) List(_ context.Context, o interface{}) ([]resour } type SFNStateMachine struct { - svc *sfn.SFN - ARN *string + svc *sfn.SFN + ARN *string `description:"The Amazon Resource Name (ARN) that identifies the state machine."` + Name *string `description:"The name of the state machine."` + Type *string `description:"The type of the state machine."` + CreationDate *time.Time `description:"The date the state machine was created."` + Tags []*sfn.Tag `description:"The tags associated with the state machine."` } func (r *SFNStateMachine) Remove(_ context.Context) error { @@ -71,6 +91,10 @@ func (r *SFNStateMachine) Remove(_ context.Context) error { return err } +func (r *SFNStateMachine) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + func (r *SFNStateMachine) String() string { return *r.ARN } From c2864f5e7672b67fc96465b88611347945715b5b Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 17:23:55 -0700 Subject: [PATCH 06/87] docs(sfn-state-machine): updating properties --- docs/resources/sfn-state-machine.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/resources/sfn-state-machine.md b/docs/resources/sfn-state-machine.md index 0fd40a6f..e4e3de89 100644 --- a/docs/resources/sfn-state-machine.md +++ b/docs/resources/sfn-state-machine.md @@ -14,7 +14,12 @@ SFNStateMachine ## Properties -- `ARN`: No Description +- `ARN`: The Amazon Resource Name (ARN) that identifies the state machine. +- `CreationDate`: The date the state machine was created. +- `Name`: The name of the state machine. +- `Type`: The type of the state machine. +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property From a5900668aaffc4c2030ac56e903466e64580ecc2 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 17:24:27 -0700 Subject: [PATCH 07/87] chore(golangci-lint): fix goimports --- resources/sfn-state-machine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/sfn-state-machine.go b/resources/sfn-state-machine.go index a6cf1458..20bbf4e8 100644 --- a/resources/sfn-state-machine.go +++ b/resources/sfn-state-machine.go @@ -2,7 +2,6 @@ package resources import ( "context" - "github.com/ekristen/libnuke/pkg/types" "time" "github.com/aws/aws-sdk-go/aws" @@ -10,6 +9,7 @@ import ( "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) From 2791fdba5e8eb512a0d80f46abc8055b32608ee2 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 17:29:42 -0700 Subject: [PATCH 08/87] feat(neptune-cluster): tags and properties --- resources/neptune-clusters.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/resources/neptune-clusters.go b/resources/neptune-clusters.go index 7e1c6781..92307b23 100644 --- a/resources/neptune-clusters.go +++ b/resources/neptune-clusters.go @@ -8,6 +8,7 @@ import ( "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) @@ -42,9 +43,20 @@ func (l *NeptuneClusterLister) List(_ context.Context, o interface{}) ([]resourc } for _, dbCluster := range output.DBClusters { + var dbTags []*neptune.Tag + tags, err := svc.ListTagsForResource(&neptune.ListTagsForResourceInput{ + ResourceName: dbCluster.DBClusterArn, + }) + if err != nil { + opts.Logger.WithError(err).Warn("failed to list tags for resource") + } else { + dbTags = tags.TagList + } + resources = append(resources, &NeptuneCluster{ - svc: svc, - ID: dbCluster.DBClusterIdentifier, + svc: svc, + ID: dbCluster.DBClusterIdentifier, + Tags: dbTags, }) } @@ -59,8 +71,9 @@ func (l *NeptuneClusterLister) List(_ context.Context, o interface{}) ([]resourc } type NeptuneCluster struct { - svc *neptune.Neptune - ID *string + svc *neptune.Neptune + ID *string + Tags []*neptune.Tag } func (f *NeptuneCluster) Remove(_ context.Context) error { @@ -72,6 +85,10 @@ func (f *NeptuneCluster) Remove(_ context.Context) error { return err } +func (f *NeptuneCluster) Properties() types.Properties { + return types.NewPropertiesFromStruct(f) +} + func (f *NeptuneCluster) String() string { return *f.ID } From 53a2f700f6d70c177beca55226a99ff20fbeb750 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 17:30:06 -0700 Subject: [PATCH 09/87] refactor(neptune-cluster): standardization --- .../{neptune-clusters.go => neptune-cluster.go} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename resources/{neptune-clusters.go => neptune-cluster.go} (84%) diff --git a/resources/neptune-clusters.go b/resources/neptune-cluster.go similarity index 84% rename from resources/neptune-clusters.go rename to resources/neptune-cluster.go index 92307b23..abdda876 100644 --- a/resources/neptune-clusters.go +++ b/resources/neptune-cluster.go @@ -76,19 +76,19 @@ type NeptuneCluster struct { Tags []*neptune.Tag } -func (f *NeptuneCluster) Remove(_ context.Context) error { - _, err := f.svc.DeleteDBCluster(&neptune.DeleteDBClusterInput{ - DBClusterIdentifier: f.ID, +func (r *NeptuneCluster) Remove(_ context.Context) error { + _, err := r.svc.DeleteDBCluster(&neptune.DeleteDBClusterInput{ + DBClusterIdentifier: r.ID, SkipFinalSnapshot: aws.Bool(true), }) return err } -func (f *NeptuneCluster) Properties() types.Properties { - return types.NewPropertiesFromStruct(f) +func (r *NeptuneCluster) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } -func (f *NeptuneCluster) String() string { - return *f.ID +func (r *NeptuneCluster) String() string { + return *r.ID } From e3a0fd3e0b5cb13f9bcf57b7d88d22ccd5a9d53c Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 18:01:39 -0700 Subject: [PATCH 10/87] fix(rds-instance): filter out neptune instances --- resources/rds-instances.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/rds-instances.go b/resources/rds-instances.go index 7bc0af26..997fd93d 100644 --- a/resources/rds-instances.go +++ b/resources/rds-instances.go @@ -4,6 +4,8 @@ import ( "context" "time" + "github.com/gotidy/ptr" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" @@ -52,6 +54,12 @@ func (l *RDSInstanceLister) List(_ context.Context, o interface{}) ([]resource.R resources := make([]resource.Resource, 0) for _, instance := range resp.DBInstances { + // Note: NeptuneInstance handles Neptune instances + if ptr.ToString(instance.Engine) == "neptune" { + opts.Logger.Debug("skipping neptune instance, it is handled by NeptuneInstance") + continue + } + tags, err := svc.ListTagsForResource(&rds.ListTagsForResourceInput{ ResourceName: instance.DBInstanceArn, }) From 2cc68d015c274566ff2cce8fde9fc6b261568837 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 18:02:05 -0700 Subject: [PATCH 11/87] feat(neptune): support deletion protect, properties, tags --- resources/neptune-cluster.go | 49 +++++++++++++++++++++++---- resources/neptune-instance.go | 62 +++++++++++++++++++++++++++++------ 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/resources/neptune-cluster.go b/resources/neptune-cluster.go index abdda876..a20abe26 100644 --- a/resources/neptune-cluster.go +++ b/resources/neptune-cluster.go @@ -2,12 +2,16 @@ package resources import ( "context" + "fmt" + + "github.com/gotidy/ptr" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/neptune" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + libsettings "github.com/ekristen/libnuke/pkg/settings" "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" @@ -21,6 +25,12 @@ func init() { Scope: nuke.Account, Resource: &NeptuneCluster{}, Lister: &NeptuneClusterLister{}, + DependsOn: []string{ + NeptuneInstanceResource, + }, + Settings: []string{ + "DisableDeletionProtection", + }, }) } @@ -54,9 +64,10 @@ func (l *NeptuneClusterLister) List(_ context.Context, o interface{}) ([]resourc } resources = append(resources, &NeptuneCluster{ - svc: svc, - ID: dbCluster.DBClusterIdentifier, - Tags: dbTags, + svc: svc, + ID: dbCluster.DBClusterIdentifier, + Status: dbCluster.Status, + Tags: dbTags, }) } @@ -71,15 +82,39 @@ func (l *NeptuneClusterLister) List(_ context.Context, o interface{}) ([]resourc } type NeptuneCluster struct { - svc *neptune.Neptune - ID *string - Tags []*neptune.Tag + svc *neptune.Neptune + settings *libsettings.Setting + + ID *string + Status *string + Tags []*neptune.Tag +} + +func (r *NeptuneCluster) Settings(settings *libsettings.Setting) { + r.settings = settings +} + +func (r *NeptuneCluster) Filter() error { + if ptr.ToString(r.Status) == "deleting" { + return fmt.Errorf("already deleting") + } + return nil } func (r *NeptuneCluster) Remove(_ context.Context) error { + if r.settings.GetBool("DisableDeletionProtection") { + _, err := r.svc.ModifyDBCluster(&neptune.ModifyDBClusterInput{ + DBClusterIdentifier: r.ID, + DeletionProtection: ptr.Bool(false), + }) + if err != nil { + return err + } + } + _, err := r.svc.DeleteDBCluster(&neptune.DeleteDBClusterInput{ DBClusterIdentifier: r.ID, - SkipFinalSnapshot: aws.Bool(true), + SkipFinalSnapshot: ptr.Bool(true), }) return err diff --git a/resources/neptune-instance.go b/resources/neptune-instance.go index 0d846b60..e889e6c6 100644 --- a/resources/neptune-instance.go +++ b/resources/neptune-instance.go @@ -2,7 +2,8 @@ package resources import ( "context" - + "fmt" + "github.com/gotidy/ptr" "github.com/sirupsen/logrus" "github.com/aws/aws-sdk-go/aws" @@ -10,6 +11,7 @@ import ( "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + libsettings "github.com/ekristen/libnuke/pkg/settings" "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" @@ -23,6 +25,10 @@ func init() { Scope: nuke.Account, Resource: &NeptuneInstance{}, Lister: &NeptuneInstanceLister{}, + Settings: []string{ + "DisableClusterDeletionProtection", + "DisableDeletionProtection", + }, }) } @@ -63,10 +69,12 @@ func (l *NeptuneInstanceLister) List(_ context.Context, o interface{}) ([]resour } resources = append(resources, &NeptuneInstance{ - svc: svc, - ID: dbInstance.DBInstanceIdentifier, - Name: dbInstance.DBName, - Tags: dbTags, + svc: svc, + ID: dbInstance.DBInstanceIdentifier, + ClusterID: dbInstance.DBClusterIdentifier, + Name: dbInstance.DBName, + Status: dbInstance.DBInstanceStatus, + Tags: dbTags, }) } @@ -81,16 +89,50 @@ func (l *NeptuneInstanceLister) List(_ context.Context, o interface{}) ([]resour } type NeptuneInstance struct { - svc *neptune.Neptune - ID *string - Name *string - Tags []*neptune.Tag + svc *neptune.Neptune + settings *libsettings.Setting + ID *string + ClusterID *string + Name *string + Status *string + Tags []*neptune.Tag +} + +func (r *NeptuneInstance) Settings(settings *libsettings.Setting) { + r.settings = settings +} + +func (r *NeptuneInstance) Filter() error { + if ptr.ToString(r.Status) == "deleting" { + return fmt.Errorf("already deleting") + } + return nil } func (r *NeptuneInstance) Remove(_ context.Context) error { + if r.settings.GetBool("DisableClusterDeletionProtection") { + _, err := r.svc.ModifyDBCluster(&neptune.ModifyDBClusterInput{ + DBClusterIdentifier: r.ClusterID, + DeletionProtection: ptr.Bool(false), + }) + if err != nil { + return err + } + } + + if r.settings.GetBool("DisableDeletionProtection") { + _, err := r.svc.ModifyDBInstance(&neptune.ModifyDBInstanceInput{ + DBInstanceIdentifier: r.ID, + DeletionProtection: ptr.Bool(false), + }) + if err != nil { + return err + } + } + _, err := r.svc.DeleteDBInstance(&neptune.DeleteDBInstanceInput{ DBInstanceIdentifier: r.ID, - SkipFinalSnapshot: aws.Bool(true), + SkipFinalSnapshot: ptr.Bool(true), }) return err From ebb8d05837a337798a28bc0da834aa5110b214a5 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 18:03:29 -0700 Subject: [PATCH 12/87] docs(neptune): auto-generated, settings and properties --- docs/resources/neptune-cluster.md | 26 ++++++++++++++++++++++++++ docs/resources/neptune-instance.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/docs/resources/neptune-cluster.md b/docs/resources/neptune-cluster.md index f68c11ae..334f4bb1 100644 --- a/docs/resources/neptune-cluster.md +++ b/docs/resources/neptune-cluster.md @@ -15,6 +15,9 @@ NeptuneCluster - `ID`: No Description +- `Status`: No Description +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property @@ -28,3 +31,26 @@ the filter. The string value is always what is used in the output of the log format when a resource is identified. +## Settings + +- `DisableDeletionProtection` + + +### DisableDeletionProtection + +!!! note + There is currently no description for this setting. Often times settings are fairly self-explanatory. However, we + are working on adding descriptions for all settings. + +```text +DisableDeletionProtection +``` + +### DependsOn + +!!! important - Experimental Feature + This resource depends on a resource using the experimental feature. This means that the resource will + only be deleted if all the resources of a particular type are deleted first or reach a terminal state. + +- [NeptuneInstance](./neptune-instance.md) + diff --git a/docs/resources/neptune-instance.md b/docs/resources/neptune-instance.md index 89855d98..d8177a5b 100644 --- a/docs/resources/neptune-instance.md +++ b/docs/resources/neptune-instance.md @@ -14,8 +14,10 @@ NeptuneInstance ## Properties +- `ClusterID`: No Description - `ID`: No Description - `Name`: No Description +- `Status`: No Description - `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) @@ -31,3 +33,30 @@ the filter. The string value is always what is used in the output of the log format when a resource is identified. +## Settings + +- `DisableClusterDeletionProtection` +- `DisableDeletionProtection` + + +### DisableClusterDeletionProtection + +!!! note + There is currently no description for this setting. Often times settings are fairly self-explanatory. However, we + are working on adding descriptions for all settings. + +```text +DisableClusterDeletionProtection +``` + + +### DisableDeletionProtection + +!!! note + There is currently no description for this setting. Often times settings are fairly self-explanatory. However, we + are working on adding descriptions for all settings. + +```text +DisableDeletionProtection +``` + From 462e441e14befbfdfaf07bcd9747aaef72d60725 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 12 Dec 2024 18:04:24 -0700 Subject: [PATCH 13/87] chore(golangci-lint): fix goimports --- resources/neptune-instance.go | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/neptune-instance.go b/resources/neptune-instance.go index e889e6c6..1e24100c 100644 --- a/resources/neptune-instance.go +++ b/resources/neptune-instance.go @@ -3,6 +3,7 @@ package resources import ( "context" "fmt" + "github.com/gotidy/ptr" "github.com/sirupsen/logrus" From f01fa7c98fe70e1cf9891a064573986d510a433a Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 13 Dec 2024 08:22:16 -0700 Subject: [PATCH 14/87] fix(cloudwatch-alarm): query for both composite and metric alarms --- resources/cloudwatch-alarms.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/cloudwatch-alarms.go b/resources/cloudwatch-alarms.go index a7d9bc22..f1e6b002 100644 --- a/resources/cloudwatch-alarms.go +++ b/resources/cloudwatch-alarms.go @@ -3,6 +3,8 @@ package resources import ( "context" + "github.com/gotidy/ptr" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudwatch" @@ -33,6 +35,10 @@ func (l *CloudWatchAlarmLister) List(_ context.Context, o interface{}) ([]resour resources := make([]resource.Resource, 0) params := &cloudwatch.DescribeAlarmsInput{ + AlarmTypes: []*string{ + ptr.String(cloudwatch.AlarmTypeCompositeAlarm), + ptr.String(cloudwatch.AlarmTypeMetricAlarm), + }, MaxRecords: aws.Int64(100), } From 5248581f08415ec2b123d8fa5501a786ec335fc3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 21:05:25 +0000 Subject: [PATCH 15/87] fix(deps): update aws-sdk-go-v2 monorepo --- go.mod | 28 ++++++++++++++-------------- go.sum | 28 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index cd60d0de..e1687efd 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.21.6 require ( github.com/aws/aws-sdk-go v1.54.20 - github.com/aws/aws-sdk-go-v2 v1.32.6 - github.com/aws/aws-sdk-go-v2/config v1.28.6 - github.com/aws/aws-sdk-go-v2/credentials v1.17.47 - github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 - github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 + github.com/aws/aws-sdk-go-v2 v1.32.7 + github.com/aws/aws-sdk-go-v2/config v1.28.7 + github.com/aws/aws-sdk-go-v2/credentials v1.17.48 + github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 + github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/smithy-go v1.22.1 github.com/ekristen/libnuke v0.22.0 github.com/fatih/color v1.18.0 @@ -26,17 +26,17 @@ require ( require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 76269efa..f4fc7bbe 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= +github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= @@ -16,30 +18,40 @@ github.com/aws/aws-sdk-go-v2/config v1.28.1 h1:oxIvOUXy8x0U3fR//0eq+RdCKimWI900+ github.com/aws/aws-sdk-go-v2/config v1.28.1/go.mod h1:bRQcttQJiARbd5JZxw6wG0yIK3eLeSCPdg6uqmmlIiI= github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= +github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= +github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= github.com/aws/aws-sdk-go-v2/credentials v1.17.41 h1:7gXo+Axmp+R4Z+AK8YFQO0ZV3L0gizGINCOWxSLY9W8= github.com/aws/aws-sdk-go-v2/credentials v1.17.41/go.mod h1:u4Eb8d3394YLubphT4jLEwN1rLNq2wFOlT6OuxFwPzU= github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM= github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M= github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 h1:TMH3f/SCAWdNtXXVPPu5D6wrr4G5hI1rAxbcocKfC7Q= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17/go.mod h1:1ZRXLdTpzdJb9fwTMXiLipENRxkGMTn1sfKexGllQCw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 h1:68jFVtt3NulEzojFesM/WVarlFpCaXLKaBxDpzkQ9OQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18/go.mod h1:Fjnn5jQVIo6VyedMc0/EhPpfNlPl7dHV916O6B+49aE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 h1:7edmS3VOBDhK00b/MwGtGglCm7hhwNYnjJs/PgFdMQE= @@ -48,6 +60,8 @@ github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22 h1:yV+hCAHZZYJQcwAaszoBNwLbPIt github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22/go.mod h1:kbR1TL8llqB1eGnVbybcA4/wgScxdylOdyAd51yxPdw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= @@ -58,42 +72,56 @@ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3 h1:kT6BcZsmMtNkP/i github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3/go.mod h1:Z8uGua2k4PPaGOYn66pK02rhMrot3Xk3tpBuUFPomZU= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 h1:t7iUP9+4wdc5lt3E41huP+GvQZJD38WLsgVp4iOtAjg= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2/go.mod h1:/niFCtmuQNxqx9v8WAPq5qh7EH25U4BF6tjoyq9bObM= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3 h1:ZC7Y/XgKUxwqcdhO5LE8P6oGP1eh6xlQReWNKfhvJno= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3/go.mod h1:WqfO7M9l9yUAw0HcHaikwRd/H6gzYdz7vjejCA5e2oY= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= github.com/aws/aws-sdk-go-v2/service/s3 v1.65.2 h1:yi8m+jepdp6foK14xXLGkYBenxnlcfJ45ka4Pg7fDSQ= github.com/aws/aws-sdk-go-v2/service/s3 v1.65.2/go.mod h1:cB6oAuus7YXRZhWCc1wIwPywwZ1XwweNp2TVAEGYeB8= github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2 h1:p9TNFL8bFUMd+38YIpTAXpoxyz0MxC7FlbFEH4P4E1U= github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2/go.mod h1:fNjyo0Coen9QTwQLWeV6WO2Nytwiu+cCcWaTdKCAqqE= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 h1:bSYXVyUzoTHoKalBmwaZxs97HU9DWWI3ehHSAMa7xOk= github.com/aws/aws-sdk-go-v2/service/sso v1.24.2/go.mod h1:skMqY7JElusiOUjMJMOv1jJsP7YUg7DrhgqZZWuzu1U= github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 h1:UTpsIf0loCIWEbrqdLb+0RxnTXfWh2vhw4nQmFi4nPc= github.com/aws/aws-sdk-go-v2/service/sso v1.24.3/go.mod h1:FZ9j3PFHHAR+w0BSEjK955w5YD2UwB/l/H0yAK3MJvI= github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/LfUWUF+zFPEcY9QXb7o= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 h1:2YCmIXv3tmiItw0LlYf6v7gEHebLY45kBEnPezbUKyU= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3/go.mod h1:u19stRyNPxGhj6dRm+Cdgu6N75qnbW7+QN0q0dsAk58= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo= github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo= github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 h1:wVnQ6tigGsRqSWDEEyH6lSAJ9OyFUsSnbaUWChuSGzs= github.com/aws/aws-sdk-go-v2/service/sts v1.32.3/go.mod h1:VZa9yTFyj4o10YGsmDO4gbQJUvvhY72fhumT8W4LqsE= github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= From 0bef229f4fd1f8707535e95753b25cfd3d385be1 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 12:03:37 -0700 Subject: [PATCH 16/87] feat(cloudwatch-alarm): support composite alarms --- ...oudwatch-alarms.go => cloudwatch-alarm.go} | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) rename resources/{cloudwatch-alarms.go => cloudwatch-alarm.go} (67%) diff --git a/resources/cloudwatch-alarms.go b/resources/cloudwatch-alarm.go similarity index 67% rename from resources/cloudwatch-alarms.go rename to resources/cloudwatch-alarm.go index f1e6b002..ca9155e9 100644 --- a/resources/cloudwatch-alarms.go +++ b/resources/cloudwatch-alarm.go @@ -2,7 +2,6 @@ package resources import ( "context" - "github.com/gotidy/ptr" "github.com/aws/aws-sdk-go/aws" @@ -54,9 +53,23 @@ func (l *CloudWatchAlarmLister) List(_ context.Context, o interface{}) ([]resour return nil, err } resources = append(resources, &CloudWatchAlarm{ - svc: svc, - alarmName: metricAlarm.AlarmName, - tags: tags, + svc: svc, + Name: metricAlarm.AlarmName, + Type: ptr.String(cloudwatch.AlarmTypeMetricAlarm), + Tags: tags, + }) + } + + for _, compositeAlarm := range output.CompositeAlarms { + tags, err := GetAlarmTags(svc, compositeAlarm.AlarmArn) + if err != nil { + return nil, err + } + resources = append(resources, &CloudWatchAlarm{ + svc: svc, + Name: compositeAlarm.AlarmName, + Type: ptr.String(cloudwatch.AlarmTypeCompositeAlarm), + Tags: tags, }) } @@ -80,29 +93,24 @@ func GetAlarmTags(svc *cloudwatch.CloudWatch, arn *string) ([]*cloudwatch.Tag, e } type CloudWatchAlarm struct { - svc *cloudwatch.CloudWatch - alarmName *string - tags []*cloudwatch.Tag + svc *cloudwatch.CloudWatch + Name *string + Type *string + Tags []*cloudwatch.Tag } -func (f *CloudWatchAlarm) Remove(_ context.Context) error { - _, err := f.svc.DeleteAlarms(&cloudwatch.DeleteAlarmsInput{ - AlarmNames: []*string{f.alarmName}, +func (r *CloudWatchAlarm) Remove(_ context.Context) error { + _, err := r.svc.DeleteAlarms(&cloudwatch.DeleteAlarmsInput{ + AlarmNames: []*string{r.Name}, }) return err } -func (f *CloudWatchAlarm) Properties() types.Properties { - properties := types.NewProperties() - properties.Set("Name", f.alarmName) - - for _, tag := range f.tags { - properties.SetTag(tag.Key, tag.Value) - } - return properties +func (r *CloudWatchAlarm) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } -func (f *CloudWatchAlarm) String() string { - return *f.alarmName +func (r *CloudWatchAlarm) String() string { + return *r.Name } From 85f11522d1484858bddbae2c12857f38b9d57158 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 12:09:26 -0700 Subject: [PATCH 17/87] fix(ec2-tgw): ensure state is set --- resources/ec2-tgw.go | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/ec2-tgw.go b/resources/ec2-tgw.go index eada36ec..9e6f5a54 100644 --- a/resources/ec2-tgw.go +++ b/resources/ec2-tgw.go @@ -51,6 +51,7 @@ func (l *EC2TGWLister) List(_ context.Context, o interface{}) ([]resource.Resour ID: tgw.TransitGatewayId, OwnerID: tgw.OwnerId, Tags: tgw.Tags, + State: tgw.State, accountID: opts.AccountID, }) } From a2f2cff447ec045e5e557c1a3eae7edda4c656ee Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 12:11:11 -0700 Subject: [PATCH 18/87] chore(cloudwatch-alarm): address golangci-lint rule violation --- resources/cloudwatch-alarm.go | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/cloudwatch-alarm.go b/resources/cloudwatch-alarm.go index ca9155e9..127e226f 100644 --- a/resources/cloudwatch-alarm.go +++ b/resources/cloudwatch-alarm.go @@ -2,6 +2,7 @@ package resources import ( "context" + "github.com/gotidy/ptr" "github.com/aws/aws-sdk-go/aws" From c2f936c5842829c69d0386d14ed8510aa6b8bcfd Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 12:14:05 -0700 Subject: [PATCH 19/87] fix(resource-explorer2-index): limit to current region --- resources/resource-explorer2-indexes.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/resource-explorer2-indexes.go b/resources/resource-explorer2-indexes.go index d771d6d4..b0f9a2b1 100644 --- a/resources/resource-explorer2-indexes.go +++ b/resources/resource-explorer2-indexes.go @@ -33,7 +33,10 @@ func (l *ResourceExplorer2IndexLister) List(_ context.Context, o interface{}) ([ svc := resourceexplorer2.New(opts.Session) var resources []resource.Resource - params := &resourceexplorer2.ListIndexesInput{} + params := &resourceexplorer2.ListIndexesInput{ + Regions: aws.StringSlice([]string{opts.Region.Name}), + MaxResults: aws.Int64(100), + } for { output, err := svc.ListIndexes(params) From e457ef36f3db482f5cd070f18ad2f9456aeda2ea Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 12:15:04 -0700 Subject: [PATCH 20/87] refactor(resource-explorer2-index): standardization --- ...plorer2-indexes.go => resource-explorer2-index.go} | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) rename resources/{resource-explorer2-indexes.go => resource-explorer2-index.go} (86%) diff --git a/resources/resource-explorer2-indexes.go b/resources/resource-explorer2-index.go similarity index 86% rename from resources/resource-explorer2-indexes.go rename to resources/resource-explorer2-index.go index b0f9a2b1..03cb03e3 100644 --- a/resources/resource-explorer2-indexes.go +++ b/resources/resource-explorer2-index.go @@ -2,7 +2,6 @@ package resources import ( "context" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/resourceexplorer2" "github.com/ekristen/aws-nuke/v3/pkg/nuke" @@ -61,14 +60,14 @@ func (l *ResourceExplorer2IndexLister) List(_ context.Context, o interface{}) ([ return resources, nil } -func (f *ResourceExplorer2Index) Remove(_ context.Context) error { - _, err := f.svc.DeleteIndex(&resourceexplorer2.DeleteIndexInput{ - Arn: f.indexArn, +func (r *ResourceExplorer2Index) Remove(_ context.Context) error { + _, err := r.svc.DeleteIndex(&resourceexplorer2.DeleteIndexInput{ + Arn: r.indexArn, }) return err } -func (f *ResourceExplorer2Index) String() string { - return *f.indexArn +func (r *ResourceExplorer2Index) String() string { + return *r.indexArn } From aa8a6ffdec287f996b5a4dc7c12d1d3a24fb2865 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 12:17:15 -0700 Subject: [PATCH 21/87] feat(resource-explorer2-index): add properties --- resources/resource-explorer2-index.go | 30 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/resources/resource-explorer2-index.go b/resources/resource-explorer2-index.go index 03cb03e3..06a5ea4c 100644 --- a/resources/resource-explorer2-index.go +++ b/resources/resource-explorer2-index.go @@ -2,11 +2,15 @@ package resources import ( "context" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/resourceexplorer2" - "github.com/ekristen/aws-nuke/v3/pkg/nuke" + "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) const ResourceExplorer2IndexResource = "ResourceExplorer2Index" @@ -22,11 +26,6 @@ func init() { type ResourceExplorer2IndexLister struct{} -type ResourceExplorer2Index struct { - svc *resourceexplorer2.ResourceExplorer2 - indexArn *string -} - func (l *ResourceExplorer2IndexLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) svc := resourceexplorer2.New(opts.Session) @@ -45,8 +44,9 @@ func (l *ResourceExplorer2IndexLister) List(_ context.Context, o interface{}) ([ for _, index := range output.Indexes { resources = append(resources, &ResourceExplorer2Index{ - svc: svc, - indexArn: index.Arn, + svc: svc, + ARN: index.Arn, + Type: index.Type, }) } @@ -60,14 +60,24 @@ func (l *ResourceExplorer2IndexLister) List(_ context.Context, o interface{}) ([ return resources, nil } +type ResourceExplorer2Index struct { + svc *resourceexplorer2.ResourceExplorer2 + ARN *string + Type *string +} + func (r *ResourceExplorer2Index) Remove(_ context.Context) error { _, err := r.svc.DeleteIndex(&resourceexplorer2.DeleteIndexInput{ - Arn: r.indexArn, + Arn: r.ARN, }) return err } func (r *ResourceExplorer2Index) String() string { - return *r.indexArn + return *r.ARN +} + +func (r *ResourceExplorer2Index) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } From af756810f6a3808c6b43ff0f259628a44d6b8566 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 13:29:45 -0700 Subject: [PATCH 22/87] feat(kms-alias): pass key tags through to alias with key: prefix --- resources/kms-alias.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/resources/kms-alias.go b/resources/kms-alias.go index f8669f18..f32efc04 100644 --- a/resources/kms-alias.go +++ b/resources/kms-alias.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/aws/aws-sdk-go/service/kms" "github.com/aws/aws-sdk-go/service/kms/kmsiface" @@ -43,9 +44,21 @@ func (l *KMSAliasLister) List(_ context.Context, o interface{}) ([]resource.Reso err := svc.ListAliasesPages(nil, func(page *kms.ListAliasesOutput, lastPage bool) bool { for _, alias := range page.Aliases { + var tags []*kms.Tag + if alias.TargetKeyId != nil { + keyTags, err := svc.ListResourceTags(&kms.ListResourceTagsInput{ + KeyId: alias.TargetKeyId, + }) + if err != nil { + opts.Logger.WithError(err).Error("failed to list tags for key") + } + tags = keyTags.Tags + } + resources = append(resources, &KMSAlias{ svc: svc, Name: alias.AliasName, + Tags: tags, }) } return true @@ -58,8 +71,11 @@ func (l *KMSAliasLister) List(_ context.Context, o interface{}) ([]resource.Reso } type KMSAlias struct { - svc kmsiface.KMSAPI - Name *string + svc kmsiface.KMSAPI + Name *string `description:"The name of the KMS alias"` + CreationDate *time.Time `description:"The creation date of the KMS alias"` + TargetKeyID *string `description:"The KMS Key ID that the alias points to"` + Tags []*kms.Tag `property:"tagPrefix=key:tag"` } func (r *KMSAlias) Filter() error { From 15c88324adbad236eadcb44a804e362e4ed3aabc Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 13:31:06 -0700 Subject: [PATCH 23/87] docs(kms-alias): auto-generated --- docs/resources/kms-alias.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/resources/kms-alias.md b/docs/resources/kms-alias.md index aebe4705..c59fff3e 100644 --- a/docs/resources/kms-alias.md +++ b/docs/resources/kms-alias.md @@ -14,7 +14,11 @@ KMSAlias ## Properties -- `Name`: No Description +- `CreationDate`: The creation date of the KMS alias +- `Name`: The name of the KMS alias +- `TargetKeyID`: The KMS Key ID that the alias points to +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property From 74753efe792bf303fb1322b15f1e06f4b6b72b4d Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 13:48:24 -0700 Subject: [PATCH 24/87] feat(route53-resource-record): pass hosted zone tags to resource record set with zone:tag prefix --- ...-records.go => route53-resource-record.go} | 44 +++++++++-------- resources/route53-resource-record_test.go | 49 +++++++++++++++++++ 2 files changed, 73 insertions(+), 20 deletions(-) rename resources/{route53-resource-records.go => route53-resource-record.go} (72%) create mode 100644 resources/route53-resource-record_test.go diff --git a/resources/route53-resource-records.go b/resources/route53-resource-record.go similarity index 72% rename from resources/route53-resource-records.go rename to resources/route53-resource-record.go index 70bab19d..610e5f89 100644 --- a/resources/route53-resource-records.go +++ b/resources/route53-resource-record.go @@ -44,7 +44,7 @@ func (l *Route53ResourceRecordSetLister) List(ctx context.Context, o interface{} for _, r := range sub { zone := r.(*Route53HostedZone) - rrs, err := ListResourceRecordsForZone(svc, zone.id, zone.name) + rrs, err := ListResourceRecordsForZone(svc, zone.id, zone.name, zone.tags) if err != nil { return nil, err } @@ -55,13 +55,13 @@ func (l *Route53ResourceRecordSetLister) List(ctx context.Context, o interface{} return resources, nil } -func ListResourceRecordsForZone(svc *route53.Route53, zoneID, zoneName *string) ([]resource.Resource, error) { +func ListResourceRecordsForZone(svc *route53.Route53, zoneID, zoneName *string, zoneTags []*route53.Tag) ([]resource.Resource, error) { + resources := make([]resource.Resource, 0) + params := &route53.ListResourceRecordSetsInput{ HostedZoneId: zoneID, } - resources := make([]resource.Resource, 0) - for { resp, err := svc.ListResourceRecordSets(params) if err != nil { @@ -70,10 +70,13 @@ func ListResourceRecordsForZone(svc *route53.Route53, zoneID, zoneName *string) for _, rrs := range resp.ResourceRecordSets { resources = append(resources, &Route53ResourceRecordSet{ - svc: svc, - hostedZoneID: zoneID, - hostedZoneName: zoneName, - data: rrs, + svc: svc, + hostedZoneID: zoneID, + HostedZoneName: zoneName, + resourceRecordSet: rrs, + Name: rrs.Name, + Type: rrs.Type, + Tags: zoneTags, }) } @@ -90,19 +93,22 @@ func ListResourceRecordsForZone(svc *route53.Route53, zoneID, zoneName *string) } type Route53ResourceRecordSet struct { - svc *route53.Route53 - hostedZoneID *string - hostedZoneName *string - data *route53.ResourceRecordSet - changeID *string + svc *route53.Route53 + resourceRecordSet *route53.ResourceRecordSet // Note: this is required for the deletion + changeID *string + hostedZoneID *string + HostedZoneName *string `description:"The name of the zone to which the record belongs"` + Name *string `description:"The name of the record"` + Type *string `description:"The type of the record"` + Tags []*route53.Tag `property:"tagPrefix=zone:tag"` } func (r *Route53ResourceRecordSet) Filter() error { - if *r.data.Type == "NS" && *r.hostedZoneName == *r.data.Name { + if *r.Type == "NS" && *r.HostedZoneName == *r.Name { return fmt.Errorf("cannot delete NS record") } - if *r.data.Type == "SOA" { + if *r.Type == "SOA" { return fmt.Errorf("cannot delete SOA record") } @@ -116,7 +122,7 @@ func (r *Route53ResourceRecordSet) Remove(_ context.Context) error { Changes: []*route53.Change{ { Action: aws.String("DELETE"), - ResourceRecordSet: r.data, + ResourceRecordSet: r.resourceRecordSet, }, }, }, @@ -133,11 +139,9 @@ func (r *Route53ResourceRecordSet) Remove(_ context.Context) error { } func (r *Route53ResourceRecordSet) Properties() types.Properties { - return types.NewProperties(). - Set("Name", r.data.Name). - Set("Type", r.data.Type) + return types.NewPropertiesFromStruct(r) } func (r *Route53ResourceRecordSet) String() string { - return ptr.ToString(r.data.Name) + return ptr.ToString(r.Name) } diff --git a/resources/route53-resource-record_test.go b/resources/route53-resource-record_test.go new file mode 100644 index 00000000..e8fc6d7e --- /dev/null +++ b/resources/route53-resource-record_test.go @@ -0,0 +1,49 @@ +package resources + +import ( + "testing" + + "github.com/gotidy/ptr" + "github.com/stretchr/testify/assert" + + "github.com/aws/aws-sdk-go/service/route53" +) + +func TestRoute53ResourceRecordSet_Properties(t *testing.T) { + cases := []struct { + name string + recordType string + }{ + { + name: "example.com", + recordType: "NS", + }, + { + name: "example.com", + recordType: "SOA", + }, + { + name: "subdomain.example.com", + recordType: "A", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + r := &Route53ResourceRecordSet{ + resourceRecordSet: &route53.ResourceRecordSet{ + Name: ptr.String(tc.name), + Type: ptr.String(tc.recordType), + }, + Name: ptr.String(tc.name), + Type: ptr.String(tc.recordType), + } + + got := r.Properties() + assert.Equal(t, tc.name, got.Get("Name")) + assert.Equal(t, tc.recordType, got.Get("Type")) + + assert.Equal(t, tc.name, r.String()) + }) + } +} From 7b6462512befdb8332465ae7969a4375d87a7ade Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 15:39:35 -0700 Subject: [PATCH 25/87] refactor(s3-bucket): query on region vs querying region separate --- resources/s3-bucket-helpers.go | 172 ++++++++++++++++++++++ resources/s3-bucket.go | 237 ++++-------------------------- resources/s3-bucket_test.go | 29 ++-- resources/s3-multipart-uploads.go | 3 +- resources/s3-objects.go | 3 +- 5 files changed, 221 insertions(+), 223 deletions(-) create mode 100644 resources/s3-bucket-helpers.go diff --git a/resources/s3-bucket-helpers.go b/resources/s3-bucket-helpers.go new file mode 100644 index 00000000..6e3b4fcf --- /dev/null +++ b/resources/s3-bucket-helpers.go @@ -0,0 +1,172 @@ +package resources + +import ( + "context" + "time" + + "github.com/gotidy/ptr" + "github.com/sirupsen/logrus" + + "github.com/aws/aws-sdk-go-v2/service/s3" + s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" + + "github.com/ekristen/aws-nuke/v3/pkg/awsmod" +) + +func bypassGovernanceRetention(input *s3.DeleteObjectsInput) { + input.BypassGovernanceRetention = ptr.Bool(true) +} + +type s3DeleteVersionListIterator struct { + Bucket *string + Paginator *s3.ListObjectVersionsPaginator + objects []s3types.ObjectVersion + lastNotify time.Time + BypassGovernanceRetention *bool + err error +} + +func newS3DeleteVersionListIterator( + svc *s3.Client, + input *s3.ListObjectVersionsInput, + bypass bool, + opts ...func(*s3DeleteVersionListIterator)) awsmod.BatchDeleteIterator { + iter := &s3DeleteVersionListIterator{ + Bucket: input.Bucket, + Paginator: s3.NewListObjectVersionsPaginator(svc, input), + BypassGovernanceRetention: ptr.Bool(bypass), + } + + for _, opt := range opts { + opt(iter) + } + + return iter +} + +// Next will use the S3API client to iterate through a list of objects. +func (iter *s3DeleteVersionListIterator) Next() bool { + if len(iter.objects) > 0 { + iter.objects = iter.objects[1:] + if len(iter.objects) > 0 { + return true + } + } + + if !iter.Paginator.HasMorePages() { + return false + } + + page, err := iter.Paginator.NextPage(context.TODO()) + if err != nil { + iter.err = err + return false + } + + iter.objects = page.Versions + for _, entry := range page.DeleteMarkers { + iter.objects = append(iter.objects, s3types.ObjectVersion{ + Key: entry.Key, + VersionId: entry.VersionId, + }) + } + + if len(iter.objects) > 500 && (iter.lastNotify.IsZero() || time.Since(iter.lastNotify) > 120*time.Second) { + logrus.Infof( + "S3Bucket: %s - empty bucket operation in progress, this could take a while, please be patient", + *iter.Bucket) + iter.lastNotify = time.Now().UTC() + } + + return len(iter.objects) > 0 +} + +// Err will return the last known error from Next. +func (iter *s3DeleteVersionListIterator) Err() error { + return iter.err +} + +// DeleteObject will return the current object to be deleted. +func (iter *s3DeleteVersionListIterator) DeleteObject() awsmod.BatchDeleteObject { + return awsmod.BatchDeleteObject{ + Object: &s3.DeleteObjectInput{ + Bucket: iter.Bucket, + Key: iter.objects[0].Key, + VersionId: iter.objects[0].VersionId, + BypassGovernanceRetention: iter.BypassGovernanceRetention, + }, + } +} + +type s3ObjectDeleteListIterator struct { + Bucket *string + Paginator *s3.ListObjectsV2Paginator + objects []s3types.Object + lastNotify time.Time + BypassGovernanceRetention bool + err error +} + +func newS3ObjectDeleteListIterator( + svc *s3.Client, + input *s3.ListObjectsV2Input, + bypass bool, + opts ...func(*s3ObjectDeleteListIterator)) awsmod.BatchDeleteIterator { + iter := &s3ObjectDeleteListIterator{ + Bucket: input.Bucket, + Paginator: s3.NewListObjectsV2Paginator(svc, input), + BypassGovernanceRetention: bypass, + } + + for _, opt := range opts { + opt(iter) + } + return iter +} + +// Next will use the S3API client to iterate through a list of objects. +func (iter *s3ObjectDeleteListIterator) Next() bool { + if len(iter.objects) > 0 { + iter.objects = iter.objects[1:] + if len(iter.objects) > 0 { + return true + } + } + + if !iter.Paginator.HasMorePages() { + return false + } + + page, err := iter.Paginator.NextPage(context.TODO()) + if err != nil { + iter.err = err + return false + } + + iter.objects = page.Contents + + if len(iter.objects) > 500 && (iter.lastNotify.IsZero() || time.Since(iter.lastNotify) > 120*time.Second) { + logrus.Infof( + "S3Bucket: %s - empty bucket operation in progress, this could take a while, please be patient", + *iter.Bucket) + iter.lastNotify = time.Now().UTC() + } + + return len(iter.objects) > 0 +} + +// Err will return the last known error from Next. +func (iter *s3ObjectDeleteListIterator) Err() error { + return iter.err +} + +// DeleteObject will return the current object to be deleted. +func (iter *s3ObjectDeleteListIterator) DeleteObject() awsmod.BatchDeleteObject { + return awsmod.BatchDeleteObject{ + Object: &s3.DeleteObjectInput{ + Bucket: iter.Bucket, + Key: iter.objects[0].Key, + BypassGovernanceRetention: ptr.Bool(iter.BypassGovernanceRetention), + }, + } +} diff --git a/resources/s3-bucket.go b/resources/s3-bucket.go index 96a99d1e..d264214e 100644 --- a/resources/s3-bucket.go +++ b/resources/s3-bucket.go @@ -9,7 +9,6 @@ import ( "github.com/gotidy/ptr" "github.com/sirupsen/logrus" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/s3" s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/aws/smithy-go" @@ -48,7 +47,7 @@ func (l *S3BucketLister) List(ctx context.Context, o interface{}) ([]resource.Re opts := o.(*nuke.ListerOpts) svc := s3.NewFromConfig(*opts.Config) - buckets, err := DescribeS3Buckets(ctx, svc) + buckets, err := DescribeS3Buckets(ctx, svc, opts) if err != nil { return nil, err } @@ -57,13 +56,13 @@ func (l *S3BucketLister) List(ctx context.Context, o interface{}) ([]resource.Re for _, bucket := range buckets { newBucket := &S3Bucket{ svc: svc, - name: aws.ToString(bucket.Name), - creationDate: aws.ToTime(bucket.CreationDate), - tags: make([]s3types.Tag, 0), + Name: bucket.Name, + CreationDate: bucket.CreationDate, + Tags: make([]s3types.Tag, 0), } lockCfg, err := svc.GetObjectLockConfiguration(ctx, &s3.GetObjectLockConfigurationInput{ - Bucket: &newBucket.name, + Bucket: newBucket.Name, }) if err != nil { // check if aws error is NoSuchObjectLockConfiguration @@ -92,7 +91,7 @@ func (l *S3BucketLister) List(ctx context.Context, o interface{}) ([]resource.Re continue } - newBucket.tags = tags.TagSet + newBucket.Tags = tags.TagSet resources = append(resources, newBucket) } @@ -105,32 +104,27 @@ type DescribeS3BucketsAPIClient interface { GetBucketLocation(context.Context, *s3.GetBucketLocationInput, ...func(*s3.Options)) (*s3.GetBucketLocationOutput, error) } -func DescribeS3Buckets(ctx context.Context, svc DescribeS3BucketsAPIClient) ([]s3types.Bucket, error) { - resp, err := svc.ListBuckets(ctx, nil) - if err != nil { - return nil, err +func DescribeS3Buckets(ctx context.Context, svc DescribeS3BucketsAPIClient, opts *nuke.ListerOpts) ([]s3types.Bucket, error) { + buckets := make([]s3types.Bucket, 0) + + params := &s3.ListBucketsInput{ + BucketRegion: ptr.String(opts.Region.Name), + MaxBuckets: ptr.Int32(100), } - buckets := make([]s3types.Bucket, 0) - for _, out := range resp.Buckets { - bucketLocationResponse, err := svc.GetBucketLocation(ctx, &s3.GetBucketLocationInput{Bucket: out.Name}) + for { + resp, err := svc.ListBuckets(ctx, params) if err != nil { - continue + return nil, err } - location := string(bucketLocationResponse.LocationConstraint) - if location == "" { - location = "us-east-1" - } + buckets = append(buckets, resp.Buckets...) - region := svc.Options().Region - if region == "" { - region = "us-east-1" + if resp.ContinuationToken == nil { + break } - if location == region { - buckets = append(buckets, out) - } + params.ContinuationToken = resp.ContinuationToken } return buckets, nil @@ -139,22 +133,22 @@ func DescribeS3Buckets(ctx context.Context, svc DescribeS3BucketsAPIClient) ([]s type S3Bucket struct { svc *s3.Client settings *libsettings.Setting - name string - creationDate time.Time - tags []s3types.Tag + Name *string + CreationDate *time.Time + Tags []s3types.Tag ObjectLock s3types.ObjectLockEnabled } func (r *S3Bucket) Remove(ctx context.Context) error { _, err := r.svc.DeleteBucketPolicy(ctx, &s3.DeleteBucketPolicyInput{ - Bucket: &r.name, + Bucket: r.Name, }) if err != nil { return err } _, err = r.svc.PutBucketLogging(ctx, &s3.PutBucketLoggingInput{ - Bucket: &r.name, + Bucket: r.Name, BucketLoggingStatus: &s3types.BucketLoggingStatus{}, }) if err != nil { @@ -177,7 +171,7 @@ func (r *S3Bucket) Remove(ctx context.Context) error { } _, err = r.svc.DeleteBucket(ctx, &s3.DeleteBucketInput{ - Bucket: &r.name, + Bucket: r.Name, }) return err @@ -188,12 +182,12 @@ func (r *S3Bucket) RemoveAllLegalHolds(ctx context.Context) error { return nil } - if r.ObjectLock == "" || r.ObjectLock != s3types.ObjectLockEnabledEnabled { + if r.ObjectLock != s3types.ObjectLockEnabledEnabled { return nil } params := &s3.ListObjectsV2Input{ - Bucket: &r.name, + Bucket: r.Name, } for { @@ -206,7 +200,7 @@ func (r *S3Bucket) RemoveAllLegalHolds(ctx context.Context) error { for _, obj := range res.Contents { _, err := r.svc.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{ - Bucket: &r.name, + Bucket: r.Name, Key: obj.Key, LegalHold: &s3types.ObjectLockLegalHold{Status: s3types.ObjectLockLegalHoldStatusOff}, }) @@ -225,7 +219,7 @@ func (r *S3Bucket) RemoveAllLegalHolds(ctx context.Context) error { func (r *S3Bucket) RemoveAllVersions(ctx context.Context) error { params := &s3.ListObjectVersionsInput{ - Bucket: &r.name, + Bucket: r.Name, } var setBypass bool @@ -242,7 +236,7 @@ func (r *S3Bucket) RemoveAllVersions(ctx context.Context) error { func (r *S3Bucket) RemoveAllObjects(ctx context.Context) error { params := &s3.ListObjectsV2Input{ - Bucket: &r.name, + Bucket: r.Name, } var setBypass bool @@ -262,176 +256,9 @@ func (r *S3Bucket) Settings(settings *libsettings.Setting) { } func (r *S3Bucket) Properties() types.Properties { - properties := types.NewProperties(). - Set("Name", r.name). - Set("CreationDate", r.creationDate.Format(time.RFC3339)). - Set("ObjectLock", r.ObjectLock) - - for _, tag := range r.tags { - properties.SetTag(tag.Key, tag.Value) - } - - return properties + return types.NewPropertiesFromStruct(r) } func (r *S3Bucket) String() string { - return fmt.Sprintf("s3://%s", r.name) -} - -func bypassGovernanceRetention(input *s3.DeleteObjectsInput) { - input.BypassGovernanceRetention = ptr.Bool(true) -} - -type s3DeleteVersionListIterator struct { - Bucket *string - Paginator *s3.ListObjectVersionsPaginator - objects []s3types.ObjectVersion - lastNotify time.Time - BypassGovernanceRetention *bool - err error -} - -func newS3DeleteVersionListIterator( - svc *s3.Client, - input *s3.ListObjectVersionsInput, - bypass bool, - opts ...func(*s3DeleteVersionListIterator)) awsmod.BatchDeleteIterator { - iter := &s3DeleteVersionListIterator{ - Bucket: input.Bucket, - Paginator: s3.NewListObjectVersionsPaginator(svc, input), - BypassGovernanceRetention: ptr.Bool(bypass), - } - - for _, opt := range opts { - opt(iter) - } - - return iter -} - -// Next will use the S3API client to iterate through a list of objects. -func (iter *s3DeleteVersionListIterator) Next() bool { - if len(iter.objects) > 0 { - iter.objects = iter.objects[1:] - if len(iter.objects) > 0 { - return true - } - } - - if !iter.Paginator.HasMorePages() { - return false - } - - page, err := iter.Paginator.NextPage(context.TODO()) - if err != nil { - iter.err = err - return false - } - - iter.objects = page.Versions - for _, entry := range page.DeleteMarkers { - iter.objects = append(iter.objects, s3types.ObjectVersion{ - Key: entry.Key, - VersionId: entry.VersionId, - }) - } - - if len(iter.objects) > 500 && (iter.lastNotify.IsZero() || time.Since(iter.lastNotify) > 120*time.Second) { - logrus.Infof( - "S3Bucket: %s - empty bucket operation in progress, this could take a while, please be patient", - *iter.Bucket) - iter.lastNotify = time.Now().UTC() - } - - return len(iter.objects) > 0 -} - -// Err will return the last known error from Next. -func (iter *s3DeleteVersionListIterator) Err() error { - return iter.err -} - -// DeleteObject will return the current object to be deleted. -func (iter *s3DeleteVersionListIterator) DeleteObject() awsmod.BatchDeleteObject { - return awsmod.BatchDeleteObject{ - Object: &s3.DeleteObjectInput{ - Bucket: iter.Bucket, - Key: iter.objects[0].Key, - VersionId: iter.objects[0].VersionId, - BypassGovernanceRetention: iter.BypassGovernanceRetention, - }, - } -} - -type s3ObjectDeleteListIterator struct { - Bucket *string - Paginator *s3.ListObjectsV2Paginator - objects []s3types.Object - lastNotify time.Time - BypassGovernanceRetention bool - err error -} - -func newS3ObjectDeleteListIterator( - svc *s3.Client, - input *s3.ListObjectsV2Input, - bypass bool, - opts ...func(*s3ObjectDeleteListIterator)) awsmod.BatchDeleteIterator { - iter := &s3ObjectDeleteListIterator{ - Bucket: input.Bucket, - Paginator: s3.NewListObjectsV2Paginator(svc, input), - BypassGovernanceRetention: bypass, - } - - for _, opt := range opts { - opt(iter) - } - return iter -} - -// Next will use the S3API client to iterate through a list of objects. -func (iter *s3ObjectDeleteListIterator) Next() bool { - if len(iter.objects) > 0 { - iter.objects = iter.objects[1:] - if len(iter.objects) > 0 { - return true - } - } - - if !iter.Paginator.HasMorePages() { - return false - } - - page, err := iter.Paginator.NextPage(context.TODO()) - if err != nil { - iter.err = err - return false - } - - iter.objects = page.Contents - - if len(iter.objects) > 500 && (iter.lastNotify.IsZero() || time.Since(iter.lastNotify) > 120*time.Second) { - logrus.Infof( - "S3Bucket: %s - empty bucket operation in progress, this could take a while, please be patient", - *iter.Bucket) - iter.lastNotify = time.Now().UTC() - } - - return len(iter.objects) > 0 -} - -// Err will return the last known error from Next. -func (iter *s3ObjectDeleteListIterator) Err() error { - return iter.err -} - -// DeleteObject will return the current object to be deleted. -func (iter *s3ObjectDeleteListIterator) DeleteObject() awsmod.BatchDeleteObject { - return awsmod.BatchDeleteObject{ - Object: &s3.DeleteObjectInput{ - Bucket: iter.Bucket, - Key: iter.objects[0].Key, - BypassGovernanceRetention: ptr.Bool(iter.BypassGovernanceRetention), - }, - } + return fmt.Sprintf("s3://%s", *r.Name) } diff --git a/resources/s3-bucket_test.go b/resources/s3-bucket_test.go index 71cd42a7..22bfc828 100644 --- a/resources/s3-bucket_test.go +++ b/resources/s3-bucket_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/gotidy/ptr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -29,14 +30,14 @@ func (readSeekCloser) Close() error { return nil } type TestS3BucketSuite struct { suite.Suite - bucket string + bucket *string svc *s3.Client } func (suite *TestS3BucketSuite) SetupSuite() { var err error - suite.bucket = fmt.Sprintf("aws-nuke-testing-bucket-%d", time.Now().UnixNano()) + suite.bucket = ptr.String(fmt.Sprintf("aws-nuke-testing-bucket-%d", time.Now().UnixNano())) ctx := context.TODO() @@ -50,7 +51,7 @@ func (suite *TestS3BucketSuite) SetupSuite() { // Create the bucket _, err = suite.svc.CreateBucket(ctx, &s3.CreateBucketInput{ - Bucket: aws.String(suite.bucket), + Bucket: suite.bucket, CreateBucketConfiguration: &s3types.CreateBucketConfiguration{ LocationConstraint: s3types.BucketLocationConstraint("us-west-2"), }, @@ -61,7 +62,7 @@ func (suite *TestS3BucketSuite) SetupSuite() { // enable versioning _, err = suite.svc.PutBucketVersioning(ctx, &s3.PutBucketVersioningInput{ - Bucket: aws.String(suite.bucket), + Bucket: suite.bucket, VersioningConfiguration: &s3types.VersioningConfiguration{ Status: s3types.BucketVersioningStatusEnabled, }, @@ -72,7 +73,7 @@ func (suite *TestS3BucketSuite) SetupSuite() { // Set the object lock configuration to governance mode _, err = suite.svc.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{ - Bucket: aws.String(suite.bucket), + Bucket: suite.bucket, ObjectLockConfiguration: &s3types.ObjectLockConfiguration{ ObjectLockEnabled: s3types.ObjectLockEnabledEnabled, Rule: &s3types.ObjectLockRule{ @@ -89,7 +90,7 @@ func (suite *TestS3BucketSuite) SetupSuite() { // Create an object in the bucket _, err = suite.svc.PutObject(ctx, &s3.PutObjectInput{ - Bucket: aws.String(suite.bucket), + Bucket: suite.bucket, Key: aws.String("test-object"), Body: readSeekCloser{strings.NewReader("test content")}, ChecksumAlgorithm: s3types.ChecksumAlgorithmCrc32, @@ -101,7 +102,7 @@ func (suite *TestS3BucketSuite) SetupSuite() { func (suite *TestS3BucketSuite) TearDownSuite() { iterator := newS3DeleteVersionListIterator(suite.svc, &s3.ListObjectVersionsInput{ - Bucket: &suite.bucket, + Bucket: suite.bucket, }, true) if err := awsmod.NewBatchDeleteWithClient(suite.svc).Delete(context.TODO(), iterator, bypassGovernanceRetention); err != nil { if !strings.Contains(err.Error(), "NoSuchBucket") { @@ -110,7 +111,7 @@ func (suite *TestS3BucketSuite) TearDownSuite() { } iterator2 := newS3ObjectDeleteListIterator(suite.svc, &s3.ListObjectsV2Input{ - Bucket: &suite.bucket, + Bucket: suite.bucket, }, true) if err := awsmod.NewBatchDeleteWithClient(suite.svc).Delete(context.TODO(), iterator2, bypassGovernanceRetention); err != nil { if !strings.Contains(err.Error(), "NoSuchBucket") { @@ -119,7 +120,7 @@ func (suite *TestS3BucketSuite) TearDownSuite() { } _, err := suite.svc.DeleteBucket(context.TODO(), &s3.DeleteBucketInput{ - Bucket: aws.String(suite.bucket), + Bucket: suite.bucket, }) if err != nil { if !strings.Contains(err.Error(), "NoSuchBucket") { @@ -135,7 +136,7 @@ type TestS3BucketObjectLockSuite struct { func (suite *TestS3BucketObjectLockSuite) TestS3BucketObjectLock() { // Verify the object lock configuration result, err := suite.svc.GetObjectLockConfiguration(context.TODO(), &s3.GetObjectLockConfigurationInput{ - Bucket: aws.String(suite.bucket), + Bucket: suite.bucket, }) if err != nil { suite.T().Fatalf("failed to get object lock configuration, %v", err) @@ -150,7 +151,7 @@ func (suite *TestS3BucketObjectLockSuite) TestS3BucketRemove() { // Create the S3Bucket object bucket := &S3Bucket{ svc: suite.svc, - name: suite.bucket, + Name: suite.bucket, settings: &libsettings.Setting{}, } @@ -165,12 +166,12 @@ type TestS3BucketBypassGovernanceSuite struct { func (suite *TestS3BucketBypassGovernanceSuite) TestS3BucketRemoveWithBypass() { // Create the S3Bucket object bucket := &S3Bucket{ - svc: suite.svc, - name: suite.bucket, - ObjectLock: s3types.ObjectLockEnabledEnabled, + svc: suite.svc, settings: &libsettings.Setting{ "BypassGovernanceRetention": true, }, + Name: suite.bucket, + ObjectLock: s3types.ObjectLockEnabledEnabled, } err := bucket.Remove(context.TODO()) diff --git a/resources/s3-multipart-uploads.go b/resources/s3-multipart-uploads.go index feda2d96..81d9a7e6 100644 --- a/resources/s3-multipart-uploads.go +++ b/resources/s3-multipart-uploads.go @@ -2,7 +2,6 @@ package resources import ( "context" - "fmt" "github.com/aws/aws-sdk-go-v2/aws" @@ -34,7 +33,7 @@ func (l *S3MultipartUploadLister) List(ctx context.Context, o interface{}) ([]re resources := make([]resource.Resource, 0) - buckets, err := DescribeS3Buckets(ctx, svc) + buckets, err := DescribeS3Buckets(ctx, svc, opts) if err != nil { return nil, err } diff --git a/resources/s3-objects.go b/resources/s3-objects.go index bf89833e..adc436ee 100644 --- a/resources/s3-objects.go +++ b/resources/s3-objects.go @@ -2,7 +2,6 @@ package resources import ( "context" - "fmt" "time" @@ -37,7 +36,7 @@ func (l *S3ObjectLister) List(ctx context.Context, o interface{}) ([]resource.Re resources := make([]resource.Resource, 0) - buckets, err := DescribeS3Buckets(ctx, svc) + buckets, err := DescribeS3Buckets(ctx, svc, opts) if err != nil { return nil, err } From ebb69b0a24ae462cc34bf4ebe380a149507dd441 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 16:01:49 -0700 Subject: [PATCH 26/87] refactor(s3-object): standardization --- resources/{s3-objects.go => s3-object.go} | 58 +++++++++----------- resources/s3-object_test.go | 65 +++++++++++++++++++++++ 2 files changed, 91 insertions(+), 32 deletions(-) rename resources/{s3-objects.go => s3-object.go} (59%) create mode 100644 resources/s3-object_test.go diff --git a/resources/s3-objects.go b/resources/s3-object.go similarity index 59% rename from resources/s3-objects.go rename to resources/s3-object.go index adc436ee..e5ca6c3b 100644 --- a/resources/s3-objects.go +++ b/resources/s3-object.go @@ -7,7 +7,6 @@ import ( "github.com/gotidy/ptr" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/ekristen/libnuke/pkg/registry" @@ -59,11 +58,11 @@ func (l *S3ObjectLister) List(ctx context.Context, o interface{}) ([]resource.Re resources = append(resources, &S3Object{ svc: svc, - bucket: aws.ToString(bucket.Name), - creationDate: aws.ToTime(bucket.CreationDate), - key: *out.Key, - versionID: out.VersionId, - latest: ptr.ToBool(out.IsLatest), + Bucket: bucket.Name, + CreationDate: bucket.CreationDate, + Key: out.Key, + VersionID: out.VersionId, + IsLatest: out.IsLatest, }) } @@ -74,11 +73,11 @@ func (l *S3ObjectLister) List(ctx context.Context, o interface{}) ([]resource.Re resources = append(resources, &S3Object{ svc: svc, - bucket: aws.ToString(bucket.Name), - creationDate: aws.ToTime(bucket.CreationDate), - key: *out.Key, - versionID: out.VersionId, - latest: ptr.ToBool(out.IsLatest), + Bucket: bucket.Name, + CreationDate: bucket.CreationDate, + Key: out.Key, + VersionID: out.VersionId, + IsLatest: out.IsLatest, }) } @@ -97,21 +96,21 @@ func (l *S3ObjectLister) List(ctx context.Context, o interface{}) ([]resource.Re type S3Object struct { svc *s3.Client - bucket string - creationDate time.Time - key string - versionID *string - latest bool + Bucket *string + CreationDate *time.Time + Key *string + VersionID *string + IsLatest *bool } -func (e *S3Object) Remove(ctx context.Context) error { +func (r *S3Object) Remove(ctx context.Context) error { params := &s3.DeleteObjectInput{ - Bucket: &e.bucket, - Key: &e.key, - VersionId: e.versionID, + Bucket: r.Bucket, + Key: r.Key, + VersionId: r.VersionID, } - _, err := e.svc.DeleteObject(ctx, params) + _, err := r.svc.DeleteObject(ctx, params) if err != nil { return err } @@ -119,18 +118,13 @@ func (e *S3Object) Remove(ctx context.Context) error { return nil } -func (e *S3Object) Properties() types.Properties { - return types.NewProperties(). - Set("Bucket", e.bucket). - Set("Key", e.key). - Set("VersionID", e.versionID). - Set("IsLatest", e.latest). - Set("CreationDate", e.creationDate) +func (r *S3Object) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } -func (e *S3Object) String() string { - if e.versionID != nil && *e.versionID != "null" && !e.latest { - return fmt.Sprintf("s3://%s/%s#%s", e.bucket, e.key, *e.versionID) +func (r *S3Object) String() string { + if r.VersionID != nil && *r.VersionID != "null" && !ptr.ToBool(r.IsLatest) { + return fmt.Sprintf("s3://%s/%s#%s", *r.Bucket, *r.Key, *r.VersionID) } - return fmt.Sprintf("s3://%s/%s", e.bucket, e.key) + return fmt.Sprintf("s3://%s/%s", *r.Bucket, *r.Key) } diff --git a/resources/s3-object_test.go b/resources/s3-object_test.go new file mode 100644 index 00000000..e196af3f --- /dev/null +++ b/resources/s3-object_test.go @@ -0,0 +1,65 @@ +package resources + +import ( + "fmt" + "testing" + "time" + + "github.com/gotidy/ptr" + "github.com/stretchr/testify/assert" +) + +func TestS3ObjectProperties(t *testing.T) { + tests := []struct { + bucket string + key string + creationDate time.Time + versionID string + isLatest bool + }{ + { + bucket: "test-bucket", + key: "test-key", + creationDate: time.Now(), + versionID: "null", + isLatest: true, + }, + { + bucket: "test-bucket", + key: "test-key", + creationDate: time.Now(), + versionID: "test-version-id", + isLatest: false, + }, + } + + for _, test := range tests { + t.Run(test.bucket, func(t *testing.T) { + obj := &S3Object{ + Bucket: ptr.String(test.bucket), + Key: ptr.String(test.key), + VersionID: ptr.String(test.versionID), + CreationDate: ptr.Time(test.creationDate), + IsLatest: ptr.Bool(test.isLatest), + } + + got := obj.Properties() + assert.Equal(t, test.bucket, got.Get("Bucket")) + assert.Equal(t, test.key, got.Get("Key")) + assert.Equal(t, test.versionID, got.Get("VersionID")) + assert.Equal(t, test.creationDate.Format(time.RFC3339), got.Get("CreationDate")) + + if test.isLatest { + assert.Equal(t, "true", got.Get("IsLatest")) + } else { + assert.Equal(t, "false", got.Get("IsLatest")) + } + + uri := fmt.Sprintf("s3://%s/%s", test.bucket, test.key) + if test.versionID != "" && test.versionID != "null" && !test.isLatest { + uri = fmt.Sprintf("%s#%s", uri, test.versionID) + } + assert.Equal(t, uri, obj.String()) + }) + } +} From d6fce3923c3cbe1298261c661ef3b736bc8bdc83 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 16:02:23 -0700 Subject: [PATCH 27/87] refactor(s3-multipart-upload): standardization --- ...part-uploads.go => s3-multipart-upload.go} | 34 +++++++-------- resources/s3-multipart-upload_test.go | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+), 19 deletions(-) rename resources/{s3-multipart-uploads.go => s3-multipart-upload.go} (70%) create mode 100644 resources/s3-multipart-upload_test.go diff --git a/resources/s3-multipart-uploads.go b/resources/s3-multipart-upload.go similarity index 70% rename from resources/s3-multipart-uploads.go rename to resources/s3-multipart-upload.go index 81d9a7e6..159a6eee 100644 --- a/resources/s3-multipart-uploads.go +++ b/resources/s3-multipart-upload.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/ekristen/libnuke/pkg/registry" @@ -56,9 +55,9 @@ func (l *S3MultipartUploadLister) List(ctx context.Context, o interface{}) ([]re resources = append(resources, &S3MultipartUpload{ svc: svc, - bucket: aws.ToString(bucket.Name), - key: *upload.Key, - uploadID: *upload.UploadId, + Bucket: bucket.Name, + Key: upload.Key, + UploadID: upload.UploadId, }) } @@ -76,19 +75,19 @@ func (l *S3MultipartUploadLister) List(ctx context.Context, o interface{}) ([]re type S3MultipartUpload struct { svc *s3.Client - bucket string - key string - uploadID string + Bucket *string + Key *string + UploadID *string } -func (e *S3MultipartUpload) Remove(ctx context.Context) error { +func (r *S3MultipartUpload) Remove(ctx context.Context) error { params := &s3.AbortMultipartUploadInput{ - Bucket: &e.bucket, - Key: &e.key, - UploadId: &e.uploadID, + Bucket: r.Bucket, + Key: r.Key, + UploadId: r.UploadID, } - _, err := e.svc.AbortMultipartUpload(ctx, params) + _, err := r.svc.AbortMultipartUpload(ctx, params) if err != nil { return err } @@ -96,13 +95,10 @@ func (e *S3MultipartUpload) Remove(ctx context.Context) error { return nil } -func (e *S3MultipartUpload) Properties() types.Properties { - return types.NewProperties(). - Set("Bucket", e.bucket). - Set("Key", e.key). - Set("UploadID", e.uploadID) +func (r *S3MultipartUpload) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } -func (e *S3MultipartUpload) String() string { - return fmt.Sprintf("s3://%s/%s#%s", e.bucket, e.key, e.uploadID) +func (r *S3MultipartUpload) String() string { + return fmt.Sprintf("s3://%s/%s#%s", *r.Bucket, *r.Key, *r.UploadID) } diff --git a/resources/s3-multipart-upload_test.go b/resources/s3-multipart-upload_test.go new file mode 100644 index 00000000..9cbe404b --- /dev/null +++ b/resources/s3-multipart-upload_test.go @@ -0,0 +1,41 @@ +package resources + +import ( + "fmt" + "testing" + + "github.com/gotidy/ptr" + "github.com/stretchr/testify/assert" +) + +func TestS3MultipartUploadProperties(t *testing.T) { + tests := []struct { + bucket string + key string + uploadID string + }{ + { + bucket: "test-bucket", + key: "test-key", + uploadID: "test-upload-id", + }, + } + + for _, test := range tests { + t.Run(test.bucket, func(t *testing.T) { + obj := &S3MultipartUpload{ + Bucket: ptr.String(test.bucket), + Key: ptr.String(test.key), + UploadID: ptr.String(test.uploadID), + } + + got := obj.Properties() + assert.Equal(t, test.bucket, got.Get("Bucket")) + assert.Equal(t, test.key, got.Get("Key")) + assert.Equal(t, test.uploadID, got.Get("UploadID")) + + uri := fmt.Sprintf("s3://%s/%s#%s", test.bucket, test.key, test.uploadID) + assert.Equal(t, uri, obj.String()) + }) + } +} From e8efa66c99c326d7e11e69cd4e2548ac127b4611 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 16:12:40 -0700 Subject: [PATCH 28/87] refactor(s3-access-point): standardization --- ...s3-access-points.go => s3-access-point.go} | 46 +++++++++--------- resources/s3-access-point_test.go | 48 +++++++++++++++++++ 2 files changed, 72 insertions(+), 22 deletions(-) rename resources/{s3-access-points.go => s3-access-point.go} (57%) create mode 100644 resources/s3-access-point_test.go diff --git a/resources/s3-access-points.go b/resources/s3-access-point.go similarity index 57% rename from resources/s3-access-points.go rename to resources/s3-access-point.go index a94ba068..e5d58e3c 100644 --- a/resources/s3-access-points.go +++ b/resources/s3-access-point.go @@ -5,7 +5,6 @@ import ( "github.com/gotidy/ptr" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3control" "github.com/ekristen/libnuke/pkg/registry" @@ -46,15 +45,20 @@ func (l *S3AccessPointLister) List(_ context.Context, o interface{}) ([]resource for _, accessPoint := range resp.AccessPointList { resources = append(resources, &S3AccessPoint{ - svc: svc, - accountID: opts.AccountID, - accessPoint: accessPoint, + svc: svc, + accountID: opts.AccountID, + Name: accessPoint.Name, + ARN: accessPoint.AccessPointArn, + Alias: accessPoint.Alias, + Bucket: accessPoint.Bucket, + NetworkOrigin: accessPoint.NetworkOrigin, }) } if resp.NextToken == nil { break } + params.NextToken = resp.NextToken } @@ -62,30 +66,28 @@ func (l *S3AccessPointLister) List(_ context.Context, o interface{}) ([]resource } type S3AccessPoint struct { - svc *s3control.S3Control - accountID *string - accessPoint *s3control.AccessPoint + svc *s3control.S3Control + accountID *string + Name *string + ARN *string + Alias *string + Bucket *string + NetworkOrigin *string } -func (e *S3AccessPoint) Remove(_ context.Context) error { - _, err := e.svc.DeleteAccessPoint(&s3control.DeleteAccessPointInput{ - AccountId: e.accountID, - Name: aws.String(*e.accessPoint.Name), +func (r *S3AccessPoint) Remove(_ context.Context) error { + _, err := r.svc.DeleteAccessPoint(&s3control.DeleteAccessPointInput{ + AccountId: r.accountID, + Name: r.Name, }) return err } -func (e *S3AccessPoint) Properties() types.Properties { - properties := types.NewProperties() - properties.Set("AccessPointArn", e.accessPoint.AccessPointArn). - Set("Alias", e.accessPoint.Alias). - Set("Bucket", e.accessPoint.Bucket). - Set("Name", e.accessPoint.Name). - Set("NetworkOrigin", e.accessPoint.NetworkOrigin) - - return properties +func (r *S3AccessPoint) Properties() types.Properties { + return types.NewPropertiesFromStruct(r). + Set("AccessPointArn", r.ARN) // TODO(ek): this is an alias, should be deprecated for ARN } -func (e *S3AccessPoint) String() string { - return ptr.ToString(e.accessPoint.AccessPointArn) +func (r *S3AccessPoint) String() string { + return ptr.ToString(r.ARN) // TODO(ek): this should be the Name not the ARN } diff --git a/resources/s3-access-point_test.go b/resources/s3-access-point_test.go new file mode 100644 index 00000000..8ca90d6b --- /dev/null +++ b/resources/s3-access-point_test.go @@ -0,0 +1,48 @@ +package resources + +import ( + "fmt" + "github.com/gotidy/ptr" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestS3AccessPointProperties(t *testing.T) { + tests := []struct { + accountID string + name string + alias string + bucket string + networkOrigin string + }{ + { + accountID: "123456789012", + name: "test-access-point", + alias: "some-alias", + bucket: "some-bucket", + networkOrigin: "some-network-origin", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + obj := &S3AccessPoint{ + accountID: ptr.String(tc.accountID), + ARN: ptr.String(fmt.Sprintf("arn:aws:s3:::%s:%s", tc.accountID, tc.name)), + Name: ptr.String(tc.name), + Alias: ptr.String(tc.alias), + Bucket: ptr.String(tc.bucket), + NetworkOrigin: ptr.String(tc.networkOrigin), + } + + got := obj.Properties() + assert.Equal(t, tc.name, got.Get("Name")) + assert.Equal(t, fmt.Sprintf("arn:aws:s3:::%s:%s", tc.accountID, tc.name), got.Get("AccessPointArn")) + assert.Equal(t, tc.alias, got.Get("Alias")) + assert.Equal(t, tc.bucket, got.Get("Bucket")) + assert.Equal(t, tc.networkOrigin, got.Get("NetworkOrigin")) + + assert.Equal(t, fmt.Sprintf("arn:aws:s3:::%s:%s", tc.accountID, tc.name), obj.String()) + }) + } +} From 9677bb56311e97071bdd53386f3826a10997784e Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 16:13:47 -0700 Subject: [PATCH 29/87] docs(s3): auto-generated --- docs/resources/s3-access-point.md | 7 +++++++ docs/resources/s3-bucket.md | 4 ++++ docs/resources/s3-multipart-upload.md | 5 +++++ docs/resources/s3-object.md | 7 +++++++ 4 files changed, 23 insertions(+) diff --git a/docs/resources/s3-access-point.md b/docs/resources/s3-access-point.md index e7ac6720..710ed99a 100644 --- a/docs/resources/s3-access-point.md +++ b/docs/resources/s3-access-point.md @@ -11,8 +11,15 @@ generated: true S3AccessPoint ``` +## Properties +- `ARN`: No Description +- `Alias`: No Description +- `Bucket`: No Description +- `Name`: No Description +- `NetworkOrigin`: No Description + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/docs/resources/s3-bucket.md b/docs/resources/s3-bucket.md index 8212bffa..71bdf295 100644 --- a/docs/resources/s3-bucket.md +++ b/docs/resources/s3-bucket.md @@ -23,7 +23,11 @@ AWS::S3::Bucket ## Properties +- `CreationDate`: No Description +- `Name`: No Description - `ObjectLock`: No Description +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property diff --git a/docs/resources/s3-multipart-upload.md b/docs/resources/s3-multipart-upload.md index bb17d334..da9ead91 100644 --- a/docs/resources/s3-multipart-upload.md +++ b/docs/resources/s3-multipart-upload.md @@ -11,8 +11,13 @@ generated: true S3MultipartUpload ``` +## Properties +- `Bucket`: No Description +- `Key`: No Description +- `UploadID`: No Description + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/docs/resources/s3-object.md b/docs/resources/s3-object.md index 5c70b67e..13eb4392 100644 --- a/docs/resources/s3-object.md +++ b/docs/resources/s3-object.md @@ -11,8 +11,15 @@ generated: true S3Object ``` +## Properties +- `Bucket`: No Description +- `CreationDate`: No Description +- `IsLatest`: No Description +- `Key`: No Description +- `VersionID`: No Description + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. From 027cbe25548d0999eaf766c8a872735f6485c2a9 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 17:32:50 -0700 Subject: [PATCH 30/87] chore(s3-access-point): fix lint violation --- resources/s3-access-point_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/s3-access-point_test.go b/resources/s3-access-point_test.go index 8ca90d6b..b7e119ed 100644 --- a/resources/s3-access-point_test.go +++ b/resources/s3-access-point_test.go @@ -2,9 +2,10 @@ package resources import ( "fmt" + "testing" + "github.com/gotidy/ptr" "github.com/stretchr/testify/assert" - "testing" ) func TestS3AccessPointProperties(t *testing.T) { From fd6965b0b8a607dd40aa577663f36e527c180cce Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 5 Oct 2024 23:42:46 -0600 Subject: [PATCH 31/87] feat: structured logging --- go.mod | 16 ++++------------ go.sum | 26 ++++++++------------------ pkg/commands/global/global.go | 18 +++++++++++++++++- pkg/commands/nuke/nuke.go | 23 +++++++++++++---------- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index e1687efd..5fc0f177 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,8 @@ go 1.21.6 require ( github.com/aws/aws-sdk-go v1.54.20 - github.com/aws/aws-sdk-go-v2 v1.32.7 - github.com/aws/aws-sdk-go-v2/config v1.28.7 - github.com/aws/aws-sdk-go-v2/credentials v1.17.48 - github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 - github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 - github.com/aws/smithy-go v1.22.1 - github.com/ekristen/libnuke v0.22.0 - github.com/fatih/color v1.18.0 + github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b + github.com/fatih/color v1.17.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 github.com/gotidy/ptr v1.4.0 @@ -50,10 +44,8 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stevenle/topsort v0.2.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.20.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index f4fc7bbe..58c87700 100644 --- a/go.sum +++ b/go.sum @@ -136,24 +136,16 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ekristen/libnuke v0.14.1-0.20241005174646-b4565465aba3 h1:2nywOK8Z3BMqwvfP8eO1seMA5KrPdBkXWDsTmYxqKLY= +github.com/ekristen/libnuke v0.14.1-0.20241005174646-b4565465aba3/go.mod h1:sBdA04l9IMMejQw5gO9k6o/a0GffSYhgZYaUSdRjIac= github.com/ekristen/libnuke v0.19.1 h1:n52PMccQjs4MsaYPtulavxmKyHFq4xz3KCy6mpjoX/I= github.com/ekristen/libnuke v0.19.1/go.mod h1:riI1tjCf6r+et/9oUBd1vQeFmn2Sn6UeFUR0nWkMeYw= -github.com/ekristen/libnuke v0.19.2 h1:dlmqeHBHaQN+gv6Cg7+DwehpayocAABTYzSaTmaP6Pk= -github.com/ekristen/libnuke v0.19.2/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.20.0 h1:GV6ebfPt3ac+5ygto3hdIH5PN9ppXPAAJo7C00ngOCI= -github.com/ekristen/libnuke v0.20.0/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.21.0 h1:8bBlx4Bj9WP1inxz6+iGxXW6V2iDDJidbT+0xsQDlLE= -github.com/ekristen/libnuke v0.21.0/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.21.1 h1:fngmzmV2JyjDagxh8SzPDcJ5dvmhDjvbU+XhA1vsHPs= -github.com/ekristen/libnuke v0.21.1/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.21.3 h1:AF5oY1jcgbxycDqYOgdYTtVm9jl6Uyj4IRea4mIfCSU= -github.com/ekristen/libnuke v0.21.3/go.mod h1:FgzH3PKJqk/31Lty+B3MFwjWc7fz+NpGylVtgzIDFII= -github.com/ekristen/libnuke v0.21.4 h1:krEZZIAITp/begn/ltNfp9xJ7WznUI/tnEPCIFmHVas= -github.com/ekristen/libnuke v0.21.4/go.mod h1:FgzH3PKJqk/31Lty+B3MFwjWc7fz+NpGylVtgzIDFII= -github.com/ekristen/libnuke v0.21.8 h1:JyMyMUbh/ti8r1wjXe5B8h8xhPibFN2tu9zVlkn1vyw= -github.com/ekristen/libnuke v0.21.8/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.0 h1:x5ZxadNiH0DZJOtdxVDKZaaBymojdi65Pi+k3ITfv7M= -github.com/ekristen/libnuke v0.22.0/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.19.3-0.20241006045936-4b1a28a65e86 h1:gQJQgZtIt5rhtro+aPd8gqWkYn3SvlZPMPkINNW/1gw= +github.com/ekristen/libnuke v0.19.3-0.20241006045936-4b1a28a65e86/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= +github.com/ekristen/libnuke v0.19.3-0.20241006053205-54f2d13f031c h1:O4en6xA3Uo4DN86WhNpy4AOgXlcmPLAe5W63Sv+0ARI= +github.com/ekristen/libnuke v0.19.3-0.20241006053205-54f2d13f031c/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= +github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b h1:GRW0OG3TP0HMtCStQkrR9RB8v1a2FAQXUIO21GnYj9o= +github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -216,7 +208,6 @@ go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJh golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -254,7 +245,6 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/commands/global/global.go b/pkg/commands/global/global.go index d104fe83..48d58ba9 100644 --- a/pkg/commands/global/global.go +++ b/pkg/commands/global/global.go @@ -7,6 +7,8 @@ import ( "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" + + "github.com/ekristen/libnuke/pkg/log" ) func Flags() []cli.Flag { @@ -30,6 +32,11 @@ func Flags() []cli.Flag { Name: "log-full-timestamp", Usage: "force log output to always show full timestamp", }, + &cli.StringFlag{ + Name: "log-format", + Usage: "log format", + Value: "standard", + }, } return globalFlags @@ -48,7 +55,16 @@ func Before(c *cli.Context) error { } } - logrus.SetFormatter(formatter) + logFormatter := &log.CustomFormatter{ + FallbackFormatter: formatter, + } + + switch c.String("log-format") { + case "json": + logrus.SetFormatter(&logrus.JSONFormatter{}) + default: + logrus.SetFormatter(logFormatter) + } switch c.String("log-level") { case "trace": diff --git a/pkg/commands/nuke/nuke.go b/pkg/commands/nuke/nuke.go index 67f8e0c4..f91535a2 100644 --- a/pkg/commands/nuke/nuke.go +++ b/pkg/commands/nuke/nuke.go @@ -74,14 +74,16 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo } } + logger := logrus.StandardLogger() + // Parse the user supplied configuration file to pass in part to configure the nuke process. parsedConfig, err := config.New(libconfig.Options{ Path: c.Path("config"), Deprecations: registry.GetDeprecatedResourceTypeMapping(), - Log: logrus.WithField("component", "config"), + Log: logger.WithField("component", "config"), }) if err != nil { - logrus.Errorf("Failed to parse config file %s", c.Path("config")) + logger.Errorf("Failed to parse config file %s", c.Path("config")) return err } @@ -95,7 +97,7 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo err = fmt.Errorf( "the custom region '%s' must be specified in the configuration 'endpoints'"+ " to determine its partition", defaultRegion) - logrus.WithError(err).Errorf("unable to resolve partition for region: %s", defaultRegion) + logger.WithError(err).Errorf("unable to resolve partition for region: %s", defaultRegion) return err } } @@ -119,7 +121,7 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo n := libnuke.New(params, filters, parsedConfig.Settings) n.SetRunSleep(c.Duration("run-sleep-delay")) - n.SetLogger(logrus.WithField("component", "libnuke")) + n.SetLogger(logger.WithField("component", "libnuke")) n.RegisterVersion(fmt.Sprintf("> %s", common.AppVersion.String())) // Register our custom validate handler that validates the account and AWS nuke unique alias checks @@ -161,23 +163,23 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo if slices.Contains(parsedConfig.Regions, "all") { parsedConfig.Regions = account.Regions() - logrus.Info( + logger.Info( `"all" detected in region list, only enabled regions and "global" will be used, all others ignored`) if len(parsedConfig.Regions) > 1 { - logrus.Warnf(`additional regions defined along with "all", these will be ignored!`) + logger.Warnf(`additional regions defined along with "all", these will be ignored!`) } - logrus.Infof("The following regions are enabled for the account (%d total):", len(parsedConfig.Regions)) + logger.Infof("The following regions are enabled for the account (%d total):", len(parsedConfig.Regions)) printableRegions := make([]string, 0) for i, region := range parsedConfig.Regions { printableRegions = append(printableRegions, region) if i%6 == 0 { // print 5 regions per line - logrus.Infof("> %s", strings.Join(printableRegions, ", ")) + logger.Infof("> %s", strings.Join(printableRegions, ", ")) printableRegions = make([]string, 0) } else if i == len(parsedConfig.Regions)-1 { - logrus.Infof("> %s", strings.Join(printableRegions, ", ")) + logger.Infof("> %s", strings.Join(printableRegions, ", ")) } } } @@ -191,11 +193,12 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo scannerActual := scanner.New(regionName, resourceTypes, &nuke.ListerOpts{ Region: region, AccountID: ptr.String(account.ID()), - Logger: logrus.WithFields(logrus.Fields{ + Logger: logger.WithFields(logrus.Fields{ "component": "scanner", "region": regionName, }), }) + scannerActual.SetLogger(logger) // Step 3 - Register a mutate function that will be called to modify the lister options for each resource type // see pkg/nuke/resource.go for the MutateOpts function. Its purpose is to create the proper session for the From 19c38949bcfb1303af130188047da7dbd7787b51 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sun, 6 Oct 2024 00:06:24 -0600 Subject: [PATCH 32/87] feat: add kv log-format --- go.mod | 2 +- go.sum | 4 ++++ pkg/commands/global/global.go | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5fc0f177..55f3912d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.6 require ( github.com/aws/aws-sdk-go v1.54.20 - github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b + github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea github.com/fatih/color v1.17.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 58c87700..c5e135c8 100644 --- a/go.sum +++ b/go.sum @@ -146,6 +146,10 @@ github.com/ekristen/libnuke v0.19.3-0.20241006053205-54f2d13f031c h1:O4en6xA3Uo4 github.com/ekristen/libnuke v0.19.3-0.20241006053205-54f2d13f031c/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b h1:GRW0OG3TP0HMtCStQkrR9RB8v1a2FAQXUIO21GnYj9o= github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= +github.com/ekristen/libnuke v0.19.3-0.20241006055534-9f6008b7a6bd h1:91btyxCOv3NGFibfeLp1Z4Pu5pJv/xVf/2RhDwNrKZs= +github.com/ekristen/libnuke v0.19.3-0.20241006055534-9f6008b7a6bd/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= +github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea h1:JAijGxg3Dq9CGjnzN9OPRXBHJP0gzyv+we8UijpKvHU= +github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= diff --git a/pkg/commands/global/global.go b/pkg/commands/global/global.go index 48d58ba9..f04bc653 100644 --- a/pkg/commands/global/global.go +++ b/pkg/commands/global/global.go @@ -62,6 +62,11 @@ func Before(c *cli.Context) error { switch c.String("log-format") { case "json": logrus.SetFormatter(&logrus.JSONFormatter{}) + case "kv": + logrus.SetFormatter(&logrus.TextFormatter{ + DisableColors: true, + FullTimestamp: true, + }) default: logrus.SetFormatter(logFormatter) } From 4bc40ae972bfaa7e62fa0e9ccaa555867567f794 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sun, 6 Oct 2024 00:31:06 -0600 Subject: [PATCH 33/87] chore(deps): update libnuke for structured logging testing --- go.mod | 2 +- go.sum | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 55f3912d..6084b3f5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.6 require ( github.com/aws/aws-sdk-go v1.54.20 - github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea + github.com/ekristen/libnuke v0.19.3-0.20241006062416-6d82056ddbbf github.com/fatih/color v1.17.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index c5e135c8..ece98d5f 100644 --- a/go.sum +++ b/go.sum @@ -150,6 +150,10 @@ github.com/ekristen/libnuke v0.19.3-0.20241006055534-9f6008b7a6bd h1:91btyxCOv3N github.com/ekristen/libnuke v0.19.3-0.20241006055534-9f6008b7a6bd/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea h1:JAijGxg3Dq9CGjnzN9OPRXBHJP0gzyv+we8UijpKvHU= github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= +github.com/ekristen/libnuke v0.19.3-0.20241006062138-3abb64a459ba h1:LWXLToojlg5aX8yCgoGg89X+zbOW3ZVcpFvmTjf/n8U= +github.com/ekristen/libnuke v0.19.3-0.20241006062138-3abb64a459ba/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= +github.com/ekristen/libnuke v0.19.3-0.20241006062416-6d82056ddbbf h1:0Rn5zPKG8KyHWrItAWaKGcGnMk51qXSMBarGK8A5zMk= +github.com/ekristen/libnuke v0.19.3-0.20241006062416-6d82056ddbbf/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= From 36ab9b5e26ab5540080ada3b1f346bac462862fb Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 20:05:22 -0700 Subject: [PATCH 34/87] fix: various tweaks for structured logging --- go.mod | 14 +++- go.sum | 152 ++++++---------------------------- pkg/commands/global/global.go | 48 +++++++++-- pkg/commands/nuke/nuke.go | 4 +- pkg/nuke/prompt.go | 11 ++- 5 files changed, 85 insertions(+), 144 deletions(-) diff --git a/go.mod b/go.mod index 6084b3f5..85be7f29 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,14 @@ go 1.21.6 require ( github.com/aws/aws-sdk-go v1.54.20 - github.com/ekristen/libnuke v0.19.3-0.20241006062416-6d82056ddbbf - github.com/fatih/color v1.17.0 + github.com/aws/aws-sdk-go-v2 v1.32.7 + github.com/aws/aws-sdk-go-v2/config v1.28.7 + github.com/aws/aws-sdk-go-v2/credentials v1.17.48 + github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 + github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 + github.com/aws/smithy-go v1.22.1 + github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b + github.com/fatih/color v1.18.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 github.com/gotidy/ptr v1.4.0 @@ -44,8 +50,8 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stevenle/topsort v0.2.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.25.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index ece98d5f..a54f9746 100644 --- a/go.sum +++ b/go.sum @@ -1,161 +1,73 @@ github.com/aws/aws-sdk-go v1.54.20 h1:FZ2UcXya7bUkvkpf7TaPmiL7EubK0go1nlXGLRwEsoo= github.com/aws/aws-sdk-go v1.54.20/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI= -github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= -github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk= -github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= -github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= -github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.27.43 h1:p33fDDihFC390dhhuv8nOmX419wjOSDQRb+USt20RrU= -github.com/aws/aws-sdk-go-v2/config v1.27.43/go.mod h1:pYhbtvg1siOOg8h5an77rXle9tVG8T+BWLWAo7cOukc= -github.com/aws/aws-sdk-go-v2/config v1.28.1 h1:oxIvOUXy8x0U3fR//0eq+RdCKimWI900+SV+10xsCBw= -github.com/aws/aws-sdk-go-v2/config v1.28.1/go.mod h1:bRQcttQJiARbd5JZxw6wG0yIK3eLeSCPdg6uqmmlIiI= -github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= -github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= -github.com/aws/aws-sdk-go-v2/credentials v1.17.41 h1:7gXo+Axmp+R4Z+AK8YFQO0ZV3L0gizGINCOWxSLY9W8= -github.com/aws/aws-sdk-go-v2/credentials v1.17.41/go.mod h1:u4Eb8d3394YLubphT4jLEwN1rLNq2wFOlT6OuxFwPzU= -github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM= -github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M= -github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= -github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 h1:TMH3f/SCAWdNtXXVPPu5D6wrr4G5hI1rAxbcocKfC7Q= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17/go.mod h1:1ZRXLdTpzdJb9fwTMXiLipENRxkGMTn1sfKexGllQCw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 h1:68jFVtt3NulEzojFesM/WVarlFpCaXLKaBxDpzkQ9OQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18/go.mod h1:Fjnn5jQVIo6VyedMc0/EhPpfNlPl7dHV916O6B+49aE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 h1:7edmS3VOBDhK00b/MwGtGglCm7hhwNYnjJs/PgFdMQE= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21/go.mod h1:Q9o5h4HoIWG8XfzxqiuK/CGUbepCJ8uTlaE3bAbxytQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22 h1:yV+hCAHZZYJQcwAaszoBNwLbPItHvApxT0kVIw6jRgs= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22/go.mod h1:kbR1TL8llqB1eGnVbybcA4/wgScxdylOdyAd51yxPdw= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 h1:4FMHqLfk0efmTqhXVRL5xYRqlEBNBiRI7N6w4jsEdd4= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2/go.mod h1:LWoqeWlK9OZeJxsROW2RqrSPvQHKTpp69r/iDjwsSaw= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3 h1:kT6BcZsmMtNkP/iYMcRG+mIEA/IbeiUimXtGmqF39y0= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3/go.mod h1:Z8uGua2k4PPaGOYn66pK02rhMrot3Xk3tpBuUFPomZU= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 h1:t7iUP9+4wdc5lt3E41huP+GvQZJD38WLsgVp4iOtAjg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2/go.mod h1:/niFCtmuQNxqx9v8WAPq5qh7EH25U4BF6tjoyq9bObM= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3 h1:ZC7Y/XgKUxwqcdhO5LE8P6oGP1eh6xlQReWNKfhvJno= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3/go.mod h1:WqfO7M9l9yUAw0HcHaikwRd/H6gzYdz7vjejCA5e2oY= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= -github.com/aws/aws-sdk-go-v2/service/s3 v1.65.2 h1:yi8m+jepdp6foK14xXLGkYBenxnlcfJ45ka4Pg7fDSQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.65.2/go.mod h1:cB6oAuus7YXRZhWCc1wIwPywwZ1XwweNp2TVAEGYeB8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2 h1:p9TNFL8bFUMd+38YIpTAXpoxyz0MxC7FlbFEH4P4E1U= -github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2/go.mod h1:fNjyo0Coen9QTwQLWeV6WO2Nytwiu+cCcWaTdKCAqqE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= -github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 h1:bSYXVyUzoTHoKalBmwaZxs97HU9DWWI3ehHSAMa7xOk= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.2/go.mod h1:skMqY7JElusiOUjMJMOv1jJsP7YUg7DrhgqZZWuzu1U= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 h1:UTpsIf0loCIWEbrqdLb+0RxnTXfWh2vhw4nQmFi4nPc= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.3/go.mod h1:FZ9j3PFHHAR+w0BSEjK955w5YD2UwB/l/H0yAK3MJvI= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/LfUWUF+zFPEcY9QXb7o= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 h1:2YCmIXv3tmiItw0LlYf6v7gEHebLY45kBEnPezbUKyU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3/go.mod h1:u19stRyNPxGhj6dRm+Cdgu6N75qnbW7+QN0q0dsAk58= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= -github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo= -github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo= -github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 h1:wVnQ6tigGsRqSWDEEyH6lSAJ9OyFUsSnbaUWChuSGzs= -github.com/aws/aws-sdk-go-v2/service/sts v1.32.3/go.mod h1:VZa9yTFyj4o10YGsmDO4gbQJUvvhY72fhumT8W4LqsE= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= -github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= -github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ekristen/libnuke v0.14.1-0.20241005174646-b4565465aba3 h1:2nywOK8Z3BMqwvfP8eO1seMA5KrPdBkXWDsTmYxqKLY= -github.com/ekristen/libnuke v0.14.1-0.20241005174646-b4565465aba3/go.mod h1:sBdA04l9IMMejQw5gO9k6o/a0GffSYhgZYaUSdRjIac= -github.com/ekristen/libnuke v0.19.1 h1:n52PMccQjs4MsaYPtulavxmKyHFq4xz3KCy6mpjoX/I= -github.com/ekristen/libnuke v0.19.1/go.mod h1:riI1tjCf6r+et/9oUBd1vQeFmn2Sn6UeFUR0nWkMeYw= -github.com/ekristen/libnuke v0.19.3-0.20241006045936-4b1a28a65e86 h1:gQJQgZtIt5rhtro+aPd8gqWkYn3SvlZPMPkINNW/1gw= -github.com/ekristen/libnuke v0.19.3-0.20241006045936-4b1a28a65e86/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.19.3-0.20241006053205-54f2d13f031c h1:O4en6xA3Uo4DN86WhNpy4AOgXlcmPLAe5W63Sv+0ARI= -github.com/ekristen/libnuke v0.19.3-0.20241006053205-54f2d13f031c/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b h1:GRW0OG3TP0HMtCStQkrR9RB8v1a2FAQXUIO21GnYj9o= -github.com/ekristen/libnuke v0.19.3-0.20241006053811-62434176922b/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.19.3-0.20241006055534-9f6008b7a6bd h1:91btyxCOv3NGFibfeLp1Z4Pu5pJv/xVf/2RhDwNrKZs= -github.com/ekristen/libnuke v0.19.3-0.20241006055534-9f6008b7a6bd/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea h1:JAijGxg3Dq9CGjnzN9OPRXBHJP0gzyv+we8UijpKvHU= -github.com/ekristen/libnuke v0.19.3-0.20241006060217-60ab37420cea/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.19.3-0.20241006062138-3abb64a459ba h1:LWXLToojlg5aX8yCgoGg89X+zbOW3ZVcpFvmTjf/n8U= -github.com/ekristen/libnuke v0.19.3-0.20241006062138-3abb64a459ba/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/ekristen/libnuke v0.19.3-0.20241006062416-6d82056ddbbf h1:0Rn5zPKG8KyHWrItAWaKGcGnMk51qXSMBarGK8A5zMk= -github.com/ekristen/libnuke v0.19.3-0.20241006062416-6d82056ddbbf/go.mod h1:DIN5VmrH6AUwaXc25RHcH/V+JKALdl16CN9iJvFtbK4= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/ekristen/libnuke v0.22.1-0.20241224013520-52c0542b4b2d h1:JHgyOVL4FbtGnbz9i78HUqz47q2MKuSoiS3uL3VkgqQ= +github.com/ekristen/libnuke v0.22.1-0.20241224013520-52c0542b4b2d/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224014659-f83bc5d4d5d5 h1:d3ch1E1VVgWX1UTtQfROnp4P9z43/mkwC/WeFUVRjuM= +github.com/ekristen/libnuke v0.22.1-0.20241224014659-f83bc5d4d5d5/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224015245-2b08d39f8c3d h1:IjUGUL8nNJI9YBbZA97gKpku9Qja5xXoKmGlNQI4h/o= +github.com/ekristen/libnuke v0.22.1-0.20241224015245-2b08d39f8c3d/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224015748-4a35c1b48a4f h1:st1ewH2ViyeCOnD2E15ZGQDSz/gncFwT0fSCqg2b7Yk= +github.com/ekristen/libnuke v0.22.1-0.20241224015748-4a35c1b48a4f/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224020243-7be2a0e103e7 h1:s57amu5jKJcDhu9X307mQfBF/CUewEkvnAxIrlVF3Kw= +github.com/ekristen/libnuke v0.22.1-0.20241224020243-7be2a0e103e7/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224020743-601f40417a76 h1:2iTjRT0tjhPlWzsi+UGWFESwLayKIPTMkteeYjZBLBg= +github.com/ekristen/libnuke v0.22.1-0.20241224020743-601f40417a76/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224020946-0c31c1eee047 h1:OHf7bPVx48sHgdYaC/pTbMGt7LQoGNzZqlz3epGehzU= +github.com/ekristen/libnuke v0.22.1-0.20241224020946-0c31c1eee047/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224021517-58558eca46d9 h1:TKlvpnx9k+hjeTNjquOxRPq47xl0FW4Jf4Wzwx9qZ9c= +github.com/ekristen/libnuke v0.22.1-0.20241224021517-58558eca46d9/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224024221-2402c0d76567 h1:+dAYoLLg2UCwGelfEf13VxoFb66PrpjZfOIccpRYtiE= +github.com/ekristen/libnuke v0.22.1-0.20241224024221-2402c0d76567/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224025404-3a4e531fa303 h1:iFEyPC/lBbaF68Db3mFZOp7xQgXfaKHocg4M17PkE9Q= +github.com/ekristen/libnuke v0.22.1-0.20241224025404-3a4e531fa303/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa h1:5f23sLGBiZRow/3Htpy9srvWTfHdRuhPI/devs4Q08c= +github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b h1:muIBxgtxFu+7OfkfH2CD7q9AfcQaYmOVCRsUxFRYERY= +github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -198,12 +110,8 @@ github.com/stevenle/topsort v0.2.0 h1:LLWgtp34HPX6/RBDRS0kElVxGOTzGBLI1lSAa5Lb46 github.com/stevenle/topsort v0.2.0/go.mod h1:ck2WG2/ZrOr6dLApQ/5Xrqy5wv3T0qhKYWE7r9tkibc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= -github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= @@ -221,10 +129,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -235,19 +139,11 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/commands/global/global.go b/pkg/commands/global/global.go index f04bc653..5b7c945e 100644 --- a/pkg/commands/global/global.go +++ b/pkg/commands/global/global.go @@ -17,12 +17,13 @@ func Flags() []cli.Flag { Name: "log-level", Usage: "Log Level", Aliases: []string{"l"}, - EnvVars: []string{"LOGLEVEL"}, + EnvVars: []string{"LOGLEVEL", "AWS_NUKE_LOG_LEVEL"}, Value: "info", }, &cli.BoolFlag{ - Name: "log-caller", - Usage: "log the caller (aka line number and file)", + Name: "log-caller", + Usage: "log the caller (aka line number and file)", + EnvVars: []string{"AWS_NUKE_LOG_CALLER"}, }, &cli.BoolFlag{ Name: "log-disable-color", @@ -33,9 +34,15 @@ func Flags() []cli.Flag { Usage: "force log output to always show full timestamp", }, &cli.StringFlag{ - Name: "log-format", - Usage: "log format", - Value: "standard", + Name: "log-format", + Usage: "log format", + Value: "standard", + EnvVars: []string{"AWS_NUKE_LOG_FORMAT"}, + }, + &cli.BoolFlag{ + Name: "json", + Usage: "output as json, shorthand for --log-format=json", + EnvVars: []string{"AWS_NUKE_LOG_FORMAT_JSON"}, }, } @@ -59,9 +66,17 @@ func Before(c *cli.Context) error { FallbackFormatter: formatter, } + if c.Bool("json") { + _ = c.Set("log-format", "json") + } + switch c.String("log-format") { case "json": - logrus.SetFormatter(&logrus.JSONFormatter{}) + logrus.SetFormatter(&logrus.JSONFormatter{ + DisableHTMLEscape: true, + }) + // note: this is a hack to remove the _handler key from the log output + logrus.AddHook(&StructuredHook{}) case "kv": logrus.SetFormatter(&logrus.TextFormatter{ DisableColors: true, @@ -86,3 +101,22 @@ func Before(c *cli.Context) error { return nil } + +type StructuredHook struct { +} + +func (h *StructuredHook) Levels() []logrus.Level { + return logrus.AllLevels +} + +func (h *StructuredHook) Fire(e *logrus.Entry) error { + if e.Data == nil { + return nil + } + + if _, ok := e.Data["_handler"]; ok { + delete(e.Data, "_handler") + } + + return nil +} diff --git a/pkg/commands/nuke/nuke.go b/pkg/commands/nuke/nuke.go index f91535a2..69ec1b90 100644 --- a/pkg/commands/nuke/nuke.go +++ b/pkg/commands/nuke/nuke.go @@ -122,7 +122,7 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo n.SetRunSleep(c.Duration("run-sleep-delay")) n.SetLogger(logger.WithField("component", "libnuke")) - n.RegisterVersion(fmt.Sprintf("> %s", common.AppVersion.String())) + n.RegisterVersion(common.AppVersion.String()) // Register our custom validate handler that validates the account and AWS nuke unique alias checks n.RegisterValidateHandler(func() error { @@ -130,7 +130,7 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo }) // Register our custom prompt handler that shows the account information - p := &nuke.Prompt{Parameters: params, Account: account} + p := &nuke.Prompt{Parameters: params, Account: account, Logger: logger} n.RegisterPrompt(p.Prompt) // Get any specific account level configuration diff --git a/pkg/nuke/prompt.go b/pkg/nuke/prompt.go index d4dc1265..fb494346 100644 --- a/pkg/nuke/prompt.go +++ b/pkg/nuke/prompt.go @@ -4,6 +4,8 @@ import ( "fmt" "time" + "github.com/sirupsen/logrus" + libnuke "github.com/ekristen/libnuke/pkg/nuke" "github.com/ekristen/libnuke/pkg/utils" @@ -15,18 +17,21 @@ import ( type Prompt struct { Parameters *libnuke.Parameters Account *awsutil.Account + Logger *logrus.Logger } // Prompt is the actual function called by the libnuke process during it's run func (p *Prompt) Prompt() error { forceSleep := time.Duration(p.Parameters.ForceSleep) * time.Second - fmt.Printf("Do you really want to nuke the account with "+ - "the ID %s and the alias '%s'?\n", p.Account.ID(), p.Account.Alias()) if p.Parameters.Force { - fmt.Printf("Waiting %v before continuing.\n", forceSleep) + p.Logger.WithField("_handler", "println").Info("no-prompt flag set, continuing without prompting user") + p.Logger.WithField("_handler", "println").Infof("waiting %v before continuing", forceSleep) time.Sleep(forceSleep) } else { + fmt.Printf("Do you really want to nuke the account with "+ + "the ID %s and the alias '%s'?\n", p.Account.ID(), p.Account.Alias()) + fmt.Printf("Do you want to continue? Enter account alias to continue.\n") if err := utils.Prompt(p.Account.Alias()); err != nil { return err From 86396974d3442d667c5655c571019ad864cb9b93 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Mon, 23 Dec 2024 20:11:18 -0700 Subject: [PATCH 35/87] chore(golangci-lint): gocritic cleanup --- pkg/commands/global/global.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/commands/global/global.go b/pkg/commands/global/global.go index 5b7c945e..652bf097 100644 --- a/pkg/commands/global/global.go +++ b/pkg/commands/global/global.go @@ -114,9 +114,7 @@ func (h *StructuredHook) Fire(e *logrus.Entry) error { return nil } - if _, ok := e.Data["_handler"]; ok { - delete(e.Data, "_handler") - } + delete(e.Data, "_handler") return nil } From 0a4b89a3f8d0f97a4c01af6ce5bea2b841feb0e0 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 26 Dec 2024 10:43:27 -0700 Subject: [PATCH 36/87] fix(cognito-userpool): include user attribute update settings --- resources/cognito-userpool.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/resources/cognito-userpool.go b/resources/cognito-userpool.go index 33fd6e82..897576e1 100644 --- a/resources/cognito-userpool.go +++ b/resources/cognito-userpool.go @@ -99,13 +99,21 @@ type CognitoUserPool struct { func (r *CognitoUserPool) Remove(_ context.Context) error { if r.settings.GetBool("DisableDeletionProtection") { - _, err := r.svc.UpdateUserPool(&cognitoidentityprovider.UpdateUserPoolInput{ - UserPoolId: r.ID, - DeletionProtection: ptr.String("INACTIVE"), + userPool, err := r.svc.DescribeUserPool(&cognitoidentityprovider.DescribeUserPoolInput{ + UserPoolId: r.ID, }) if err != nil { return err } + + _, updateErr := r.svc.UpdateUserPool(&cognitoidentityprovider.UpdateUserPoolInput{ + UserPoolId: r.ID, + DeletionProtection: ptr.String("INACTIVE"), + UserAttributeUpdateSettings: userPool.UserPool.UserAttributeUpdateSettings, + }) + if updateErr != nil { + return updateErr + } } _, err := r.svc.DeleteUserPool(&cognitoidentityprovider.DeleteUserPoolInput{ From 593b1c617e24d971faaad0111a0f32274ad91557 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 26 Dec 2024 11:05:08 -0700 Subject: [PATCH 37/87] fix(cognito-userpool): include auto verified attributes --- resources/cognito-userpool.go | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/cognito-userpool.go b/resources/cognito-userpool.go index 897576e1..fd528d73 100644 --- a/resources/cognito-userpool.go +++ b/resources/cognito-userpool.go @@ -110,6 +110,7 @@ func (r *CognitoUserPool) Remove(_ context.Context) error { UserPoolId: r.ID, DeletionProtection: ptr.String("INACTIVE"), UserAttributeUpdateSettings: userPool.UserPool.UserAttributeUpdateSettings, + AutoVerifiedAttributes: userPool.UserPool.AutoVerifiedAttributes, }) if updateErr != nil { return updateErr From 1e64bc0a4d627d0b55e6a72c0e38780a06df6349 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 26 Dec 2024 11:05:42 -0700 Subject: [PATCH 38/87] test(cognito-userpool): fix tests to account for new changes --- resources/cognito-userpool_mock_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/resources/cognito-userpool_mock_test.go b/resources/cognito-userpool_mock_test.go index fc0bfa57..32244630 100644 --- a/resources/cognito-userpool_mock_test.go +++ b/resources/cognito-userpool_mock_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/golang/mock/gomock" + "github.com/gotidy/ptr" "github.com/stretchr/testify/assert" "github.com/aws/aws-sdk-go/aws" @@ -85,9 +86,24 @@ func Test_Mock_CognitoUserPool_Remove_DeletionProtection(t *testing.T) { mockSvc := mock_cognitoidentityprovideriface.NewMockCognitoIdentityProviderAPI(ctrl) + mockSvc.EXPECT().DescribeUserPool(&cognitoidentityprovider.DescribeUserPoolInput{ + UserPoolId: aws.String("test-pool-id"), + }).Return(&cognitoidentityprovider.DescribeUserPoolOutput{ + UserPool: &cognitoidentityprovider.UserPoolType{ + UserAttributeUpdateSettings: &cognitoidentityprovider.UserAttributeUpdateSettingsType{ + AttributesRequireVerificationBeforeUpdate: []*string{ptr.String("email")}, + }, + AutoVerifiedAttributes: []*string{ptr.String("email")}, + }, + }, nil) + mockSvc.EXPECT().UpdateUserPool(&cognitoidentityprovider.UpdateUserPoolInput{ UserPoolId: aws.String("test-pool-id"), DeletionProtection: aws.String("INACTIVE"), + UserAttributeUpdateSettings: &cognitoidentityprovider.UserAttributeUpdateSettingsType{ + AttributesRequireVerificationBeforeUpdate: []*string{ptr.String("email")}, + }, + AutoVerifiedAttributes: []*string{ptr.String("email")}, }).Return(&cognitoidentityprovider.UpdateUserPoolOutput{}, nil) mockSvc.EXPECT().DeleteUserPool(&cognitoidentityprovider.DeleteUserPoolInput{ From f47c098137979d01a16ac57f52557568de6e92b7 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 26 Dec 2024 14:17:09 -0700 Subject: [PATCH 39/87] fix(deps): update to 0.24.0 for structured logging --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 85be7f29..bddba488 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/smithy-go v1.22.1 - github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b + github.com/ekristen/libnuke v0.24.0 github.com/fatih/color v1.18.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index a54f9746..9536d20d 100644 --- a/go.sum +++ b/go.sum @@ -68,6 +68,8 @@ github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa h1:5f23sLGBiZR github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b h1:muIBxgtxFu+7OfkfH2CD7q9AfcQaYmOVCRsUxFRYERY= github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= +github.com/ekristen/libnuke v0.24.0 h1:DCzkOwT+n/2G37KoNSlTOU1DoAVcBgtdaJSNpQl6NJw= +github.com/ekristen/libnuke v0.24.0/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= From b190ddf5c63a581a2579eb0597396ded71c58db4 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 26 Dec 2024 14:00:33 -0700 Subject: [PATCH 40/87] feat: expand resource names using globs --- pkg/commands/list/list.go | 10 ++++++---- pkg/commands/nuke/nuke.go | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/commands/list/list.go b/pkg/commands/list/list.go index 7f4a580b..7bfa0633 100644 --- a/pkg/commands/list/list.go +++ b/pkg/commands/list/list.go @@ -1,7 +1,6 @@ package list import ( - "sort" "strings" "github.com/fatih/color" @@ -15,9 +14,12 @@ import ( ) func execute(c *cli.Context) error { - ls := registry.GetNames() - - sort.Strings(ls) + var ls []string + if c.Args().Len() > 0 { + ls = registry.ExpandNames(c.Args().Slice()) + } else { + ls = registry.GetNames() + } for _, name := range ls { if strings.HasPrefix(name, "AWS::") { diff --git a/pkg/commands/nuke/nuke.go b/pkg/commands/nuke/nuke.go index 69ec1b90..63d21954 100644 --- a/pkg/commands/nuke/nuke.go +++ b/pkg/commands/nuke/nuke.go @@ -141,17 +141,17 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo resourceTypes := types.ResolveResourceTypes( registry.GetNames(), []types.Collection{ - n.Parameters.Includes, + registry.ExpandNames(n.Parameters.Includes), parsedConfig.ResourceTypes.GetIncludes(), accountConfig.ResourceTypes.GetIncludes(), }, []types.Collection{ - n.Parameters.Excludes, + registry.ExpandNames(n.Parameters.Excludes), parsedConfig.ResourceTypes.Excludes, accountConfig.ResourceTypes.Excludes, }, []types.Collection{ - n.Parameters.Alternatives, + registry.ExpandNames(n.Parameters.Alternatives), parsedConfig.ResourceTypes.GetAlternatives(), accountConfig.ResourceTypes.GetAlternatives(), }, From 9876f61f60d24247e77cb6ec162d779879a7beb8 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 26 Dec 2024 14:09:22 -0700 Subject: [PATCH 41/87] docs: add docs about name expansion --- docs/features/name-expansion.md | 51 +++++++++++++++++++++++++++++++++ docs/features/overview.md | 2 ++ docs/index.md | 1 + mkdocs.yml | 1 + 4 files changed, 55 insertions(+) create mode 100644 docs/features/name-expansion.md diff --git a/docs/features/name-expansion.md b/docs/features/name-expansion.md new file mode 100644 index 00000000..1e142357 --- /dev/null +++ b/docs/features/name-expansion.md @@ -0,0 +1,51 @@ +# Name Expansion + +This allows you to use wildcards in the resource names to match multiple resources. This is primarily useful when you +want to target a group of resource type for either inclusion or exclusion. + +Resource Name expansion is valid for use in the following areas: + +!!! warning +This feature is currently **NOT** supported in filters. + +- cli includes/excludes +- config resource types includes/excludes +- account resource types includes/excludes + +## Examples + +### CLI + +```console +aws-nuke run --config config.yaml --include "Cognito*" +``` + +This can also be used with `resource-types` subcommand to see what resource types are available, and you can specify +multiple wildcard arguments. + +```console +aws-nuke resource-types "Cognito*" "IAM*" +``` + +### Config + +```yaml +resource-types: + includes: + - "Cognito*" + excludes: + - "OpsWorks*" +``` + +### Account Config + +```yaml +accounts: + '012345678912': + resource-types: + includes: + - "Cognito*" + excludes: + - "OpsWorks*" +``` + diff --git a/docs/features/overview.md b/docs/features/overview.md index 95aa6a6d..305fb16e 100644 --- a/docs/features/overview.md +++ b/docs/features/overview.md @@ -6,6 +6,8 @@ Some of the new features include: - [Run Against All Enabled Regions](enabled-regions.md) - [Bypass Alias Check - Allow the skip of an alias on an account](bypass-alias-check.md) - [Signed Binaries](signed-binaries.md) +- [Filter Groups (Experimental)](filter-groups.md) +- [Name Expansion](name-expansion.md) Additionally, there are a few new sub commands to the tool to help with setup and debugging purposes: diff --git a/docs/index.md b/docs/index.md index 9f6499bc..6930e3b1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,6 +18,7 @@ This is not a comprehensive list, but here are some of the highlights: * New Feature: [Run Against All Enabled Regions](features/enabled-regions.md) * New Feature: [Bypass Alias Check - Allow the skip of an alias on an account](features/bypass-alias-check.md) * New Feature: [Filter Groups (Experimental)](features/filter-groups.md) +* New Feature: [Name Expansion](features/name-expansion.md) * Breaking Change: `root` command no longer triggers the run, must use subcommand `run` (alias: `nuke`) * Completely rewrote the core of the tool as a dedicated library [libnuke](https://github.com/ekristen/libnuke) * This library has over 95% test coverage which makes iteration and new features easier to implement. diff --git a/mkdocs.yml b/mkdocs.yml index af4bdb8a..668c7bf7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -79,6 +79,7 @@ nav: - Global Filters: features/global-filters.md - Filter Groups: features/filter-groups.md - Enabled Regions: features/enabled-regions.md + - Name Expansion: features/name-expansion.md - Signed Binaries: features/signed-binaries.md - CLI: - Usage: cli-usage.md From 323856a4f34a5a63063fad50c4f7ff800a5b5978 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 1 Jan 2025 18:03:25 -0700 Subject: [PATCH 42/87] fix(neptune-cluster): filter on engine to neptune --- resources/neptune-cluster.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/neptune-cluster.go b/resources/neptune-cluster.go index a20abe26..9d20535d 100644 --- a/resources/neptune-cluster.go +++ b/resources/neptune-cluster.go @@ -43,6 +43,12 @@ func (l *NeptuneClusterLister) List(_ context.Context, o interface{}) ([]resourc resources := make([]resource.Resource, 0) params := &neptune.DescribeDBClustersInput{ + Filters: []*neptune.Filter{ + { + Name: aws.String("engine"), + Values: []*string{aws.String("neptune")}, + }, + }, MaxRecords: aws.Int64(100), } From 78cb22c96eb22a4279301d47daf04e9bd6b5d097 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 1 Jan 2025 18:06:26 -0700 Subject: [PATCH 43/87] refactor(neptune-snapshot): standardization --- ...{neptune-snapshots.go => neptune-snapshot.go} | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) rename resources/{neptune-snapshots.go => neptune-snapshot.go} (79%) diff --git a/resources/neptune-snapshots.go b/resources/neptune-snapshot.go similarity index 79% rename from resources/neptune-snapshots.go rename to resources/neptune-snapshot.go index bfbde6cb..1dca4dce 100644 --- a/resources/neptune-snapshots.go +++ b/resources/neptune-snapshot.go @@ -2,9 +2,9 @@ package resources import ( "context" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/neptune" + "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" @@ -66,14 +66,18 @@ type NeptuneSnapshot struct { ID *string } -func (f *NeptuneSnapshot) Remove(_ context.Context) error { - _, err := f.svc.DeleteDBClusterSnapshot(&neptune.DeleteDBClusterSnapshotInput{ - DBClusterSnapshotIdentifier: f.ID, +func (r *NeptuneSnapshot) Remove(_ context.Context) error { + _, err := r.svc.DeleteDBClusterSnapshot(&neptune.DeleteDBClusterSnapshotInput{ + DBClusterSnapshotIdentifier: r.ID, }) return err } -func (f *NeptuneSnapshot) String() string { - return *f.ID +func (r *NeptuneSnapshot) String() string { + return *r.ID +} + +func (r *NeptuneSnapshot) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } From 6d956b503603a86f56f6dccb3723946d2c34816e Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 1 Jan 2025 18:07:05 -0700 Subject: [PATCH 44/87] feat(neptune-snapshot): new properties: Status and CreateTime --- resources/neptune-snapshot.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/resources/neptune-snapshot.go b/resources/neptune-snapshot.go index 1dca4dce..05d2689d 100644 --- a/resources/neptune-snapshot.go +++ b/resources/neptune-snapshot.go @@ -2,9 +2,11 @@ package resources import ( "context" + "github.com/ekristen/libnuke/pkg/types" + "time" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/neptune" - "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" @@ -46,8 +48,10 @@ func (l *NeptuneSnapshotLister) List(_ context.Context, o interface{}) ([]resour for _, dbClusterSnapshot := range output.DBClusterSnapshots { resources = append(resources, &NeptuneSnapshot{ - svc: svc, - ID: dbClusterSnapshot.DBClusterSnapshotIdentifier, + svc: svc, + ID: dbClusterSnapshot.DBClusterSnapshotIdentifier, + Status: dbClusterSnapshot.Status, + CreateTime: dbClusterSnapshot.SnapshotCreateTime, }) } @@ -62,8 +66,10 @@ func (l *NeptuneSnapshotLister) List(_ context.Context, o interface{}) ([]resour } type NeptuneSnapshot struct { - svc *neptune.Neptune - ID *string + svc *neptune.Neptune + ID *string + Status *string + CreateTime *time.Time } func (r *NeptuneSnapshot) Remove(_ context.Context) error { From 733d9fdc6e8cea0f9ff37736b69b62a595ad751e Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 1 Jan 2025 18:07:25 -0700 Subject: [PATCH 45/87] fix(neptune-snapshot): filter to just neptune engine --- resources/neptune-snapshot.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/neptune-snapshot.go b/resources/neptune-snapshot.go index 05d2689d..12684c2f 100644 --- a/resources/neptune-snapshot.go +++ b/resources/neptune-snapshot.go @@ -38,6 +38,12 @@ func (l *NeptuneSnapshotLister) List(_ context.Context, o interface{}) ([]resour params := &neptune.DescribeDBClusterSnapshotsInput{ MaxRecords: aws.Int64(100), + Filters: []*neptune.Filter{ + { + Name: aws.String("engine"), + Values: []*string{aws.String("neptune")}, + }, + }, } for { From 26f7af9db0036f1398df6e75a28f9076f7c2e9ce Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Wed, 1 Jan 2025 18:19:38 -0700 Subject: [PATCH 46/87] chore: golangci-lint --- resources/neptune-snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/neptune-snapshot.go b/resources/neptune-snapshot.go index 12684c2f..f1979dd0 100644 --- a/resources/neptune-snapshot.go +++ b/resources/neptune-snapshot.go @@ -2,7 +2,6 @@ package resources import ( "context" - "github.com/ekristen/libnuke/pkg/types" "time" "github.com/aws/aws-sdk-go/aws" @@ -10,6 +9,7 @@ import ( "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) From e5c3b38ff5b6dc58b9419b89f7388dcc6fe6b441 Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Thu, 2 Jan 2025 10:52:12 -0600 Subject: [PATCH 47/87] Go mod tidy --- Makefile | 5 ++++- go.sum | 24 ------------------------ 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index 81e33d52..e4024d23 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,9 @@ docs-serve: docs-seed: cp README.md docs/index.md +build: + go build -ldflags '-s -w -extldflags="-static"' -o bin/aws-nuke main.go + generate: go generate ./... @@ -14,4 +17,4 @@ test: go test ./... test-integration: - go test ./... -tags=integration \ No newline at end of file + go test ./... -tags=integration diff --git a/go.sum b/go.sum index 9536d20d..9db29455 100644 --- a/go.sum +++ b/go.sum @@ -44,30 +44,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ekristen/libnuke v0.22.1-0.20241224013520-52c0542b4b2d h1:JHgyOVL4FbtGnbz9i78HUqz47q2MKuSoiS3uL3VkgqQ= -github.com/ekristen/libnuke v0.22.1-0.20241224013520-52c0542b4b2d/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224014659-f83bc5d4d5d5 h1:d3ch1E1VVgWX1UTtQfROnp4P9z43/mkwC/WeFUVRjuM= -github.com/ekristen/libnuke v0.22.1-0.20241224014659-f83bc5d4d5d5/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224015245-2b08d39f8c3d h1:IjUGUL8nNJI9YBbZA97gKpku9Qja5xXoKmGlNQI4h/o= -github.com/ekristen/libnuke v0.22.1-0.20241224015245-2b08d39f8c3d/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224015748-4a35c1b48a4f h1:st1ewH2ViyeCOnD2E15ZGQDSz/gncFwT0fSCqg2b7Yk= -github.com/ekristen/libnuke v0.22.1-0.20241224015748-4a35c1b48a4f/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224020243-7be2a0e103e7 h1:s57amu5jKJcDhu9X307mQfBF/CUewEkvnAxIrlVF3Kw= -github.com/ekristen/libnuke v0.22.1-0.20241224020243-7be2a0e103e7/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224020743-601f40417a76 h1:2iTjRT0tjhPlWzsi+UGWFESwLayKIPTMkteeYjZBLBg= -github.com/ekristen/libnuke v0.22.1-0.20241224020743-601f40417a76/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224020946-0c31c1eee047 h1:OHf7bPVx48sHgdYaC/pTbMGt7LQoGNzZqlz3epGehzU= -github.com/ekristen/libnuke v0.22.1-0.20241224020946-0c31c1eee047/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224021517-58558eca46d9 h1:TKlvpnx9k+hjeTNjquOxRPq47xl0FW4Jf4Wzwx9qZ9c= -github.com/ekristen/libnuke v0.22.1-0.20241224021517-58558eca46d9/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224024221-2402c0d76567 h1:+dAYoLLg2UCwGelfEf13VxoFb66PrpjZfOIccpRYtiE= -github.com/ekristen/libnuke v0.22.1-0.20241224024221-2402c0d76567/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224025404-3a4e531fa303 h1:iFEyPC/lBbaF68Db3mFZOp7xQgXfaKHocg4M17PkE9Q= -github.com/ekristen/libnuke v0.22.1-0.20241224025404-3a4e531fa303/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa h1:5f23sLGBiZRow/3Htpy9srvWTfHdRuhPI/devs4Q08c= -github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b h1:muIBxgtxFu+7OfkfH2CD7q9AfcQaYmOVCRsUxFRYERY= -github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/ekristen/libnuke v0.24.0 h1:DCzkOwT+n/2G37KoNSlTOU1DoAVcBgtdaJSNpQl6NJw= github.com/ekristen/libnuke v0.24.0/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= From cf59f755e3e887fa00dc8d1d14147091b961f0b7 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 2 Jan 2025 12:34:07 -0700 Subject: [PATCH 48/87] chore(kms): improve logging --- resources/kms-alias.go | 2 +- resources/kms-key.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/kms-alias.go b/resources/kms-alias.go index f32efc04..aa13b603 100644 --- a/resources/kms-alias.go +++ b/resources/kms-alias.go @@ -50,7 +50,7 @@ func (l *KMSAliasLister) List(_ context.Context, o interface{}) ([]resource.Reso KeyId: alias.TargetKeyId, }) if err != nil { - opts.Logger.WithError(err).Error("failed to list tags for key") + opts.Logger.WithError(err).Debug("failed to list tags for key for the alias") } tags = keyTags.Tags } diff --git a/resources/kms-key.go b/resources/kms-key.go index 04ce5f13..572bba5b 100644 --- a/resources/kms-key.go +++ b/resources/kms-key.go @@ -88,7 +88,7 @@ func (l *KMSKeyLister) List(_ context.Context, o interface{}) ([]resource.Resour if errors.As(err, &awsError) { if awsError.Code() == "AccessDeniedException" { inaccessibleKeys = true - logrus.WithError(err).Debug("unable to list tags") + logrus.WithError(err).Debug("unable to list tags - inaccessible key") continue } else { logrus.WithError(err).Error("unable to list tags") From 38e00530e6cc20f174c62ee038aa9f4b392fe073 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 2 Jan 2025 12:39:43 -0700 Subject: [PATCH 49/87] refactor(resource-explorer2-view): standardization with properties --- ...r2-views.go => resource-explorer2-view.go} | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) rename resources/{resource-explorer2-views.go => resource-explorer2-view.go} (71%) diff --git a/resources/resource-explorer2-views.go b/resources/resource-explorer2-view.go similarity index 71% rename from resources/resource-explorer2-views.go rename to resources/resource-explorer2-view.go index 9f11a32f..99540efd 100644 --- a/resources/resource-explorer2-views.go +++ b/resources/resource-explorer2-view.go @@ -5,9 +5,12 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/resourceexplorer2" - "github.com/ekristen/aws-nuke/v3/pkg/nuke" + "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) const ResourceExplorer2ViewResource = "ResourceExplorer2View" @@ -23,11 +26,6 @@ func init() { type ResourceExplorer2ViewLister struct{} -type ResourceExplorer2View struct { - svc *resourceexplorer2.ResourceExplorer2 - viewArn *string -} - func (l *ResourceExplorer2ViewLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) svc := resourceexplorer2.New(opts.Session) @@ -43,8 +41,8 @@ func (l *ResourceExplorer2ViewLister) List(_ context.Context, o interface{}) ([] for _, view := range output.Views { resources = append(resources, &ResourceExplorer2View{ - svc: svc, - viewArn: view, + svc: svc, + ARN: view, }) } @@ -58,14 +56,23 @@ func (l *ResourceExplorer2ViewLister) List(_ context.Context, o interface{}) ([] return resources, nil } -func (f *ResourceExplorer2View) Remove(_ context.Context) error { - _, err := f.svc.DeleteView(&resourceexplorer2.DeleteViewInput{ - ViewArn: f.viewArn, +type ResourceExplorer2View struct { + svc *resourceexplorer2.ResourceExplorer2 + ARN *string `description:"The ARN of the Resource Explorer View"` +} + +func (r *ResourceExplorer2View) Remove(_ context.Context) error { + _, err := r.svc.DeleteView(&resourceexplorer2.DeleteViewInput{ + ViewArn: r.ARN, }) return err } -func (f *ResourceExplorer2View) String() string { - return *f.viewArn +func (r *ResourceExplorer2View) String() string { + return *r.ARN +} + +func (r *ResourceExplorer2View) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } From 287ba27b9fcf5cfb07181632bf88154667749240 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Thu, 2 Jan 2025 16:35:19 -0700 Subject: [PATCH 50/87] docs: update installation for install and verification --- docs/installation.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 529583ea..e2aefb2e 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -3,7 +3,7 @@ Preferred installation order is the following: 1. [GitHub Release](#github-releases-preferred) -2. [ekristen's homebrew tap](#ekristens-homebrew-tap-macoslinux) +2. [Homebrew Tap](#ekristens-homebrew-tap-macoslinux) 3. [Homebrew Core](#homebrew-core-macoslinux) Docker images are also available via the GitHub Container Registry. @@ -13,18 +13,24 @@ Docker images are also available via the GitHub Container Registry. !!! success - "Recommended" This supports all operating systems and most architectures. -You can download pre-compiled binaries from the [releases](https://github.com/ekristen/aws-nuke/releases) page. +You can download pre-compiled binaries from the [releases](https://github.com/ekristen/aws-nuke/releases) page, or you can use my tool +[distillery](https://github.com/ekristen/distillery) to download and install the latest version. -## ekristen's Homebrew Tap (MacOS/Linux) +```console +dist install ekristen/aws-nuke +``` + +## Homebrew Tap (macOS) !!! info - I control this tap, and it sources the binaries directly from the GitHub releases. However, it only supports MacOS. + I control this tap, and it sources the binaries directly from the GitHub releases. However, it only supports MacOS + and it tends to lag a bit behind. ```console brew install ekristen/tap/aws-nuke ``` -## Homebrew Core (MacOS/Linux) +## Homebrew Core (macOS/Linux) !!! note I do not control the Homebrew Core formula, so it may not be up to date. Additionally, it is not compiled with @@ -50,3 +56,18 @@ To compile **aws-nuke** from source you need a working [Golang](https://golang.o goreleaser build --clean --snapshot --single-target ``` +## Verifying Binaries + +All the binaries are signed with [cosign](https://github.com/sigstore/cosign) and are signed with keyless signatures. +You can verify the build using the public transparency log and the cosign binary. + +**Note:** swap out `VERSION` with `vX.Y.Z`. + +```console +cosign verify-blob \ + --signature https://github.com/ekristen/aws-nuke/releases/download/VERSION/checksums.txt.sig \ + --certificate https://github.com/ekristen/aws-nuke/releases/download/VERSION/checksums.txt.pem \ + --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ + --certificate-identity "https://github.com/ekristen/aws-nuke/.github/workflows/goreleaser.yml@refs/tags/VERSION" \ + https://github.com/ekristen/aws-nuke/releases/download/VERSION/checksums.txt +``` From e78c12d208ebc5e8691c2c79e12c2df73b14be7e Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Fri, 3 Jan 2025 09:34:35 -0600 Subject: [PATCH 51/87] Merge network firewall resource to new repo --- ...rewall-firewall.md => network-firewall.md} | 4 +- go.mod | 3 +- go.sum | 7 +- mkdocs.yml | 1 + resources/cloudcontrol.go | 1 - resources/network-firewall.go | 131 ++++++++++++++++++ 6 files changed, 140 insertions(+), 7 deletions(-) rename docs/resources/{aws-network-firewall-firewall.md => network-firewall.md} (93%) create mode 100644 resources/network-firewall.go diff --git a/docs/resources/aws-network-firewall-firewall.md b/docs/resources/network-firewall.md similarity index 93% rename from docs/resources/aws-network-firewall-firewall.md rename to docs/resources/network-firewall.md index 9784a352..f5ee57f9 100644 --- a/docs/resources/aws-network-firewall-firewall.md +++ b/docs/resources/network-firewall.md @@ -2,13 +2,13 @@ generated: true --- -# AWS::NetworkFirewall::Firewall +# NetworkFirewall ## Resource ```text -AWS::NetworkFirewall::Firewall +NetworkFirewall ``` ### Alternative Resource diff --git a/go.mod b/go.mod index bddba488..e218a2a6 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.32.7 github.com/aws/aws-sdk-go-v2/config v1.28.7 github.com/aws/aws-sdk-go-v2/credentials v1.17.48 + github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/smithy-go v1.22.1 @@ -16,6 +17,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gotidy/ptr v1.4.0 github.com/pkg/errors v0.9.1 + github.com/rebuy-de/aws-nuke/v2 v2.25.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 @@ -52,6 +54,5 @@ require ( github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.25.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 9db29455..1061ce09 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQz github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= +github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 h1:QylOhJ/Cq8rXpdGY0TVDozlqlrioqhBGL8lr0humk/Y= +github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7/go.mod h1:JRNqD9oCs0lfhwggQ9try04MIaPU8lJ5XIufAUv49Ho= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= @@ -58,11 +60,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -77,6 +76,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rebuy-de/aws-nuke/v2 v2.25.0 h1:uM/KoDOOIau1Gcx++D3oDFL1vlLPj9uuzQKtNVZxrHs= +github.com/rebuy-de/aws-nuke/v2 v2.25.0/go.mod h1:2TTX8eMpEsFZPYCK1QaAb9uPYtdO9MeLQPKM9GQfY/w= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= diff --git a/mkdocs.yml b/mkdocs.yml index 668c7bf7..8b838a75 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -487,6 +487,7 @@ nav: - Neptune Cluster: resources/neptune-cluster.md - Neptune Instance: resources/neptune-instance.md - Neptune Snapshot: resources/neptune-snapshot.md + - Network Firewall: resources/network-firewall.md - Network Manager Connect Peer: resources/network-manager-connect-peer.md - Network Manager Core Network: resources/network-manager-core-network.md - Network Manager Global Network: resources/network-manager-global-network.md diff --git a/resources/cloudcontrol.go b/resources/cloudcontrol.go index 7d95560e..7f789711 100644 --- a/resources/cloudcontrol.go +++ b/resources/cloudcontrol.go @@ -47,7 +47,6 @@ func init() { registerCloudControl("AWS::Timestream::ScheduledQuery") registerCloudControl("AWS::Timestream::Table") registerCloudControl("AWS::Transfer::Workflow") - registerCloudControl("AWS::NetworkFirewall::Firewall") registerCloudControl("AWS::NetworkFirewall::FirewallPolicy") registerCloudControl("AWS::NetworkFirewall::RuleGroup") } diff --git a/resources/network-firewall.go b/resources/network-firewall.go new file mode 100644 index 00000000..e45b85bb --- /dev/null +++ b/resources/network-firewall.go @@ -0,0 +1,131 @@ +package resources + +import ( + "context" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/networkfirewall" + networkfirwallTypes "github.com/aws/aws-sdk-go-v2/service/networkfirewall/types" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type NetworkFirewall struct { + svc *networkfirewall.Client + + firewall networkfirwallTypes.FirewallMetadata + logConfig *networkfirwallTypes.LoggingConfiguration + tags []networkfirwallTypes.Tag +} + +const NetworkFirewallResource = "NetworkFirewall" + +func init() { + registry.Register(®istry.Registration{ + Name: NetworkFirewallResource, + Scope: nuke.Account, + Resource: &NetworkFirewall{}, + Lister: &NetworkFirewallLister{}, + DependsOn: []string{}, + AlternativeResource: "AWS::NetworkFirewall::Firewall", + Settings: []string{}, + }) +} + +type NetworkFirewallLister struct{} + +func (l *NetworkFirewallLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := networkfirewall.NewFromConfig(*opts.Config) + + resources := make([]resource.Resource, 0) + params := &networkfirewall.ListFirewallsInput{ + MaxResults: aws.Int32(100), + } + + for { + resp, err := svc.ListFirewalls(ctx, params) + if err != nil { + return nil, err + } + for _, firewall := range resp.Firewalls { + tagParams := &networkfirewall.ListTagsForResourceInput{ + ResourceArn: firewall.FirewallArn, + MaxResults: aws.Int32(100), + } + tags := []networkfirwallTypes.Tag{} + for { + tagResp, tagErr := svc.ListTagsForResource(ctx, tagParams) + if tagErr != nil { + return nil, tagErr + } + tags = append(tags, tagResp.Tags...) + if tagResp.NextToken == nil { + break + } + tagParams.NextToken = tagResp.NextToken + } + // logging configuration required to delete firewall + logResp, err := svc.DescribeLoggingConfiguration(ctx, &networkfirewall.DescribeLoggingConfigurationInput{ + FirewallArn: firewall.FirewallArn, + }) + if err != nil { + return nil, err + } + resources = append(resources, &NetworkFirewall{ + svc: svc, + firewall: firewall, + logConfig: logResp.LoggingConfiguration, + tags: tags, + }) + } + if resp.NextToken == nil { + break + } + params.NextToken = resp.NextToken + } + return resources, nil +} + +func (i *NetworkFirewall) Remove(ctx context.Context) error { + if i.logConfig != nil { + for index := 1; index <= len(i.logConfig.LogDestinationConfigs); index++ { + // aws forces to only remove one at a time + _, err := i.svc.UpdateLoggingConfiguration(ctx, &networkfirewall.UpdateLoggingConfigurationInput{ + FirewallArn: i.firewall.FirewallArn, + LoggingConfiguration: &networkfirwallTypes.LoggingConfiguration{ + LogDestinationConfigs: i.logConfig.LogDestinationConfigs[index:], + }, + }) + if err != nil { + return err + } + } + } + params := &networkfirewall.DeleteFirewallInput{ + FirewallArn: i.firewall.FirewallArn, + } + _, err := i.svc.DeleteFirewall(ctx, params) + if err != nil { + return err + } + return nil +} + +func (i *NetworkFirewall) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("ARN", *i.firewall.FirewallArn) + properties.Set("Name", *i.firewall.FirewallName) + properties.Set("Logging Configured", i.logConfig != nil) + for _, tag := range i.tags { + properties.SetTag(tag.Key, *tag.Value) + } + return properties +} + +func (i *NetworkFirewall) String() string { + return *i.firewall.FirewallArn +} From a3658e6d4fa8b1899c8324981448ec51a93062e2 Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Fri, 3 Jan 2025 10:04:34 -0600 Subject: [PATCH 52/87] Merge kms handling to new repo --- docs/resources/kms-key.md | 13 +++++++++++++ mkdocs.yml | 1 - resources/kms-key.go | 27 +++++++++++++++++++++------ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/docs/resources/kms-key.md b/docs/resources/kms-key.md index b2cb038b..1801030f 100644 --- a/docs/resources/kms-key.md +++ b/docs/resources/kms-key.md @@ -33,6 +33,19 @@ the filter. The string value is always what is used in the output of the log format when a resource is identified. +## Settings + +- `IgnoreErrors` + + +### IgnoreErrors + +KMS keys can be often in a state that can't be deleted if the KMS policy had been malformed. Give option to igore error in order to not fail the overall nuke. + +```text +IgnoreErrors +``` + ### DependsOn !!! important - Experimental Feature diff --git a/mkdocs.yml b/mkdocs.yml index 8b838a75..84699eda 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -121,7 +121,6 @@ nav: - AWS Ecr Replication Configuration: resources/aws-ecr-replication-configuration.md - AWS Mwaa Environment: resources/aws-mwaa-environment.md - AWS Network Firewall Firewall Policy: resources/aws-network-firewall-firewall-policy.md - - AWS Network Firewall Firewall: resources/aws-network-firewall-firewall.md - AWS Network Firewall Rule Group: resources/aws-network-firewall-rule-group.md - AWS Synthetics Canary: resources/aws-synthetics-canary.md - AWS Timestream Database: resources/aws-timestream-database.md diff --git a/resources/kms-key.go b/resources/kms-key.go index 04ce5f13..bdd1d092 100644 --- a/resources/kms-key.go +++ b/resources/kms-key.go @@ -15,6 +15,7 @@ import ( "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + libsettings "github.com/ekristen/libnuke/pkg/settings" "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" @@ -31,6 +32,9 @@ func init() { DependsOn: []string{ KMSAliasResource, }, + Settings: []string{ + "IgnoreErrors", + }, }) } @@ -126,12 +130,13 @@ func (l *KMSKeyLister) List(_ context.Context, o interface{}) ([]resource.Resour } type KMSKey struct { - svc kmsiface.KMSAPI - ID *string - State *string - Manager *string - Alias *string - Tags []*kms.Tag + svc kmsiface.KMSAPI + settings *libsettings.Setting + ID *string + State *string + Manager *string + Alias *string + Tags []*kms.Tag } func (r *KMSKey) Filter() error { @@ -151,6 +156,12 @@ func (r *KMSKey) Remove(_ context.Context) error { KeyId: r.ID, PendingWindowInDays: aws.Int64(7), }) + + // Ignore errors if the setting is enabled as AWS KMS keys can be in a state where they can't be deleted and we don't want to fail the whole nuke if setting is enabled + if err != nil && r.settings.GetBool("IgnoreErrors") { + fmt.Printf("ignoring error for key %s. Error: %v\n", *r.ID, err) + return nil + } return err } @@ -158,6 +169,10 @@ func (r *KMSKey) String() string { return *r.ID } +func (r *KMSKey) Settings(settings *libsettings.Setting) { + r.settings = settings +} + func (r *KMSKey) Properties() types.Properties { return types.NewPropertiesFromStruct(r) } From 52755875b5d0ce166efd493396ffaf769db13351 Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Fri, 3 Jan 2025 10:29:27 -0600 Subject: [PATCH 53/87] Merge new changes to appstream to new repo --- go.mod | 1 + go.sum | 2 + resources/appstream-images.go | 86 ++++++++++++++++++++++++++--------- resources/network-firewall.go | 2 - 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index e218a2a6..7a64c814 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.32.7 github.com/aws/aws-sdk-go-v2/config v1.28.7 github.com/aws/aws-sdk-go-v2/credentials v1.17.48 + github.com/aws/aws-sdk-go-v2/service/appstream v1.42.0 github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 diff --git a/go.sum b/go.sum index 1061ce09..42a566e6 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvK github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo= +github.com/aws/aws-sdk-go-v2/service/appstream v1.42.0 h1:d36PMCY+OwgPqJfgPvByG2gjel2e0mcMt0JNEYMeFm4= +github.com/aws/aws-sdk-go-v2/service/appstream v1.42.0/go.mod h1:HeQk2RRQrmFPeFN1vcg9ZUx85LNH154LjGWaHW6hD3k= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY= diff --git a/resources/appstream-images.go b/resources/appstream-images.go index 2682f807..017b0108 100644 --- a/resources/appstream-images.go +++ b/resources/appstream-images.go @@ -7,10 +7,11 @@ import ( "github.com/gotidy/ptr" - "github.com/aws/aws-sdk-go/service/appstream" + "github.com/aws/aws-sdk-go-v2/service/appstream" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) @@ -28,38 +29,75 @@ func init() { type AppStreamImageLister struct{} -func (l *AppStreamImageLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { +func (l *AppStreamImageLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) + svc := appstream.NewFromConfig(*opts.Config) - svc := appstream.New(opts.Session) resources := make([]resource.Resource, 0) - - params := &appstream.DescribeImagesInput{} - - output, err := svc.DescribeImages(params) - if err != nil { - return nil, err - } - - for _, image := range output.Images { - resources = append(resources, &AppStreamImage{ - svc: svc, - name: image.Name, - visibility: image.Visibility, - }) + var nextToken *string + + for ok := true; ok; ok = (nextToken != nil) { + params := &appstream.DescribeImagesInput{ + NextToken: nextToken, + } + + output, err := svc.DescribeImages(ctx, params) + if err != nil { + return nil, err + } + nextToken = output.NextToken + + for _, image := range output.Images { + sharedAccounts := []*string{} + visibility := string(image.Visibility) + + // Filter out public images + if strings.ToUpper(visibility) != "PUBLIC" { + imagePerms, err := svc.DescribeImagePermissions(ctx, &appstream.DescribeImagePermissionsInput{ + Name: image.Name, + }) + + if err != nil { + return nil, err + } + + for _, permission := range imagePerms.SharedImagePermissionsList { + sharedAccounts = append(sharedAccounts, permission.SharedAccountId) + } + + resources = append(resources, &AppStreamImage{ + svc: svc, + name: image.Name, + visibility: &visibility, + sharedAccounts: sharedAccounts, + }) + } + } } return resources, nil } type AppStreamImage struct { - svc *appstream.AppStream - name *string - visibility *string + svc *appstream.Client + name *string + visibility *string + sharedAccounts []*string } -func (f *AppStreamImage) Remove(_ context.Context) error { - _, err := f.svc.DeleteImage(&appstream.DeleteImageInput{ +func (f *AppStreamImage) Remove(ctx context.Context) error { + for _, account := range f.sharedAccounts { + _, err := f.svc.DeleteImagePermissions(ctx, &appstream.DeleteImagePermissionsInput{ + Name: f.name, + SharedAccountId: account, + }) + if err != nil { + fmt.Println("Error deleting image permissions", err) + return err + } + } + + _, err := f.svc.DeleteImage(ctx, &appstream.DeleteImageInput{ Name: f.name, }) @@ -76,3 +114,7 @@ func (f *AppStreamImage) Filter() error { } return nil } + +func (f *AppStreamImage) Properties() types.Properties { + return types.NewPropertiesFromStruct(f) +} diff --git a/resources/network-firewall.go b/resources/network-firewall.go index e45b85bb..3137fddb 100644 --- a/resources/network-firewall.go +++ b/resources/network-firewall.go @@ -29,9 +29,7 @@ func init() { Scope: nuke.Account, Resource: &NetworkFirewall{}, Lister: &NetworkFirewallLister{}, - DependsOn: []string{}, AlternativeResource: "AWS::NetworkFirewall::Firewall", - Settings: []string{}, }) } From 9eaecd6c3312c93949e54a4b0185a96eee1845ff Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Fri, 3 Jan 2025 10:33:04 -0600 Subject: [PATCH 54/87] Merge new changes to wafregionalrule to new repo --- go.mod | 2 ++ go.sum | 4 +++ resources/msk-cluster.go | 33 +++++++++++--------- resources/wafregional-rules.go | 56 ++++++++++++++++++---------------- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 7a64c814..f1836927 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,11 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.28.7 github.com/aws/aws-sdk-go-v2/credentials v1.17.48 github.com/aws/aws-sdk-go-v2/service/appstream v1.42.0 + github.com/aws/aws-sdk-go-v2/service/kafka v1.38.9 github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 + github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.8 github.com/aws/smithy-go v1.22.1 github.com/ekristen/libnuke v0.24.0 github.com/fatih/color v1.18.0 diff --git a/go.sum b/go.sum index 42a566e6..4ada5afa 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQz github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= +github.com/aws/aws-sdk-go-v2/service/kafka v1.38.9 h1:2y8YEVPTiGBPPoHBLWU+hEnbA/rLVCEWkeF9nD0ox38= +github.com/aws/aws-sdk-go-v2/service/kafka v1.38.9/go.mod h1:8BomElFc/tYQysVPwmZXFytmn2CwnxKW51HqYD2B29Q= github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 h1:QylOhJ/Cq8rXpdGY0TVDozlqlrioqhBGL8lr0humk/Y= github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7/go.mod h1:JRNqD9oCs0lfhwggQ9try04MIaPU8lJ5XIufAUv49Ho= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= @@ -38,6 +40,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= +github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.8 h1:LG+3JxLZqa1wGrp9bCA0mP+jeqGokKEleLmk9cgnvUw= +github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.8/go.mod h1:k5QM358GxnQJ19Y/XGCnY8a6pLrb2CfvC2S59UKq0ok= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= diff --git a/resources/msk-cluster.go b/resources/msk-cluster.go index 31b221ba..402d18ee 100644 --- a/resources/msk-cluster.go +++ b/resources/msk-cluster.go @@ -3,8 +3,8 @@ package resources import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/kafka" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/kafka" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" @@ -26,42 +26,45 @@ func init() { type MSKClusterLister struct{} -func (l *MSKClusterLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { +func (l *MSKClusterLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) + svc := kafka.NewFromConfig(*opts.Config) - svc := kafka.New(opts.Session) + params := &kafka.ListClustersV2Input{} + resp, err := svc.ListClustersV2(ctx, params) - resp, err := svc.ListClusters(&kafka.ListClustersInput{}) if err != nil { return nil, err } - resources := make([]resource.Resource, 0) for _, cluster := range resp.ClusterInfoList { resources = append(resources, &MSKCluster{ - svc: svc, - arn: *cluster.ClusterArn, - name: *cluster.ClusterName, - tags: cluster.Tags, + svc: svc, + context: ctx, + arn: *cluster.ClusterArn, + name: *cluster.ClusterName, + tags: cluster.Tags, }) - } + } return resources, nil } type MSKCluster struct { - svc *kafka.Kafka + svc *kafka.Client + context context.Context + arn string name string - tags map[string]*string + tags map[string]string } -func (m *MSKCluster) Remove(_ context.Context) error { +func (m *MSKCluster) Remove(ctx context.Context) error { params := &kafka.DeleteClusterInput{ ClusterArn: &m.arn, } - _, err := m.svc.DeleteCluster(params) + _, err := m.svc.DeleteCluster(ctx, params) if err != nil { return err } diff --git a/resources/wafregional-rules.go b/resources/wafregional-rules.go index a294d368..dd68e597 100644 --- a/resources/wafregional-rules.go +++ b/resources/wafregional-rules.go @@ -3,9 +3,8 @@ package resources import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/waf" - "github.com/aws/aws-sdk-go/service/wafregional" + "github.com/aws/aws-sdk-go-v2/service/wafregional" + wafregionalTypes "github.com/aws/aws-sdk-go-v2/service/wafregional/types" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" @@ -27,26 +26,29 @@ func init() { type WAFRegionalRuleLister struct{} -func (l *WAFRegionalRuleLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { +func (l *WAFRegionalRuleLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) - svc := wafregional.New(opts.Session) + svc := wafregional.NewFromConfig(*opts.Config) resources := make([]resource.Resource, 0) - params := &waf.ListRulesInput{ - Limit: aws.Int64(50), + params := &wafregional.ListRulesInput{ + Limit: 50, } for { - resp, err := svc.ListRules(params) + resp, err := svc.ListRules(ctx, params) if err != nil { return nil, err } for _, rule := range resp.Rules { - ruleResp, _ := svc.GetRule(&waf.GetRuleInput{ + ruleResp, err := svc.GetRule(ctx, &wafregional.GetRuleInput{ RuleId: rule.RuleId, }) + if err != nil { + return nil, err + } resources = append(resources, &WAFRegionalRule{ svc: svc, ID: rule.RuleId, @@ -66,42 +68,44 @@ func (l *WAFRegionalRuleLister) List(_ context.Context, o interface{}) ([]resour } type WAFRegionalRule struct { - svc *wafregional.WAFRegional + svc *wafregional.Client ID *string name *string - rule *waf.Rule + rule *wafregionalTypes.Rule } -func (f *WAFRegionalRule) Remove(_ context.Context) error { - tokenOutput, err := f.svc.GetChangeToken(&waf.GetChangeTokenInput{}) +func (f *WAFRegionalRule) Remove(ctx context.Context) error { + tokenOutput, err := f.svc.GetChangeToken(ctx, &wafregional.GetChangeTokenInput{}) if err != nil { return err } - var ruleUpdates []*waf.RuleUpdate + ruleUpdates := []wafregionalTypes.RuleUpdate{} for _, predicate := range f.rule.Predicates { - ruleUpdates = append(ruleUpdates, &waf.RuleUpdate{ - Action: aws.String(waf.ChangeActionDelete), - Predicate: predicate, + ruleUpdates = append(ruleUpdates, wafregionalTypes.RuleUpdate{ + Action: wafregionalTypes.ChangeActionDelete, + Predicate: &predicate, }) } - _, err = f.svc.UpdateRule(&waf.UpdateRuleInput{ - ChangeToken: tokenOutput.ChangeToken, - RuleId: f.ID, - Updates: ruleUpdates, - }) + if len(ruleUpdates) > 0 { + _, err = f.svc.UpdateRule(ctx, &wafregional.UpdateRuleInput{ + ChangeToken: tokenOutput.ChangeToken, + RuleId: f.ID, + Updates: ruleUpdates, + }) - if err != nil { - return err + if err != nil { + return err + } } - tokenOutput, err = f.svc.GetChangeToken(&waf.GetChangeTokenInput{}) + tokenOutput, err = f.svc.GetChangeToken(ctx, &wafregional.GetChangeTokenInput{}) if err != nil { return err } - _, err = f.svc.DeleteRule(&waf.DeleteRuleInput{ + _, err = f.svc.DeleteRule(ctx, &wafregional.DeleteRuleInput{ RuleId: f.ID, ChangeToken: tokenOutput.ChangeToken, }) From 54919bdedffe8c8689f3759292e1480ab469d7a4 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 13:08:09 -0700 Subject: [PATCH 55/87] feat(resource): add SSMQuickSetupConfigurationManager resource --- go.mod | 1 + go.sum | 2 + .../ssmquicksetup-configuration-manager.go | 82 +++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 resources/ssmquicksetup-configuration-manager.go diff --git a/go.mod b/go.mod index bddba488..47630365 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect github.com/benbjohnson/clock v1.3.0 // indirect diff --git a/go.sum b/go.sum index 9536d20d..0416494b 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEH github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= +github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 h1:4siT1z3nEVxJq1jZYu1SRoct5xgbKen+ammCuZBZ2zI= +github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2/go.mod h1:KSO1+erW2SUB6Mw/Qamu1fOT5fn/mzd9G79ENbYqyRQ= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= diff --git a/resources/ssmquicksetup-configuration-manager.go b/resources/ssmquicksetup-configuration-manager.go new file mode 100644 index 00000000..60ef9283 --- /dev/null +++ b/resources/ssmquicksetup-configuration-manager.go @@ -0,0 +1,82 @@ +package resources + +import ( + "context" + "strings" + + "github.com/gotidy/ptr" + + "github.com/aws/aws-sdk-go-v2/service/ssmquicksetup" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const SSMQuickSetupConfigurationManagerResource = "SSMQuickSetupConfigurationManager" + +func init() { + registry.Register(®istry.Registration{ + Name: SSMQuickSetupConfigurationManagerResource, + Scope: nuke.Account, + Resource: &SSMQuickSetupConfigurationManager{}, + Lister: &SSMQuickSetupConfigurationManagerLister{}, + }) +} + +type SSMQuickSetupConfigurationManagerLister struct{} + +func (l *SSMQuickSetupConfigurationManagerLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := ssmquicksetup.NewFromConfig(*opts.Config) + var resources []resource.Resource + + res, err := svc.ListConfigurationManagers(ctx, &ssmquicksetup.ListConfigurationManagersInput{}) + if err != nil { + return nil, err + } + + for _, p := range res.ConfigurationManagersList { + resources = append(resources, &SSMQuickSetupConfigurationManager{ + svc: svc, + ARN: p.ManagerArn, + Name: p.Name, + }) + } + + return resources, nil +} + +type SSMQuickSetupConfigurationManager struct { + svc *ssmquicksetup.Client + ARN *string + Name *string +} + +// GetName returns the name of the resource or the last part of the ARN if not set so that the stringer resource has +// a value to display +func (r *SSMQuickSetupConfigurationManager) GetName() string { + if ptr.ToString(r.Name) != "" { + return ptr.ToString(r.Name) + } + + parts := strings.Split(ptr.ToString(r.ARN), "/") + return parts[len(parts)-1] +} + +func (r *SSMQuickSetupConfigurationManager) Remove(ctx context.Context) error { + _, err := r.svc.DeleteConfigurationManager(ctx, &ssmquicksetup.DeleteConfigurationManagerInput{ + ManagerArn: r.ARN, + }) + return err +} + +func (r *SSMQuickSetupConfigurationManager) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *SSMQuickSetupConfigurationManager) String() string { + return r.GetName() +} From 8ba1fbb0b6d5bbbfc1c3058eadf136eaab6ed6cf Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 13:08:24 -0700 Subject: [PATCH 56/87] chore(tools/create-resource): switch to aws-go-sdk-v2 --- tools/create-resource/main.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/create-resource/main.go b/tools/create-resource/main.go index 148b42ec..a2c5e6de 100644 --- a/tools/create-resource/main.go +++ b/tools/create-resource/main.go @@ -16,8 +16,7 @@ const resourceTemplate = `package resources import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/{{.Service}}" + "github.com/aws/aws-sdk-go-v2/service/{{.Service}}" "github.com/ekristen/libnuke/pkg/resource" "github.com/ekristen/libnuke/pkg/registry" @@ -41,12 +40,12 @@ type {{.Combined}}Lister struct{} func (l *{{.Combined}}Lister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) - svc := {{.Service}}.New(opts.Session) + svc := {{.Service}}.NewFromConfig(*opts.Config) var resources []resource.Resource // NOTE: you might have to modify the code below to actually work, this currently does not // inspect the aws sdk instead is a jumping off point - res, err := svc.List{{.ResourceTypeTitle}}s(&{{.Service}}.List{{.ResourceTypeTitle}}sInput{}) + res, err := svc.List{{.ResourceTypeTitle}}s(ctx, &{{.Service}}.List{{.ResourceTypeTitle}}sInput{}) if err != nil { return nil, err } @@ -63,13 +62,13 @@ func (l *{{.Combined}}Lister) List(_ context.Context, o interface{}) ([]resource } type {{.Combined}} struct { - svc *{{.Service}}.{{.ServiceTitle}} + svc *{{.Service}}.Client ID *string Tags []*{{.Service}}.Tag } -func (r *{{.Combined}}) Remove(_ context.Context) error { - _, err := r.svc.Delete{{.ResourceTypeTitle}}(&{{.Service}}.Delete{{.ResourceTypeTitle}}Input{ +func (r *{{.Combined}}) Remove(ctx context.Context) error { + _, err := r.svc.Delete{{.ResourceTypeTitle}}(ctx, &{{.Service}}.Delete{{.ResourceTypeTitle}}Input{ {{.ResourceTypeTitle}}Id: r.id, }) return err From d31fa127b02d636c78c7a42b8e32396a091ef9d1 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 13:37:42 -0700 Subject: [PATCH 57/87] fix(command/run): logging output back to stdout --- pkg/commands/nuke/nuke.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/commands/nuke/nuke.go b/pkg/commands/nuke/nuke.go index 63d21954..8b6719e5 100644 --- a/pkg/commands/nuke/nuke.go +++ b/pkg/commands/nuke/nuke.go @@ -3,6 +3,7 @@ package nuke import ( "context" "fmt" + "os" "slices" "strings" "time" @@ -75,6 +76,7 @@ func execute(c *cli.Context) error { //nolint:funlen,gocyclo } logger := logrus.StandardLogger() + logger.SetOutput(os.Stdout) // Parse the user supplied configuration file to pass in part to configure the nuke process. parsedConfig, err := config.New(libconfig.Options{ From 03086f45b9eb401b08148e9d9cce50937e42d944 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 13:44:53 -0700 Subject: [PATCH 58/87] docs: auto-generated docs for resources --- .../ssm-quick-setup-configuration-manager.md | 31 +++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 32 insertions(+) create mode 100644 docs/resources/ssm-quick-setup-configuration-manager.md diff --git a/docs/resources/ssm-quick-setup-configuration-manager.md b/docs/resources/ssm-quick-setup-configuration-manager.md new file mode 100644 index 00000000..ba66cfaa --- /dev/null +++ b/docs/resources/ssm-quick-setup-configuration-manager.md @@ -0,0 +1,31 @@ +--- +generated: true +--- + +# SSMQuickSetupConfigurationManager + + +## Resource + +```text +SSMQuickSetupConfigurationManager +``` + +## Properties + + +- `ARN`: No Description +- `Name`: No Description + +!!! note - Using Properties + Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property + names to write filters for what you want to **keep** and omit from the nuke process. + +### String Property + +The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all +resources support properties. To write a filter against the string representation, simply omit the `property` field in +the filter. + +The string value is always what is used in the output of the log format when a resource is identified. + diff --git a/mkdocs.yml b/mkdocs.yml index 668c7bf7..e188d0cb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -558,6 +558,7 @@ nav: - SSM Maintenance Window: resources/ssm-maintenance-window.md - SSM Parameter: resources/ssm-parameter.md - SSM Patch Baseline: resources/ssm-patch-baseline.md + - SSM Quick Setup Configuration Manager: resources/ssm-quick-setup-configuration-manager.md - SSM Resource Data Sync: resources/ssm-resource-data-sync.md - Sage Maker App: resources/sage-maker-app.md - Sage Maker Domain: resources/sage-maker-domain.md From 1cf5971a479f0c5976a6acd9eacbc8f63bb3391f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 20:53:57 +0000 Subject: [PATCH 59/87] fix(deps): update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.72.0 --- go.mod | 4 ++-- go.sum | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 47630365..bc36f7f3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,8 @@ require ( github.com/aws/aws-sdk-go-v2 v1.32.7 github.com/aws/aws-sdk-go-v2/config v1.28.7 github.com/aws/aws-sdk-go-v2/credentials v1.17.48 - github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 + github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 + github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/smithy-go v1.22.1 github.com/ekristen/libnuke v0.24.0 @@ -35,7 +36,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect - github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect github.com/benbjohnson/clock v1.3.0 // indirect diff --git a/go.sum b/go.sum index 0416494b..d5ecf236 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEH github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= +github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk= +github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 h1:4siT1z3nEVxJq1jZYu1SRoct5xgbKen+ammCuZBZ2zI= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2/go.mod h1:KSO1+erW2SUB6Mw/Qamu1fOT5fn/mzd9G79ENbYqyRQ= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= From 0dc03fd9da6435c5458bae00915087e233891171 Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Fri, 3 Jan 2025 15:21:16 -0600 Subject: [PATCH 60/87] Merge cloudformation changes to new repo --- go.mod | 2 + go.sum | 4 + resources/cloudformation-stack.go | 276 +++++++++++--------- resources/cloudformation-stack_test.go | 333 ------------------------- 4 files changed, 171 insertions(+), 444 deletions(-) delete mode 100644 resources/cloudformation-stack_test.go diff --git a/go.mod b/go.mod index f1836927..31718fdb 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.28.7 github.com/aws/aws-sdk-go-v2/credentials v1.17.48 github.com/aws/aws-sdk-go-v2/service/appstream v1.42.0 + github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 + github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 github.com/aws/aws-sdk-go-v2/service/kafka v1.38.9 github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 diff --git a/go.sum b/go.sum index 4ada5afa..e1a435a0 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,10 @@ github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVT github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo= github.com/aws/aws-sdk-go-v2/service/appstream v1.42.0 h1:d36PMCY+OwgPqJfgPvByG2gjel2e0mcMt0JNEYMeFm4= github.com/aws/aws-sdk-go-v2/service/appstream v1.42.0/go.mod h1:HeQk2RRQrmFPeFN1vcg9ZUx85LNH154LjGWaHW6hD3k= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2 h1:6USen+lDo8xYQutfnzhSeNLKEykNmBPfrcBmYKhLP38= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.56.2/go.mod h1:10A7sHyxlTZSB7419K2wq/1tn0x/K9/drbD2j8VRZVc= +github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 h1:2sFIoFzU1IEL9epJWubJm9Dhrn45aTNEJuwsesaCGnk= +github.com/aws/aws-sdk-go-v2/service/iam v1.38.3/go.mod h1:KzlNINwfr/47tKkEhgk0r10/OZq3rjtyWy0txL3lM+I= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY= diff --git a/resources/cloudformation-stack.go b/resources/cloudformation-stack.go index 5ed09049..265306db 100644 --- a/resources/cloudformation-stack.go +++ b/resources/cloudformation-stack.go @@ -4,29 +4,31 @@ import ( "context" "errors" "fmt" - "strings" "time" - "github.com/gotidy/ptr" - "github.com/sirupsen/logrus" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/cloudformation" - "github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudformation" + cloudformationTypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types" + "github.com/aws/aws-sdk-go-v2/service/iam" - liberrors "github.com/ekristen/libnuke/pkg/errors" + "github.com/ekristen/aws-nuke/v3/pkg/nuke" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" - "github.com/ekristen/libnuke/pkg/settings" + libsettings "github.com/ekristen/libnuke/pkg/settings" "github.com/ekristen/libnuke/pkg/types" - "github.com/ekristen/aws-nuke/v3/pkg/nuke" + "github.com/aws/smithy-go" + + "github.com/sirupsen/logrus" ) -const CloudformationMaxDeleteAttempt = 3 +const ( + CloudFormationStackResource = "CloudFormationStack" -const CloudFormationStackResource = "CloudFormationStack" + CloudformationMaxDeleteAttempt = 3 + MaxWaitTime = time.Duration(5) * time.Minute + ServiceRoleName = "nuke-service-role-CFS" +) func init() { registry.Register(®istry.Registration{ @@ -36,28 +38,34 @@ func init() { Lister: &CloudFormationStackLister{}, Settings: []string{ "DisableDeletionProtection", + "EnableAutomaticRoleManagment", }, }) } type CloudFormationStackLister struct{} -func (l *CloudFormationStackLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { +func (l *CloudFormationStackLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) - - svc := cloudformation.New(opts.Session) + svc := cloudformation.NewFromConfig(*opts.Config) params := &cloudformation.DescribeStacksInput{} resources := make([]resource.Resource, 0) for { - resp, err := svc.DescribeStacks(params) + resp, err := svc.DescribeStacks(ctx, params) if err != nil { return nil, err } for _, stack := range resp.Stacks { + if stack.ParentId != nil && *stack.ParentId != "" { + continue + } + resources = append(resources, &CloudFormationStack{ svc: svc, + iamSvc: iam.NewFromConfig(*opts.Config), + context: ctx, stack: stack, maxDeleteAttempts: CloudformationMaxDeleteAttempt, }) @@ -74,43 +82,54 @@ func (l *CloudFormationStackLister) List(_ context.Context, o interface{}) ([]re } type CloudFormationStack struct { - svc cloudformationiface.CloudFormationAPI - stack *cloudformation.Stack + svc *cloudformation.Client + iamSvc *iam.Client + context context.Context + + stack cloudformationTypes.Stack + deleteRoleArn *string maxDeleteAttempts int - settings *settings.Setting + settings *libsettings.Setting } -func (cfs *CloudFormationStack) Filter() error { - if ptr.ToString(cfs.stack.Description) == "DO NOT MODIFY THIS STACK! This stack is managed by Config Conformance Packs." { - return fmt.Errorf("stack is managed by Config Conformance Packs") - } - return nil +func (cfs *CloudFormationStack) Settings(settings *libsettings.Setting) { + cfs.settings = settings } -func (cfs *CloudFormationStack) Settings(setting *settings.Setting) { - cfs.settings = setting -} +func (cfs *CloudFormationStack) Remove(ctx context.Context) error { + if cfs.settings.GetBool("EnableAutomaticRoleManagment") { + fmt.Println("Enabling automatic role management") + } else { + fmt.Println("Automatic role management not enabled") + } -func (cfs *CloudFormationStack) Remove(_ context.Context) error { - return cfs.removeWithAttempts(0) + if cfs.settings.GetBool("EnableAutomaticRoleManagment") { + err := cfs.createServiceRoleArn(ctx) + if err != nil { + return err + } + } + + err := cfs.removeWithAttempts(0) + + if cfs.settings.GetBool("EnableAutomaticRoleManagment") { + delErr := cfs.deleteServiceRoleArn() + if delErr != nil { + return delErr + } + } + + return err } func (cfs *CloudFormationStack) removeWithAttempts(attempt int) error { if err := cfs.doRemove(); err != nil { - // TODO: pass logrus in via ListerOpts so that it can be used here instead of global - - logrus.Errorf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d delete failed: %s", - *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts, err.Error()) - - var awsErr awserr.Error - ok := errors.As(err, &awsErr) - if ok && awsErr.Code() == "ValidationError" && - awsErr.Message() == "Stack ["+*cfs.stack.StackName+"] cannot be deleted while TerminationProtection is enabled" { - // check if the setting for the resource is set to allow deletion protection to be disabled + logrus.Errorf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d delete failed: %s", *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts, err.Error()) + var re *smithy.OperationError + if errors.As(err, &re) && re.Error() == "Stack ["+*cfs.stack.StackName+"] cannot be deleted while TerminationProtection is enabled" { if cfs.settings.GetBool("DisableDeletionProtection") { - logrus.Infof("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d updating termination protection", - *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts) - _, err = cfs.svc.UpdateTerminationProtection(&cloudformation.UpdateTerminationProtectionInput{ + logrus.Infof("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d updating termination protection", *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts) + _, err = cfs.svc.UpdateTerminationProtection(cfs.context, &cloudformation.UpdateTerminationProtectionInput{ EnableTerminationProtection: aws.Bool(false), StackName: cfs.stack.StackName, }) @@ -118,8 +137,7 @@ func (cfs *CloudFormationStack) removeWithAttempts(attempt int) error { return err } } else { - logrus.Warnf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d set feature flag to disable deletion protection", - *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts) + logrus.Warnf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d set feature flag to disable deletion protection", *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts) return err } } @@ -133,123 +151,159 @@ func (cfs *CloudFormationStack) removeWithAttempts(attempt int) error { } } -func GetParentStack(svc cloudformationiface.CloudFormationAPI, stackID string) (*cloudformation.Stack, error) { - o, err := svc.DescribeStacks(&cloudformation.DescribeStacksInput{}) - if err != nil { - return nil, err - } - - for _, o := range o.Stacks { - if *o.StackId == stackID { - return o, nil - } - } - - return nil, nil //nolint:nilnil -} - -func (cfs *CloudFormationStack) doRemove() error { //nolint:gocyclo - if cfs.stack.ParentId != nil { - p, err := GetParentStack(cfs.svc, *cfs.stack.ParentId) - if err != nil { - return err - } - - if p != nil { - return liberrors.ErrHoldResource("waiting for parent stack") - } - } - - o, err := cfs.svc.DescribeStacks(&cloudformation.DescribeStacksInput{ +func (cfs *CloudFormationStack) doRemove() error { + o, err := cfs.svc.DescribeStacks(cfs.context, &cloudformation.DescribeStacksInput{ StackName: cfs.stack.StackName, }) if err != nil { - var awsErr awserr.Error - if errors.As(err, &awsErr) { - if awsErr.Code() == "ValidationFailed" && strings.HasSuffix(awsErr.Message(), " does not exist") { - logrus.Infof("CloudFormationStack stackName=%s no longer exists", *cfs.stack.StackName) - return nil - } + var re *smithy.OperationError + if errors.As(err, &re) { + logrus.Infof("CloudFormationStack stackName=%s no longer exists", *cfs.stack.StackName) + return nil } return err } stack := o.Stacks[0] - if *stack.StackStatus == cloudformation.StackStatusDeleteComplete { - // stack already deleted, no need to re-delete + if stack.StackStatus == cloudformationTypes.StackStatusDeleteComplete { + //stack already deleted, no need to re-delete return nil - } else if *stack.StackStatus == cloudformation.StackStatusDeleteInProgress { + } else if stack.StackStatus == cloudformationTypes.StackStatusDeleteInProgress { logrus.Infof("CloudFormationStack stackName=%s delete in progress. Waiting", *cfs.stack.StackName) - return cfs.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ + waiter := cloudformation.NewStackDeleteCompleteWaiter(cfs.svc) + return waiter.Wait(cfs.context, &cloudformation.DescribeStacksInput{ StackName: cfs.stack.StackName, - }) - } else if *stack.StackStatus == cloudformation.StackStatusDeleteFailed { + }, MaxWaitTime) + } else if stack.StackStatus == cloudformationTypes.StackStatusDeleteFailed { logrus.Infof("CloudFormationStack stackName=%s delete failed. Attempting to retain and delete stack", *cfs.stack.StackName) - // This means the CFS has undetectable resources. + // This means the CFS has undeleteable resources. // In order to move on with nuking, we retain them in the deletion. - retainableResources, err := cfs.svc.ListStackResources(&cloudformation.ListStackResourcesInput{ + retainableResources, err := cfs.svc.ListStackResources(cfs.context, &cloudformation.ListStackResourcesInput{ StackName: cfs.stack.StackName, }) if err != nil { return err } - retain := make([]*string, 0) + retain := make([]string, 0) for _, r := range retainableResources.StackResourceSummaries { - if *r.ResourceStatus != cloudformation.ResourceStatusDeleteComplete { - retain = append(retain, r.LogicalResourceId) + if r.ResourceStatus != cloudformationTypes.ResourceStatusDeleteComplete { + retain = append(retain, *r.LogicalResourceId) } } - _, err = cfs.svc.DeleteStack(&cloudformation.DeleteStackInput{ + _, err = cfs.svc.DeleteStack(cfs.context, &cloudformation.DeleteStackInput{ StackName: cfs.stack.StackName, RetainResources: retain, + RoleARN: cfs.deleteRoleArn, }) if err != nil { return err } - return cfs.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ + waiter := cloudformation.NewStackDeleteCompleteWaiter(cfs.svc) + return waiter.Wait(cfs.context, &cloudformation.DescribeStacksInput{ StackName: cfs.stack.StackName, - }) + }, MaxWaitTime) } else { - if err := cfs.waitForStackToStabilize(*stack.StackStatus); err != nil { + deleteWaiter := cloudformation.NewStackDeleteCompleteWaiter(cfs.svc) + if err := cfs.waitForStackToStabilize(stack.StackStatus); err != nil { return err - } else if _, err := cfs.svc.DeleteStack(&cloudformation.DeleteStackInput{ + } else if _, err := cfs.svc.DeleteStack(cfs.context, &cloudformation.DeleteStackInput{ StackName: cfs.stack.StackName, + RoleARN: cfs.deleteRoleArn, }); err != nil { return err - } else if err := cfs.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ + } else if err := deleteWaiter.Wait(cfs.context, &cloudformation.DescribeStacksInput{ StackName: cfs.stack.StackName, - }); err != nil { + }, MaxWaitTime); err != nil { return err } else { return nil } } } -func (cfs *CloudFormationStack) waitForStackToStabilize(currentStatus string) error { +func (cfs *CloudFormationStack) waitForStackToStabilize(currentStatus cloudformationTypes.StackStatus) error { switch currentStatus { - case cloudformation.StackStatusUpdateInProgress, - cloudformation.StackStatusUpdateRollbackCompleteCleanupInProgress, - cloudformation.StackStatusUpdateRollbackInProgress: - logrus.Infof("CloudFormationStack stackName=%s update in progress. Waiting to stabalize", *cfs.stack.StackName) - - return cfs.svc.WaitUntilStackUpdateComplete(&cloudformation.DescribeStacksInput{ + case cloudformationTypes.StackStatusUpdateInProgress: + fallthrough + case cloudformationTypes.StackStatusUpdateRollbackCompleteCleanupInProgress: + fallthrough + case cloudformationTypes.StackStatusUpdateRollbackInProgress: + logrus.Infof("CloudFormationStack stackName=%s update in progress. Waiting to stabilize", *cfs.stack.StackName) + waiter := cloudformation.NewStackUpdateCompleteWaiter(cfs.svc) + return waiter.Wait(cfs.context, &cloudformation.DescribeStacksInput{ StackName: cfs.stack.StackName, - }) - case cloudformation.StackStatusCreateInProgress, - cloudformation.StackStatusRollbackInProgress: - logrus.Infof("CloudFormationStack stackName=%s create in progress. Waiting to stabalize", *cfs.stack.StackName) - - return cfs.svc.WaitUntilStackCreateComplete(&cloudformation.DescribeStacksInput{ + }, MaxWaitTime) + case cloudformationTypes.StackStatusCreateInProgress: + fallthrough + case cloudformationTypes.StackStatusRollbackInProgress: + logrus.Infof("CloudFormationStack stackName=%s create in progress. Waiting to stabilize", *cfs.stack.StackName) + waiter := cloudformation.NewStackCreateCompleteWaiter(cfs.svc) + return waiter.Wait(cfs.context, &cloudformation.DescribeStacksInput{ StackName: cfs.stack.StackName, - }) + }, MaxWaitTime) default: return nil } } +func (cfs *CloudFormationStack) createServiceRoleArn(ctx context.Context) error { + serviceRoleName := ServiceRoleName + "-" + *cfs.stack.StackName + if len(serviceRoleName) > 64 { + serviceRoleName = serviceRoleName[:64] + } + + role, _ := cfs.iamSvc.GetRole(ctx, &iam.GetRoleInput{ + RoleName: &serviceRoleName, + }) + if role != nil && role.Role != nil { + // the role exists, the stack is ready for deletion + return nil + } + + fmt.Println("Creating role") + params := iam.CreateRoleInput{ + AssumeRolePolicyDocument: aws.String("{\"Version\": \"2012-10-17\",\"Statement\": [{\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"cloudformation.amazonaws.com\"},\"Action\": \"sts:AssumeRole\"}]}"), + Description: aws.String("A service role is required to delete a cloudformation stack for which a specific role was used which no longer exists"), + RoleName: aws.String(serviceRoleName), + } + + roleCreationOutput, err := cfs.iamSvc.CreateRole(ctx, ¶ms) + if err != nil { + fmt.Println("Role creation failed") + fmt.Println(err.Error()) + return err + } + cfs.deleteRoleArn = roleCreationOutput.Role.Arn + fmt.Println("Deletion role arn: ", *cfs.deleteRoleArn) + + // Ensure role has permission to clean up resources created via the stack + attachPolicyParams := &iam.AttachRolePolicyInput{ + PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"), + RoleName: &serviceRoleName, + } + + _, err = cfs.iamSvc.AttachRolePolicy(ctx, attachPolicyParams) + if err != nil { + fmt.Println("Policy attachment failed") + fmt.Println(err.Error()) + return err + } + time.Sleep(1 * time.Second) + return err +} + +func (cfs *CloudFormationStack) deleteServiceRoleArn() error { + serviceRoleName := ServiceRoleName + "-" + *cfs.stack.StackName + params := iam.DeleteRoleInput{ + RoleName: &serviceRoleName, + } + + _, err := cfs.iamSvc.DeleteRole(cfs.context, ¶ms) + return err +} + func (cfs *CloudFormationStack) Properties() types.Properties { properties := types.NewProperties() properties.Set("Name", cfs.stack.StackName) diff --git a/resources/cloudformation-stack_test.go b/resources/cloudformation-stack_test.go deleted file mode 100644 index 9b7bc82a..00000000 --- a/resources/cloudformation-stack_test.go +++ /dev/null @@ -1,333 +0,0 @@ -package resources - -import ( - "context" - - "testing" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/cloudformation" - - libsettings "github.com/ekristen/libnuke/pkg/settings" - - "github.com/ekristen/aws-nuke/v3/mocks/mock_cloudformationiface" -) - -func TestCloudformationStack_Remove_StackAlreadyDeleted(t *testing.T) { - a := assert.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) - - stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, - settings: &libsettings.Setting{ - "DisableDeletionProtection": true, - }, - } - - mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(&cloudformation.DescribeStacksOutput{ - Stacks: []*cloudformation.Stack{ - { - StackStatus: aws.String(cloudformation.StackStatusDeleteComplete), - }, - }, - }, nil) - - err := stack.Remove(context.TODO()) - a.Nil(err) -} - -func TestCloudformationStack_Remove_StackDoesNotExist(t *testing.T) { - a := assert.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) - - stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, - settings: &libsettings.Setting{ - "DisableDeletionProtection": true, - }, - } - - mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil, awserr.New("ValidationFailed", "Stack with id foobar does not exist", nil)) - - err := stack.Remove(context.TODO()) - a.Nil(err) -} - -func TestCloudformationStack_Remove_DeleteFailed(t *testing.T) { - a := assert.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) - - stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, - settings: &libsettings.Setting{ - "DisableDeletionProtection": true, - }, - } - - gomock.InOrder( - mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(&cloudformation.DescribeStacksOutput{ - Stacks: []*cloudformation.Stack{ - { - StackStatus: aws.String(cloudformation.StackStatusDeleteFailed), - }, - }, - }, nil), - mockCloudformation.EXPECT().ListStackResources(gomock.Eq(&cloudformation.ListStackResourcesInput{ - StackName: aws.String("foobar"), - })).Return(&cloudformation.ListStackResourcesOutput{ - StackResourceSummaries: []*cloudformation.StackResourceSummary{ - { - ResourceStatus: aws.String(cloudformation.ResourceStatusDeleteComplete), - LogicalResourceId: aws.String("fooDeleteComplete"), - }, - { - ResourceStatus: aws.String(cloudformation.ResourceStatusDeleteFailed), - LogicalResourceId: aws.String("fooDeleteFailed"), - }, - }, - }, nil), - mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), - RetainResources: []*string{ - aws.String("fooDeleteFailed"), - }, - })).Return(nil, nil), - mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil), - ) - - err := stack.Remove(context.TODO()) - a.Nil(err) -} - -// if the stack is currently in delete in progress -func TestCloudformationStack_Remove_DeleteInProgress(t *testing.T) { - a := assert.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) - - stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, - settings: &libsettings.Setting{ - "DisableDeletionProtection": true, - }, - } - - gomock.InOrder( - mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(&cloudformation.DescribeStacksOutput{ - Stacks: []*cloudformation.Stack{ - { - StackStatus: aws.String(cloudformation.StackStatusDeleteInProgress), - }, - }, - }, nil), - - mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil), - ) - - err := stack.Remove(context.TODO()) - a.Nil(err) -} - -func TestCloudformationStack_Remove_Stack_InCompletedStatus(t *testing.T) { - tests := []string{ - cloudformation.StackStatusCreateComplete, - cloudformation.StackStatusCreateFailed, - cloudformation.StackStatusReviewInProgress, - cloudformation.StackStatusRollbackComplete, - cloudformation.StackStatusRollbackFailed, - cloudformation.StackStatusUpdateComplete, - cloudformation.StackStatusUpdateRollbackComplete, - cloudformation.StackStatusUpdateRollbackFailed, - } - - for _, stackStatus := range tests { - t.Run(stackStatus, func(t *testing.T) { - a := assert.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) - - stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, - settings: &libsettings.Setting{ - "DisableDeletionProtection": true, - }, - } - - gomock.InOrder( - mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(&cloudformation.DescribeStacksOutput{ - Stacks: []*cloudformation.Stack{ - { - StackStatus: aws.String(stackStatus), - }, - }, - }, nil), - - mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), - })).Return(nil, nil), - - mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil), - ) - - err := stack.Remove(context.TODO()) - a.Nil(err) - }) - } -} - -func TestCloudformationStack_Remove_Stack_CreateInProgress(t *testing.T) { - tests := []string{ - cloudformation.StackStatusCreateInProgress, - cloudformation.StackStatusRollbackInProgress, - } - - for _, stackStatus := range tests { - t.Run(stackStatus, func(t *testing.T) { - a := assert.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) - - stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, - settings: &libsettings.Setting{ - "DisableDeletionProtection": true, - }, - } - - gomock.InOrder( - mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(&cloudformation.DescribeStacksOutput{ - Stacks: []*cloudformation.Stack{ - { - StackStatus: aws.String(stackStatus), - }, - }, - }, nil), - - mockCloudformation.EXPECT().WaitUntilStackCreateComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil), - - mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), - })).Return(nil, nil), - - mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil), - ) - - err := stack.Remove(context.TODO()) - a.Nil(err) - }) - } -} - -func TestCloudformationStack_Remove_Stack_UpdateInProgress(t *testing.T) { - tests := []string{ - cloudformation.StackStatusUpdateInProgress, - cloudformation.StackStatusUpdateRollbackCompleteCleanupInProgress, - cloudformation.StackStatusUpdateRollbackInProgress, - } - - for _, stackStatus := range tests { - t.Run(stackStatus, func(t *testing.T) { - a := assert.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) - - stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, - settings: &libsettings.Setting{ - "DisableDeletionProtection": true, - }, - } - - gomock.InOrder( - mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(&cloudformation.DescribeStacksOutput{ - Stacks: []*cloudformation.Stack{ - { - StackStatus: aws.String(stackStatus), - }, - }, - }, nil), - - mockCloudformation.EXPECT().WaitUntilStackUpdateComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil), - - mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), - })).Return(nil, nil), - - mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), - })).Return(nil), - ) - - err := stack.Remove(context.TODO()) - a.Nil(err) - }) - } -} From b8982b47c564af35964fef97609ffd6926cc2cd3 Mon Sep 17 00:00:00 2001 From: ayogun Date: Thu, 2 Jan 2025 02:56:57 +0100 Subject: [PATCH 61/87] feat(cloudwatchlogs-loggroup): add RetentionInDays properties --- resources/cloudwatchlogs-loggroups.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/cloudwatchlogs-loggroups.go b/resources/cloudwatchlogs-loggroups.go index eae192be..bd325236 100644 --- a/resources/cloudwatchlogs-loggroups.go +++ b/resources/cloudwatchlogs-loggroups.go @@ -87,14 +87,14 @@ func (l *CloudWatchLogsLogGroupLister) List(_ context.Context, o interface{}) ([ lastEvent = time.Unix(*logGroup.CreationTime/1000, 0) } + resources = append(resources, &CloudWatchLogsLogGroup{ svc: svc, logGroup: logGroup, lastEvent: lastEvent.Format(time.RFC3339), tags: tagResp.Tags, }) - } - + } if output.NextToken == nil { break } @@ -128,10 +128,13 @@ func (f *CloudWatchLogsLogGroup) Properties() types.Properties { properties := types.NewProperties(). Set("logGroupName", f.logGroup.LogGroupName). Set("CreatedTime", f.logGroup.CreationTime). - Set("LastEvent", f.lastEvent) + Set("LastEvent", f.lastEvent). + Set("RetentionInDays", f.logGroup.RetentionInDays) + for k, v := range f.tags { properties.SetTag(&k, v) } return properties } + From 76177af44c20094395ca03249d80ef69310a00ba Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 15:32:24 -0700 Subject: [PATCH 62/87] fix(cloudwatchlogs-loggroup): property RetentionInDays should always have a value --- resources/cloudwatchlogs-loggroups.go | 29 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/resources/cloudwatchlogs-loggroups.go b/resources/cloudwatchlogs-loggroups.go index bd325236..6bf96fb4 100644 --- a/resources/cloudwatchlogs-loggroups.go +++ b/resources/cloudwatchlogs-loggroups.go @@ -2,6 +2,7 @@ package resources import ( "context" + "github.com/gotidy/ptr" "strings" "time" @@ -87,14 +88,19 @@ func (l *CloudWatchLogsLogGroupLister) List(_ context.Context, o interface{}) ([ lastEvent = time.Unix(*logGroup.CreationTime/1000, 0) } + var retentionInDays int64 + if logGroup.RetentionInDays != nil { + retentionInDays = ptr.ToInt64(logGroup.RetentionInDays) + } resources = append(resources, &CloudWatchLogsLogGroup{ - svc: svc, - logGroup: logGroup, - lastEvent: lastEvent.Format(time.RFC3339), - tags: tagResp.Tags, + svc: svc, + logGroup: logGroup, + lastEvent: lastEvent.Format(time.RFC3339), + retentionInDays: retentionInDays, + tags: tagResp.Tags, }) - } + } if output.NextToken == nil { break } @@ -106,10 +112,11 @@ func (l *CloudWatchLogsLogGroupLister) List(_ context.Context, o interface{}) ([ } type CloudWatchLogsLogGroup struct { - svc *cloudwatchlogs.CloudWatchLogs - logGroup *cloudwatchlogs.LogGroup - lastEvent string - tags map[string]*string + svc *cloudwatchlogs.CloudWatchLogs + logGroup *cloudwatchlogs.LogGroup + lastEvent string + retentionInDays int64 + tags map[string]*string } func (f *CloudWatchLogsLogGroup) Remove(_ context.Context) error { @@ -129,12 +136,10 @@ func (f *CloudWatchLogsLogGroup) Properties() types.Properties { Set("logGroupName", f.logGroup.LogGroupName). Set("CreatedTime", f.logGroup.CreationTime). Set("LastEvent", f.lastEvent). - Set("RetentionInDays", f.logGroup.RetentionInDays) - + Set("RetentionInDays", f.retentionInDays) for k, v := range f.tags { properties.SetTag(&k, v) } return properties } - From 19ebeb32ff835379d11570abfc139fd4edb925fc Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 15:33:22 -0700 Subject: [PATCH 63/87] refactor(cloudwatchlogs-loggroup): standardization --- ...oggroups.go => cloudwatchlogs-loggroup.go} | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) rename resources/{cloudwatchlogs-loggroups.go => cloudwatchlogs-loggroup.go} (86%) diff --git a/resources/cloudwatchlogs-loggroups.go b/resources/cloudwatchlogs-loggroup.go similarity index 86% rename from resources/cloudwatchlogs-loggroups.go rename to resources/cloudwatchlogs-loggroup.go index 6bf96fb4..e6d1f42c 100644 --- a/resources/cloudwatchlogs-loggroups.go +++ b/resources/cloudwatchlogs-loggroup.go @@ -119,26 +119,26 @@ type CloudWatchLogsLogGroup struct { tags map[string]*string } -func (f *CloudWatchLogsLogGroup) Remove(_ context.Context) error { - _, err := f.svc.DeleteLogGroup(&cloudwatchlogs.DeleteLogGroupInput{ - LogGroupName: f.logGroup.LogGroupName, +func (r *CloudWatchLogsLogGroup) Remove(_ context.Context) error { + _, err := r.svc.DeleteLogGroup(&cloudwatchlogs.DeleteLogGroupInput{ + LogGroupName: r.logGroup.LogGroupName, }) return err } -func (f *CloudWatchLogsLogGroup) String() string { - return *f.logGroup.LogGroupName +func (r *CloudWatchLogsLogGroup) String() string { + return *r.logGroup.LogGroupName } -func (f *CloudWatchLogsLogGroup) Properties() types.Properties { +func (r *CloudWatchLogsLogGroup) Properties() types.Properties { properties := types.NewProperties(). - Set("logGroupName", f.logGroup.LogGroupName). - Set("CreatedTime", f.logGroup.CreationTime). - Set("LastEvent", f.lastEvent). - Set("RetentionInDays", f.retentionInDays) + Set("logGroupName", r.logGroup.LogGroupName). + Set("CreatedTime", r.logGroup.CreationTime). + Set("LastEvent", r.lastEvent). + Set("RetentionInDays", r.retentionInDays) - for k, v := range f.tags { + for k, v := range r.tags { properties.SetTag(&k, v) } return properties From d8ffbc5881f187d749495256ad06392b53b84982 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 15:54:09 -0700 Subject: [PATCH 64/87] feat(cloudwatchlogs-loggroup): new properties, refactor properties struct --- resources/cloudwatchlogs-loggroup.go | 47 +++++++++++++--------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/resources/cloudwatchlogs-loggroup.go b/resources/cloudwatchlogs-loggroup.go index e6d1f42c..c4cfcb69 100644 --- a/resources/cloudwatchlogs-loggroup.go +++ b/resources/cloudwatchlogs-loggroup.go @@ -2,13 +2,12 @@ package resources import ( "context" - "github.com/gotidy/ptr" "strings" "time" + "github.com/gotidy/ptr" "go.uber.org/ratelimit" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/ekristen/libnuke/pkg/registry" @@ -47,7 +46,7 @@ func (l *CloudWatchLogsLogGroupLister) List(_ context.Context, o interface{}) ([ streamRl := ratelimit.New(15) params := &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(50), + Limit: ptr.Int64(50), } for { @@ -73,9 +72,9 @@ func (l *CloudWatchLogsLogGroupLister) List(_ context.Context, o interface{}) ([ // get last event ingestion time lsResp, err := svc.DescribeLogStreams(&cloudwatchlogs.DescribeLogStreamsInput{ LogGroupName: logGroup.LogGroupName, - OrderBy: aws.String("LastEventTime"), - Limit: aws.Int64(1), - Descending: aws.Bool(true), + OrderBy: ptr.String("LastEventTime"), + Limit: ptr.Int64(1), + Descending: ptr.Bool(true), }) if err != nil { return nil, err @@ -95,10 +94,12 @@ func (l *CloudWatchLogsLogGroupLister) List(_ context.Context, o interface{}) ([ resources = append(resources, &CloudWatchLogsLogGroup{ svc: svc, - logGroup: logGroup, - lastEvent: lastEvent.Format(time.RFC3339), - retentionInDays: retentionInDays, - tags: tagResp.Tags, + Name: logGroup.LogGroupName, + CreatedTime: logGroup.CreationTime, + CreationTime: ptr.Time(time.Unix(*logGroup.CreationTime/1000, 0).UTC()), + LastEvent: ptr.Time(lastEvent), // TODO(v4): convert to UTC + RetentionInDays: retentionInDays, + Tags: tagResp.Tags, }) } if output.NextToken == nil { @@ -113,33 +114,27 @@ func (l *CloudWatchLogsLogGroupLister) List(_ context.Context, o interface{}) ([ type CloudWatchLogsLogGroup struct { svc *cloudwatchlogs.CloudWatchLogs - logGroup *cloudwatchlogs.LogGroup - lastEvent string - retentionInDays int64 - tags map[string]*string + Name *string `description:"The name of the log group"` + CreatedTime *int64 `description:"The creation time of the log group in unix timestamp format"` + CreationTime *time.Time `description:"The creation time of the log group in RFC3339 format"` + LastEvent *time.Time `description:"The last event time of the log group in RFC3339 format"` + RetentionInDays int64 `description:"The number of days to retain log events in the log group"` + Tags map[string]*string } func (r *CloudWatchLogsLogGroup) Remove(_ context.Context) error { _, err := r.svc.DeleteLogGroup(&cloudwatchlogs.DeleteLogGroupInput{ - LogGroupName: r.logGroup.LogGroupName, + LogGroupName: r.Name, }) return err } func (r *CloudWatchLogsLogGroup) String() string { - return *r.logGroup.LogGroupName + return *r.Name } func (r *CloudWatchLogsLogGroup) Properties() types.Properties { - properties := types.NewProperties(). - Set("logGroupName", r.logGroup.LogGroupName). - Set("CreatedTime", r.logGroup.CreationTime). - Set("LastEvent", r.lastEvent). - Set("RetentionInDays", r.retentionInDays) - - for k, v := range r.tags { - properties.SetTag(&k, v) - } - return properties + return types.NewPropertiesFromStruct(r). + Set("logGroupName", r.Name) // TODO(v4): remove this property } From 8f99b2145f3b2475cd6f5368af502869efc2d227 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 15:55:51 -0700 Subject: [PATCH 65/87] test(cloudwatchlogs-loggroup): add property test coverage --- resources/cloudwatchlogs-loggroup_test.go | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 resources/cloudwatchlogs-loggroup_test.go diff --git a/resources/cloudwatchlogs-loggroup_test.go b/resources/cloudwatchlogs-loggroup_test.go new file mode 100644 index 00000000..e2d2e669 --- /dev/null +++ b/resources/cloudwatchlogs-loggroup_test.go @@ -0,0 +1,34 @@ +package resources + +import ( + "strconv" + "testing" + "time" + + "github.com/gotidy/ptr" + "github.com/stretchr/testify/assert" +) + +func TestCloudWatchLogsLogGroupProperties(t *testing.T) { + now := time.Now().UTC() + + r := &CloudWatchLogsLogGroup{ + Name: ptr.String("test-log-group"), + CreatedTime: ptr.Int64(now.Unix()), + CreationTime: ptr.Time(now), + LastEvent: ptr.Time(now), + RetentionInDays: 7, + Tags: map[string]*string{ + "Environment": ptr.String("production"), + }, + } + + properties := r.Properties() + assert.Equal(t, properties.Get("logGroupName"), "test-log-group") + assert.Equal(t, properties.Get("Name"), "test-log-group") + assert.Equal(t, properties.Get("CreatedTime"), strconv.Itoa(int(now.Unix()))) + assert.Equal(t, properties.Get("CreationTime"), now.Format(time.RFC3339)) + assert.Equal(t, properties.Get("LastEvent"), now.Format(time.RFC3339)) + assert.Equal(t, properties.Get("RetentionInDays"), "7") + assert.Equal(t, properties.Get("tag:Environment"), "production") +} From c89b72a3a3eab7502ad61d15683341c24a8e18ca Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 15:56:26 -0700 Subject: [PATCH 66/87] docs(resources): auto-generated --- docs/resources/cloud-watch-logs-log-group.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/resources/cloud-watch-logs-log-group.md b/docs/resources/cloud-watch-logs-log-group.md index a9d24a0d..a105b73e 100644 --- a/docs/resources/cloud-watch-logs-log-group.md +++ b/docs/resources/cloud-watch-logs-log-group.md @@ -11,8 +11,17 @@ generated: true CloudWatchLogsLogGroup ``` +## Properties +- `CreatedTime`: The creation time of the log group in unix timestamp format +- `CreationTime`: The creation time of the log group in RFC3339 format +- `LastEvent`: The last event time of the log group in RFC3339 format +- `Name`: The name of the log group +- `RetentionInDays`: The number of days to retain log events in the log group +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. From 995b7b0567f33d0a9fcc9b81ea24e1b0e9ca36ae Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 18:54:36 -0700 Subject: [PATCH 67/87] refactor(iam-password-policy): standardization --- resources/iam-account-setting-password-policy.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/iam-account-setting-password-policy.go b/resources/iam-account-setting-password-policy.go index 0e555db8..46af99ab 100644 --- a/resources/iam-account-setting-password-policy.go +++ b/resources/iam-account-setting-password-policy.go @@ -2,7 +2,6 @@ package resources import ( "context" - "errors" "github.com/aws/aws-sdk-go/aws/awserr" @@ -11,6 +10,7 @@ import ( "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) @@ -64,8 +64,8 @@ type IAMAccountSettingPasswordPolicy struct { policy *iam.PasswordPolicy } -func (e *IAMAccountSettingPasswordPolicy) Remove(_ context.Context) error { - _, err := e.svc.DeleteAccountPasswordPolicy(&iam.DeleteAccountPasswordPolicyInput{}) +func (r *IAMAccountSettingPasswordPolicy) Remove(_ context.Context) error { + _, err := r.svc.DeleteAccountPasswordPolicy(&iam.DeleteAccountPasswordPolicyInput{}) if err != nil { return err } @@ -73,6 +73,10 @@ func (e *IAMAccountSettingPasswordPolicy) Remove(_ context.Context) error { return nil } -func (e *IAMAccountSettingPasswordPolicy) String() string { +func (r *IAMAccountSettingPasswordPolicy) String() string { return "custom" } + +func (r *IAMAccountSettingPasswordPolicy) Properties() types.Properties { + return types.NewProperties().Set("type", "custom") +} From 60243b35cf381eef996c8e082df12d51512e25c7 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 18:56:01 -0700 Subject: [PATCH 68/87] feat(cloudwatchevents-target): add properties --- resources/cloudwatchevents-targets.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/cloudwatchevents-targets.go b/resources/cloudwatchevents-targets.go index 932b4368..bccfeebe 100644 --- a/resources/cloudwatchevents-targets.go +++ b/resources/cloudwatchevents-targets.go @@ -2,6 +2,7 @@ package resources import ( "context" + "github.com/ekristen/libnuke/pkg/types" "fmt" @@ -87,6 +88,12 @@ func (target *CloudWatchEventsTarget) Remove(_ context.Context) error { func (target *CloudWatchEventsTarget) String() string { // TODO: change this to IAM format rule -> target and mark as breaking change for filters - // TODO: add properties for rule and target separately return fmt.Sprintf("Rule: %s Target ID: %s", *target.ruleName, *target.targetID) } + +func (target *CloudWatchEventsTarget) Properties() types.Properties { + return types.NewProperties(). + Set("Name", target.ruleName). + Set("TargetID", target.targetID). + Set("BusName", target.busName) +} From 424fc858a56095316468b110c0e0ad709bf66f0f Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 18:57:09 -0700 Subject: [PATCH 69/87] refactor(cloudwatchevents-target): standardization --- ...-targets.go => cloudwatchevents-target.go} | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) rename resources/{cloudwatchevents-targets.go => cloudwatchevents-target.go} (77%) diff --git a/resources/cloudwatchevents-targets.go b/resources/cloudwatchevents-target.go similarity index 77% rename from resources/cloudwatchevents-targets.go rename to resources/cloudwatchevents-target.go index bccfeebe..ffd1f085 100644 --- a/resources/cloudwatchevents-targets.go +++ b/resources/cloudwatchevents-target.go @@ -2,8 +2,6 @@ package resources import ( "context" - "github.com/ekristen/libnuke/pkg/types" - "fmt" "github.com/aws/aws-sdk-go/aws" @@ -11,6 +9,7 @@ import ( "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" "github.com/ekristen/aws-nuke/v3/pkg/nuke" ) @@ -75,25 +74,25 @@ type CloudWatchEventsTarget struct { busName *string } -func (target *CloudWatchEventsTarget) Remove(_ context.Context) error { - ids := []*string{target.targetID} - _, err := target.svc.RemoveTargets(&cloudwatchevents.RemoveTargetsInput{ +func (r *CloudWatchEventsTarget) Remove(_ context.Context) error { + ids := []*string{r.targetID} + _, err := r.svc.RemoveTargets(&cloudwatchevents.RemoveTargetsInput{ Ids: ids, - Rule: target.ruleName, - EventBusName: target.busName, + Rule: r.ruleName, + EventBusName: r.busName, Force: aws.Bool(true), }) return err } -func (target *CloudWatchEventsTarget) String() string { +func (r *CloudWatchEventsTarget) String() string { // TODO: change this to IAM format rule -> target and mark as breaking change for filters - return fmt.Sprintf("Rule: %s Target ID: %s", *target.ruleName, *target.targetID) + return fmt.Sprintf("Rule: %s Target ID: %s", *r.ruleName, *r.targetID) } -func (target *CloudWatchEventsTarget) Properties() types.Properties { +func (r *CloudWatchEventsTarget) Properties() types.Properties { return types.NewProperties(). - Set("Name", target.ruleName). - Set("TargetID", target.targetID). - Set("BusName", target.busName) + Set("Name", r.ruleName). + Set("TargetID", r.targetID). + Set("BusName", r.busName) } From 4b31fa6dafef58b3491150bc4358e037f4d641d5 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 18:58:37 -0700 Subject: [PATCH 70/87] refactor(cloudwatchevent-target): properties from struct --- resources/cloudwatchevents-target.go | 30 +++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/resources/cloudwatchevents-target.go b/resources/cloudwatchevents-target.go index ffd1f085..9992cf84 100644 --- a/resources/cloudwatchevents-target.go +++ b/resources/cloudwatchevents-target.go @@ -4,7 +4,8 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" + "github.com/gotidy/ptr" + "github.com/aws/aws-sdk-go/service/cloudwatchevents" "github.com/ekristen/libnuke/pkg/registry" @@ -56,9 +57,9 @@ func (l *CloudWatchEventsTargetLister) List(_ context.Context, o interface{}) ([ for _, target := range targetResp.Targets { resources = append(resources, &CloudWatchEventsTarget{ svc: svc, - ruleName: rule.Name, - targetID: target.Id, - busName: bus.Name, + Name: rule.Name, + TargetID: target.Id, + BusName: bus.Name, }) } } @@ -69,30 +70,27 @@ func (l *CloudWatchEventsTargetLister) List(_ context.Context, o interface{}) ([ type CloudWatchEventsTarget struct { svc *cloudwatchevents.CloudWatchEvents - targetID *string - ruleName *string - busName *string + TargetID *string + Name *string + BusName *string } func (r *CloudWatchEventsTarget) Remove(_ context.Context) error { - ids := []*string{r.targetID} + ids := []*string{r.TargetID} _, err := r.svc.RemoveTargets(&cloudwatchevents.RemoveTargetsInput{ Ids: ids, - Rule: r.ruleName, - EventBusName: r.busName, - Force: aws.Bool(true), + Rule: r.Name, + EventBusName: r.BusName, + Force: ptr.Bool(true), }) return err } func (r *CloudWatchEventsTarget) String() string { // TODO: change this to IAM format rule -> target and mark as breaking change for filters - return fmt.Sprintf("Rule: %s Target ID: %s", *r.ruleName, *r.targetID) + return fmt.Sprintf("Rule: %s Target ID: %s", *r.Name, *r.TargetID) } func (r *CloudWatchEventsTarget) Properties() types.Properties { - return types.NewProperties(). - Set("Name", r.ruleName). - Set("TargetID", r.targetID). - Set("BusName", r.busName) + return types.NewPropertiesFromStruct(r) } From e33c74fe73fe350db5d58c4cad37451e7d612e99 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 19:01:07 -0700 Subject: [PATCH 71/87] docs(cloudwatchevent-target): auto-generated --- docs/resources/cloud-watch-events-target.md | 5 +++++ resources/cloudwatchevents-target.go | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/resources/cloud-watch-events-target.md b/docs/resources/cloud-watch-events-target.md index ed49fc9f..397d1dc2 100644 --- a/docs/resources/cloud-watch-events-target.md +++ b/docs/resources/cloud-watch-events-target.md @@ -11,8 +11,13 @@ generated: true CloudWatchEventsTarget ``` +## Properties +- `BusName`: The name of the event bus the rule applies to +- `Name`: The name of the rule +- `TargetID`: The ID of the target for the rule + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/resources/cloudwatchevents-target.go b/resources/cloudwatchevents-target.go index 9992cf84..c8918b66 100644 --- a/resources/cloudwatchevents-target.go +++ b/resources/cloudwatchevents-target.go @@ -70,9 +70,9 @@ func (l *CloudWatchEventsTargetLister) List(_ context.Context, o interface{}) ([ type CloudWatchEventsTarget struct { svc *cloudwatchevents.CloudWatchEvents - TargetID *string - Name *string - BusName *string + TargetID *string `description:"The ID of the target for the rule"` + Name *string `description:"The name of the rule"` + BusName *string `description:"The name of the event bus the rule applies to"` } func (r *CloudWatchEventsTarget) Remove(_ context.Context) error { From 1c42a86c16f6017e82fac454a1b568822a7f7820 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Fri, 3 Jan 2025 20:03:21 -0700 Subject: [PATCH 72/87] feat(cloudformation-stack): new setting CreateRoleToDeleteStack --- go.mod | 1 + go.sum | 2 + resources/cloudformation-stack.go | 216 ++++++++++++++++--------- resources/cloudformation-stack_test.go | 143 +++++++++------- 4 files changed, 222 insertions(+), 140 deletions(-) diff --git a/go.mod b/go.mod index 47630365..6d489c4c 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect + github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect diff --git a/go.sum b/go.sum index 0416494b..c12f7a25 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvK github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo= +github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 h1:2sFIoFzU1IEL9epJWubJm9Dhrn45aTNEJuwsesaCGnk= +github.com/aws/aws-sdk-go-v2/service/iam v1.38.3/go.mod h1:KzlNINwfr/47tKkEhgk0r10/OZq3rjtyWy0txL3lM+I= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY= diff --git a/resources/cloudformation-stack.go b/resources/cloudformation-stack.go index 5ed09049..4bfc002d 100644 --- a/resources/cloudformation-stack.go +++ b/resources/cloudformation-stack.go @@ -15,6 +15,9 @@ import ( "github.com/aws/aws-sdk-go/service/cloudformation" "github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface" + "github.com/aws/aws-sdk-go-v2/service/iam" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + liberrors "github.com/ekristen/libnuke/pkg/errors" "github.com/ekristen/libnuke/pkg/registry" "github.com/ekristen/libnuke/pkg/resource" @@ -36,6 +39,7 @@ func init() { Lister: &CloudFormationStackLister{}, Settings: []string{ "DisableDeletionProtection", + "CreateRoleToDeleteStack", }, }) } @@ -46,6 +50,7 @@ func (l *CloudFormationStackLister) List(_ context.Context, o interface{}) ([]re opts := o.(*nuke.ListerOpts) svc := cloudformation.New(opts.Session) + iamSvc := iam.NewFromConfig(*opts.Config) params := &cloudformation.DescribeStacksInput{} resources := make([]resource.Resource, 0) @@ -56,11 +61,26 @@ func (l *CloudFormationStackLister) List(_ context.Context, o interface{}) ([]re return nil, err } for _, stack := range resp.Stacks { - resources = append(resources, &CloudFormationStack{ + newResource := &CloudFormationStack{ svc: svc, - stack: stack, + iamSvc: iamSvc, + logger: opts.Logger, maxDeleteAttempts: CloudformationMaxDeleteAttempt, - }) + Name: stack.StackName, + Status: stack.StackStatus, + description: stack.Description, + parentID: stack.ParentId, + roleARN: stack.RoleARN, + CreationTime: stack.CreationTime, + LastUpdatedTime: stack.LastUpdatedTime, + Tags: stack.Tags, + } + + if newResource.LastUpdatedTime == nil { + newResource.LastUpdatedTime = newResource.CreationTime + } + + resources = append(resources, newResource) } if resp.NextToken == nil { @@ -75,58 +95,106 @@ func (l *CloudFormationStackLister) List(_ context.Context, o interface{}) ([]re type CloudFormationStack struct { svc cloudformationiface.CloudFormationAPI - stack *cloudformation.Stack - maxDeleteAttempts int + iamSvc *iam.Client settings *settings.Setting + logger *logrus.Entry + Name *string + Status *string + CreationTime *time.Time + LastUpdatedTime *time.Time + Tags []*cloudformation.Tag + description *string + parentID *string + roleARN *string + maxDeleteAttempts int } -func (cfs *CloudFormationStack) Filter() error { - if ptr.ToString(cfs.stack.Description) == "DO NOT MODIFY THIS STACK! This stack is managed by Config Conformance Packs." { +func (r *CloudFormationStack) Filter() error { + if ptr.ToString(r.description) == "DO NOT MODIFY THIS STACK! This stack is managed by Config Conformance Packs." { return fmt.Errorf("stack is managed by Config Conformance Packs") } return nil } -func (cfs *CloudFormationStack) Settings(setting *settings.Setting) { - cfs.settings = setting +func (r *CloudFormationStack) Settings(setting *settings.Setting) { + r.settings = setting } -func (cfs *CloudFormationStack) Remove(_ context.Context) error { - return cfs.removeWithAttempts(0) +func (r *CloudFormationStack) Remove(ctx context.Context) error { + return r.removeWithAttempts(ctx, 0) } -func (cfs *CloudFormationStack) removeWithAttempts(attempt int) error { - if err := cfs.doRemove(); err != nil { - // TODO: pass logrus in via ListerOpts so that it can be used here instead of global +func (r *CloudFormationStack) createRole(ctx context.Context) error { + roleParts := strings.Split(ptr.ToString(r.roleARN), "/") + _, err := r.iamSvc.CreateRole(ctx, &iam.CreateRoleInput{ + RoleName: ptr.String(roleParts[len(roleParts)-1]), + AssumeRolePolicyDocument: ptr.String(`{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "cloudformation.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +}`), + Tags: []iamtypes.Tag{ + { + Key: ptr.String("Managed"), + Value: ptr.String("aws-nuke"), + }, + }, + }) + + return err +} - logrus.Errorf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d delete failed: %s", - *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts, err.Error()) +func (r *CloudFormationStack) removeWithAttempts(ctx context.Context, attempt int) error { + if err := r.doRemove(); err != nil { + r.logger.Errorf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d delete failed: %s", + *r.Name, attempt, r.maxDeleteAttempts, err.Error()) var awsErr awserr.Error ok := errors.As(err, &awsErr) - if ok && awsErr.Code() == "ValidationError" && - awsErr.Message() == "Stack ["+*cfs.stack.StackName+"] cannot be deleted while TerminationProtection is enabled" { - // check if the setting for the resource is set to allow deletion protection to be disabled - if cfs.settings.GetBool("DisableDeletionProtection") { - logrus.Infof("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d updating termination protection", - *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts) - _, err = cfs.svc.UpdateTerminationProtection(&cloudformation.UpdateTerminationProtectionInput{ - EnableTerminationProtection: aws.Bool(false), - StackName: cfs.stack.StackName, - }) - if err != nil { + if ok && awsErr.Code() == "ValidationError" { + if awsErr.Message() == fmt.Sprintf("Role %s is invalid or cannot be assumed", *r.roleARN) { + if r.settings.GetBool("CreateRoleToDeleteStack") { + r.logger.Infof("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d creating role to delete stack", + *r.Name, attempt, r.maxDeleteAttempts) + if err := r.createRole(ctx); err != nil { + return err + } + } else { + r.logger.Warnf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d set feature flag to create role to delete stack", + *r.Name, attempt, r.maxDeleteAttempts) + return err + } + } else if awsErr.Message() == fmt.Sprintf("Stack [%s] cannot be deleted while TerminationProtection is enabled", *r.Name) { + // check if the setting for the resource is set to allow deletion protection to be disabled + if r.settings.GetBool("DisableDeletionProtection") { + r.logger.Infof("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d updating termination protection", + *r.Name, attempt, r.maxDeleteAttempts) + _, err = r.svc.UpdateTerminationProtection(&cloudformation.UpdateTerminationProtectionInput{ + EnableTerminationProtection: aws.Bool(false), + StackName: r.Name, + }) + if err != nil { + return err + } + } else { + r.logger.Warnf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d set feature flag to disable deletion protection", + *r.Name, attempt, r.maxDeleteAttempts) return err } - } else { - logrus.Warnf("CloudFormationStack stackName=%s attempt=%d maxAttempts=%d set feature flag to disable deletion protection", - *cfs.stack.StackName, attempt, cfs.maxDeleteAttempts) - return err } } - if attempt >= cfs.maxDeleteAttempts { + + if attempt >= r.maxDeleteAttempts { return errors.New("CFS might not be deleted after this run") } else { - return cfs.removeWithAttempts(attempt + 1) + return r.removeWithAttempts(ctx, attempt+1) } } else { return nil @@ -148,9 +216,9 @@ func GetParentStack(svc cloudformationiface.CloudFormationAPI, stackID string) ( return nil, nil //nolint:nilnil } -func (cfs *CloudFormationStack) doRemove() error { //nolint:gocyclo - if cfs.stack.ParentId != nil { - p, err := GetParentStack(cfs.svc, *cfs.stack.ParentId) +func (r *CloudFormationStack) doRemove() error { //nolint:gocyclo + if r.parentID != nil { + p, err := GetParentStack(r.svc, *r.parentID) if err != nil { return err } @@ -160,14 +228,14 @@ func (cfs *CloudFormationStack) doRemove() error { //nolint:gocyclo } } - o, err := cfs.svc.DescribeStacks(&cloudformation.DescribeStacksInput{ - StackName: cfs.stack.StackName, + o, err := r.svc.DescribeStacks(&cloudformation.DescribeStacksInput{ + StackName: r.Name, }) if err != nil { var awsErr awserr.Error if errors.As(err, &awsErr) { if awsErr.Code() == "ValidationFailed" && strings.HasSuffix(awsErr.Message(), " does not exist") { - logrus.Infof("CloudFormationStack stackName=%s no longer exists", *cfs.stack.StackName) + r.logger.Infof("CloudFormationStack stackName=%s no longer exists", *r.Name) return nil } } @@ -179,16 +247,16 @@ func (cfs *CloudFormationStack) doRemove() error { //nolint:gocyclo // stack already deleted, no need to re-delete return nil } else if *stack.StackStatus == cloudformation.StackStatusDeleteInProgress { - logrus.Infof("CloudFormationStack stackName=%s delete in progress. Waiting", *cfs.stack.StackName) - return cfs.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ - StackName: cfs.stack.StackName, + r.logger.Infof("CloudFormationStack stackName=%s delete in progress. Waiting", *r.Name) + return r.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ + StackName: r.Name, }) } else if *stack.StackStatus == cloudformation.StackStatusDeleteFailed { - logrus.Infof("CloudFormationStack stackName=%s delete failed. Attempting to retain and delete stack", *cfs.stack.StackName) + r.logger.Infof("CloudFormationStack stackName=%s delete failed. Attempting to retain and delete stack", *r.Name) // This means the CFS has undetectable resources. // In order to move on with nuking, we retain them in the deletion. - retainableResources, err := cfs.svc.ListStackResources(&cloudformation.ListStackResourcesInput{ - StackName: cfs.stack.StackName, + retainableResources, err := r.svc.ListStackResources(&cloudformation.ListStackResourcesInput{ + StackName: r.Name, }) if err != nil { return err @@ -202,25 +270,25 @@ func (cfs *CloudFormationStack) doRemove() error { //nolint:gocyclo } } - _, err = cfs.svc.DeleteStack(&cloudformation.DeleteStackInput{ - StackName: cfs.stack.StackName, + if _, err = r.svc.DeleteStack(&cloudformation.DeleteStackInput{ + StackName: r.Name, RetainResources: retain, - }) - if err != nil { + }); err != nil { return err } - return cfs.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ - StackName: cfs.stack.StackName, + + return r.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ + StackName: r.Name, }) } else { - if err := cfs.waitForStackToStabilize(*stack.StackStatus); err != nil { + if err := r.waitForStackToStabilize(*stack.StackStatus); err != nil { return err - } else if _, err := cfs.svc.DeleteStack(&cloudformation.DeleteStackInput{ - StackName: cfs.stack.StackName, + } else if _, err := r.svc.DeleteStack(&cloudformation.DeleteStackInput{ + StackName: r.Name, }); err != nil { return err - } else if err := cfs.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ - StackName: cfs.stack.StackName, + } else if err := r.svc.WaitUntilStackDeleteComplete(&cloudformation.DescribeStacksInput{ + StackName: r.Name, }); err != nil { return err } else { @@ -228,45 +296,33 @@ func (cfs *CloudFormationStack) doRemove() error { //nolint:gocyclo } } } -func (cfs *CloudFormationStack) waitForStackToStabilize(currentStatus string) error { + +func (r *CloudFormationStack) waitForStackToStabilize(currentStatus string) error { switch currentStatus { case cloudformation.StackStatusUpdateInProgress, cloudformation.StackStatusUpdateRollbackCompleteCleanupInProgress, cloudformation.StackStatusUpdateRollbackInProgress: - logrus.Infof("CloudFormationStack stackName=%s update in progress. Waiting to stabalize", *cfs.stack.StackName) + r.logger.Infof("CloudFormationStack stackName=%s update in progress. Waiting to stabalize", *r.Name) - return cfs.svc.WaitUntilStackUpdateComplete(&cloudformation.DescribeStacksInput{ - StackName: cfs.stack.StackName, + return r.svc.WaitUntilStackUpdateComplete(&cloudformation.DescribeStacksInput{ + StackName: r.Name, }) case cloudformation.StackStatusCreateInProgress, cloudformation.StackStatusRollbackInProgress: - logrus.Infof("CloudFormationStack stackName=%s create in progress. Waiting to stabalize", *cfs.stack.StackName) + r.logger.Infof("CloudFormationStack stackName=%s create in progress. Waiting to stabalize", *r.Name) - return cfs.svc.WaitUntilStackCreateComplete(&cloudformation.DescribeStacksInput{ - StackName: cfs.stack.StackName, + return r.svc.WaitUntilStackCreateComplete(&cloudformation.DescribeStacksInput{ + StackName: r.Name, }) default: return nil } } -func (cfs *CloudFormationStack) Properties() types.Properties { - properties := types.NewProperties() - properties.Set("Name", cfs.stack.StackName) - properties.Set("CreationTime", cfs.stack.CreationTime.Format(time.RFC3339)) - if cfs.stack.LastUpdatedTime == nil { - properties.Set("LastUpdatedTime", cfs.stack.CreationTime.Format(time.RFC3339)) - } else { - properties.Set("LastUpdatedTime", cfs.stack.LastUpdatedTime.Format(time.RFC3339)) - } - - for _, tagValue := range cfs.stack.Tags { - properties.SetTag(tagValue.Key, tagValue.Value) - } - - return properties +func (r *CloudFormationStack) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) } -func (cfs *CloudFormationStack) String() string { - return *cfs.stack.StackName +func (r *CloudFormationStack) String() string { + return *r.Name } diff --git a/resources/cloudformation-stack_test.go b/resources/cloudformation-stack_test.go index 9b7bc82a..9eba55a7 100644 --- a/resources/cloudformation-stack_test.go +++ b/resources/cloudformation-stack_test.go @@ -2,13 +2,14 @@ package resources import ( "context" - "testing" + "time" "github.com/golang/mock/gomock" + "github.com/gotidy/ptr" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cloudformation" @@ -17,6 +18,35 @@ import ( "github.com/ekristen/aws-nuke/v3/mocks/mock_cloudformationiface" ) +func TestCloudformationStack_Properties(t *testing.T) { + a := assert.New(t) + + now := time.Now() + + stack := CloudFormationStack{ + Name: ptr.String("foobar"), + Status: ptr.String(cloudformation.StackStatusCreateComplete), + CreationTime: ptr.Time(now), + LastUpdatedTime: ptr.Time(now), + Tags: []*cloudformation.Tag{ + { + Key: ptr.String("Name"), + Value: ptr.String("foobar"), + }, + }, + } + + props := stack.Properties() + + a.Equal("foobar", props.Get("Name")) + a.Equal(cloudformation.StackStatusCreateComplete, props.Get("Status")) + a.Equal(now.Format(time.RFC3339), props.Get("CreationTime")) + a.Equal(now.Format(time.RFC3339), props.Get("LastUpdatedTime")) + a.Equal("foobar", props.Get("tag:Name")) + + a.Equal("foobar", stack.String()) +} + func TestCloudformationStack_Remove_StackAlreadyDeleted(t *testing.T) { a := assert.New(t) ctrl := gomock.NewController(t) @@ -25,21 +55,20 @@ func TestCloudformationStack_Remove_StackAlreadyDeleted(t *testing.T) { mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, + svc: mockCloudformation, + logger: logrus.NewEntry(logrus.StandardLogger()), + Name: ptr.String("foobar"), settings: &libsettings.Setting{ "DisableDeletionProtection": true, }, } mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(&cloudformation.DescribeStacksOutput{ Stacks: []*cloudformation.Stack{ { - StackStatus: aws.String(cloudformation.StackStatusDeleteComplete), + StackStatus: ptr.String(cloudformation.StackStatusDeleteComplete), }, }, }, nil) @@ -56,17 +85,16 @@ func TestCloudformationStack_Remove_StackDoesNotExist(t *testing.T) { mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, + svc: mockCloudformation, + logger: logrus.NewEntry(logrus.StandardLogger()), + Name: ptr.String("foobar"), settings: &libsettings.Setting{ "DisableDeletionProtection": true, }, } mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil, awserr.New("ValidationFailed", "Stack with id foobar does not exist", nil)) err := stack.Remove(context.TODO()) @@ -81,10 +109,9 @@ func TestCloudformationStack_Remove_DeleteFailed(t *testing.T) { mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, + svc: mockCloudformation, + logger: logrus.NewEntry(logrus.StandardLogger()), + Name: ptr.String("foobar"), settings: &libsettings.Setting{ "DisableDeletionProtection": true, }, @@ -92,36 +119,36 @@ func TestCloudformationStack_Remove_DeleteFailed(t *testing.T) { gomock.InOrder( mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(&cloudformation.DescribeStacksOutput{ Stacks: []*cloudformation.Stack{ { - StackStatus: aws.String(cloudformation.StackStatusDeleteFailed), + StackStatus: ptr.String(cloudformation.StackStatusDeleteFailed), }, }, }, nil), mockCloudformation.EXPECT().ListStackResources(gomock.Eq(&cloudformation.ListStackResourcesInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(&cloudformation.ListStackResourcesOutput{ StackResourceSummaries: []*cloudformation.StackResourceSummary{ { - ResourceStatus: aws.String(cloudformation.ResourceStatusDeleteComplete), - LogicalResourceId: aws.String("fooDeleteComplete"), + ResourceStatus: ptr.String(cloudformation.ResourceStatusDeleteComplete), + LogicalResourceId: ptr.String("fooDeleteComplete"), }, { - ResourceStatus: aws.String(cloudformation.ResourceStatusDeleteFailed), - LogicalResourceId: aws.String("fooDeleteFailed"), + ResourceStatus: ptr.String(cloudformation.ResourceStatusDeleteFailed), + LogicalResourceId: ptr.String("fooDeleteFailed"), }, }, }, nil), mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), RetainResources: []*string{ - aws.String("fooDeleteFailed"), + ptr.String("fooDeleteFailed"), }, })).Return(nil, nil), mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil), ) @@ -138,10 +165,9 @@ func TestCloudformationStack_Remove_DeleteInProgress(t *testing.T) { mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, + svc: mockCloudformation, + logger: logrus.NewEntry(logrus.StandardLogger()), + Name: ptr.String("foobar"), settings: &libsettings.Setting{ "DisableDeletionProtection": true, }, @@ -149,17 +175,17 @@ func TestCloudformationStack_Remove_DeleteInProgress(t *testing.T) { gomock.InOrder( mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(&cloudformation.DescribeStacksOutput{ Stacks: []*cloudformation.Stack{ { - StackStatus: aws.String(cloudformation.StackStatusDeleteInProgress), + StackStatus: ptr.String(cloudformation.StackStatusDeleteInProgress), }, }, }, nil), mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil), ) @@ -188,10 +214,9 @@ func TestCloudformationStack_Remove_Stack_InCompletedStatus(t *testing.T) { mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, + svc: mockCloudformation, + logger: logrus.NewEntry(logrus.StandardLogger()), + Name: ptr.String("foobar"), settings: &libsettings.Setting{ "DisableDeletionProtection": true, }, @@ -199,21 +224,21 @@ func TestCloudformationStack_Remove_Stack_InCompletedStatus(t *testing.T) { gomock.InOrder( mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(&cloudformation.DescribeStacksOutput{ Stacks: []*cloudformation.Stack{ { - StackStatus: aws.String(stackStatus), + StackStatus: ptr.String(stackStatus), }, }, }, nil), mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil, nil), mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil), ) @@ -238,10 +263,9 @@ func TestCloudformationStack_Remove_Stack_CreateInProgress(t *testing.T) { mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, + svc: mockCloudformation, + logger: logrus.NewEntry(logrus.StandardLogger()), + Name: ptr.String("foobar"), settings: &libsettings.Setting{ "DisableDeletionProtection": true, }, @@ -249,25 +273,25 @@ func TestCloudformationStack_Remove_Stack_CreateInProgress(t *testing.T) { gomock.InOrder( mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(&cloudformation.DescribeStacksOutput{ Stacks: []*cloudformation.Stack{ { - StackStatus: aws.String(stackStatus), + StackStatus: ptr.String(stackStatus), }, }, }, nil), mockCloudformation.EXPECT().WaitUntilStackCreateComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil), mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil, nil), mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil), ) @@ -293,10 +317,9 @@ func TestCloudformationStack_Remove_Stack_UpdateInProgress(t *testing.T) { mockCloudformation := mock_cloudformationiface.NewMockCloudFormationAPI(ctrl) stack := CloudFormationStack{ - svc: mockCloudformation, - stack: &cloudformation.Stack{ - StackName: aws.String("foobar"), - }, + svc: mockCloudformation, + logger: logrus.NewEntry(logrus.StandardLogger()), + Name: ptr.String("foobar"), settings: &libsettings.Setting{ "DisableDeletionProtection": true, }, @@ -304,25 +327,25 @@ func TestCloudformationStack_Remove_Stack_UpdateInProgress(t *testing.T) { gomock.InOrder( mockCloudformation.EXPECT().DescribeStacks(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(&cloudformation.DescribeStacksOutput{ Stacks: []*cloudformation.Stack{ { - StackStatus: aws.String(stackStatus), + StackStatus: ptr.String(stackStatus), }, }, }, nil), mockCloudformation.EXPECT().WaitUntilStackUpdateComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil), mockCloudformation.EXPECT().DeleteStack(gomock.Eq(&cloudformation.DeleteStackInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil, nil), mockCloudformation.EXPECT().WaitUntilStackDeleteComplete(gomock.Eq(&cloudformation.DescribeStacksInput{ - StackName: aws.String("foobar"), + StackName: ptr.String("foobar"), })).Return(nil), ) From bafba216296e07f3424faae395eafde3ecde640d Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 09:54:14 -0700 Subject: [PATCH 73/87] fix(cloudformation-stack): remove role afterwards --- resources/cloudformation-stack.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/resources/cloudformation-stack.go b/resources/cloudformation-stack.go index 4bfc002d..66840a7b 100644 --- a/resources/cloudformation-stack.go +++ b/resources/cloudformation-stack.go @@ -107,6 +107,8 @@ type CloudFormationStack struct { parentID *string roleARN *string maxDeleteAttempts int + roleCreated bool + roleName string } func (r *CloudFormationStack) Filter() error { @@ -148,6 +150,20 @@ func (r *CloudFormationStack) createRole(ctx context.Context) error { }, }) + r.roleCreated = true + r.roleName = roleParts[len(roleParts)-1] + + return err +} + +func (r *CloudFormationStack) removeRole(ctx context.Context) error { + if !r.roleCreated { + return nil + } + + _, err := r.iamSvc.DeleteRole(ctx, &iam.DeleteRoleInput{ + RoleName: ptr.String(r.roleName), + }) return err } @@ -196,9 +212,9 @@ func (r *CloudFormationStack) removeWithAttempts(ctx context.Context, attempt in } else { return r.removeWithAttempts(ctx, attempt+1) } - } else { - return nil } + + return r.removeRole(ctx) } func GetParentStack(svc cloudformationiface.CloudFormationAPI, stackID string) (*cloudformation.Stack, error) { From cc5548aa8fea35984ef057cdddc92a4cd7d3dacc Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 10:51:30 -0700 Subject: [PATCH 74/87] feat: adding support for s3 access grants, grant, instance, location --- go.mod | 1 + go.sum | 2 + resources/s3-access-grants-grant.go | 84 ++++++++++++++++++++++++++ resources/s3-access-grants-instance.go | 73 ++++++++++++++++++++++ resources/s3-access-grants-location.go | 81 +++++++++++++++++++++++++ 5 files changed, 241 insertions(+) create mode 100644 resources/s3-access-grants-grant.go create mode 100644 resources/s3-access-grants-instance.go create mode 100644 resources/s3-access-grants-location.go diff --git a/go.mod b/go.mod index 6d489c4c..4e46deec 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect diff --git a/go.sum b/go.sum index c12f7a25..dc96dcfc 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEH github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= +github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 h1:xxGbXbGtO/VMz2JqB1UwEDlSchryUss0KmQJSZ0oTUE= +github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1/go.mod h1:6BuUa52of67a+ri/poTH82XiL+rTGQWUPZCmf2cfVHI= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 h1:4siT1z3nEVxJq1jZYu1SRoct5xgbKen+ammCuZBZ2zI= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2/go.mod h1:KSO1+erW2SUB6Mw/Qamu1fOT5fn/mzd9G79ENbYqyRQ= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= diff --git a/resources/s3-access-grants-grant.go b/resources/s3-access-grants-grant.go new file mode 100644 index 00000000..5eb792a8 --- /dev/null +++ b/resources/s3-access-grants-grant.go @@ -0,0 +1,84 @@ +package resources + +import ( + "context" + "strings" + "time" + + "github.com/gotidy/ptr" + + "github.com/aws/aws-sdk-go-v2/service/s3control" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const S3AccessGrantsGrantResource = "S3AccessGrantsGrant" + +func init() { + registry.Register(®istry.Registration{ + Name: S3AccessGrantsGrantResource, + Scope: nuke.Account, + Resource: &S3AccessGrantsGrant{}, + Lister: &S3AccessGrantsGrantLister{}, + }) +} + +type S3AccessGrantsGrantLister struct{} + +func (l *S3AccessGrantsGrantLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := s3control.NewFromConfig(*opts.Config) + var resources []resource.Resource + + res, err := svc.ListAccessGrants(ctx, &s3control.ListAccessGrantsInput{ + AccountId: opts.AccountID, + }) + if err != nil { + if strings.Contains(err.Error(), "AccessGrantsInstanceNotExistsError") { + return resources, nil + } else { + return nil, err + } + } + + for _, p := range res.AccessGrantsList { + resources = append(resources, &S3AccessGrantsGrant{ + svc: svc, + ID: p.AccessGrantId, + GrantScope: p.GrantScope, + GranteeType: ptr.String(string(p.Grantee.GranteeType)), + GranteeID: p.Grantee.GranteeIdentifier, + CreatedAt: p.CreatedAt, + }) + } + + return resources, nil +} + +type S3AccessGrantsGrant struct { + svc *s3control.Client + ID *string `description:"The ID of the access grant."` + GrantScope *string `description:"The scope of the access grant."` + GranteeType *string `description:"The type of the grantee, (e.g. IAM)."` + GranteeID *string `description:"The ARN of the grantee."` + CreatedAt *time.Time `description:"The date and time the access grant was created."` +} + +func (r *S3AccessGrantsGrant) Remove(ctx context.Context) error { + _, err := r.svc.DeleteAccessGrant(ctx, &s3control.DeleteAccessGrantInput{ + AccessGrantId: r.ID, + }) + return err +} + +func (r *S3AccessGrantsGrant) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *S3AccessGrantsGrant) String() string { + return *r.ID +} diff --git a/resources/s3-access-grants-instance.go b/resources/s3-access-grants-instance.go new file mode 100644 index 00000000..05e9b0d8 --- /dev/null +++ b/resources/s3-access-grants-instance.go @@ -0,0 +1,73 @@ +package resources + +import ( + "context" + "time" + + "github.com/aws/aws-sdk-go-v2/service/s3control" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const S3AccessGrantsInstanceResource = "S3AccessGrantsInstance" + +func init() { + registry.Register(®istry.Registration{ + Name: S3AccessGrantsInstanceResource, + Scope: nuke.Account, + Resource: &S3AccessGrantsInstance{}, + Lister: &S3AccessGrantsInstanceLister{}, + }) +} + +type S3AccessGrantsInstanceLister struct{} + +func (l *S3AccessGrantsInstanceLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := s3control.NewFromConfig(*opts.Config) + var resources []resource.Resource + + res, err := svc.ListAccessGrantsInstances(ctx, &s3control.ListAccessGrantsInstancesInput{ + AccountId: opts.AccountID, + }) + if err != nil { + return nil, err + } + + for _, entity := range res.AccessGrantsInstancesList { + resources = append(resources, &S3AccessGrantsInstance{ + svc: svc, + accountID: opts.AccountID, + ID: entity.AccessGrantsInstanceId, + CreatedAt: entity.CreatedAt, + }) + } + + return resources, nil +} + +type S3AccessGrantsInstance struct { + svc *s3control.Client + accountID *string + ID *string `description:"The ID of the access grants instance."` + CreatedAt *time.Time `description:"The time the access grants instance was created."` +} + +func (r *S3AccessGrantsInstance) Remove(ctx context.Context) error { + _, err := r.svc.DeleteAccessGrantsInstance(ctx, &s3control.DeleteAccessGrantsInstanceInput{ + AccountId: r.accountID, + }) + return err +} + +func (r *S3AccessGrantsInstance) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *S3AccessGrantsInstance) String() string { + return *r.ID +} diff --git a/resources/s3-access-grants-location.go b/resources/s3-access-grants-location.go new file mode 100644 index 00000000..c8ac0186 --- /dev/null +++ b/resources/s3-access-grants-location.go @@ -0,0 +1,81 @@ +package resources + +import ( + "context" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/service/s3control" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const S3AccessGrantsLocationResource = "S3AccessGrantsLocation" + +func init() { + registry.Register(®istry.Registration{ + Name: S3AccessGrantsLocationResource, + Scope: nuke.Account, + Resource: &S3AccessGrantsLocation{}, + Lister: &S3AccessGrantsLocationLister{}, + }) +} + +type S3AccessGrantsLocationLister struct{} + +func (l *S3AccessGrantsLocationLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := s3control.NewFromConfig(*opts.Config) + var resources []resource.Resource + + res, err := svc.ListAccessGrantsLocations(ctx, &s3control.ListAccessGrantsLocationsInput{ + AccountId: opts.AccountID, + }) + if err != nil { + if strings.Contains(err.Error(), "AccessGrantsInstanceNotExistsError") { + return resources, nil + } else { + return nil, err + } + } + + for _, entity := range res.AccessGrantsLocationsList { + resources = append(resources, &S3AccessGrantsLocation{ + svc: svc, + accountID: opts.AccountID, + ID: entity.AccessGrantsLocationId, + LocationScope: entity.LocationScope, + CreatedAt: entity.CreatedAt, + }) + } + + return resources, nil +} + +type S3AccessGrantsLocation struct { + svc *s3control.Client + accountID *string + ID *string `description:"The ID of the access grants location."` + LocationScope *string `description:"The scope of the access grants location."` + CreatedAt *time.Time `description:"The time the access grants location was created."` +} + +func (r *S3AccessGrantsLocation) Remove(ctx context.Context) error { + _, err := r.svc.DeleteAccessGrantsLocation(ctx, &s3control.DeleteAccessGrantsLocationInput{ + AccessGrantsLocationId: r.ID, + AccountId: r.accountID, + }) + return err +} + +func (r *S3AccessGrantsLocation) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *S3AccessGrantsLocation) String() string { + return *r.ID +} From fd912a0a0782ee2b90c8cbc61731e5e8aefd1f06 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 10:51:45 -0700 Subject: [PATCH 75/87] chore(tools/create-resource): improving resource gen --- tools/create-resource/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/create-resource/main.go b/tools/create-resource/main.go index a2c5e6de..339f1a66 100644 --- a/tools/create-resource/main.go +++ b/tools/create-resource/main.go @@ -38,7 +38,7 @@ func init() { type {{.Combined}}Lister struct{} -func (l *{{.Combined}}Lister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { +func (l *{{.Combined}}Lister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) svc := {{.Service}}.NewFromConfig(*opts.Config) var resources []resource.Resource @@ -50,10 +50,10 @@ func (l *{{.Combined}}Lister) List(_ context.Context, o interface{}) ([]resource return nil, err } - for _, p := range res.{{.ResourceTypeTitle}}s { + for _, p := range res.{{.ResourceTypeTitle}}sList { resources = append(resources, &{{.Combined}}{ svc: svc, - ID: p.Id, + ID: p.{{.ResourceTypeTitle}}Id, Tags: p.Tags, }) } From 96cbeeb42d6e906efd227e8cfb3ffbfa4a7895c8 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 10:53:48 -0700 Subject: [PATCH 76/87] docs(resources): auto-generated --- docs/resources/cloud-formation-stack.md | 20 +++++++++++ docs/resources/cloud-watch-alarm.md | 6 ++++ docs/resources/ec2-tgw.md | 7 ++++ docs/resources/neptune-snapshot.md | 2 ++ docs/resources/resource-explorer-2index.md | 4 +++ docs/resources/resource-explorer-2view.md | 3 ++ .../resources/route-53-resource-record-set.md | 7 ++++ docs/resources/s3-access-grants-grant.md | 34 +++++++++++++++++++ docs/resources/s3-access-grants-instance.md | 31 +++++++++++++++++ docs/resources/s3-access-grants-location.md | 32 +++++++++++++++++ mkdocs.yml | 3 ++ 11 files changed, 149 insertions(+) create mode 100644 docs/resources/s3-access-grants-grant.md create mode 100644 docs/resources/s3-access-grants-instance.md create mode 100644 docs/resources/s3-access-grants-location.md diff --git a/docs/resources/cloud-formation-stack.md b/docs/resources/cloud-formation-stack.md index e7402ac1..0ec3fd84 100644 --- a/docs/resources/cloud-formation-stack.md +++ b/docs/resources/cloud-formation-stack.md @@ -11,8 +11,16 @@ generated: true CloudFormationStack ``` +## Properties +- `CreationTime`: No Description +- `LastUpdatedTime`: No Description +- `Name`: No Description +- `Status`: No Description +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. @@ -28,6 +36,7 @@ The string value is always what is used in the output of the log format when a r ## Settings - `DisableDeletionProtection` +- `CreateRoleToDeleteStack` ### DisableDeletionProtection @@ -40,3 +49,14 @@ The string value is always what is used in the output of the log format when a r DisableDeletionProtection ``` + +### CreateRoleToDeleteStack + +!!! note + There is currently no description for this setting. Often times settings are fairly self-explanatory. However, we + are working on adding descriptions for all settings. + +```text +CreateRoleToDeleteStack +``` + diff --git a/docs/resources/cloud-watch-alarm.md b/docs/resources/cloud-watch-alarm.md index faaadf34..64b58c02 100644 --- a/docs/resources/cloud-watch-alarm.md +++ b/docs/resources/cloud-watch-alarm.md @@ -11,8 +11,14 @@ generated: true CloudWatchAlarm ``` +## Properties +- `Name`: No Description +- `Type`: No Description +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/docs/resources/ec2-tgw.md b/docs/resources/ec2-tgw.md index 45df81e7..459cb40d 100644 --- a/docs/resources/ec2-tgw.md +++ b/docs/resources/ec2-tgw.md @@ -11,8 +11,15 @@ generated: true EC2TGW ``` +## Properties +- `ID`: The ID of the transit gateway. +- `OwnerId`: The ID of the AWS account that owns the transit gateway. +- `State`: The state of the transit gateway. +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/docs/resources/neptune-snapshot.md b/docs/resources/neptune-snapshot.md index a1c02696..4a938c37 100644 --- a/docs/resources/neptune-snapshot.md +++ b/docs/resources/neptune-snapshot.md @@ -14,7 +14,9 @@ NeptuneSnapshot ## Properties +- `CreateTime`: No Description - `ID`: No Description +- `Status`: No Description !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property diff --git a/docs/resources/resource-explorer-2index.md b/docs/resources/resource-explorer-2index.md index fcac6e61..244bb6de 100644 --- a/docs/resources/resource-explorer-2index.md +++ b/docs/resources/resource-explorer-2index.md @@ -11,8 +11,12 @@ generated: true ResourceExplorer2Index ``` +## Properties +- `ARN`: No Description +- `Type`: No Description + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/docs/resources/resource-explorer-2view.md b/docs/resources/resource-explorer-2view.md index a2841730..44bc5f9a 100644 --- a/docs/resources/resource-explorer-2view.md +++ b/docs/resources/resource-explorer-2view.md @@ -11,8 +11,11 @@ generated: true ResourceExplorer2View ``` +## Properties +- `ARN`: The ARN of the Resource Explorer View + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/docs/resources/route-53-resource-record-set.md b/docs/resources/route-53-resource-record-set.md index 608876d1..558d85ec 100644 --- a/docs/resources/route-53-resource-record-set.md +++ b/docs/resources/route-53-resource-record-set.md @@ -11,8 +11,15 @@ generated: true Route53ResourceRecordSet ``` +## Properties +- `HostedZoneName`: The name of the zone to which the record belongs +- `Name`: The name of the record +- `Type`: The type of the record +- `tag::`: This resource has tags with property `Tags`. These are key/value pairs that are + added as their own property with the prefix of `tag:` (e.g. [tag:example: "value"]) + !!! note - Using Properties Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property names to write filters for what you want to **keep** and omit from the nuke process. diff --git a/docs/resources/s3-access-grants-grant.md b/docs/resources/s3-access-grants-grant.md new file mode 100644 index 00000000..3976c384 --- /dev/null +++ b/docs/resources/s3-access-grants-grant.md @@ -0,0 +1,34 @@ +--- +generated: true +--- + +# S3AccessGrantsGrant + + +## Resource + +```text +S3AccessGrantsGrant +``` + +## Properties + + +- `CreatedAt`: The date and time the access grant was created. +- `GrantScope`: The scope of the access grant. +- `GranteeID`: The ARN of the grantee. +- `GranteeType`: The type of the grantee, (e.g. IAM). +- `ID`: The ID of the access grant. + +!!! note - Using Properties + Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property + names to write filters for what you want to **keep** and omit from the nuke process. + +### String Property + +The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all +resources support properties. To write a filter against the string representation, simply omit the `property` field in +the filter. + +The string value is always what is used in the output of the log format when a resource is identified. + diff --git a/docs/resources/s3-access-grants-instance.md b/docs/resources/s3-access-grants-instance.md new file mode 100644 index 00000000..8d313ae5 --- /dev/null +++ b/docs/resources/s3-access-grants-instance.md @@ -0,0 +1,31 @@ +--- +generated: true +--- + +# S3AccessGrantsInstance + + +## Resource + +```text +S3AccessGrantsInstance +``` + +## Properties + + +- `CreatedAt`: The time the access grants instance was created. +- `ID`: The ID of the access grants instance. + +!!! note - Using Properties + Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property + names to write filters for what you want to **keep** and omit from the nuke process. + +### String Property + +The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all +resources support properties. To write a filter against the string representation, simply omit the `property` field in +the filter. + +The string value is always what is used in the output of the log format when a resource is identified. + diff --git a/docs/resources/s3-access-grants-location.md b/docs/resources/s3-access-grants-location.md new file mode 100644 index 00000000..e5396b29 --- /dev/null +++ b/docs/resources/s3-access-grants-location.md @@ -0,0 +1,32 @@ +--- +generated: true +--- + +# S3AccessGrantsLocation + + +## Resource + +```text +S3AccessGrantsLocation +``` + +## Properties + + +- `CreatedAt`: The time the access grants location was created. +- `ID`: The ID of the access grants location. +- `LocationScope`: The scope of the access grants location. + +!!! note - Using Properties + Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property + names to write filters for what you want to **keep** and omit from the nuke process. + +### String Property + +The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all +resources support properties. To write a filter against the string representation, simply omit the `property` field in +the filter. + +The string value is always what is used in the output of the log format when a resource is identified. + diff --git a/mkdocs.yml b/mkdocs.yml index e188d0cb..05a7a309 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -543,6 +543,9 @@ nav: - Route 53 Resolver Rule: resources/route-53-resolver-rule.md - Route 53 Resource Record Set: resources/route-53-resource-record-set.md - Route 53 Traffic Policy: resources/route-53-traffic-policy.md + - S3 Access Grants Grant: resources/s3-access-grants-grant.md + - S3 Access Grants Instance: resources/s3-access-grants-instance.md + - S3 Access Grants Location: resources/s3-access-grants-location.md - S3 Access Point: resources/s3-access-point.md - S3 Bucket: resources/s3-bucket.md - S3 Multipart Upload: resources/s3-multipart-upload.md From 0c8daf7fac89f7895124b659bd191acee0a4fab3 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 11:00:04 -0700 Subject: [PATCH 77/87] feat: new resource TransferWebApp --- go.mod | 2 ++ go.sum | 4 +++ resources/transfer-web-app.go | 66 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 resources/transfer-web-app.go diff --git a/go.mod b/go.mod index 4e46deec..e38bd110 100644 --- a/go.mod +++ b/go.mod @@ -40,9 +40,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect + github.com/aws/aws-sdk-go-v2/service/transfer v1.55.1 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index dc96dcfc..f7fd7210 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= +github.com/aws/aws-sdk-go-v2/service/transfer v1.55.1 h1:bENkaFtA6rxHAwNPjYbgwYxUHGJbL7QocCt8nKZ7d10= +github.com/aws/aws-sdk-go-v2/service/transfer v1.55.1/go.mod h1:C7x9hpm90ZocJ9GbauHMkVMU0m7knEiKhOaa4um9tBU= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= @@ -84,6 +86,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gotidy/ptr v1.4.0 h1:7++suUs+HNHMnyz6/AW3SE+4EnBhupPSQTSI7QNijVc= github.com/gotidy/ptr v1.4.0/go.mod h1:MjRBG6/IETiiZGWI8LrRtISXEji+8b/jigmj2q0mEyM= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/resources/transfer-web-app.go b/resources/transfer-web-app.go new file mode 100644 index 00000000..37fa19dd --- /dev/null +++ b/resources/transfer-web-app.go @@ -0,0 +1,66 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/service/transfer" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const TransferWebAppResource = "TransferWebApp" + +func init() { + registry.Register(®istry.Registration{ + Name: TransferWebAppResource, + Scope: nuke.Account, + Resource: &TransferWebApp{}, + Lister: &TransferWebAppLister{}, + }) +} + +type TransferWebAppLister struct{} + +func (l *TransferWebAppLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := transfer.NewFromConfig(*opts.Config) + var resources []resource.Resource + + res, err := svc.ListWebApps(ctx, &transfer.ListWebAppsInput{}) + if err != nil { + return nil, err + } + + for _, entry := range res.WebApps { + resources = append(resources, &TransferWebApp{ + svc: svc, + ID: entry.WebAppId, + }) + } + + return resources, nil +} + +type TransferWebApp struct { + svc *transfer.Client + ID *string +} + +func (r *TransferWebApp) Remove(ctx context.Context) error { + _, err := r.svc.DeleteWebApp(ctx, &transfer.DeleteWebAppInput{ + WebAppId: r.ID, + }) + return err +} + +func (r *TransferWebApp) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *TransferWebApp) String() string { + return *r.ID +} From 2b8a886001b38f5f1904607eba79452746cc3439 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 11:00:18 -0700 Subject: [PATCH 78/87] chore(tool/create-resource): more improvements --- tools/create-resource/main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/create-resource/main.go b/tools/create-resource/main.go index 339f1a66..c67a3a4a 100644 --- a/tools/create-resource/main.go +++ b/tools/create-resource/main.go @@ -3,6 +3,7 @@ package main import ( "bytes" "fmt" + "github.com/iancoleman/strcase" "os" "strings" "text/template" @@ -69,7 +70,7 @@ type {{.Combined}} struct { func (r *{{.Combined}}) Remove(ctx context.Context) error { _, err := r.svc.Delete{{.ResourceTypeTitle}}(ctx, &{{.Service}}.Delete{{.ResourceTypeTitle}}Input{ - {{.ResourceTypeTitle}}Id: r.id, + {{.ResourceTypeTitle}}Id: r.ID, }) return err } @@ -106,8 +107,8 @@ func main() { Service: strings.ToLower(service), ServiceTitle: caser.String(service), ResourceType: resourceType, - ResourceTypeTitle: caser.String(resourceType), - Combined: fmt.Sprintf("%s%s", caser.String(service), caser.String(resourceType)), + ResourceTypeTitle: strcase.ToCamel(resourceType), + Combined: fmt.Sprintf("%s%s", caser.String(service), strcase.ToCamel(resourceType)), } tmpl, err := template.New("resource").Parse(resourceTemplate) From 24984f68a11e6504766dbd7991abf2a1a6b10fb2 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 11:01:19 -0700 Subject: [PATCH 79/87] docs(resources): auto-generated --- docs/resources/transfer-web-app.md | 30 ++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 31 insertions(+) create mode 100644 docs/resources/transfer-web-app.md diff --git a/docs/resources/transfer-web-app.md b/docs/resources/transfer-web-app.md new file mode 100644 index 00000000..38078214 --- /dev/null +++ b/docs/resources/transfer-web-app.md @@ -0,0 +1,30 @@ +--- +generated: true +--- + +# TransferWebApp + + +## Resource + +```text +TransferWebApp +``` + +## Properties + + +- `ID`: No Description + +!!! note - Using Properties + Properties are what [Filters](../config-filtering.md) are written against in your configuration. You use the property + names to write filters for what you want to **keep** and omit from the nuke process. + +### String Property + +The string representation of a resource is generally the value of the Name, ID or ARN field of the resource. Not all +resources support properties. To write a filter against the string representation, simply omit the `property` field in +the filter. + +The string value is always what is used in the output of the log format when a resource is identified. + diff --git a/mkdocs.yml b/mkdocs.yml index 05a7a309..144221b8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -610,6 +610,7 @@ nav: - Transcribe Vocabulary: resources/transcribe-vocabulary.md - Transfer Server User: resources/transfer-server-user.md - Transfer Server: resources/transfer-server.md + - Transfer Web App: resources/transfer-web-app.md - WAF Regional Byte Match Set Ip: resources/waf-regional-byte-match-set-ip.md - WAF Regional Byte Match Set: resources/waf-regional-byte-match-set.md - WAF Regional Ip Set Ip: resources/waf-regional-ip-set-ip.md From 38bfa1cd7e1649cd00a0acca08ba78f18f25d0c5 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 11:05:35 -0700 Subject: [PATCH 80/87] chore(tools/create-resource): goimports --- tools/create-resource/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/create-resource/main.go b/tools/create-resource/main.go index c67a3a4a..bae0c114 100644 --- a/tools/create-resource/main.go +++ b/tools/create-resource/main.go @@ -3,13 +3,14 @@ package main import ( "bytes" "fmt" - "github.com/iancoleman/strcase" "os" "strings" "text/template" "golang.org/x/text/cases" "golang.org/x/text/language" + + "github.com/iancoleman/strcase" ) const resourceTemplate = `package resources From de5feb28291eb15c25f776bf1843235d413abee9 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 11:07:29 -0700 Subject: [PATCH 81/87] feat(mobile-project): remove resource, no longer supported by AWS --- resources/mobile-projects.go | 77 ------------------------------------ 1 file changed, 77 deletions(-) delete mode 100644 resources/mobile-projects.go diff --git a/resources/mobile-projects.go b/resources/mobile-projects.go deleted file mode 100644 index 48b5a763..00000000 --- a/resources/mobile-projects.go +++ /dev/null @@ -1,77 +0,0 @@ -package resources - -import ( - "context" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/mobile" - - "github.com/ekristen/libnuke/pkg/registry" - "github.com/ekristen/libnuke/pkg/resource" - - "github.com/ekristen/aws-nuke/v3/pkg/nuke" -) - -const MobileProjectResource = "MobileProject" - -func init() { - registry.Register(®istry.Registration{ - Name: MobileProjectResource, - Scope: nuke.Account, - Resource: &MobileProject{}, - Lister: &MobileProjectLister{}, - }) -} - -type MobileProjectLister struct{} - -func (l *MobileProjectLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { - opts := o.(*nuke.ListerOpts) - - svc := mobile.New(opts.Session) - svc.ClientInfo.SigningName = "AWSMobileHubService" - resources := make([]resource.Resource, 0) - - params := &mobile.ListProjectsInput{ - MaxResults: aws.Int64(100), - } - - for { - output, err := svc.ListProjects(params) - if err != nil { - return nil, err - } - - for _, project := range output.Projects { - resources = append(resources, &MobileProject{ - svc: svc, - projectID: project.ProjectId, - }) - } - - if output.NextToken == nil { - break - } - - params.NextToken = output.NextToken - } - - return resources, nil -} - -type MobileProject struct { - svc *mobile.Mobile - projectID *string -} - -func (f *MobileProject) Remove(_ context.Context) error { - _, err := f.svc.DeleteProject(&mobile.DeleteProjectInput{ - ProjectId: f.projectID, - }) - - return err -} - -func (f *MobileProject) String() string { - return *f.projectID -} From d1f2ccc096f9a3610c1088c6e15a12e033b5c369 Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 11:12:39 -0700 Subject: [PATCH 82/87] fix(s3-access-grants-grant): pass account id to delete input --- resources/s3-access-grants-grant.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/s3-access-grants-grant.go b/resources/s3-access-grants-grant.go index 5eb792a8..bc5798df 100644 --- a/resources/s3-access-grants-grant.go +++ b/resources/s3-access-grants-grant.go @@ -48,6 +48,7 @@ func (l *S3AccessGrantsGrantLister) List(ctx context.Context, o interface{}) ([] for _, p := range res.AccessGrantsList { resources = append(resources, &S3AccessGrantsGrant{ svc: svc, + accountID: opts.AccountID, ID: p.AccessGrantId, GrantScope: p.GrantScope, GranteeType: ptr.String(string(p.Grantee.GranteeType)), @@ -61,6 +62,7 @@ func (l *S3AccessGrantsGrantLister) List(ctx context.Context, o interface{}) ([] type S3AccessGrantsGrant struct { svc *s3control.Client + accountID *string ID *string `description:"The ID of the access grant."` GrantScope *string `description:"The scope of the access grant."` GranteeType *string `description:"The type of the grantee, (e.g. IAM)."` @@ -70,6 +72,7 @@ type S3AccessGrantsGrant struct { func (r *S3AccessGrantsGrant) Remove(ctx context.Context) error { _, err := r.svc.DeleteAccessGrant(ctx, &s3control.DeleteAccessGrantInput{ + AccountId: r.accountID, AccessGrantId: r.ID, }) return err From 340043afffc389c939af8cd6e372117975939d68 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:17:03 +0000 Subject: [PATCH 83/87] fix(deps): update module github.com/aws/aws-sdk-go to v1.55.5 --- go.mod | 11 +++++------ go.sum | 6 ++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 715536b7..75baea7f 100644 --- a/go.mod +++ b/go.mod @@ -3,19 +3,23 @@ module github.com/ekristen/aws-nuke/v3 go 1.21.6 require ( - github.com/aws/aws-sdk-go v1.54.20 + github.com/aws/aws-sdk-go v1.55.5 github.com/aws/aws-sdk-go-v2 v1.32.7 github.com/aws/aws-sdk-go-v2/config v1.28.7 github.com/aws/aws-sdk-go-v2/credentials v1.17.48 + github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 + github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 + github.com/aws/aws-sdk-go-v2/service/transfer v1.55.1 github.com/aws/smithy-go v1.22.1 github.com/ekristen/libnuke v0.24.0 github.com/fatih/color v1.18.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 github.com/gotidy/ptr v1.4.0 + github.com/iancoleman/strcase v0.3.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 @@ -32,20 +36,15 @@ require ( github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect - github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect - github.com/aws/aws-sdk-go-v2/service/transfer v1.55.1 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/iancoleman/strcase v0.3.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index b0847562..a23428f6 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/aws/aws-sdk-go v1.54.20 h1:FZ2UcXya7bUkvkpf7TaPmiL7EubK0go1nlXGLRwEsoo= github.com/aws/aws-sdk-go v1.54.20/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= @@ -28,10 +30,10 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQz github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= -github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 h1:xxGbXbGtO/VMz2JqB1UwEDlSchryUss0KmQJSZ0oTUE= -github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1/go.mod h1:6BuUa52of67a+ri/poTH82XiL+rTGQWUPZCmf2cfVHI= github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk= github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= +github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 h1:xxGbXbGtO/VMz2JqB1UwEDlSchryUss0KmQJSZ0oTUE= +github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1/go.mod h1:6BuUa52of67a+ri/poTH82XiL+rTGQWUPZCmf2cfVHI= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 h1:4siT1z3nEVxJq1jZYu1SRoct5xgbKen+ammCuZBZ2zI= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2/go.mod h1:KSO1+erW2SUB6Mw/Qamu1fOT5fn/mzd9G79ENbYqyRQ= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= From a45fa0b0ff317728c5a4c760a1fa25bfbc06714c Mon Sep 17 00:00:00 2001 From: "ekristen-dev[bot]" <169176299+ekristen-dev[bot]@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:17:42 +0000 Subject: [PATCH 84/87] chore: update mocks --- go.mod | 2 ++ go.sum | 4 ++++ mocks/mock_autoscalingiface/mock.go | 2 +- mocks/mock_budgetsiface/mock.go | 2 +- mocks/mock_cloudformationiface/mock.go | 2 +- mocks/mock_cognitoidentityprovideriface/mock.go | 2 +- mocks/mock_dynamodbiface/mock.go | 2 +- mocks/mock_ecsiface/mock.go | 2 +- mocks/mock_elasticacheiface/mock.go | 2 +- mocks/mock_gameliftiface/mock.go | 2 +- mocks/mock_glueiface/mock.go | 2 +- mocks/mock_iamiface/mock.go | 2 +- mocks/mock_kmsiface/mock.go | 2 +- mocks/mock_pinpointsmsvoicev2iface/mock.go | 2 +- mocks/mock_quicksightiface/mock.go | 2 +- mocks/mock_route53iface/mock.go | 2 +- mocks/mock_route53resolveriface/mock.go | 2 +- mocks/mock_sagemakeriface/mock.go | 2 +- mocks/mock_secretsmanageriface/mock.go | 2 +- mocks/mock_servicediscoveryiface/mock.go | 2 +- mocks/mock_sqsiface/mock.go | 2 +- 21 files changed, 25 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 75baea7f..c920f443 100644 --- a/go.mod +++ b/go.mod @@ -55,8 +55,10 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stevenle/topsort v0.2.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.25.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index a23428f6..7f650516 100644 --- a/go.sum +++ b/go.sum @@ -138,6 +138,8 @@ go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJh golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -163,6 +165,8 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/mocks/mock_autoscalingiface/mock.go b/mocks/mock_autoscalingiface/mock.go index ff258ab8..5e99d36c 100644 --- a/mocks/mock_autoscalingiface/mock.go +++ b/mocks/mock_autoscalingiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/autoscaling/autoscalingiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/autoscaling/autoscalingiface/interface.go // Package mock_autoscalingiface is a generated GoMock package. package mock_autoscalingiface diff --git a/mocks/mock_budgetsiface/mock.go b/mocks/mock_budgetsiface/mock.go index 6fe7c07a..3cfbcbcd 100644 --- a/mocks/mock_budgetsiface/mock.go +++ b/mocks/mock_budgetsiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.20/service/budgets/budgetsiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/budgets/budgetsiface/interface.go // Package mock_budgetsiface is a generated GoMock package. package mock_budgetsiface diff --git a/mocks/mock_cloudformationiface/mock.go b/mocks/mock_cloudformationiface/mock.go index e3c8cb00..25d74838 100644 --- a/mocks/mock_cloudformationiface/mock.go +++ b/mocks/mock_cloudformationiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/cloudformation/cloudformationiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/cloudformation/cloudformationiface/interface.go // Package mock_cloudformationiface is a generated GoMock package. package mock_cloudformationiface diff --git a/mocks/mock_cognitoidentityprovideriface/mock.go b/mocks/mock_cognitoidentityprovideriface/mock.go index dfa97a78..a99d8f46 100644 --- a/mocks/mock_cognitoidentityprovideriface/mock.go +++ b/mocks/mock_cognitoidentityprovideriface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.20/service/cognitoidentityprovider/cognitoidentityprovideriface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/cognitoidentityprovider/cognitoidentityprovideriface/interface.go // Package mock_cognitoidentityprovideriface is a generated GoMock package. package mock_cognitoidentityprovideriface diff --git a/mocks/mock_dynamodbiface/mock.go b/mocks/mock_dynamodbiface/mock.go index 8bfe43cf..0f81d40d 100644 --- a/mocks/mock_dynamodbiface/mock.go +++ b/mocks/mock_dynamodbiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/dynamodb/dynamodbiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/dynamodb/dynamodbiface/interface.go // Package mock_dynamodbiface is a generated GoMock package. package mock_dynamodbiface diff --git a/mocks/mock_ecsiface/mock.go b/mocks/mock_ecsiface/mock.go index 59ee7ad3..8bbeca19 100644 --- a/mocks/mock_ecsiface/mock.go +++ b/mocks/mock_ecsiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/ecs/ecsiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/ecs/ecsiface/interface.go // Package mock_ecsiface is a generated GoMock package. package mock_ecsiface diff --git a/mocks/mock_elasticacheiface/mock.go b/mocks/mock_elasticacheiface/mock.go index b94990b7..f7b82d2f 100644 --- a/mocks/mock_elasticacheiface/mock.go +++ b/mocks/mock_elasticacheiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/elasticache/elasticacheiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/elasticache/elasticacheiface/interface.go // Package mock_elasticacheiface is a generated GoMock package. package mock_elasticacheiface diff --git a/mocks/mock_gameliftiface/mock.go b/mocks/mock_gameliftiface/mock.go index 5c4f5a0b..945a21c7 100644 --- a/mocks/mock_gameliftiface/mock.go +++ b/mocks/mock_gameliftiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.20/service/gamelift/gameliftiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/gamelift/gameliftiface/interface.go // Package mock_gameliftiface is a generated GoMock package. package mock_gameliftiface diff --git a/mocks/mock_glueiface/mock.go b/mocks/mock_glueiface/mock.go index 72bd32ec..17d2290d 100644 --- a/mocks/mock_glueiface/mock.go +++ b/mocks/mock_glueiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/glue/glueiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/glue/glueiface/interface.go // Package mock_glueiface is a generated GoMock package. package mock_glueiface diff --git a/mocks/mock_iamiface/mock.go b/mocks/mock_iamiface/mock.go index 4d2c24bf..6ebcb6ab 100644 --- a/mocks/mock_iamiface/mock.go +++ b/mocks/mock_iamiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/iam/iamiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/iam/iamiface/interface.go // Package mock_iamiface is a generated GoMock package. package mock_iamiface diff --git a/mocks/mock_kmsiface/mock.go b/mocks/mock_kmsiface/mock.go index 7cb010c1..48034af5 100644 --- a/mocks/mock_kmsiface/mock.go +++ b/mocks/mock_kmsiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.20/service/kms/kmsiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/kms/kmsiface/interface.go // Package mock_kmsiface is a generated GoMock package. package mock_kmsiface diff --git a/mocks/mock_pinpointsmsvoicev2iface/mock.go b/mocks/mock_pinpointsmsvoicev2iface/mock.go index 7eb1916d..87911fbc 100644 --- a/mocks/mock_pinpointsmsvoicev2iface/mock.go +++ b/mocks/mock_pinpointsmsvoicev2iface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.20/service/pinpointsmsvoicev2/pinpointsmsvoicev2iface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/pinpointsmsvoicev2/pinpointsmsvoicev2iface/interface.go // Package mock_pinpointsmsvoicev2iface is a generated GoMock package. package mock_pinpointsmsvoicev2iface diff --git a/mocks/mock_quicksightiface/mock.go b/mocks/mock_quicksightiface/mock.go index d8b805d8..7a0b639b 100644 --- a/mocks/mock_quicksightiface/mock.go +++ b/mocks/mock_quicksightiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/quicksight/quicksightiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/quicksight/quicksightiface/interface.go // Package mock_quicksightiface is a generated GoMock package. package mock_quicksightiface diff --git a/mocks/mock_route53iface/mock.go b/mocks/mock_route53iface/mock.go index c7d73ef0..bc853083 100644 --- a/mocks/mock_route53iface/mock.go +++ b/mocks/mock_route53iface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.20/service/route53/route53iface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/route53/route53iface/interface.go // Package mock_route53iface is a generated GoMock package. package mock_route53iface diff --git a/mocks/mock_route53resolveriface/mock.go b/mocks/mock_route53resolveriface/mock.go index 7eeabf8f..01c8dc84 100644 --- a/mocks/mock_route53resolveriface/mock.go +++ b/mocks/mock_route53resolveriface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /Users/ekristen/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.20/service/route53resolver/route53resolveriface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/route53resolver/route53resolveriface/interface.go // Package mock_route53resolveriface is a generated GoMock package. package mock_route53resolveriface diff --git a/mocks/mock_sagemakeriface/mock.go b/mocks/mock_sagemakeriface/mock.go index b0e6dee3..5b7636ad 100644 --- a/mocks/mock_sagemakeriface/mock.go +++ b/mocks/mock_sagemakeriface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/sagemaker/sagemakeriface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/sagemaker/sagemakeriface/interface.go // Package mock_sagemakeriface is a generated GoMock package. package mock_sagemakeriface diff --git a/mocks/mock_secretsmanageriface/mock.go b/mocks/mock_secretsmanageriface/mock.go index 52fded1f..0705229b 100644 --- a/mocks/mock_secretsmanageriface/mock.go +++ b/mocks/mock_secretsmanageriface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/secretsmanager/secretsmanageriface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/secretsmanager/secretsmanageriface/interface.go // Package mock_secretsmanageriface is a generated GoMock package. package mock_secretsmanageriface diff --git a/mocks/mock_servicediscoveryiface/mock.go b/mocks/mock_servicediscoveryiface/mock.go index 30c6b6fc..dbd4e756 100644 --- a/mocks/mock_servicediscoveryiface/mock.go +++ b/mocks/mock_servicediscoveryiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/servicediscovery/servicediscoveryiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/servicediscovery/servicediscoveryiface/interface.go // Package mock_servicediscoveryiface is a generated GoMock package. package mock_servicediscoveryiface diff --git a/mocks/mock_sqsiface/mock.go b/mocks/mock_sqsiface/mock.go index cb78567c..2dab2425 100644 --- a/mocks/mock_sqsiface/mock.go +++ b/mocks/mock_sqsiface/mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.54.19/service/sqs/sqsiface/interface.go +// Source: /home/runner/go/pkg/mod/github.com/aws/aws-sdk-go@v1.55.5/service/sqs/sqsiface/interface.go // Package mock_sqsiface is a generated GoMock package. package mock_sqsiface From 0fb1c07bf83bbda0d8190de4adc1ab09446bfb0c Mon Sep 17 00:00:00 2001 From: Erik Kristensen Date: Sat, 4 Jan 2025 11:29:38 -0700 Subject: [PATCH 85/87] chore: go mod tidy --- go.mod | 9 ++++----- go.sum | 28 ++-------------------------- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 715536b7..be39dd5f 100644 --- a/go.mod +++ b/go.mod @@ -7,15 +7,19 @@ require ( github.com/aws/aws-sdk-go-v2 v1.32.7 github.com/aws/aws-sdk-go-v2/config v1.28.7 github.com/aws/aws-sdk-go-v2/credentials v1.17.48 + github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 + github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 + github.com/aws/aws-sdk-go-v2/service/transfer v1.55.1 github.com/aws/smithy-go v1.22.1 github.com/ekristen/libnuke v0.24.0 github.com/fatih/color v1.18.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 github.com/gotidy/ptr v1.4.0 + github.com/iancoleman/strcase v0.3.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 @@ -32,20 +36,15 @@ require ( github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/service/iam v1.38.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect - github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect - github.com/aws/aws-sdk-go-v2/service/transfer v1.55.1 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/iancoleman/strcase v0.3.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index b0847562..7072f371 100644 --- a/go.sum +++ b/go.sum @@ -28,10 +28,10 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQz github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c= -github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 h1:xxGbXbGtO/VMz2JqB1UwEDlSchryUss0KmQJSZ0oTUE= -github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1/go.mod h1:6BuUa52of67a+ri/poTH82XiL+rTGQWUPZCmf2cfVHI= github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk= github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= +github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1 h1:xxGbXbGtO/VMz2JqB1UwEDlSchryUss0KmQJSZ0oTUE= +github.com/aws/aws-sdk-go-v2/service/s3control v1.52.1/go.mod h1:6BuUa52of67a+ri/poTH82XiL+rTGQWUPZCmf2cfVHI= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2 h1:4siT1z3nEVxJq1jZYu1SRoct5xgbKen+ammCuZBZ2zI= github.com/aws/aws-sdk-go-v2/service/ssmquicksetup v1.3.2/go.mod h1:KSO1+erW2SUB6Mw/Qamu1fOT5fn/mzd9G79ENbYqyRQ= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= @@ -52,30 +52,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ekristen/libnuke v0.22.1-0.20241224013520-52c0542b4b2d h1:JHgyOVL4FbtGnbz9i78HUqz47q2MKuSoiS3uL3VkgqQ= -github.com/ekristen/libnuke v0.22.1-0.20241224013520-52c0542b4b2d/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224014659-f83bc5d4d5d5 h1:d3ch1E1VVgWX1UTtQfROnp4P9z43/mkwC/WeFUVRjuM= -github.com/ekristen/libnuke v0.22.1-0.20241224014659-f83bc5d4d5d5/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224015245-2b08d39f8c3d h1:IjUGUL8nNJI9YBbZA97gKpku9Qja5xXoKmGlNQI4h/o= -github.com/ekristen/libnuke v0.22.1-0.20241224015245-2b08d39f8c3d/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224015748-4a35c1b48a4f h1:st1ewH2ViyeCOnD2E15ZGQDSz/gncFwT0fSCqg2b7Yk= -github.com/ekristen/libnuke v0.22.1-0.20241224015748-4a35c1b48a4f/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224020243-7be2a0e103e7 h1:s57amu5jKJcDhu9X307mQfBF/CUewEkvnAxIrlVF3Kw= -github.com/ekristen/libnuke v0.22.1-0.20241224020243-7be2a0e103e7/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224020743-601f40417a76 h1:2iTjRT0tjhPlWzsi+UGWFESwLayKIPTMkteeYjZBLBg= -github.com/ekristen/libnuke v0.22.1-0.20241224020743-601f40417a76/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224020946-0c31c1eee047 h1:OHf7bPVx48sHgdYaC/pTbMGt7LQoGNzZqlz3epGehzU= -github.com/ekristen/libnuke v0.22.1-0.20241224020946-0c31c1eee047/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224021517-58558eca46d9 h1:TKlvpnx9k+hjeTNjquOxRPq47xl0FW4Jf4Wzwx9qZ9c= -github.com/ekristen/libnuke v0.22.1-0.20241224021517-58558eca46d9/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224024221-2402c0d76567 h1:+dAYoLLg2UCwGelfEf13VxoFb66PrpjZfOIccpRYtiE= -github.com/ekristen/libnuke v0.22.1-0.20241224024221-2402c0d76567/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224025404-3a4e531fa303 h1:iFEyPC/lBbaF68Db3mFZOp7xQgXfaKHocg4M17PkE9Q= -github.com/ekristen/libnuke v0.22.1-0.20241224025404-3a4e531fa303/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa h1:5f23sLGBiZRow/3Htpy9srvWTfHdRuhPI/devs4Q08c= -github.com/ekristen/libnuke v0.22.1-0.20241224025840-baf271d9f9fa/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= -github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b h1:muIBxgtxFu+7OfkfH2CD7q9AfcQaYmOVCRsUxFRYERY= -github.com/ekristen/libnuke v0.22.1-0.20241224030010-41dfb426634b/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/ekristen/libnuke v0.24.0 h1:DCzkOwT+n/2G37KoNSlTOU1DoAVcBgtdaJSNpQl6NJw= github.com/ekristen/libnuke v0.24.0/go.mod h1:+hh3UCSxmkfBweQJv9pa5twY82n7MhO4DK+AA+oUoTM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= From d338de189366a1a6622f6398eeddfdc53c65c1b6 Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Mon, 6 Jan 2025 15:30:34 -0600 Subject: [PATCH 86/87] Filter out aws vaults, prioritize cloudformation before iam --- resources/backup-vaults-access-policies.go | 9 +++++++++ resources/iam-role.go | 1 + 2 files changed, 10 insertions(+) diff --git a/resources/backup-vaults-access-policies.go b/resources/backup-vaults-access-policies.go index 2395a78f..315b9893 100644 --- a/resources/backup-vaults-access-policies.go +++ b/resources/backup-vaults-access-policies.go @@ -2,6 +2,8 @@ package resources import ( "context" + "fmt" + "strings" "github.com/ekristen/aws-nuke/v3/pkg/nuke" "github.com/ekristen/libnuke/pkg/registry" @@ -133,3 +135,10 @@ func (b *BackupVaultAccessPolicy) Remove(_ context.Context) error { func (b *BackupVaultAccessPolicy) String() string { return b.backupVaultName } + +func (b *BackupVaultAccessPolicy) Filter() error { + if strings.HasPrefix(b.backupVaultName, "aws/efs/automatic-backup-vault") { + return fmt.Errorf("cannot delete EFS automatic backups backup policy") + } + return nil +} diff --git a/resources/iam-role.go b/resources/iam-role.go index a90a0243..ccc3e8f4 100644 --- a/resources/iam-role.go +++ b/resources/iam-role.go @@ -30,6 +30,7 @@ func init() { Lister: &IAMRoleLister{}, DependsOn: []string{ IAMRolePolicyAttachmentResource, + CloudFormationStackResource, // IAM roles can be used in deletion of CloudFormation stacks }, DeprecatedAliases: []string{ "IamRole", From 13ad24b0bf8d28bea8e37162a405abfe4e82d43c Mon Sep 17 00:00:00 2001 From: bjfish25 Date: Tue, 7 Jan 2025 10:15:22 -0600 Subject: [PATCH 87/87] Merge sagemaker changes to new repo --- go.mod | 1 + go.sum | 2 + resources/sagemaker-algorithms.go | 73 ++++++++++++++++++++++++++++ resources/sagemaker-trainingjobs.go | 74 +++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 resources/sagemaker-algorithms.go create mode 100644 resources/sagemaker-trainingjobs.go diff --git a/go.mod b/go.mod index 31718fdb..28060447 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/kafka v1.38.9 github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 + github.com/aws/aws-sdk-go-v2/service/sagemaker v1.171.0 github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 github.com/aws/aws-sdk-go-v2/service/wafregional v1.25.8 github.com/aws/smithy-go v1.22.1 diff --git a/go.sum b/go.sum index e1a435a0..6cd21491 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7 h1:QylOhJ/Cq8rXpdGY github.com/aws/aws-sdk-go-v2/service/networkfirewall v1.44.7/go.mod h1:JRNqD9oCs0lfhwggQ9try04MIaPU8lJ5XIufAUv49Ho= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1 h1:aOVVZJgWbaH+EJYPvEgkNhCEbXXvH7+oML36oaPK3zE= github.com/aws/aws-sdk-go-v2/service/s3 v1.71.1/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q= +github.com/aws/aws-sdk-go-v2/service/sagemaker v1.171.0 h1:LDOuosm4ZFlLMglxAqO94T5SZ6Jaawa1e2hiUAqWAts= +github.com/aws/aws-sdk-go-v2/service/sagemaker v1.171.0/go.mod h1:hzu5ncs1K7l08GCup8WRVxw/uqgw1BQLDyfVomRM3sY= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= diff --git a/resources/sagemaker-algorithms.go b/resources/sagemaker-algorithms.go new file mode 100644 index 00000000..42b08bdd --- /dev/null +++ b/resources/sagemaker-algorithms.go @@ -0,0 +1,73 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/sagemaker" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" +) + +const SageMakerAlgorithmResource = "SageMakerAlgorithm" + +func init() { + registry.Register(®istry.Registration{ + Name: SageMakerAlgorithmResource, + Scope: nuke.Account, + Resource: &SageMakerAlgorithm{}, + Lister: &SageMakerAlgorithmLister{}, + }) +} + +type SageMakerAlgorithmLister struct{} + +type SageMakerAlgorithm struct { + svc *sagemaker.Client + algorithmName *string +} + +func (l *SageMakerAlgorithmLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := sagemaker.NewFromConfig(*opts.Config) + + resources := make([]resource.Resource, 0) + params := &sagemaker.ListAlgorithmsInput{ + MaxResults: aws.Int32(30), + } + for { + resp, err := svc.ListAlgorithms(ctx, params) + if err != nil { + return nil, err + } + for _, algorithm := range resp.AlgorithmSummaryList { + resources = append(resources, &SageMakerAlgorithm{ + svc: svc, + algorithmName: algorithm.AlgorithmName, + }) + } + if resp.NextToken == nil { + break + } + params.NextToken = resp.NextToken + } + return resources, nil +} + +func (f *SageMakerAlgorithm) Remove(ctx context.Context) error { + _, err := f.svc.DeleteAlgorithm(ctx, &sagemaker.DeleteAlgorithmInput{ + AlgorithmName: f.algorithmName, + }) + return err +} + +func (f *SageMakerAlgorithm) String() string { + return *f.algorithmName +} + +func (f *SageMakerAlgorithm) Properties() types.Properties { + return types.NewPropertiesFromStruct(f) +} diff --git a/resources/sagemaker-trainingjobs.go b/resources/sagemaker-trainingjobs.go new file mode 100644 index 00000000..334e6bcc --- /dev/null +++ b/resources/sagemaker-trainingjobs.go @@ -0,0 +1,74 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/sagemaker" + "github.com/ekristen/aws-nuke/v3/pkg/nuke" + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" +) + +const SageMakerTrainingJobListerResource = "SageMakerTrainingJobLister" + +func init() { + registry.Register(®istry.Registration{ + Name: SageMakerTrainingJobListerResource, + Scope: nuke.Account, + Resource: &SageMakerTrainingJobLister{}, + Lister: &SageMakerTrainingJobLister{}, + }) +} + +type SageMakerTrainingJobLister struct{} + +type SageMakerTrainingJob struct { + svc *sagemaker.Client + trainingJobName *string +} + +func (l *SageMakerTrainingJobLister) List(ctx context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + svc := sagemaker.NewFromConfig(*opts.Config) + + resources := make([]resource.Resource, 0) + params := &sagemaker.ListTrainingJobsInput{ + MaxResults: aws.Int32(30), + } + for { + resp, err := svc.ListTrainingJobs(ctx, params) + if err != nil { + return nil, err + } + for _, trainingJob := range resp.TrainingJobSummaries { + resources = append(resources, &SageMakerTrainingJob{ + svc: svc, + trainingJobName: trainingJob.TrainingJobName, + }) + } + if resp.NextToken == nil { + break + } + params.NextToken = resp.NextToken + } + return resources, nil +} + +func (f *SageMakerTrainingJob) Remove(ctx context.Context) error { + _, err := f.svc.StopTrainingJob(ctx, &sagemaker.StopTrainingJobInput{ + TrainingJobName: f.trainingJobName, + }) + return err +} + +func (f *SageMakerTrainingJob) String() string { + return *f.trainingJobName +} + +func (f *SageMakerTrainingJob) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("TrainingJobName", f.trainingJobName) + return properties +}