Skip to content

Commit

Permalink
Merge pull request #5 from gruntwork-io/v0.0.2
Browse files Browse the repository at this point in the history
aws-nuke v0.0.2
  • Loading branch information
brikis98 authored Feb 7, 2018
2 parents 698207e + fa51aa5 commit 069d76f
Show file tree
Hide file tree
Showing 21 changed files with 1,099 additions and 44 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ This repo contains a CLI tool to delete all AWS resources in an account. aws-nuk

The currently supported functionality includes:

* Deleting all Auto scaling groups in an AWS account
* Deleting all Elastic Load Balancers (Classic and V2) in an AWS account
* Deleting all EBS Volumes in an AWS account
* Deleting all unprotected EC2 instances in an AWS account

### WARNING: THIS TOOL IS HIGHLY DESTRUCTIVE, ALL SUPPORTED RESOURCES WILL BE DELETED. ITS EFFECTS ARE IRREVERSIBLE AND SHOULD NEVER BE USED IN A PRODUCTION ENVIRONMENT
Expand All @@ -19,6 +22,14 @@ The currently supported functionality includes:

Simply running `aws-nuke` will start the process of cleaning up your AWS account. You'll be shown a list of resources that'll be deleted as well as a prompt to confirm before any deletion actually takes place.

### Excluding Regions

You can use the `--exclude-region` flag to exclude resources in certain regions from being deleted. For example the following command does not nuke resources in `ap-south-1` and `ap-south-2` regions:

```shell
aws-nuke --exclude-region ap-south-1 --exclude-region ap-south-2
```

Happy Nuking!!!

## Credentials
Expand Down
63 changes: 63 additions & 0 deletions aws/asg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package aws

import (
awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/gruntwork-io/aws-nuke/logging"
"github.com/gruntwork-io/gruntwork-cli/errors"
)

// Returns a formatted string of ASG Names
func getAllAutoScalingGroups(session *session.Session, region string) ([]*string, error) {
svc := autoscaling.New(session)
result, err := svc.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{})
if err != nil {
return nil, errors.WithStackTrace(err)
}

var groupNames []*string
for _, group := range result.AutoScalingGroups {
groupNames = append(groupNames, group.AutoScalingGroupName)
}

return groupNames, nil
}

// Deletes all Auto Scaling Groups
func nukeAllAutoScalingGroups(session *session.Session, groupNames []*string) error {
svc := autoscaling.New(session)

if len(groupNames) == 0 {
logging.Logger.Infof("No Auto Scaling Groups to nuke in region %s", *session.Config.Region)
return nil
}

logging.Logger.Infof("Deleting all Auto Scaling Groups in region %s", *session.Config.Region)

for _, groupName := range groupNames {
params := &autoscaling.DeleteAutoScalingGroupInput{
AutoScalingGroupName: groupName,
ForceDelete: awsgo.Bool(true),
}

_, err := svc.DeleteAutoScalingGroup(params)
if err != nil {
logging.Logger.Errorf("[Failed] %s", err)
return errors.WithStackTrace(err)
}

logging.Logger.Infof("Deleted Auto Scaling Group: %s", *groupName)
}

err := svc.WaitUntilGroupNotExists(&autoscaling.DescribeAutoScalingGroupsInput{
AutoScalingGroupNames: groupNames,
})

if err != nil {
return errors.WithStackTrace(err)
}

logging.Logger.Infof("[OK] %d Auto Scaling Group(s) deleted in %s", len(groupNames), *session.Config.Region)
return nil
}
98 changes: 98 additions & 0 deletions aws/asg_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package aws

import (
"testing"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/gruntwork-io/aws-nuke/util"
"github.com/gruntwork-io/gruntwork-cli/errors"
"github.com/stretchr/testify/assert"
)

func createTestAutoScalingGroup(t *testing.T, session *session.Session, name string) {
svc := autoscaling.New(session)
instance := createTestEC2Instance(t, session, name)

param := &autoscaling.CreateAutoScalingGroupInput{
AutoScalingGroupName: &name,
InstanceId: instance.InstanceId,
MinSize: awsgo.Int64(1),
MaxSize: awsgo.Int64(2),
}

_, err := svc.CreateAutoScalingGroup(param)
if err != nil {
assert.Failf(t, "Could not create test ASG: %s", errors.WithStackTrace(err).Error())
}

err = svc.WaitUntilGroupExists(&autoscaling.DescribeAutoScalingGroupsInput{
AutoScalingGroupNames: []*string{&name},
})

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
}
}

func TestListAutoScalingGroups(t *testing.T) {
t.Parallel()

region := getRandomRegion()
session, err := session.NewSession(&awsgo.Config{
Region: awsgo.String(region)},
)

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
}

groupName := "aws-nuke-test-" + util.UniqueID()
createTestAutoScalingGroup(t, session, groupName)
// clean up after this test
defer nukeAllAutoScalingGroups(session, []*string{&groupName})

groupNames, err := getAllAutoScalingGroups(session, region)
if err != nil {
assert.Fail(t, "Unable to fetch list of Auto Scaling Groups")
}

assert.Contains(t, awsgo.StringValueSlice(groupNames), groupName)
}

func TestNukeAutoScalingGroups(t *testing.T) {
t.Parallel()

region := getRandomRegion()
session, err := session.NewSession(&awsgo.Config{
Region: awsgo.String(region)},
)
svc := autoscaling.New(session)

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
}

groupName := "aws-nuke-test-" + util.UniqueID()
createTestAutoScalingGroup(t, session, groupName)

_, err = svc.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{
AutoScalingGroupNames: []*string{&groupName},
})

if err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
}

if err := nukeAllAutoScalingGroups(session, []*string{&groupName}); err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
}

groupNames, err := getAllAutoScalingGroups(session, region)
if err != nil {
assert.Fail(t, "Unable to fetch list of Auto Scaling Groups")
}

assert.NotContains(t, awsgo.StringValueSlice(groupNames), groupName)
}
31 changes: 31 additions & 0 deletions aws/asg_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package aws

import (
awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/gruntwork-io/gruntwork-cli/errors"
)

// ASGroups - represents all auto scaling groups
type ASGroups struct {
GroupNames []string
}

// ResourceName - the simple name of the aws resource
func (group ASGroups) ResourceName() string {
return "asg"
}

// ResourceIdentifiers - The group names of the auto scaling groups
func (group ASGroups) ResourceIdentifiers() []string {
return group.GroupNames
}

// Nuke - nuke 'em all!!!
func (group ASGroups) Nuke(session *session.Session) error {
if err := nukeAllAutoScalingGroups(session, awsgo.StringSlice(group.GroupNames)); err != nil {
return errors.WithStackTrace(err)
}

return nil
}
97 changes: 90 additions & 7 deletions aws/aws.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,59 @@
package aws

import (
"math/rand"
"time"

awsgo "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/gruntwork-io/aws-nuke/logging"
"github.com/gruntwork-io/gruntwork-cli/collections"
"github.com/gruntwork-io/gruntwork-cli/errors"
)

// Returns a list of all AWS regions
func getAllRegions() []string {
// GetAllRegions - Returns a list of all AWS regions
func GetAllRegions() []string {
// chinese and government regions are not accessible with regular accounts
reservedRegions := []string{
"cn-north-1", "cn-northwest-1", "us-gov-west-1",
}

resolver := endpoints.DefaultResolver()
partitions := resolver.(endpoints.EnumPartitions).Partitions()

var regions []string
for _, p := range partitions {
for id := range p.Regions() {
regions = append(regions, id)
if !collections.ListContainsElement(reservedRegions, id) {
regions = append(regions, id)
}
}
}

return regions
}

func getRandomRegion() string {
allRegions := GetAllRegions()
rand.Seed(time.Now().UnixNano())
randIndex := rand.Intn(len(allRegions))
return allRegions[randIndex]
}

// GetAllResources - Lists all aws resources
func GetAllResources() (*AwsAccountResources, error) {
func GetAllResources(regions []string, excludedRegions []string) (*AwsAccountResources, error) {
account := AwsAccountResources{
Resources: make(map[string]AwsRegionResource),
}

for _, region := range getAllRegions() {
for _, region := range regions {
// Ignore all cli excluded regions
if collections.ListContainsElement(excludedRegions, region) {
logging.Logger.Infoln("Skipping region: " + region)
continue
}

session, err := session.NewSession(&awsgo.Config{
Region: awsgo.String(region)},
)
Expand All @@ -39,6 +64,49 @@ func GetAllResources() (*AwsAccountResources, error) {

resourcesInRegion := AwsRegionResource{}

// The order in which resources are nuked is important
// because of dependencies between resources

// ASG Names
groupNames, err := getAllAutoScalingGroups(session, region)
if err != nil {
return nil, errors.WithStackTrace(err)
}

asGroups := ASGroups{
GroupNames: awsgo.StringValueSlice(groupNames),
}

resourcesInRegion.Resources = append(resourcesInRegion.Resources, asGroups)
// End ASG Names

// LoadBalancer Names
elbNames, err := getAllElbInstances(session, region)
if err != nil {
return nil, errors.WithStackTrace(err)
}

loadBalancers := LoadBalancers{
Names: awsgo.StringValueSlice(elbNames),
}

resourcesInRegion.Resources = append(resourcesInRegion.Resources, loadBalancers)
// End LoadBalancer Names

// LoadBalancerV2 Arns
elbv2Arns, err := getAllElbv2Instances(session, region)
if err != nil {
return nil, errors.WithStackTrace(err)
}

loadBalancersV2 := LoadBalancersV2{
Arns: awsgo.StringValueSlice(elbv2Arns),
}

resourcesInRegion.Resources = append(resourcesInRegion.Resources, loadBalancersV2)
// End LoadBalancerV2 Arns

// EC2 Instances
instanceIds, err := getAllEc2Instances(session, region)
if err != nil {
return nil, errors.WithStackTrace(err)
Expand All @@ -49,15 +117,30 @@ func GetAllResources() (*AwsAccountResources, error) {
}

resourcesInRegion.Resources = append(resourcesInRegion.Resources, ec2Instances)
// End EC2 Instances

// EBS Volumes
volumeIds, err := getAllEbsVolumes(session, region)
if err != nil {
return nil, errors.WithStackTrace(err)
}

ebsVolumes := EBSVolumes{
VolumeIds: awsgo.StringValueSlice(volumeIds),
}

resourcesInRegion.Resources = append(resourcesInRegion.Resources, ebsVolumes)
// End EBS Volumes

account.Resources[region] = resourcesInRegion
}

return &account, nil
}

// NukeAllResources - Nukes all aws resources
func NukeAllResources(account *AwsAccountResources) error {
for _, region := range getAllRegions() {
func NukeAllResources(account *AwsAccountResources, regions []string) error {
for _, region := range regions {
session, err := session.NewSession(&awsgo.Config{
Region: awsgo.String(region)},
)
Expand Down
Loading

0 comments on commit 069d76f

Please sign in to comment.