Skip to content

Commit

Permalink
add support for cloud control (#753)
Browse files Browse the repository at this point in the history
* add support for cloud control

* delete cloud control resources

* show list capabilities for cloud control

* add more cloud control resources

* handle cloud control properties

* add cloud control documentation
  • Loading branch information
svenwltr committed Mar 29, 2022
1 parent 9faa056 commit 5e375e7
Show file tree
Hide file tree
Showing 31 changed files with 494 additions and 52 deletions.
53 changes: 49 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,51 @@ If an exclude is used, then all its resource types will not be deleted.
aws-nuke resource-types
```

### AWS Cloud Control API Support

> This feature is not yet released and is probably part of `v2.18`.
_aws-nuke_ supports removing resources via the AWS Cloud Control API. When
executing _aws-nuke_ it will automatically remove a manually managed set of
resources via Cloud Control.

Only a subset of Cloud Control supported resources will be removed
automatically, because there might be resources that were already implemented
and adding them too would bypass existing filters in user configs as Cloud
Control has another naming scheme and a different set of properties. Moreover,
there are some Cloud Control resources that need special handling which is not
yet supported by _aws-nuke_.

Even though the subset of automatically supported Cloud Control resources is
limited, you can can configure _aws-nuke_ to make it try any additional
resource. Either via command line flags of via the config file.

For the config file you have to add the resource to
the`resource-types.cloud-control` list:

```yaml
resource-types:
cloud-control:
- AWS::EC2::TransitGateway
- AWS::EC2::VPC
```
If you want to use the command line, you have to add a `--cloud-control` flag
for each resource you want to add:

```sh
aws-nuke \
-c nuke-config.yaml \
--cloud-control AWS::EC2::TransitGateway \
--cloud-control AWS::EC2::VPC
```

**Note:** There are some resources that are supported by Cloud Control and are
already natively implemented by _aws-nuke_. If you configure to use Cloud
Control for those resources, it will not execute the natively implemented code
for this resource. For example with the `--cloud-control AWS::EC2::VPC` it will
not use the `EC2VPC` resource.


### Feature Flags

Expand Down Expand Up @@ -460,9 +505,9 @@ There are also additional comparision types than an exact match:
Details about the syntax can be found in the [library
documentation](https://golang.org/pkg/regexp/syntax/).
* `dateOlderThan` - The identifier is parsed as a timestamp. After the offset is added to it (specified in the `value` field), the resulting timestamp must be AFTER the current
time. Details on offset syntax can be found in
time. Details on offset syntax can be found in
the [library documentation](https://golang.org/pkg/time/#ParseDuration). Supported
date formats are epoch time, `2006-01-02`, `2006/01/02`, `2006-01-02T15:04:05Z`,
date formats are epoch time, `2006-01-02`, `2006/01/02`, `2006-01-02T15:04:05Z`,
`2006-01-02T15:04:05.999999999Z07:00`, and `2006-01-02T15:04:05Z07:00`.

To use a non-default comparision type, it is required to specify an object with
Expand Down Expand Up @@ -561,13 +606,13 @@ presets:
The easiest way of installing it, is to download the latest
[release](https://github.com/rebuy-de/aws-nuke/releases) from GitHub.

#### Example for Linux Intel/AMD
#### Example for Linux Intel/AMD

Download and extract
`$ wget -c https://github.com/rebuy-de/aws-nuke/releases/download/v2.16.0/aws-nuke-v2.16.0-linux-amd64.tar.gz -O - | sudo tar -xz -C $HOME/bin`

Run
`$ aws-nuke-v2.16.0-linux-amd64`
`$ aws-nuke-v2.16.0-linux-amd64`

### Compile from Source

Expand Down
6 changes: 6 additions & 0 deletions cmd/nuke.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func (n *Nuke) Scan() error {

resourceTypes := ResolveResourceTypes(
resources.GetListerNames(),
resources.GetCloudControlMapping(),
[]types.Collection{
n.Parameters.Targets,
n.Config.ResourceTypes.Targets,
Expand All @@ -149,6 +150,11 @@ func (n *Nuke) Scan() error {
n.Config.ResourceTypes.Excludes,
accountConfig.ResourceTypes.Excludes,
},
[]types.Collection{
n.Parameters.CloudControl,
n.Config.ResourceTypes.CloudControl,
accountConfig.ResourceTypes.CloudControl,
},
)

queue := make(Queue, 0)
Expand Down
5 changes: 3 additions & 2 deletions cmd/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import (
type NukeParameters struct {
ConfigPath string

Targets []string
Excludes []string
Targets []string
Excludes []string
CloudControl []string

NoDryRun bool
Force bool
Expand Down
4 changes: 2 additions & 2 deletions cmd/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ func (i *Item) Print() {

// List gets all resource items of the same resource type like the Item.
func (i *Item) List() ([]resources.Resource, error) {
listers := resources.GetListers()
lister := resources.GetLister(i.Type)
sess, err := i.Region.Session(i.Type)
if err != nil {
return nil, err
}
return listers[i.Type](sess)
return lister(sess)
}

func (i *Item) GetProperty(key string) (string, error) {
Expand Down
8 changes: 7 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func NewRootCommand() *cobra.Command {
command.PersistentFlags().StringVar(
&creds.AssumeRoleArn, "assume-role-arn", "",
"AWS IAM role arn to assume. "+
"The credentials provided via --access-key-id or --profile must "+
"The credentials provided via --access-key-id or --profile must "+
"be allowed to assume this role. ")
command.PersistentFlags().StringVar(
&defaultRegion, "default-region", "",
Expand All @@ -134,6 +134,12 @@ func NewRootCommand() *cobra.Command {
&params.Excludes, "exclude", "e", []string{},
"Prevent nuking of certain resource types (eg IAMServerCertificate). "+
"This flag can be used multiple times.")
command.PersistentFlags().StringSliceVar(
&params.CloudControl, "cloud-control", []string{},
"Nuke given resource via Cloud Control API. "+
"If there is an old-style method for the same resource, the old-style one will not be executed. "+
"Note that old-style and cloud-control filters are not compatible! "+
"This flag can be used multiple times.")
command.PersistentFlags().BoolVar(
&params.NoDryRun, "no-dry-run", false,
"If specified, it actually deletes found resources. "+
Expand Down
18 changes: 17 additions & 1 deletion cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,23 @@ func Prompt(expect string) error {
return nil
}

func ResolveResourceTypes(base types.Collection, include, exclude []types.Collection) types.Collection {
func ResolveResourceTypes(
base types.Collection, mapping map[string]string,
include, exclude, cloudControl []types.Collection) types.Collection {

for _, cl := range cloudControl {
oldStyle := types.Collection{}
for _, c := range cl {
os, found := mapping[c]
if found {
oldStyle = append(oldStyle, os)
}
}

base = base.Union(cl)
base = base.Remove(oldStyle)
}

for _, i := range include {
if len(i) > 0 {
base = base.Intersect(i)
Expand Down
44 changes: 30 additions & 14 deletions cmd/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,58 @@ import (

func TestResolveResourceTypes(t *testing.T) {
cases := []struct {
base types.Collection
include []types.Collection
exclude []types.Collection
result types.Collection
name string
base types.Collection
mapping map[string]string
include []types.Collection
exclude []types.Collection
cloudControl []types.Collection
result types.Collection
}{
{
base: types.Collection{"a", "b", "c", "d"},
include: []types.Collection{types.Collection{"a", "b", "c"}},
include: []types.Collection{{"a", "b", "c"}},
result: types.Collection{"a", "b", "c"},
},
{
base: types.Collection{"a", "b", "c", "d"},
exclude: []types.Collection{types.Collection{"b", "d"}},
exclude: []types.Collection{{"b", "d"}},
result: types.Collection{"a", "c"},
},
{
base: types.Collection{"a", "b"},
include: []types.Collection{types.Collection{}},
include: []types.Collection{{}},
result: types.Collection{"a", "b"},
},
{
base: types.Collection{"c", "b"},
exclude: []types.Collection{types.Collection{}},
exclude: []types.Collection{{}},
result: types.Collection{"c", "b"},
},
{
base: types.Collection{"a", "b", "c", "d"},
include: []types.Collection{types.Collection{"a", "b", "c"}},
exclude: []types.Collection{types.Collection{"a"}},
include: []types.Collection{{"a", "b", "c"}},
exclude: []types.Collection{{"a"}},
result: types.Collection{"b", "c"},
},
{
name: "CloudControlAdd",
base: types.Collection{"a", "b"},
cloudControl: []types.Collection{{"x"}},
result: types.Collection{"a", "b", "x"},
},
{
name: "CloudControlReplaceOldStyle",
base: types.Collection{"a", "b", "c"},
mapping: map[string]string{"z": "b"},
cloudControl: []types.Collection{{"z"}},
result: types.Collection{"a", "z", "c"},
},
}

for i, tc := range cases {
t.Run(fmt.Sprint(i), func(t *testing.T) {
r := ResolveResourceTypes(tc.base, tc.include, tc.exclude)
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
r := ResolveResourceTypes(tc.base, tc.mapping, tc.include, tc.exclude, tc.cloudControl)

sort.Strings(r)
sort.Strings(tc.result)
Expand All @@ -72,7 +88,7 @@ func TestIsTrue(t *testing.T) {

trueStrings := []string{"true", " true", "true ", " TrUe "}
for _, ts := range trueStrings {
if ! IsTrue(ts) {
if !IsTrue(ts) {
t.Fatalf("IsTrue falsely returned 'false' for: %s", ts)
}
}
Expand Down
100 changes: 100 additions & 0 deletions dev/list-cloudcontrol/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package main

import (
"encoding/json"
"fmt"
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/fatih/color"
"github.com/rebuy-de/aws-nuke/resources"
"github.com/rebuy-de/rebuy-go-sdk/v3/pkg/cmdutil"
"github.com/sirupsen/logrus"
)

type CFTypeSchema struct {
Handlers map[string]interface{} `json:"handlers"`
}

func main() {
ctx := cmdutil.SignalRootContext()

sess, err := session.NewSession(&aws.Config{
Region: aws.String(endpoints.UsEast1RegionID),
})
if err != nil {
logrus.Fatal(err)
}

cf := cloudformation.New(sess)

mapping := resources.GetCloudControlMapping()

in := &cloudformation.ListTypesInput{
Type: aws.String(cloudformation.RegistryTypeResource),
Visibility: aws.String(cloudformation.VisibilityPublic),
ProvisioningType: aws.String(cloudformation.ProvisioningTypeFullyMutable),
}

err = cf.ListTypesPagesWithContext(ctx, in, func(out *cloudformation.ListTypesOutput, _ bool) bool {
if out == nil {
return true
}

for _, summary := range out.TypeSummaries {
if summary == nil {
continue
}

typeName := aws.StringValue(summary.TypeName)
color.New(color.Bold).Printf("%-55s", typeName)
if !strings.HasPrefix(typeName, "AWS::") {
color.HiBlack("does not have a valid prefix")
continue
}

describe, err := cf.DescribeType(&cloudformation.DescribeTypeInput{
Type: aws.String(cloudformation.RegistryTypeResource),
TypeName: aws.String(typeName),
})
if err != nil {
color.New(color.FgRed).Println(err)
continue
}

var schema CFTypeSchema
err = json.Unmarshal([]byte(aws.StringValue(describe.Schema)), &schema)
if err != nil {
color.New(color.FgRed).Println(err)
continue
}

_, canList := schema.Handlers["list"]
if !canList {
color.New(color.FgHiBlack).Println("does not support list")
continue
}

resourceName, exists := mapping[typeName]
if exists && resourceName == typeName {
fmt.Print("is only covered by ")
color.New(color.FgGreen, color.Bold).Println(resourceName)
continue
} else if exists {
fmt.Print("is also covered by ")
color.New(color.FgBlue, color.Bold).Println(resourceName)
continue
}

color.New(color.FgYellow).Println("is not configured")
}

return true
})
if err != nil {
logrus.Fatal(err)
}
}
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/golang/mock v1.6.0
github.com/mb0/glob v0.0.0-20160210091149-1eb79d2de6c4
github.com/pkg/errors v0.9.1
github.com/rebuy-de/rebuy-go-sdk/v3 v3.11.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.3.0
github.com/stretchr/testify v1.7.0
Expand All @@ -17,6 +18,8 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gemnasium/logrus-graylog-hook/v3 v3.0.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
Expand All @@ -25,7 +28,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/mod v0.5.0 // indirect
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
golang.org/x/tools v0.1.5 // indirect
golang.org/x/tools v0.1.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
Loading

0 comments on commit 5e375e7

Please sign in to comment.