diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 31c447e79..91d476edf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,7 @@ updates: day: "tuesday" time: "10:00" timezone: "Europe/Berlin" + groups: + golang: + patterns: + - "*" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 609a73d5d..adf9ac405 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,15 +16,77 @@ jobs: - name: Setup Go uses: actions/setup-go@v3 with: - go-version: '1.20' + go-version: '1.21' - name: Setup tools run: | go install golang.org/x/lint/golint@latest - name: Checkout code uses: actions/checkout@v3 + - name: Check Formatting + run: | + if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then + echo "### Go formatting is off, please execute 'gofmt -w -s .' - see following diff: ###" + gofmt -s -d . + exit 1 + fi - name: Test Project run: | make test - name: Build Project run: | make + + docker_build: + runs-on: ubuntu-22.04 + name: Docker Build + if: github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == 'rebuy-de/aws-nuke' && github.event.pull_request.user.login != 'dependabot[bot]') + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Generate image tags + shell: bash + run: | + if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then + BRANCH="$(echo ${GITHUB_HEAD_REF} | tr '/' '_')" + echo "tags=quay.io/rebuy/aws-nuke:${BRANCH},docker.io/rebuy/aws-nuke:${BRANCH}" >> $GITHUB_OUTPUT + else + echo "tags=quay.io/rebuy/aws-nuke:main,docker.io/rebuy/aws-nuke:main,\ + quay.io/rebuy/aws-nuke:latest,docker.io/rebuy/aws-nuke:latest" >> $GITHUB_OUTPUT + fi + id: generate_tags + + - name: Set up QEMU + if: github.event_name != 'pull_request' + id: qemu + uses: docker/setup-qemu-action@v2 + with: + platforms: arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + install: true + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to Quay.io + uses: docker/login-action@v2 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_PASSWORD }} + + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + push: true + tags: ${{ steps.generate_tags.outputs.tags }} + platforms: ${{ github.event_name != 'pull_request' && 'linux/amd64,linux/arm64' || 'linux/amd64' }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 72d84be4c..248d575b9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -13,7 +13,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v3 with: - go-version: '1.20' + go-version: '1.21' - name: Checkout code uses: actions/checkout@v3 with: diff --git a/Dockerfile b/Dockerfile index 010119599..bcbea03cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20-alpine as builder +FROM golang:1.21-alpine as builder RUN apk add --no-cache git make curl openssl diff --git a/README.md b/README.md index b7b122aa0..0446134f6 100644 --- a/README.md +++ b/README.md @@ -612,10 +612,10 @@ The easiest way of installing it, is to download the latest #### Example for Linux Intel/AMD Download and extract -`$ wget -c https://github.com/rebuy-de/aws-nuke/releases/download/v2.23.0/aws-nuke-v2.23.0-linux-amd64.tar.gz -O - | tar -xz -C $HOME/bin` +`$ wget -c https://github.com/rebuy-de/aws-nuke/releases/download/v2.24.2/aws-nuke-v2.24.2-linux-amd64.tar.gz -O - | tar -xz -C $HOME/bin` Run -`$ aws-nuke-v2.23.0-linux-amd64` +`$ aws-nuke-v2.24.2-linux-amd64` ### Compile from Source @@ -639,7 +639,7 @@ $ docker run \ --rm -it \ -v /full-path/to/nuke-config.yml:/home/aws-nuke/config.yml \ -v /home/user/.aws:/home/aws-nuke/.aws \ - quay.io/rebuy/aws-nuke:v2.23.0 \ + quay.io/rebuy/aws-nuke:v2.24.2 \ --profile default \ --config /home/aws-nuke/config.yml ``` diff --git a/cmd/root.go b/cmd/root.go index 627722ac3..c39b1949b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -69,6 +69,8 @@ func NewRootCommand() *cobra.Command { awsutil.DefaultAWSPartitionID = endpoints.AwsPartitionID case endpoints.UsGovEast1RegionID, endpoints.UsGovWest1RegionID: awsutil.DefaultAWSPartitionID = endpoints.AwsUsGovPartitionID + case endpoints.CnNorth1RegionID, endpoints.CnNorthwest1RegionID: + awsutil.DefaultAWSPartitionID = endpoints.AwsCnPartitionID default: if config.CustomEndpoints.GetRegion(defaultRegion) == nil { err = fmt.Errorf("The custom region '%s' must be specified in the configuration 'endpoints'", defaultRegion) diff --git a/go.mod b/go.mod index e0b410cfa..ed24ca090 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/rebuy-de/aws-nuke/v2 -go 1.19 +go 1.21 require ( - github.com/aws/aws-sdk-go v1.44.295 + github.com/aws/aws-sdk-go v1.44.328 github.com/fatih/color v1.15.0 github.com/golang/mock v1.6.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.3.1 github.com/mb0/glob v0.0.0-20160210091149-1eb79d2de6c4 github.com/pkg/errors v0.9.1 github.com/rebuy-de/rebuy-go-sdk/v4 v4.5.1 diff --git a/go.sum b/go.sum index e679694a0..befb1b715 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/aws/aws-sdk-go v1.44.295 h1:SGjU1+MqttXfRiWHD6WU0DRhaanJgAFY+xIhEaugV8Y= -github.com/aws/aws-sdk-go v1.44.295/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.328 h1:WBwlf8ym9SDQ/GTIBO9eXyvwappKJyOetWJKl4mT7ZU= +github.com/aws/aws-sdk-go v1.44.328/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/cpuguy83/go-md2man/v2 v2.0.2/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= @@ -11,8 +11,8 @@ github.com/gemnasium/logrus-graylog-hook/v3 v3.1.0 h1:SLtCnpI5ZZaz4l7RSatEhppB1B github.com/gemnasium/logrus-graylog-hook/v3 v3.1.0/go.mod h1:wi1zWv9tIvyLSMLCAzgRP+YR24oLVQVBHfPPKjtht44= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -71,6 +71,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -98,6 +99,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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= @@ -112,6 +114,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/filter.go b/pkg/config/filter.go index 61fcbef00..019ed8ebf 100644 --- a/pkg/config/filter.go +++ b/pkg/config/filter.go @@ -84,11 +84,12 @@ func parseDate(input string) (time.Time, error) { return t, nil } - formats := []string{"2006-01-02", + formats := []string{ + "2006-01-02", "2006/01/02", "2006-01-02T15:04:05Z", - "2006-01-02 15:04:05.000 -0700 MST", // Date format used by AWS for CreateTime on ASGs - time.RFC3339Nano, // Format of t.MarshalText() and t.MarshalJSON() + "2006-01-02 15:04:05 -0700 MST", // Date format used by AWS for CreateTime on ASGs + time.RFC3339Nano, // Format of t.MarshalText() and t.MarshalJSON() time.RFC3339, } for _, f := range formats { diff --git a/pkg/config/filter_test.go b/pkg/config/filter_test.go index a26df88c7..25f0d236f 100644 --- a/pkg/config/filter_test.go +++ b/pkg/config/filter_test.go @@ -48,18 +48,22 @@ func TestUnmarshalFilter(t *testing.T) { }, { yaml: `{"type":"dateOlderThan","value":"0"}`, - match: []string{strconv.Itoa(int(future.Unix())), + match: []string{ + strconv.Itoa(int(future.Unix())), future.Format("2006-01-02"), future.Format("2006/01/02"), future.Format("2006-01-02T15:04:05Z"), + future.Format("2006-01-02 15:04:05.000 +0000 UTC"), future.Format(time.RFC3339Nano), future.Format(time.RFC3339), }, - mismatch: []string{"", + mismatch: []string{ + "", strconv.Itoa(int(past.Unix())), past.Format("2006-01-02"), past.Format("2006/01/02"), past.Format("2006-01-02T15:04:05Z"), + past.Format("2006-01-02 15:04:05.14 -0700 MST"), past.Format(time.RFC3339Nano), past.Format(time.RFC3339), }, @@ -98,5 +102,4 @@ func TestUnmarshalFilter(t *testing.T) { } }) } - } diff --git a/resources/appconfig-applications.go b/resources/appconfig-applications.go new file mode 100644 index 000000000..832e49b5f --- /dev/null +++ b/resources/appconfig-applications.go @@ -0,0 +1,53 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type AppConfigApplication struct { + svc *appconfig.AppConfig + id *string + name *string +} + +func init() { + register("AppConfigApplication", ListAppConfigApplications) +} + +func ListAppConfigApplications(sess *session.Session) ([]Resource, error) { + svc := appconfig.New(sess) + resources := []Resource{} + params := &appconfig.ListApplicationsInput{ + MaxResults: aws.Int64(50), + } + err := svc.ListApplicationsPages(params, func(page *appconfig.ListApplicationsOutput, lastPage bool) bool { + for _, item := range page.Items { + resources = append(resources, &AppConfigApplication{ + svc: svc, + id: item.Id, + name: item.Name, + }) + } + return true + }) + if err != nil { + return nil, err + } + return resources, nil +} + +func (f *AppConfigApplication) Remove() error { + _, err := f.svc.DeleteApplication(&appconfig.DeleteApplicationInput{ + ApplicationId: f.id, + }) + return err +} + +func (f *AppConfigApplication) Properties() types.Properties { + return types.NewProperties(). + Set("ID", f.id). + Set("Name", f.name) +} diff --git a/resources/appconfig-configurationprofiles.go b/resources/appconfig-configurationprofiles.go new file mode 100644 index 000000000..ef7b9b0df --- /dev/null +++ b/resources/appconfig-configurationprofiles.go @@ -0,0 +1,70 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" +) + +type AppConfigConfigurationProfile struct { + svc *appconfig.AppConfig + applicationId *string + id *string + name *string +} + +func init() { + register("AppConfigConfigurationProfile", ListAppConfigConfigurationProfiles) +} + +func ListAppConfigConfigurationProfiles(sess *session.Session) ([]Resource, error) { + svc := appconfig.New(sess) + resources := []Resource{} + applications, err := ListAppConfigApplications(sess) + if err != nil { + return nil, err + } + for _, applicationResource := range applications { + application, ok := applicationResource.(*AppConfigApplication) + if !ok { + logrus.Errorf("Unable to cast AppConfigApplication.") + continue + } + params := &appconfig.ListConfigurationProfilesInput{ + ApplicationId: application.id, + MaxResults: aws.Int64(50), + } + err := svc.ListConfigurationProfilesPages(params, func(page *appconfig.ListConfigurationProfilesOutput, lastPage bool) bool { + for _, item := range page.Items { + resources = append(resources, &AppConfigConfigurationProfile{ + svc: svc, + applicationId: application.id, + id: item.Id, + name: item.Name, + }) + } + return true + }) + if err != nil { + return nil, err + } + } + return resources, nil +} + +func (f *AppConfigConfigurationProfile) Remove() error { + _, err := f.svc.DeleteConfigurationProfile(&appconfig.DeleteConfigurationProfileInput{ + ApplicationId: f.applicationId, + ConfigurationProfileId: f.id, + }) + return err +} + +func (f *AppConfigConfigurationProfile) Properties() types.Properties { + return types.NewProperties(). + Set("ApplicationID", f.applicationId). + Set("ID", f.id). + Set("Name", f.name) +} diff --git a/resources/appconfig-deploymentstrategies.go b/resources/appconfig-deploymentstrategies.go new file mode 100644 index 000000000..aedf0b34f --- /dev/null +++ b/resources/appconfig-deploymentstrategies.go @@ -0,0 +1,63 @@ +package resources + +import ( + "fmt" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type AppConfigDeploymentStrategy struct { + svc *appconfig.AppConfig + id *string + name *string +} + +func init() { + register("AppConfigDeploymentStrategy", ListAppConfigDeploymentStrategies) +} + +func ListAppConfigDeploymentStrategies(sess *session.Session) ([]Resource, error) { + svc := appconfig.New(sess) + resources := []Resource{} + params := &appconfig.ListDeploymentStrategiesInput{ + MaxResults: aws.Int64(50), + } + err := svc.ListDeploymentStrategiesPages(params, func(page *appconfig.ListDeploymentStrategiesOutput, lastPage bool) bool { + for _, item := range page.Items { + resources = append(resources, &AppConfigDeploymentStrategy{ + svc: svc, + id: item.Id, + name: item.Name, + }) + } + return true + }) + if err != nil { + return nil, err + } + return resources, nil +} + +func (f *AppConfigDeploymentStrategy) Filter() error { + if strings.HasPrefix(*f.name, "AppConfig.") { + return fmt.Errorf("cannot delete predefined Deployment Strategy") + } + return nil +} + +func (f *AppConfigDeploymentStrategy) Remove() error { + _, err := f.svc.DeleteDeploymentStrategy(&appconfig.DeleteDeploymentStrategyInput{ + DeploymentStrategyId: f.id, + }) + return err +} + +func (f *AppConfigDeploymentStrategy) Properties() types.Properties { + return types.NewProperties(). + Set("ID", f.id). + Set("Name", f.name) +} diff --git a/resources/appconfig-environments.go b/resources/appconfig-environments.go new file mode 100644 index 000000000..093f67046 --- /dev/null +++ b/resources/appconfig-environments.go @@ -0,0 +1,70 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" +) + +type AppConfigEnvironment struct { + svc *appconfig.AppConfig + applicationId *string + id *string + name *string +} + +func init() { + register("AppConfigEnvironment", ListAppConfigEnvironments) +} + +func ListAppConfigEnvironments(sess *session.Session) ([]Resource, error) { + svc := appconfig.New(sess) + resources := []Resource{} + applications, err := ListAppConfigApplications(sess) + if err != nil { + return nil, err + } + for _, applicationResource := range applications { + application, ok := applicationResource.(*AppConfigApplication) + if !ok { + logrus.Errorf("Unable to cast AppConfigApplication.") + continue + } + params := &appconfig.ListEnvironmentsInput{ + ApplicationId: application.id, + MaxResults: aws.Int64(50), + } + err := svc.ListEnvironmentsPages(params, func(page *appconfig.ListEnvironmentsOutput, lastPage bool) bool { + for _, item := range page.Items { + resources = append(resources, &AppConfigEnvironment{ + svc: svc, + applicationId: application.id, + id: item.Id, + name: item.Name, + }) + } + return true + }) + if err != nil { + return nil, err + } + } + return resources, nil +} + +func (f *AppConfigEnvironment) Remove() error { + _, err := f.svc.DeleteEnvironment(&appconfig.DeleteEnvironmentInput{ + ApplicationId: f.applicationId, + EnvironmentId: f.id, + }) + return err +} + +func (f *AppConfigEnvironment) Properties() types.Properties { + return types.NewProperties(). + Set("ApplicationID", f.applicationId). + Set("ID", f.id). + Set("Name", f.name) +} diff --git a/resources/appconfig-hostedconfigurationversions.go b/resources/appconfig-hostedconfigurationversions.go new file mode 100644 index 000000000..e3fe4d4b5 --- /dev/null +++ b/resources/appconfig-hostedconfigurationversions.go @@ -0,0 +1,72 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/appconfig" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" +) + +type AppConfigHostedConfigurationVersion struct { + svc *appconfig.AppConfig + applicationId *string + configurationProfileId *string + versionNumber *int64 +} + +func init() { + register("AppConfigHostedConfigurationVersion", ListAppConfigHostedConfigurationVersions) +} + +func ListAppConfigHostedConfigurationVersions(sess *session.Session) ([]Resource, error) { + svc := appconfig.New(sess) + resources := []Resource{} + configurationProfiles, err := ListAppConfigConfigurationProfiles(sess) + if err != nil { + return nil, err + } + for _, configurationProfileResource := range configurationProfiles { + configurationProfile, ok := configurationProfileResource.(*AppConfigConfigurationProfile) + if !ok { + logrus.Errorf("Unable to cast AppConfigConfigurationProfile.") + continue + } + params := &appconfig.ListHostedConfigurationVersionsInput{ + ApplicationId: configurationProfile.applicationId, + ConfigurationProfileId: configurationProfile.id, + MaxResults: aws.Int64(50), + } + err := svc.ListHostedConfigurationVersionsPages(params, func(page *appconfig.ListHostedConfigurationVersionsOutput, lastPage bool) bool { + for _, item := range page.Items { + resources = append(resources, &AppConfigHostedConfigurationVersion{ + svc: svc, + applicationId: configurationProfile.applicationId, + configurationProfileId: configurationProfile.id, + versionNumber: item.VersionNumber, + }) + } + return true + }) + if err != nil { + return nil, err + } + } + return resources, nil +} + +func (f *AppConfigHostedConfigurationVersion) Remove() error { + _, err := f.svc.DeleteHostedConfigurationVersion(&appconfig.DeleteHostedConfigurationVersionInput{ + ApplicationId: f.applicationId, + ConfigurationProfileId: f.configurationProfileId, + VersionNumber: f.versionNumber, + }) + return err +} + +func (f *AppConfigHostedConfigurationVersion) Properties() types.Properties { + return types.NewProperties(). + Set("ApplicationID", f.applicationId). + Set("ConfigurationProfileID", f.configurationProfileId). + Set("VersionNumber", f.versionNumber) +} diff --git a/resources/autoscaling-groups.go b/resources/autoscaling-groups.go index 5f41ec7d9..94aff114f 100644 --- a/resources/autoscaling-groups.go +++ b/resources/autoscaling-groups.go @@ -1,6 +1,8 @@ package resources import ( + "time" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/autoscaling" @@ -27,7 +29,6 @@ func ListAutoscalingGroups(s *session.Session) ([]Resource, error) { } return !lastPage }) - if err != nil { return nil, err } @@ -65,7 +66,7 @@ func (asg *AutoScalingGroup) Properties() types.Properties { properties.SetTag(tag.Key, tag.Value) } - properties.Set("CreatedTime", asg.group.CreatedTime) + properties.Set("CreatedTime", asg.group.CreatedTime.Format(time.RFC3339)) properties.Set("Name", asg.group.AutoScalingGroupName) return properties diff --git a/resources/batch-computeenvironments.go b/resources/batch-computeenvironments.go index b9908d3e8..166574c2f 100644 --- a/resources/batch-computeenvironments.go +++ b/resources/batch-computeenvironments.go @@ -31,7 +31,7 @@ func ListBatchComputeEnvironments(sess *session.Session) ([]Resource, error) { for _, computeEnvironment := range output.ComputeEnvironments { resources = append(resources, &BatchComputeEnvironment{ - svc: svc, + svc: svc, computeEnvironmentName: computeEnvironment.ComputeEnvironmentName, }) } diff --git a/resources/batch-computeenvironmentstates.go b/resources/batch-computeenvironmentstates.go index cf867b16c..866092e74 100644 --- a/resources/batch-computeenvironmentstates.go +++ b/resources/batch-computeenvironmentstates.go @@ -35,9 +35,9 @@ func ListBatchComputeEnvironmentStates(sess *session.Session) ([]Resource, error for _, computeEnvironment := range output.ComputeEnvironments { resources = append(resources, &BatchComputeEnvironmentState{ - svc: svc, + svc: svc, computeEnvironmentName: computeEnvironment.ComputeEnvironmentName, - state: computeEnvironment.State, + state: computeEnvironment.State, }) } diff --git a/resources/billing-costandusagereports.go b/resources/billing-costandusagereports.go index b0148cbdf..2a81fe26b 100644 --- a/resources/billing-costandusagereports.go +++ b/resources/billing-costandusagereports.go @@ -39,11 +39,11 @@ func ListBillingCostandUsageReports(sess *session.Session) ([]Resource, error) { resources := []Resource{} for _, report := range reports { resources = append(resources, &BillingCostandUsageReport{ - svc: svc, - reportName: report.ReportName, - s3Bucket: report.S3Bucket, - s3Prefix: report.S3Prefix, - s3Region: report.S3Region, + svc: svc, + reportName: report.ReportName, + s3Bucket: report.S3Bucket, + s3Prefix: report.S3Prefix, + s3Region: report.S3Region, }) } diff --git a/resources/cloudfront-key-groups.go b/resources/cloudfront-key-groups.go new file mode 100644 index 000000000..8a790ed37 --- /dev/null +++ b/resources/cloudfront-key-groups.go @@ -0,0 +1,74 @@ +package resources + +import ( + "time" + + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type CloudFrontKeyGroup struct { + svc *cloudfront.CloudFront + ID *string + name *string + lastModifiedTime *time.Time +} + +func init() { + register("CloudFrontKeyGroup", ListCloudFrontKeyGroups) +} + +func ListCloudFrontKeyGroups(sess *session.Session) ([]Resource, error) { + svc := cloudfront.New(sess) + resources := []Resource{} + params := &cloudfront.ListKeyGroupsInput{} + + for { + resp, err := svc.ListKeyGroups(params) + if err != nil { + return nil, err + } + + for _, item := range resp.KeyGroupList.Items { + resources = append(resources, &CloudFrontKeyGroup{ + svc: svc, + ID: item.KeyGroup.Id, + name: item.KeyGroup.KeyGroupConfig.Name, + lastModifiedTime: item.KeyGroup.LastModifiedTime, + }) + } + + if resp.KeyGroupList.NextMarker == nil { + break + } + + params.Marker = resp.KeyGroupList.NextMarker + } + + return resources, nil +} + +func (f *CloudFrontKeyGroup) Remove() error { + resp, err := f.svc.GetKeyGroup(&cloudfront.GetKeyGroupInput{ + Id: f.ID, + }) + if err != nil { + return err + } + + _, err = f.svc.DeleteKeyGroup(&cloudfront.DeleteKeyGroupInput{ + Id: f.ID, + IfMatch: resp.ETag, + }) + + return err +} + +func (f *CloudFrontKeyGroup) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("ID", f.ID) + properties.Set("Name", f.name) + properties.Set("LastModifiedTime", f.lastModifiedTime.Format(time.RFC3339)) + return properties +} diff --git a/resources/cloudfront-origin-access-identities.go b/resources/cloudfront-origin-access-identities.go index 375121f60..943886ad6 100644 --- a/resources/cloudfront-origin-access-identities.go +++ b/resources/cloudfront-origin-access-identities.go @@ -1,58 +1,58 @@ package resources import ( - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudfront" "github.com/rebuy-de/aws-nuke/v2/pkg/types" ) type CloudFrontOriginAccessIdentity struct { - svc *cloudfront.CloudFront - ID *string + svc *cloudfront.CloudFront + ID *string } func init() { - register("CloudFrontOriginAccessIdentity", ListCloudFrontOriginAccessIdentities) + register("CloudFrontOriginAccessIdentity", ListCloudFrontOriginAccessIdentities) } func ListCloudFrontOriginAccessIdentities(sess *session.Session) ([]Resource, error) { - svc := cloudfront.New(sess) - resources := []Resource{} - - for { - resp, err := svc.ListCloudFrontOriginAccessIdentities(nil) - if err != nil { - return nil, err - } - - for _, item := range resp.CloudFrontOriginAccessIdentityList.Items { - resources = append(resources,&CloudFrontOriginAccessIdentity{ - svc: svc, - ID: item.Id, - }) - } - return resources, nil - } + svc := cloudfront.New(sess) + resources := []Resource{} + + for { + resp, err := svc.ListCloudFrontOriginAccessIdentities(nil) + if err != nil { + return nil, err + } + + for _, item := range resp.CloudFrontOriginAccessIdentityList.Items { + resources = append(resources, &CloudFrontOriginAccessIdentity{ + svc: svc, + ID: item.Id, + }) + } + return resources, nil + } } func (f *CloudFrontOriginAccessIdentity) Remove() error { - resp, err := f.svc.GetCloudFrontOriginAccessIdentity(&cloudfront.GetCloudFrontOriginAccessIdentityInput{ - Id: f.ID, - }) - if err != nil { - return err - } - - _, err = f.svc.DeleteCloudFrontOriginAccessIdentity(&cloudfront.DeleteCloudFrontOriginAccessIdentityInput{ - Id: f.ID, - IfMatch: resp.ETag, - }) - - return err + resp, err := f.svc.GetCloudFrontOriginAccessIdentity(&cloudfront.GetCloudFrontOriginAccessIdentityInput{ + Id: f.ID, + }) + if err != nil { + return err + } + + _, err = f.svc.DeleteCloudFrontOriginAccessIdentity(&cloudfront.DeleteCloudFrontOriginAccessIdentityInput{ + Id: f.ID, + IfMatch: resp.ETag, + }) + + return err } func (f *CloudFrontOriginAccessIdentity) Properties() types.Properties { - properties := types.NewProperties() - properties.Set("ID", f.ID) - return properties + properties := types.NewProperties() + properties.Set("ID", f.ID) + return properties } diff --git a/resources/cloudfront-origin-request-policy.go b/resources/cloudfront-origin-request-policy.go new file mode 100644 index 000000000..13e24718b --- /dev/null +++ b/resources/cloudfront-origin-request-policy.go @@ -0,0 +1,68 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type CloudFrontOriginRequestPolicy struct { + svc *cloudfront.CloudFront + ID *string +} + +func init() { + register("CloudFrontOriginRequestPolicy", ListCloudFrontOriginRequestPolicies) +} + +func ListCloudFrontOriginRequestPolicies(sess *session.Session) ([]Resource, error) { + svc := cloudfront.New(sess) + resources := []Resource{} + params := &cloudfront.ListOriginRequestPoliciesInput{} + + for { + resp, err := svc.ListOriginRequestPolicies(params) + if err != nil { + return nil, err + } + + for _, item := range resp.OriginRequestPolicyList.Items { + if *item.Type == "custom" { + resources = append(resources, &CloudFrontOriginRequestPolicy{ + svc: svc, + ID: item.OriginRequestPolicy.Id, + }) + } + } + + if resp.OriginRequestPolicyList.NextMarker == nil { + break + } + + params.Marker = resp.OriginRequestPolicyList.NextMarker + } + + return resources, nil +} + +func (f *CloudFrontOriginRequestPolicy) Remove() error { + resp, err := f.svc.GetOriginRequestPolicy(&cloudfront.GetOriginRequestPolicyInput{ + Id: f.ID, + }) + if err != nil { + return err + } + + _, err = f.svc.DeleteOriginRequestPolicy(&cloudfront.DeleteOriginRequestPolicyInput{ + Id: f.ID, + IfMatch: resp.ETag, + }) + + return err +} + +func (f *CloudFrontOriginRequestPolicy) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("ID", f.ID) + return properties +} diff --git a/resources/cloudfront-public-keys.go b/resources/cloudfront-public-keys.go new file mode 100644 index 000000000..3c5cd6e7b --- /dev/null +++ b/resources/cloudfront-public-keys.go @@ -0,0 +1,74 @@ +package resources + +import ( + "time" + + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudfront" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type CloudFrontPublicKey struct { + svc *cloudfront.CloudFront + ID *string + name *string + createdTime *time.Time +} + +func init() { + register("CloudFrontPublicKey", ListCloudFrontPublicKeys) +} + +func ListCloudFrontPublicKeys(sess *session.Session) ([]Resource, error) { + svc := cloudfront.New(sess) + resources := []Resource{} + params := &cloudfront.ListPublicKeysInput{} + + for { + resp, err := svc.ListPublicKeys(params) + if err != nil { + return nil, err + } + + for _, item := range resp.PublicKeyList.Items { + resources = append(resources, &CloudFrontPublicKey{ + svc: svc, + ID: item.Id, + name: item.Name, + createdTime: item.CreatedTime, + }) + } + + if resp.PublicKeyList.NextMarker == nil { + break + } + + params.Marker = resp.PublicKeyList.NextMarker + } + + return resources, nil +} + +func (f *CloudFrontPublicKey) Remove() error { + resp, err := f.svc.GetPublicKey(&cloudfront.GetPublicKeyInput{ + Id: f.ID, + }) + if err != nil { + return err + } + + _, err = f.svc.DeletePublicKey(&cloudfront.DeletePublicKeyInput{ + Id: f.ID, + IfMatch: resp.ETag, + }) + + return err +} + +func (f *CloudFrontPublicKey) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("ID", f.ID) + properties.Set("Name", f.name) + properties.Set("CreatedTime", f.createdTime.Format(time.RFC3339)) + return properties +} diff --git a/resources/codestar-connections.go b/resources/codestar-connections.go index 1d49201cd..b3d454d13 100644 --- a/resources/codestar-connections.go +++ b/resources/codestar-connections.go @@ -8,10 +8,10 @@ import ( ) type CodeStarConnection struct { - svc *codestarconnections.CodeStarConnections - connectionARN *string - connectionName *string - providerType *string + svc *codestarconnections.CodeStarConnections + connectionARN *string + connectionName *string + providerType *string } func init() { @@ -34,10 +34,10 @@ func ListCodeStarConnections(sess *session.Session) ([]Resource, error) { for _, connection := range output.Connections { resources = append(resources, &CodeStarConnection{ - svc: svc, - connectionARN: connection.ConnectionArn, - connectionName: connection.ConnectionName, - providerType: connection.ProviderType, + svc: svc, + connectionARN: connection.ConnectionArn, + connectionName: connection.ConnectionName, + providerType: connection.ProviderType, }) } @@ -68,7 +68,6 @@ func (f *CodeStarConnection) Properties() types.Properties { return properties } - func (f *CodeStarConnection) String() string { return *f.connectionName } diff --git a/resources/comprehend_dominant_language_detection_job.go b/resources/comprehend_dominant_language_detection_job.go index 26bcf8609..6a4a22ba5 100644 --- a/resources/comprehend_dominant_language_detection_job.go +++ b/resources/comprehend_dominant_language_detection_job.go @@ -22,6 +22,11 @@ func ListComprehendDominantLanguageDetectionJobs(sess *session.Session) ([]Resou return nil, err } for _, dominantLanguageDetectionJob := range resp.DominantLanguageDetectionJobPropertiesList { + switch *dominantLanguageDetectionJob.JobStatus { + case "STOPPED", "FAILED", "COMPLETED": + // if the job has already been stopped, failed, or completed; do not try to stop it again + continue + } resources = append(resources, &ComprehendDominantLanguageDetectionJob{ svc: svc, dominantLanguageDetectionJob: dominantLanguageDetectionJob, diff --git a/resources/comprehend_entities_detection_job.go b/resources/comprehend_entities_detection_job.go index 3ae804849..8baecc7a6 100644 --- a/resources/comprehend_entities_detection_job.go +++ b/resources/comprehend_entities_detection_job.go @@ -22,9 +22,9 @@ func ListComprehendEntitiesDetectionJobs(sess *session.Session) ([]Resource, err return nil, err } for _, entitiesDetectionJob := range resp.EntitiesDetectionJobPropertiesList { - if *entitiesDetectionJob.JobStatus == "STOPPED" || - *entitiesDetectionJob.JobStatus == "FAILED" { - // if the job has already been stopped, do not try to delete it again + switch *entitiesDetectionJob.JobStatus { + case "STOPPED", "FAILED", "COMPLETED": + // if the job has already been stopped, failed, or completed; do not try to stop it again continue } resources = append(resources, &ComprehendEntitiesDetectionJob{ diff --git a/resources/comprehend_events_detection_job.go b/resources/comprehend_events_detection_job.go new file mode 100644 index 000000000..e8b303eb1 --- /dev/null +++ b/resources/comprehend_events_detection_job.go @@ -0,0 +1,72 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/comprehend" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +func init() { + register("ComprehendEventsDetectionJob", ListComprehendEventsDetectionJobs) +} + +func ListComprehendEventsDetectionJobs(sess *session.Session) ([]Resource, error) { + svc := comprehend.New(sess) + + params := &comprehend.ListEventsDetectionJobsInput{} + resources := make([]Resource, 0) + + for { + resp, err := svc.ListEventsDetectionJobs(params) + if err != nil { + return nil, err + } + for _, eventsDetectionJob := range resp.EventsDetectionJobPropertiesList { + switch *eventsDetectionJob.JobStatus { + case "STOPPED", "FAILED", "COMPLETED": + // if the job has already been stopped, failed, or completed; do not try to stop it again + continue + } + resources = append(resources, &ComprehendEventsDetectionJob{ + svc: svc, + eventsDetectionJob: eventsDetectionJob, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +type ComprehendEventsDetectionJob struct { + svc *comprehend.Comprehend + eventsDetectionJob *comprehend.EventsDetectionJobProperties +} + +func (ce *ComprehendEventsDetectionJob) Remove() error { + _, err := ce.svc.StopEventsDetectionJob(&comprehend.StopEventsDetectionJobInput{ + JobId: ce.eventsDetectionJob.JobId, + }) + return err +} + +func (ce *ComprehendEventsDetectionJob) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("JobName", ce.eventsDetectionJob.JobName) + properties.Set("JobId", ce.eventsDetectionJob.JobId) + + return properties +} + +func (ce *ComprehendEventsDetectionJob) String() string { + if ce.eventsDetectionJob.JobName == nil { + return "Unnamed job" + } else { + return *ce.eventsDetectionJob.JobName + } +} diff --git a/resources/comprehend_key_phrases_detection_job.go b/resources/comprehend_key_phrases_detection_job.go index 3a78d9633..6c3009ec9 100644 --- a/resources/comprehend_key_phrases_detection_job.go +++ b/resources/comprehend_key_phrases_detection_job.go @@ -22,6 +22,11 @@ func ListComprehendKeyPhrasesDetectionJobs(sess *session.Session) ([]Resource, e return nil, err } for _, keyPhrasesDetectionJob := range resp.KeyPhrasesDetectionJobPropertiesList { + switch *keyPhrasesDetectionJob.JobStatus { + case "STOPPED", "FAILED", "COMPLETED": + // if the job has already been stopped, failed, or completed; do not try to stop it again + continue + } resources = append(resources, &ComprehendKeyPhrasesDetectionJob{ svc: svc, keyPhrasesDetectionJob: keyPhrasesDetectionJob, diff --git a/resources/comprehend_pii_entities_detection_job.go b/resources/comprehend_pii_entities_detection_job.go new file mode 100644 index 000000000..6d9234612 --- /dev/null +++ b/resources/comprehend_pii_entities_detection_job.go @@ -0,0 +1,72 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/comprehend" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +func init() { + register("ComprehendPiiEntititesDetectionJob", ListComprehendPiiEntitiesDetectionJobs) +} + +func ListComprehendPiiEntitiesDetectionJobs(sess *session.Session) ([]Resource, error) { + svc := comprehend.New(sess) + + params := &comprehend.ListPiiEntitiesDetectionJobsInput{} + resources := make([]Resource, 0) + + for { + resp, err := svc.ListPiiEntitiesDetectionJobs(params) + if err != nil { + return nil, err + } + for _, piiEntititesDetectionJob := range resp.PiiEntitiesDetectionJobPropertiesList { + switch *piiEntititesDetectionJob.JobStatus { + case "STOPPED", "FAILED", "COMPLETED": + // if the job has already been stopped, failed, or completed; do not try to stop it again + continue + } + resources = append(resources, &ComprehendPiiEntitiesDetectionJob{ + svc: svc, + piiEntititesDetectionJob: piiEntititesDetectionJob, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +type ComprehendPiiEntitiesDetectionJob struct { + svc *comprehend.Comprehend + piiEntititesDetectionJob *comprehend.PiiEntitiesDetectionJobProperties +} + +func (ce *ComprehendPiiEntitiesDetectionJob) Remove() error { + _, err := ce.svc.StopPiiEntitiesDetectionJob(&comprehend.StopPiiEntitiesDetectionJobInput{ + JobId: ce.piiEntititesDetectionJob.JobId, + }) + return err +} + +func (ce *ComprehendPiiEntitiesDetectionJob) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("JobName", ce.piiEntititesDetectionJob.JobName) + properties.Set("JobId", ce.piiEntititesDetectionJob.JobId) + + return properties +} + +func (ce *ComprehendPiiEntitiesDetectionJob) String() string { + if ce.piiEntititesDetectionJob.JobName == nil { + return "Unnamed job" + } else { + return *ce.piiEntititesDetectionJob.JobName + } +} diff --git a/resources/comprehend_sentiment_detection_job.go b/resources/comprehend_sentiment_detection_job.go index eddd20419..835e82339 100644 --- a/resources/comprehend_sentiment_detection_job.go +++ b/resources/comprehend_sentiment_detection_job.go @@ -22,9 +22,9 @@ func ListComprehendSentimentDetectionJobs(sess *session.Session) ([]Resource, er return nil, err } for _, sentimentDetectionJob := range resp.SentimentDetectionJobPropertiesList { - if *sentimentDetectionJob.JobStatus == "STOPPED" || - *sentimentDetectionJob.JobStatus == "FAILED" { - // if the job has already been stopped, do not try to delete it again + switch *sentimentDetectionJob.JobStatus { + case "STOPPED", "FAILED", "COMPLETED": + // if the job has already been stopped, failed, or completed; do not try to stop it again continue } resources = append(resources, &ComprehendSentimentDetectionJob{ diff --git a/resources/comprehend_targeted_sentiment_detection_job.go b/resources/comprehend_targeted_sentiment_detection_job.go new file mode 100644 index 000000000..b60b39b6e --- /dev/null +++ b/resources/comprehend_targeted_sentiment_detection_job.go @@ -0,0 +1,72 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/comprehend" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +func init() { + register("ComprehendTargetedSentimentDetectionJob", ListComprehendTargetedSentimentDetectionJobs) +} + +func ListComprehendTargetedSentimentDetectionJobs(sess *session.Session) ([]Resource, error) { + svc := comprehend.New(sess) + + params := &comprehend.ListTargetedSentimentDetectionJobsInput{} + resources := make([]Resource, 0) + + for { + resp, err := svc.ListTargetedSentimentDetectionJobs(params) + if err != nil { + return nil, err + } + for _, targetedSentimentDetectionJob := range resp.TargetedSentimentDetectionJobPropertiesList { + switch *targetedSentimentDetectionJob.JobStatus { + case "STOPPED", "FAILED", "COMPLETED": + // if the job has already been stopped, failed, or completed; do not try to stop it again + continue + } + resources = append(resources, &ComprehendTargetedSentimentDetectionJob{ + svc: svc, + targetedSentimentDetectionJob: targetedSentimentDetectionJob, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +type ComprehendTargetedSentimentDetectionJob struct { + svc *comprehend.Comprehend + targetedSentimentDetectionJob *comprehend.TargetedSentimentDetectionJobProperties +} + +func (ce *ComprehendTargetedSentimentDetectionJob) Remove() error { + _, err := ce.svc.StopTargetedSentimentDetectionJob(&comprehend.StopTargetedSentimentDetectionJobInput{ + JobId: ce.targetedSentimentDetectionJob.JobId, + }) + return err +} + +func (ce *ComprehendTargetedSentimentDetectionJob) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("JobName", ce.targetedSentimentDetectionJob.JobName) + properties.Set("JobId", ce.targetedSentimentDetectionJob.JobId) + + return properties +} + +func (ce *ComprehendTargetedSentimentDetectionJob) String() string { + if ce.targetedSentimentDetectionJob.JobName == nil { + return "Unnamed job" + } else { + return *ce.targetedSentimentDetectionJob.JobName + } +} diff --git a/resources/configservice-configurationrecorders.go b/resources/configservice-configurationrecorders.go index 669bbfe31..c0d958098 100644 --- a/resources/configservice-configurationrecorders.go +++ b/resources/configservice-configurationrecorders.go @@ -26,7 +26,7 @@ func ListConfigServiceConfigurationRecorders(sess *session.Session) ([]Resource, resources := make([]Resource, 0) for _, configurationRecorder := range resp.ConfigurationRecorders { resources = append(resources, &ConfigServiceConfigurationRecorder{ - svc: svc, + svc: svc, configurationRecorderName: configurationRecorder.Name, }) } diff --git a/resources/dax-subnetgroups.go b/resources/dax-subnetgroups.go index c451f6e8e..81fc63c59 100644 --- a/resources/dax-subnetgroups.go +++ b/resources/dax-subnetgroups.go @@ -1,10 +1,10 @@ package resources import ( + "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dax" - "fmt" ) type DAXSubnetGroup struct { diff --git a/resources/dynamodb-tables.go b/resources/dynamodb-tables.go index 04a57fa17..cb191e610 100644 --- a/resources/dynamodb-tables.go +++ b/resources/dynamodb-tables.go @@ -34,8 +34,8 @@ func ListDynamoDBTables(sess *session.Session) ([]Resource, error) { } resources = append(resources, &DynamoDBTable{ - svc: svc, - id: *tableName, + svc: svc, + id: *tableName, tags: tags, }) } @@ -65,7 +65,7 @@ func GetTableTags(svc *dynamodb.DynamoDB, tableName *string) ([]*dynamodb.Tag, e return make([]*dynamodb.Tag, 0), err } - tags, err := svc.ListTagsOfResource(&dynamodb.ListTagsOfResourceInput{ + tags, err := svc.ListTagsOfResource(&dynamodb.ListTagsOfResourceInput{ ResourceArn: result.Table.TableArn, }) @@ -77,17 +77,16 @@ func GetTableTags(svc *dynamodb.DynamoDB, tableName *string) ([]*dynamodb.Tag, e } func (i *DynamoDBTable) Properties() types.Properties { - properties := types.NewProperties() - properties.Set("Identifier", i.id) + properties := types.NewProperties() + properties.Set("Identifier", i.id) - for _, tag := range i.tags { - properties.SetTag(tag.Key, tag.Value) - } + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } - return properties + return properties } - func (i *DynamoDBTable) String() string { return i.id } diff --git a/resources/ec2-default-security-group-rules.go b/resources/ec2-default-security-group-rules.go index 71903cc08..c3fea3aa0 100644 --- a/resources/ec2-default-security-group-rules.go +++ b/resources/ec2-default-security-group-rules.go @@ -12,6 +12,7 @@ type EC2DefaultSecurityGroupRule struct { id *string groupId *string isEgress *bool + tags []*ec2.Tag } func init() { @@ -62,6 +63,7 @@ func ListEC2SecurityGroupRules(sess *session.Session) ([]Resource, error) { id: rule.SecurityGroupRuleId, groupId: rule.GroupId, isEgress: rule.IsEgress, + tags: rule.Tags, }) } return !lastPage @@ -103,6 +105,9 @@ func (r *EC2DefaultSecurityGroupRule) Remove() error { func (r *EC2DefaultSecurityGroupRule) Properties() types.Properties { properties := types.NewProperties() + for _, tagValue := range r.tags { + properties.SetTag(tagValue.Key, tagValue.Value) + } properties.Set("SecurityGroupId", r.groupId) return properties } diff --git a/resources/ec2-instance-connect-endpoint.go b/resources/ec2-instance-connect-endpoint.go new file mode 100644 index 000000000..70e584d19 --- /dev/null +++ b/resources/ec2-instance-connect-endpoint.go @@ -0,0 +1,98 @@ +package resources + +import ( + "time" + + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type EC2InstanceConnectEndpoint struct { + svc *ec2.EC2 + az *string + createdAt *time.Time + dnsName *string + fipsDNSName *string + id *string + ownerID *string + state *string + subnetID *string + tags []*ec2.Tag + vpcID *string +} + +func init() { + register("EC2InstanceConnectEndpoint", ListEC2InstanceConnectEndpoints) +} + +func ListEC2InstanceConnectEndpoints(sess *session.Session) ([]Resource, error) { + svc := ec2.New(sess) + params := &ec2.DescribeInstanceConnectEndpointsInput{} + resources := make([]Resource, 0) + for { + resp, err := svc.DescribeInstanceConnectEndpoints(params) + if err != nil { + return nil, err + } + + for _, endpoint := range resp.InstanceConnectEndpoints { + resources = append(resources, &EC2InstanceConnectEndpoint{ + svc: svc, + az: endpoint.AvailabilityZone, + createdAt: endpoint.CreatedAt, + dnsName: endpoint.DnsName, + fipsDNSName: endpoint.FipsDnsName, + id: endpoint.InstanceConnectEndpointId, + ownerID: endpoint.OwnerId, + state: endpoint.State, + subnetID: endpoint.SubnetId, + tags: endpoint.Tags, + vpcID: endpoint.VpcId, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +func (i *EC2InstanceConnectEndpoint) Remove() error { + params := &ec2.DeleteInstanceConnectEndpointInput{ + InstanceConnectEndpointId: i.id, + } + + _, err := i.svc.DeleteInstanceConnectEndpoint(params) + if err != nil { + return err + } + return nil +} + +func (i *EC2InstanceConnectEndpoint) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("ID", i.id) + properties.Set("AZ", i.az) + properties.Set("CreatedAt", i.createdAt.Format(time.RFC3339)) + properties.Set("DNSName", i.dnsName) + properties.Set("FIPSDNSName", i.fipsDNSName) + properties.Set("OwnerID", i.ownerID) + properties.Set("State", i.state) + properties.Set("SubnetID", i.subnetID) + properties.Set("VPCID", i.vpcID) + + for _, tagValue := range i.tags { + properties.SetTag(tagValue.Key, tagValue.Value) + } + + return properties +} + +func (i *EC2InstanceConnectEndpoint) String() string { + return *i.id +} diff --git a/resources/ec2-route-tables.go b/resources/ec2-route-tables.go index e8313c360..175e7a5c0 100644 --- a/resources/ec2-route-tables.go +++ b/resources/ec2-route-tables.go @@ -1,6 +1,8 @@ package resources import ( + "fmt" + "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" "github.com/rebuy-de/aws-nuke/v2/pkg/types" @@ -41,6 +43,16 @@ func ListEC2RouteTables(sess *session.Session) ([]Resource, error) { return resources, nil } +func (i *EC2RouteTable) Filter() error { + + for _, association := range i.routeTable.Associations { + if *association.Main { + return fmt.Errorf("Main RouteTables cannot be deleted") + } + } + return nil +} + func (e *EC2RouteTable) Remove() error { params := &ec2.DeleteRouteTableInput{ RouteTableId: e.routeTable.RouteTableId, diff --git a/resources/ec2-tgw-attachments.go b/resources/ec2-tgw-attachments.go index ca7dc3696..84cae7559 100644 --- a/resources/ec2-tgw-attachments.go +++ b/resources/ec2-tgw-attachments.go @@ -52,6 +52,21 @@ func (e *EC2TGWAttachment) Remove() error { // as part of TGW to delete VPN attachments. return fmt.Errorf("VPN attachment") } + + // Execute different API calls depending on the resource type. + if *e.tgwa.ResourceType == "peering" { + params := &ec2.DeleteTransitGatewayPeeringAttachmentInput{ + TransitGatewayAttachmentId: e.tgwa.TransitGatewayAttachmentId, + } + + _, err := e.svc.DeleteTransitGatewayPeeringAttachment(params) + if err != nil { + return err + } + + return nil + } + params := &ec2.DeleteTransitGatewayVpcAttachmentInput{ TransitGatewayAttachmentId: e.tgwa.TransitGatewayAttachmentId, } diff --git a/resources/ec2-vpn-connections.go b/resources/ec2-vpn-connections.go index be78228b5..2ca1054b9 100644 --- a/resources/ec2-vpn-connections.go +++ b/resources/ec2-vpn-connections.go @@ -9,8 +9,8 @@ import ( ) type EC2VPNConnection struct { - svc *ec2.EC2 - conn *ec2.VpnConnection + svc *ec2.EC2 + conn *ec2.VpnConnection } func init() { @@ -29,8 +29,8 @@ func ListEC2VPNConnections(sess *session.Session) ([]Resource, error) { resources := make([]Resource, 0) for _, out := range resp.VpnConnections { resources = append(resources, &EC2VPNConnection{ - svc: svc, - conn: out, + svc: svc, + conn: out, }) } diff --git a/resources/ecs-services.go b/resources/ecs-services.go index d5a3792e6..d53a669d9 100644 --- a/resources/ecs-services.go +++ b/resources/ecs-services.go @@ -52,24 +52,19 @@ func ListECSServices(sess *session.Session) ([]Resource, error) { Cluster: clusterArn, MaxResults: aws.Int64(10), } - output, err := svc.ListServices(serviceParams) + err := svc.ListServicesPages(serviceParams, func(page *ecs.ListServicesOutput, lastPage bool) bool { + for _, serviceArn := range page.ServiceArns { + resources = append(resources, &ECSService{ + svc: svc, + serviceARN: serviceArn, + clusterARN: clusterArn, + }) + } + return true + }) if err != nil { return nil, err } - - for _, serviceArn := range output.ServiceArns { - resources = append(resources, &ECSService{ - svc: svc, - serviceARN: serviceArn, - clusterARN: clusterArn, - }) - } - - if output.NextToken == nil { - continue - } - - serviceParams.NextToken = output.NextToken } return resources, nil diff --git a/resources/elasticache-usergroups.go b/resources/elasticache-usergroups.go index 50208e2ef..df001fd9a 100644 --- a/resources/elasticache-usergroups.go +++ b/resources/elasticache-usergroups.go @@ -4,6 +4,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/elasticache" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" ) type ElasticacheUserGroup struct { @@ -17,19 +18,33 @@ func init() { func ListElasticacheUserGroups(sess *session.Session) ([]Resource, error) { svc := elasticache.New(sess) + resources := []Resource{} + var nextToken *string - params := &elasticache.DescribeUserGroupsInput{MaxRecords: aws.Int64(100)} - resp, err := svc.DescribeUserGroups(params) - if err != nil { - return nil, err - } - var resources []Resource - for _, userGroup := range resp.UserGroups { - resources = append(resources, &ElasticacheUserGroup{ - svc: svc, - groupId: userGroup.UserGroupId, - }) + for { + params := &elasticache.DescribeUserGroupsInput{ + MaxRecords: aws.Int64(100), + Marker: nextToken, + } + resp, err := svc.DescribeUserGroups(params) + if err != nil { + return nil, err + } + + for _, userGroup := range resp.UserGroups { + resources = append(resources, &ElasticacheUserGroup{ + svc: svc, + groupId: userGroup.UserGroupId, + }) + } + // Check if there are more results + if resp.Marker == nil { + break // No more results, exit the loop + } + + // Set the nextToken for the next iteration + nextToken = resp.Marker } return resources, nil @@ -48,6 +63,12 @@ func (i *ElasticacheUserGroup) Remove() error { return nil } +func (i *ElasticacheUserGroup) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("ID", i.groupId) + return properties +} + func (i *ElasticacheUserGroup) String() string { return *i.groupId } diff --git a/resources/elasticache-users.go b/resources/elasticache-users.go index ff8c864ea..8bd37079c 100644 --- a/resources/elasticache-users.go +++ b/resources/elasticache-users.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/elasticache" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" ) type ElasticacheUser struct { @@ -21,20 +22,34 @@ func init() { func ListElasticacheUsers(sess *session.Session) ([]Resource, error) { svc := elasticache.New(sess) + resources := []Resource{} + var nextToken *string - params := &elasticache.DescribeUsersInput{MaxRecords: aws.Int64(100)} - resp, err := svc.DescribeUsers(params) - if err != nil { - return nil, err - } - var resources []Resource - for _, user := range resp.Users { - resources = append(resources, &ElasticacheUser{ - svc: svc, - userId: user.UserId, - userName: user.UserName, - }) + for { + params := &elasticache.DescribeUsersInput{ + MaxRecords: aws.Int64(100), + Marker: nextToken, + } + resp, err := svc.DescribeUsers(params) + if err != nil { + return nil, err + } + + for _, user := range resp.Users { + resources = append(resources, &ElasticacheUser{ + svc: svc, + userId: user.UserId, + userName: user.UserName, + }) + } + // Check if there are more results + if resp.Marker == nil { + break // No more results, exit the loop + } + + // Set the nextToken for the next iteration + nextToken = resp.Marker } return resources, nil @@ -42,7 +57,7 @@ func ListElasticacheUsers(sess *session.Session) ([]Resource, error) { func (i *ElasticacheUser) Filter() error { if strings.HasPrefix(*i.userName, "default") { - return fmt.Errorf("Cannot delete default user") + return fmt.Errorf("cannot delete default user") } return nil } @@ -60,6 +75,13 @@ func (i *ElasticacheUser) Remove() error { return nil } +func (i *ElasticacheUser) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("ID", i.userId) + properties.Set("UserName", i.userName) + return properties +} + func (i *ElasticacheUser) String() string { return *i.userId } diff --git a/resources/fms_notification_channels.go b/resources/fms_notification_channels.go index f7a832d0d..9f70e7147 100644 --- a/resources/fms_notification_channels.go +++ b/resources/fms_notification_channels.go @@ -1,10 +1,13 @@ package resources import ( + "strings" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/fms" "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" ) type FMSNotificationChannel struct { @@ -21,8 +24,9 @@ func ListFMSNotificationChannel(sess *session.Session) ([]Resource, error) { if _, err := svc.GetNotificationChannel(&fms.GetNotificationChannelInput{}); err != nil { if aerr, ok := err.(awserr.Error); ok { - if aerr.Code() != fms.ErrCodeResourceNotFoundException { - return nil, err + if strings.Contains(aerr.Message(), "No default admin could be found") { + logrus.Infof("FMSNotificationChannel: %s. Ignore if you haven't set it up.", aerr.Message()) + return nil, nil } } else { return nil, err diff --git a/resources/fms_policies.go b/resources/fms_policies.go index f369518a3..9e6176731 100644 --- a/resources/fms_policies.go +++ b/resources/fms_policies.go @@ -1,10 +1,14 @@ package resources import ( + "strings" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/fms" "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" ) type FMSPolicy struct { @@ -27,6 +31,12 @@ func ListFMSPolicies(sess *session.Session) ([]Resource, error) { for { resp, err := svc.ListPolicies(params) if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if strings.Contains(aerr.Message(), "No default admin could be found") { + logrus.Infof("FMSPolicy: %s. Ignore if you haven't set it up.", aerr.Message()) + return nil, nil + } + } return nil, err } diff --git a/resources/iam-policies.go b/resources/iam-policies.go index d4db7bec7..ece41af34 100644 --- a/resources/iam-policies.go +++ b/resources/iam-policies.go @@ -1,11 +1,11 @@ package resources import ( - "github.com/sirupsen/logrus" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/iam" "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" ) type IAMPolicy struct { diff --git a/resources/kms-keys.go b/resources/kms-keys.go index cfb1193ed..c7a67f682 100644 --- a/resources/kms-keys.go +++ b/resources/kms-keys.go @@ -86,6 +86,10 @@ func (e *KMSKey) Filter() error { return fmt.Errorf("is already in PendingDeletion state") } + if e.state == "PendingReplicaDeletion" { + return fmt.Errorf("is already in PendingReplicaDeletion state") + } + if e.manager != nil && *e.manager == kms.KeyManagerTypeAws { return fmt.Errorf("cannot delete AWS managed key") } diff --git a/resources/machinelearning-batchpredictions.go b/resources/machinelearning-batchpredictions.go index 46c9977b8..f93627226 100644 --- a/resources/machinelearning-batchpredictions.go +++ b/resources/machinelearning-batchpredictions.go @@ -1,9 +1,13 @@ package resources import ( + "strings" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/machinelearning" + "github.com/sirupsen/logrus" ) type MachineLearningBranchPrediction struct { @@ -26,6 +30,12 @@ func ListMachineLearningBranchPredictions(sess *session.Session) ([]Resource, er for { output, err := svc.DescribeBatchPredictions(params) if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if strings.Contains(aerr.Message(), "AmazonML is no longer available to new customers") { + logrus.Info("MachineLearningBranchPrediction: AmazonML is no longer available to new customers. Ignore if you haven't set it up.") + return nil, nil + } + } return nil, err } diff --git a/resources/machinelearning-datasources.go b/resources/machinelearning-datasources.go index 3dfb84a46..0b7301567 100644 --- a/resources/machinelearning-datasources.go +++ b/resources/machinelearning-datasources.go @@ -1,9 +1,13 @@ package resources import ( + "strings" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/machinelearning" + "github.com/sirupsen/logrus" ) type MachineLearningDataSource struct { @@ -26,6 +30,12 @@ func ListMachineLearningDataSources(sess *session.Session) ([]Resource, error) { for { output, err := svc.DescribeDataSources(params) if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if strings.Contains(aerr.Message(), "AmazonML is no longer available to new customers") { + logrus.Info("MachineLearningBranchPrediction: AmazonML is no longer available to new customers. Ignore if you haven't set it up.") + return nil, nil + } + } return nil, err } diff --git a/resources/machinelearning-evaluations.go b/resources/machinelearning-evaluations.go index 6ef834089..7d307ac97 100644 --- a/resources/machinelearning-evaluations.go +++ b/resources/machinelearning-evaluations.go @@ -1,9 +1,13 @@ package resources import ( + "strings" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/machinelearning" + "github.com/sirupsen/logrus" ) type MachineLearningEvaluation struct { @@ -26,6 +30,12 @@ func ListMachineLearningEvaluations(sess *session.Session) ([]Resource, error) { for { output, err := svc.DescribeEvaluations(params) if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if strings.Contains(aerr.Message(), "AmazonML is no longer available to new customers") { + logrus.Info("MachineLearningBranchPrediction: AmazonML is no longer available to new customers. Ignore if you haven't set it up.") + return nil, nil + } + } return nil, err } diff --git a/resources/machinelearning-mlmodels.go b/resources/machinelearning-mlmodels.go index b223efd00..0bde5f748 100644 --- a/resources/machinelearning-mlmodels.go +++ b/resources/machinelearning-mlmodels.go @@ -1,9 +1,13 @@ package resources import ( + "strings" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/machinelearning" + "github.com/sirupsen/logrus" ) type MachineLearningMLModel struct { @@ -26,6 +30,12 @@ func ListMachineLearningMLModels(sess *session.Session) ([]Resource, error) { for { output, err := svc.DescribeMLModels(params) if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if strings.Contains(aerr.Message(), "AmazonML is no longer available to new customers") { + logrus.Info("MachineLearningBranchPrediction: AmazonML is no longer available to new customers. Ignore if you haven't set it up.") + return nil, nil + } + } return nil, err } diff --git a/resources/memorydb-acl.go b/resources/memorydb-acl.go new file mode 100644 index 000000000..fa43e46d2 --- /dev/null +++ b/resources/memorydb-acl.go @@ -0,0 +1,84 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/memorydb" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type MemoryDBACL struct { + svc *memorydb.MemoryDB + name *string + tags []*memorydb.Tag +} + +func init() { + register("MemoryDBACL", ListMemoryDBACLs) +} + +func ListMemoryDBACLs(sess *session.Session) ([]Resource, error) { + svc := memorydb.New(sess) + var resources []Resource + + params := &memorydb.DescribeACLsInput{MaxResults: aws.Int64(50)} + for { + resp, err := svc.DescribeACLs(params) + if err != nil { + return nil, err + } + + for _, acl := range resp.ACLs { + tags, err := svc.ListTags(&memorydb.ListTagsInput{ + ResourceArn: acl.ARN, + }) + + if err != nil { + continue + } + + resources = append(resources, &MemoryDBACL{ + svc: svc, + name: acl.Name, + tags: tags.TagList, + }) + + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +func (i *MemoryDBACL) Remove() error { + params := &memorydb.DeleteACLInput{ + ACLName: i.name, + } + + _, err := i.svc.DeleteACL(params) + if err != nil { + return err + } + + return nil +} + +func (i *MemoryDBACL) String() string { + return *i.name +} + +func (i *MemoryDBACL) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("Name", i.name) + + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } + + return properties +} diff --git a/resources/memorydb-cluster.go b/resources/memorydb-cluster.go new file mode 100644 index 000000000..a2ca799e5 --- /dev/null +++ b/resources/memorydb-cluster.go @@ -0,0 +1,84 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/memorydb" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type MemoryDBCluster struct { + svc *memorydb.MemoryDB + name *string + tags []*memorydb.Tag +} + +func init() { + register("MemoryDBCluster", ListMemoryDbClusters) +} + +func ListMemoryDbClusters(sess *session.Session) ([]Resource, error) { + svc := memorydb.New(sess) + var resources []Resource + + params := &memorydb.DescribeClustersInput{MaxResults: aws.Int64(100)} + + for { + resp, err := svc.DescribeClusters(params) + if err != nil { + return nil, err + } + + for _, cluster := range resp.Clusters { + tags, err := svc.ListTags(&memorydb.ListTagsInput{ + ResourceArn: cluster.ARN, + }) + + if err != nil { + continue + } + + resources = append(resources, &MemoryDBCluster{ + svc: svc, + name: cluster.Name, + tags: tags.TagList, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +func (c *MemoryDBCluster) Remove() error { + params := &memorydb.DeleteClusterInput{ + ClusterName: c.name, + } + + _, err := c.svc.DeleteCluster(params) + if err != nil { + return err + } + + return nil +} + +func (i *MemoryDBCluster) String() string { + return *i.name +} + +func (i *MemoryDBCluster) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("Name", i.name) + + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } + + return properties +} diff --git a/resources/memorydb-parametergroups.go b/resources/memorydb-parametergroups.go new file mode 100644 index 000000000..0583d6dfe --- /dev/null +++ b/resources/memorydb-parametergroups.go @@ -0,0 +1,98 @@ +package resources + +import ( + "fmt" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/memorydb" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type MemoryDBParameterGroup struct { + svc *memorydb.MemoryDB + name *string + family *string + tags []*memorydb.Tag +} + +func init() { + register("MemoryDBParameterGroup", ListMemoryDBParameterGroups) +} + +func ListMemoryDBParameterGroups(sess *session.Session) ([]Resource, error) { + svc := memorydb.New(sess) + var resources []Resource + + params := &memorydb.DescribeParameterGroupsInput{MaxResults: aws.Int64(100)} + + for { + resp, err := svc.DescribeParameterGroups(params) + if err != nil { + return nil, err + } + + for _, parameterGroup := range resp.ParameterGroups { + tags, err := svc.ListTags(&memorydb.ListTagsInput{ + ResourceArn: parameterGroup.ARN, + }) + + if err != nil { + continue + } + + resources = append(resources, &MemoryDBParameterGroup{ + svc: svc, + name: parameterGroup.Name, + family: parameterGroup.Family, + tags: tags.TagList, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +func (i *MemoryDBParameterGroup) Filter() error { + if strings.HasPrefix(*i.name, "default.") { + return fmt.Errorf("Cannot delete default parameter group") + } + return nil +} + +func (i *MemoryDBParameterGroup) Remove() error { + params := &memorydb.DeleteParameterGroupInput{ + ParameterGroupName: i.name, + } + + _, err := i.svc.DeleteParameterGroup(params) + if err != nil { + return err + } + + return nil +} + +func (i *MemoryDBParameterGroup) String() string { + return *i.name +} + +func (i *MemoryDBParameterGroup) Properties() types.Properties { + properties := types.NewProperties() + properties. + Set("Name", i.name). + Set("Family", i.family) + + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } + + return properties +} diff --git a/resources/memorydb-subnetgroups.go b/resources/memorydb-subnetgroups.go new file mode 100644 index 000000000..b2ac7cd6c --- /dev/null +++ b/resources/memorydb-subnetgroups.go @@ -0,0 +1,85 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/memorydb" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type MemoryDBSubnetGroup struct { + svc *memorydb.MemoryDB + name *string + tags []*memorydb.Tag +} + +func init() { + register("MemoryDBSubnetGroup", ListMemoryDBSubnetGroups) +} + +func ListMemoryDBSubnetGroups(sess *session.Session) ([]Resource, error) { + svc := memorydb.New(sess) + var resources []Resource + + params := &memorydb.DescribeSubnetGroupsInput{MaxResults: aws.Int64(100)} + + for { + resp, err := svc.DescribeSubnetGroups(params) + if err != nil { + return nil, err + } + for _, subnetGroup := range resp.SubnetGroups { + tags, err := svc.ListTags(&memorydb.ListTagsInput{ + ResourceArn: subnetGroup.ARN, + }) + + if err != nil { + continue + } + + resources = append(resources, &MemoryDBSubnetGroup{ + svc: svc, + name: subnetGroup.Name, + tags: tags.TagList, + }) + + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +func (i *MemoryDBSubnetGroup) Remove() error { + params := &memorydb.DeleteSubnetGroupInput{ + SubnetGroupName: i.name, + } + + _, err := i.svc.DeleteSubnetGroup(params) + if err != nil { + return err + } + + return nil +} + +func (i *MemoryDBSubnetGroup) String() string { + return *i.name +} + +func (i *MemoryDBSubnetGroup) Properties() types.Properties { + properties := types.NewProperties() + properties. + Set("Name", i.name) + + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } + + return properties +} diff --git a/resources/memorydb-user.go b/resources/memorydb-user.go new file mode 100644 index 000000000..cfeb140d9 --- /dev/null +++ b/resources/memorydb-user.go @@ -0,0 +1,95 @@ +package resources + +import ( + "fmt" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/memorydb" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type MemoryDBUser struct { + svc *memorydb.MemoryDB + name *string + tags []*memorydb.Tag +} + +func init() { + register("MemoryDBUser", ListMemoryDBUsers) +} + +func ListMemoryDBUsers(sess *session.Session) ([]Resource, error) { + svc := memorydb.New(sess) + var resources []Resource + + params := &memorydb.DescribeUsersInput{MaxResults: aws.Int64(50)} + for { + resp, err := svc.DescribeUsers(params) + if err != nil { + return nil, err + } + + for _, user := range resp.Users { + tags, err := svc.ListTags(&memorydb.ListTagsInput{ + ResourceArn: user.ARN, + }) + + if err != nil { + continue + } + + resources = append(resources, &MemoryDBUser{ + svc: svc, + name: user.Name, + tags: tags.TagList, + }) + + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +func (i *MemoryDBUser) Filter() error { + if strings.EqualFold(*i.name, "default") { + return fmt.Errorf("Cannot delete default user") + } + return nil +} + +func (i *MemoryDBUser) Remove() error { + params := &memorydb.DeleteUserInput{ + UserName: i.name, + } + + _, err := i.svc.DeleteUser(params) + if err != nil { + return err + } + + return nil +} + +func (i *MemoryDBUser) String() string { + return *i.name +} + +func (i *MemoryDBUser) Properties() types.Properties { + properties := types.NewProperties() + properties. + Set("Name", i.name) + + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } + + return properties +} diff --git a/resources/mgn-jobs.go b/resources/mgn-jobs.go index 2159b3c55..670d4c4c7 100644 --- a/resources/mgn-jobs.go +++ b/resources/mgn-jobs.go @@ -5,6 +5,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/mgn" "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" ) type MGNJob struct { @@ -29,6 +30,10 @@ func ListMGNJobs(sess *session.Session) ([]Resource, error) { for { output, err := svc.DescribeJobs(params) if err != nil { + if IsAWSError(err, mgn.ErrCodeUninitializedAccountException) { + logrus.Info("MGNJob: Account not initialized for Application Migration Service. Ignore if you haven't set it up.") + return nil, nil + } return nil, err } diff --git a/resources/mgn-source_servers.go b/resources/mgn-source_servers.go index 706138a77..fe8404390 100644 --- a/resources/mgn-source_servers.go +++ b/resources/mgn-source_servers.go @@ -5,6 +5,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/mgn" "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/sirupsen/logrus" ) type MGNSourceServer struct { @@ -29,6 +30,10 @@ func ListMGNSourceServers(sess *session.Session) ([]Resource, error) { for { output, err := svc.DescribeSourceServers(params) if err != nil { + if IsAWSError(err, mgn.ErrCodeUninitializedAccountException) { + logrus.Info("MGNSourceServer: Account not initialized for Application Migration Service. Ignore if you haven't set it up.") + return nil, nil + } return nil, err } diff --git a/resources/rds-clusters.go b/resources/rds-clusters.go index 882712517..686a7961c 100644 --- a/resources/rds-clusters.go +++ b/resources/rds-clusters.go @@ -30,12 +30,12 @@ func ListRDSClusters(sess *session.Session) ([]Resource, error) { resources := make([]Resource, 0) for _, instance := range resp.DBClusters { tags, err := svc.ListTagsForResource(&rds.ListTagsForResourceInput{ - ResourceName: instance.DBClusterArn, - }) + ResourceName: instance.DBClusterArn, + }) - if err != nil { - continue - } + if err != nil { + continue + } resources = append(resources, &RDSDBCluster{ svc: svc, @@ -49,7 +49,7 @@ func ListRDSClusters(sess *session.Session) ([]Resource, error) { } func (i *RDSDBCluster) Remove() error { - if (i.deletionProtection) { + if i.deletionProtection { modifyParams := &rds.ModifyDBClusterInput{ DBClusterIdentifier: &i.id, DeletionProtection: aws.Bool(false), @@ -78,13 +78,13 @@ func (i *RDSDBCluster) String() string { } func (i *RDSDBCluster) Properties() types.Properties { - properties := types.NewProperties() - properties.Set("Identifier", i.id) + properties := types.NewProperties() + properties.Set("Identifier", i.id) properties.Set("Deletion Protection", i.deletionProtection) - for _, tag := range i.tags { - properties.SetTag(tag.Key, tag.Value) - } + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } - return properties + return properties } diff --git a/resources/rds-dbclusterparametergroups.go b/resources/rds-dbclusterparametergroups.go index 9466d8c52..04a53af3a 100644 --- a/resources/rds-dbclusterparametergroups.go +++ b/resources/rds-dbclusterparametergroups.go @@ -31,12 +31,12 @@ func ListRDSClusterParameterGroups(sess *session.Session) ([]Resource, error) { var resources []Resource for _, parametergroup := range resp.DBClusterParameterGroups { tags, err := svc.ListTagsForResource(&rds.ListTagsForResourceInput{ - ResourceName: parametergroup.DBClusterParameterGroupArn, - }) + ResourceName: parametergroup.DBClusterParameterGroupArn, + }) - if err != nil { - continue - } + if err != nil { + continue + } resources = append(resources, &RDSDBClusterParameterGroup{ svc: svc, diff --git a/resources/rds-subnets.go b/resources/rds-subnets.go index 17978a195..017a2c297 100644 --- a/resources/rds-subnets.go +++ b/resources/rds-subnets.go @@ -28,12 +28,12 @@ func ListRDSSubnetGroups(sess *session.Session) ([]Resource, error) { var resources []Resource for _, subnetGroup := range resp.DBSubnetGroups { tags, err := svc.ListTagsForResource(&rds.ListTagsForResourceInput{ - ResourceName: subnetGroup.DBSubnetGroupArn, - }) + ResourceName: subnetGroup.DBSubnetGroupArn, + }) - if err != nil { - continue - } + if err != nil { + continue + } resources = append(resources, &RDSDBSubnetGroup{ svc: svc, @@ -64,12 +64,12 @@ func (i *RDSDBSubnetGroup) String() string { } func (i *RDSDBSubnetGroup) Properties() types.Properties { - properties := types.NewProperties() - properties.Set("Name", i.name) + properties := types.NewProperties() + properties.Set("Name", i.name) - for _, tag := range i.tags { - properties.SetTag(tag.Key, tag.Value) - } + for _, tag := range i.tags { + properties.SetTag(tag.Key, tag.Value) + } - return properties + return properties } diff --git a/resources/redshift-scheduled-action.go b/resources/redshift-scheduled-action.go new file mode 100644 index 000000000..e98b15691 --- /dev/null +++ b/resources/redshift-scheduled-action.go @@ -0,0 +1,60 @@ +package resources + +import ( + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/redshift" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" +) + +type RedshiftScheduledAction struct { + svc *redshift.Redshift + scheduledActionName *string +} + +func init() { + register("RedshiftScheduledAction", ListRedshiftScheduledActions) +} + +func ListRedshiftScheduledActions(sess *session.Session) ([]Resource, error) { + svc := redshift.New(sess) + resources := []Resource{} + + params := &redshift.DescribeScheduledActionsInput{} + + for { + resp, err := svc.DescribeScheduledActions(params) + if err != nil { + return nil, err + } + + for _, item := range resp.ScheduledActions { + resources = append(resources, &RedshiftScheduledAction{ + svc: svc, + scheduledActionName: item.ScheduledActionName, + }) + } + + if resp.Marker == nil { + break + } + + params.Marker = resp.Marker + } + + return resources, nil +} + +func (f *RedshiftScheduledAction) Remove() error { + + _, err := f.svc.DeleteScheduledAction(&redshift.DeleteScheduledActionInput{ + ScheduledActionName: f.scheduledActionName, + }) + + return err +} + +func (f *RedshiftScheduledAction) Properties() types.Properties { + properties := types.NewProperties() + properties.Set("scheduledActionName", f.scheduledActionName) + return properties +} diff --git a/resources/redshift-subnetgroups.go b/resources/redshift-subnetgroups.go index a94600c13..767eb0f64 100644 --- a/resources/redshift-subnetgroups.go +++ b/resources/redshift-subnetgroups.go @@ -31,7 +31,7 @@ func ListRedshiftSubnetGroups(sess *session.Session) ([]Resource, error) { for _, subnetGroup := range output.ClusterSubnetGroups { resources = append(resources, &RedshiftSubnetGroup{ - svc: svc, + svc: svc, clusterSubnetGroupName: subnetGroup.ClusterSubnetGroupName, }) } diff --git a/resources/route53-resource-records.go b/resources/route53-resource-records.go index 35f3bc792..c21cfd3f7 100644 --- a/resources/route53-resource-records.go +++ b/resources/route53-resource-records.go @@ -95,7 +95,7 @@ func (r *Route53ResourceRecordSet) Remove() error { HostedZoneId: r.hostedZoneId, ChangeBatch: &route53.ChangeBatch{ Changes: []*route53.Change{ - &route53.Change{ + { Action: aws.String("DELETE"), ResourceRecordSet: r.data, }, diff --git a/resources/sfn-statemachines.go b/resources/sfn-statemachines.go index e0748f7a4..5edcd9a6d 100644 --- a/resources/sfn-statemachines.go +++ b/resources/sfn-statemachines.go @@ -47,6 +47,27 @@ func ListSFNStateMachines(sess *session.Session) ([]Resource, error) { } func (f *SFNStateMachine) Remove() error { + params := &sfn.ListExecutionsInput{ + StateMachineArn: f.ARN, + } + + for { + executions, execError := f.svc.ListExecutions(params) + if execError != nil { + break + } + for _, execs := range executions.Executions { + + f.svc.StopExecution(&sfn.StopExecutionInput{ + ExecutionArn: execs.ExecutionArn, + }) + } + + if executions.NextToken == nil { + break + } + params.NextToken = executions.NextToken + } _, err := f.svc.DeleteStateMachine(&sfn.DeleteStateMachineInput{ StateMachineArn: f.ARN, diff --git a/resources/wafregional-byte-match-set-tuples.go b/resources/wafregional-byte-match-set-tuples.go index ddf7966f7..45ba62251 100644 --- a/resources/wafregional-byte-match-set-tuples.go +++ b/resources/wafregional-byte-match-set-tuples.go @@ -70,7 +70,7 @@ func (r *WAFRegionalByteMatchSetIP) Remove() error { ChangeToken: tokenOutput.ChangeToken, ByteMatchSetId: r.matchSetid, Updates: []*waf.ByteMatchSetUpdate{ - &waf.ByteMatchSetUpdate{ + { Action: aws.String("DELETE"), ByteMatchTuple: r.tuple, }, diff --git a/resources/wafregional-ip-set-ips.go b/resources/wafregional-ip-set-ips.go index 6ad754cc2..875668a21 100644 --- a/resources/wafregional-ip-set-ips.go +++ b/resources/wafregional-ip-set-ips.go @@ -70,7 +70,7 @@ func (r *WAFRegionalIPSetIP) Remove() error { ChangeToken: tokenOutput.ChangeToken, IPSetId: r.ipSetid, Updates: []*waf.IPSetUpdate{ - &waf.IPSetUpdate{ + { Action: aws.String("DELETE"), IPSetDescriptor: r.descriptor, }, diff --git a/resources/wafregional-rate-based-rule-predicates.go b/resources/wafregional-rate-based-rule-predicates.go index 67319a56d..630387d6f 100644 --- a/resources/wafregional-rate-based-rule-predicates.go +++ b/resources/wafregional-rate-based-rule-predicates.go @@ -72,7 +72,7 @@ func (r *WAFRegionalRateBasedRulePredicate) Remove() error { RuleId: r.ruleID, RateLimit: r.rateLimit, Updates: []*waf.RuleUpdate{ - &waf.RuleUpdate{ + { Action: aws.String("DELETE"), Predicate: r.predicate, }, diff --git a/resources/wafregional-regex-match-tuples.go b/resources/wafregional-regex-match-tuples.go index f2a9ebafa..5095bfdaa 100644 --- a/resources/wafregional-regex-match-tuples.go +++ b/resources/wafregional-regex-match-tuples.go @@ -69,7 +69,7 @@ func (r *WAFRegionalRegexMatchTuple) Remove() error { ChangeToken: tokenOutput.ChangeToken, RegexMatchSetId: r.matchSetid, Updates: []*waf.RegexMatchSetUpdate{ - &waf.RegexMatchSetUpdate{ + { Action: aws.String("DELETE"), RegexMatchTuple: r.tuple, }, diff --git a/resources/wafregional-regex-pattern-tuples.go b/resources/wafregional-regex-pattern-tuples.go index 9c7d8dea3..3a0c84223 100644 --- a/resources/wafregional-regex-pattern-tuples.go +++ b/resources/wafregional-regex-pattern-tuples.go @@ -69,7 +69,7 @@ func (r *WAFRegionalRegexPatternString) Remove() error { ChangeToken: tokenOutput.ChangeToken, RegexPatternSetId: r.patternSetid, Updates: []*waf.RegexPatternSetUpdate{ - &waf.RegexPatternSetUpdate{ + { Action: aws.String("DELETE"), RegexPatternString: r.patternString, }, diff --git a/resources/wafregional-rule-predicates.go b/resources/wafregional-rule-predicates.go index d7713c200..6c9a9adf9 100644 --- a/resources/wafregional-rule-predicates.go +++ b/resources/wafregional-rule-predicates.go @@ -69,7 +69,7 @@ func (r *WAFRegionalRulePredicate) Remove() error { ChangeToken: tokenOutput.ChangeToken, RuleId: r.ruleID, Updates: []*waf.RuleUpdate{ - &waf.RuleUpdate{ + { Action: aws.String("DELETE"), Predicate: r.predicate, }, diff --git a/tools/tools.go b/tools/tools.go index 6f89b957f..356e89829 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -1,4 +1,4 @@ -// +build tools +//go:build tools package main