Skip to content

Commit

Permalink
Merge pull request #8 from gruntwork-io/nuke-time-span
Browse files Browse the repository at this point in the history
Add nuking resources older than a particular time
  • Loading branch information
brikis98 authored Feb 17, 2018
2 parents f06752f + 1d249e3 commit d7231e3
Show file tree
Hide file tree
Showing 14 changed files with 160 additions and 41 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ You can use the `--exclude-region` flag to exclude resources in certain regions
aws-nuke --exclude-region ap-south-1 --exclude-region ap-south-2
```

### Excluding Resources by Age

You can use the `--older-than` flag to only nuke resources that were created before a certain period, the possible values are all valid values for [ParseDuration](https://golang.org/pkg/time/#ParseDuration) For example the following command nukes resources that are at least one day old:

```shell
aws-nuke --older-than 24h
```

Happy Nuking!!!

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

import (
"time"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/autoscaling"
Expand All @@ -9,7 +11,7 @@ import (
)

// Returns a formatted string of ASG Names
func getAllAutoScalingGroups(session *session.Session, region string) ([]*string, error) {
func getAllAutoScalingGroups(session *session.Session, region string, excludeAfter time.Time) ([]*string, error) {
svc := autoscaling.New(session)
result, err := svc.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{})
if err != nil {
Expand All @@ -18,7 +20,9 @@ func getAllAutoScalingGroups(session *session.Session, region string) ([]*string

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

return groupNames, nil
Expand Down
14 changes: 11 additions & 3 deletions aws/asg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"testing"
"time"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
Expand All @@ -13,7 +14,7 @@ import (

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

param := &autoscaling.CreateAutoScalingGroupInput{
AutoScalingGroupName: &name,
Expand Down Expand Up @@ -53,7 +54,14 @@ func TestListAutoScalingGroups(t *testing.T) {
// clean up after this test
defer nukeAllAutoScalingGroups(session, []*string{&groupName})

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

assert.NotContains(t, awsgo.StringValueSlice(groupNames), groupName)

groupNames, err = getAllAutoScalingGroups(session, region, time.Now().Add(1*time.Hour))
if err != nil {
assert.Fail(t, "Unable to fetch list of Auto Scaling Groups")
}
Expand Down Expand Up @@ -89,7 +97,7 @@ func TestNukeAutoScalingGroups(t *testing.T) {
assert.Fail(t, errors.WithStackTrace(err).Error())
}

groupNames, err := getAllAutoScalingGroups(session, region)
groupNames, err := getAllAutoScalingGroups(session, region, time.Now().Add(1*time.Hour))
if err != nil {
assert.Fail(t, "Unable to fetch list of Auto Scaling Groups")
}
Expand Down
12 changes: 6 additions & 6 deletions aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func getRandomRegion() string {
}

// GetAllResources - Lists all aws resources
func GetAllResources(regions []string, excludedRegions []string) (*AwsAccountResources, error) {
func GetAllResources(regions []string, excludedRegions []string, excludeAfter time.Time) (*AwsAccountResources, error) {
account := AwsAccountResources{
Resources: make(map[string]AwsRegionResource),
}
Expand All @@ -68,7 +68,7 @@ func GetAllResources(regions []string, excludedRegions []string) (*AwsAccountRes
// because of dependencies between resources

// ASG Names
groupNames, err := getAllAutoScalingGroups(session, region)
groupNames, err := getAllAutoScalingGroups(session, region, excludeAfter)
if err != nil {
return nil, errors.WithStackTrace(err)
}
Expand All @@ -81,7 +81,7 @@ func GetAllResources(regions []string, excludedRegions []string) (*AwsAccountRes
// End ASG Names

// LoadBalancer Names
elbNames, err := getAllElbInstances(session, region)
elbNames, err := getAllElbInstances(session, region, excludeAfter)
if err != nil {
return nil, errors.WithStackTrace(err)
}
Expand All @@ -94,7 +94,7 @@ func GetAllResources(regions []string, excludedRegions []string) (*AwsAccountRes
// End LoadBalancer Names

// LoadBalancerV2 Arns
elbv2Arns, err := getAllElbv2Instances(session, region)
elbv2Arns, err := getAllElbv2Instances(session, region, excludeAfter)
if err != nil {
return nil, errors.WithStackTrace(err)
}
Expand All @@ -107,7 +107,7 @@ func GetAllResources(regions []string, excludedRegions []string) (*AwsAccountRes
// End LoadBalancerV2 Arns

// EC2 Instances
instanceIds, err := getAllEc2Instances(session, region)
instanceIds, err := getAllEc2Instances(session, region, excludeAfter)
if err != nil {
return nil, errors.WithStackTrace(err)
}
Expand All @@ -120,7 +120,7 @@ func GetAllResources(regions []string, excludedRegions []string) (*AwsAccountRes
// End EC2 Instances

// EBS Volumes
volumeIds, err := getAllEbsVolumes(session, region)
volumeIds, err := getAllEbsVolumes(session, region, excludeAfter)
if err != nil {
return nil, errors.WithStackTrace(err)
}
Expand Down
7 changes: 5 additions & 2 deletions aws/ebs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"strings"
"time"

"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
Expand All @@ -10,7 +11,7 @@ import (
)

// Returns a formatted string of EBS volume ids
func getAllEbsVolumes(session *session.Session, region string) ([]*string, error) {
func getAllEbsVolumes(session *session.Session, region string, excludeAfter time.Time) ([]*string, error) {
svc := ec2.New(session)

result, err := svc.DescribeVolumes(&ec2.DescribeVolumesInput{})
Expand All @@ -20,7 +21,9 @@ func getAllEbsVolumes(session *session.Session, region string) ([]*string, error

var volumeIds []*string
for _, volume := range result.Volumes {
volumeIds = append(volumeIds, volume.VolumeId)
if excludeAfter.After(*volume.CreateTime) {
volumeIds = append(volumeIds, volume.VolumeId)
}
}

return volumeIds, nil
Expand Down
14 changes: 11 additions & 3 deletions aws/ebs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"testing"
"time"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
Expand Down Expand Up @@ -79,7 +80,14 @@ func TestListEBSVolumes(t *testing.T) {
// clean up after this test
defer nukeAllEbsVolumes(session, []*string{volume.VolumeId})

volumeIds, err := getAllEbsVolumes(session, region)
volumeIds, err := getAllEbsVolumes(session, region, time.Now().Add(1*time.Hour*-1))
if err != nil {
assert.Fail(t, "Unable to fetch list of EBS Volumes")
}

assert.NotContains(t, awsgo.StringValueSlice(volumeIds), awsgo.StringValue(volume.VolumeId))

volumeIds, err = getAllEbsVolumes(session, region, time.Now().Add(1*time.Hour))
if err != nil {
assert.Fail(t, "Unable to fetch list of EBS Volumes")
}
Expand All @@ -100,7 +108,7 @@ func TestNukeEBSVolumes(t *testing.T) {
}

uniqueTestID := "aws-nuke-test-" + util.UniqueID()
createTestEC2Instance(t, session, uniqueTestID)
createTestEC2Instance(t, session, uniqueTestID, false)

output, err := ec2.New(session).DescribeVolumes(&ec2.DescribeVolumesInput{})
if err != nil {
Expand All @@ -112,7 +120,7 @@ func TestNukeEBSVolumes(t *testing.T) {
if err := nukeAllEbsVolumes(session, volumeIds); err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
}
volumes, err := getAllEbsVolumes(session, region)
volumes, err := getAllEbsVolumes(session, region, time.Now().Add(1*time.Hour))

if err != nil {
assert.Fail(t, "Unable to fetch list of EC2 Instances")
Expand Down
12 changes: 8 additions & 4 deletions aws/ec2.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package aws

import (
"time"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
Expand All @@ -9,7 +11,7 @@ import (
)

// returns only instance Ids of unprotected ec2 instances
func filterOutProtectedInstances(svc *ec2.EC2, output *ec2.DescribeInstancesOutput) ([]*string, error) {
func filterOutProtectedInstances(svc *ec2.EC2, output *ec2.DescribeInstancesOutput, excludeAfter time.Time) ([]*string, error) {
var filteredIds []*string
for _, reservation := range output.Reservations {
for _, instance := range reservation.Instances {
Expand All @@ -27,7 +29,9 @@ func filterOutProtectedInstances(svc *ec2.EC2, output *ec2.DescribeInstancesOutp
protected := *attr.DisableApiTermination.Value
// Exclude protected EC2 instances
if !protected {
filteredIds = append(filteredIds, &instanceID)
if excludeAfter.After(*instance.LaunchTime) {
filteredIds = append(filteredIds, &instanceID)
}
}
}
}
Expand All @@ -36,7 +40,7 @@ func filterOutProtectedInstances(svc *ec2.EC2, output *ec2.DescribeInstancesOutp
}

// Returns a formatted string of EC2 instance ids
func getAllEc2Instances(session *session.Session, region string) ([]*string, error) {
func getAllEc2Instances(session *session.Session, region string, excludeAfter time.Time) ([]*string, error) {
svc := ec2.New(session)

params := &ec2.DescribeInstancesInput{
Expand All @@ -56,7 +60,7 @@ func getAllEc2Instances(session *session.Session, region string) ([]*string, err
return nil, errors.WithStackTrace(err)
}

instanceIds, err := filterOutProtectedInstances(svc, output)
instanceIds, err := filterOutProtectedInstances(svc, output, excludeAfter)
if err != nil {
return nil, errors.WithStackTrace(err)
}
Expand Down
31 changes: 21 additions & 10 deletions aws/ec2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"testing"
"time"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
Expand All @@ -11,7 +12,7 @@ import (
"github.com/stretchr/testify/assert"
)

func createTestEC2Instance(t *testing.T, session *session.Session, name string) ec2.Instance {
func createTestEC2Instance(t *testing.T, session *session.Session, name string, protected bool) ec2.Instance {
svc := ec2.New(session)

imagesResult, err := svc.DescribeImages(&ec2.DescribeImagesInput{
Expand All @@ -31,10 +32,11 @@ func createTestEC2Instance(t *testing.T, session *session.Session, name string)
imageID := *imagesResult.Images[0].ImageId

params := &ec2.RunInstancesInput{
ImageId: awsgo.String(imageID),
InstanceType: awsgo.String("t2.micro"),
MinCount: awsgo.Int64(1),
MaxCount: awsgo.Int64(1),
ImageId: awsgo.String(imageID),
InstanceType: awsgo.String("t2.micro"),
MinCount: awsgo.Int64(1),
MaxCount: awsgo.Int64(1),
DisableApiTermination: awsgo.Bool(protected),
}

runResult, err := svc.RunInstances(params)
Expand Down Expand Up @@ -125,17 +127,26 @@ func TestListInstances(t *testing.T) {
}

uniqueTestID := "aws-nuke-test-" + util.UniqueID()
instance := createTestEC2Instance(t, session, uniqueTestID)
instance := createTestEC2Instance(t, session, uniqueTestID, false)
protectedInstance := createTestEC2Instance(t, session, uniqueTestID, true)
// clean up after this test
defer nukeAllEc2Instances(session, []*string{instance.InstanceId})
defer nukeAllEc2Instances(session, []*string{instance.InstanceId, protectedInstance.InstanceId})

instanceIds, err := getAllEc2Instances(session, region)
instanceIds, err := getAllEc2Instances(session, region, time.Now().Add(1*time.Hour*-1))
if err != nil {
assert.Fail(t, "Unable to fetch list of EC2 Instances")
}

assert.NotContains(t, instanceIds, instance.InstanceId)
assert.NotContains(t, instanceIds, protectedInstance.InstanceId)

instanceIds, err = getAllEc2Instances(session, region, time.Now().Add(1*time.Hour))
if err != nil {
assert.Fail(t, "Unable to fetch list of EC2 Instances")
}

assert.Contains(t, instanceIds, instance.InstanceId)
assert.NotContains(t, instanceIds, protectedInstance.InstanceId)
}

func TestNukeInstances(t *testing.T) {
Expand All @@ -151,7 +162,7 @@ func TestNukeInstances(t *testing.T) {
}

uniqueTestID := "aws-nuke-test-" + util.UniqueID()
createTestEC2Instance(t, session, uniqueTestID)
createTestEC2Instance(t, session, uniqueTestID, false)

output, err := ec2.New(session).DescribeInstances(&ec2.DescribeInstancesInput{})
if err != nil {
Expand All @@ -163,7 +174,7 @@ func TestNukeInstances(t *testing.T) {
if err := nukeAllEc2Instances(session, instanceIds); err != nil {
assert.Fail(t, errors.WithStackTrace(err).Error())
}
instances, err := getAllEc2Instances(session, region)
instances, err := getAllEc2Instances(session, region, time.Now().Add(1*time.Hour))

if err != nil {
assert.Fail(t, "Unable to fetch list of EC2 Instances")
Expand Down
6 changes: 4 additions & 2 deletions aws/elb.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func waitUntilElbDeleted(svc *elb.ELB, input *elb.DescribeLoadBalancersInput) er
}

// Returns a formatted string of ELB names
func getAllElbInstances(session *session.Session, region string) ([]*string, error) {
func getAllElbInstances(session *session.Session, region string, excludeAfter time.Time) ([]*string, error) {
svc := elb.New(session)
result, err := svc.DescribeLoadBalancers(&elb.DescribeLoadBalancersInput{})
if err != nil {
Expand All @@ -38,7 +38,9 @@ func getAllElbInstances(session *session.Session, region string) ([]*string, err

var names []*string
for _, balancer := range result.LoadBalancerDescriptions {
names = append(names, balancer.LoadBalancerName)
if excludeAfter.After(*balancer.CreatedTime) {
names = append(names, balancer.LoadBalancerName)
}
}

return names, nil
Expand Down
12 changes: 10 additions & 2 deletions aws/elb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"testing"
"time"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
Expand Down Expand Up @@ -51,7 +52,14 @@ func TestListELBs(t *testing.T) {
// clean up after this test
defer nukeAllElbInstances(session, []*string{&elbName})

elbNames, err := getAllElbInstances(session, region)
elbNames, err := getAllElbInstances(session, region, time.Now().Add(1*time.Hour*-1))
if err != nil {
assert.Fail(t, "Unable to fetch list of Auto Scaling Groups")
}

assert.NotContains(t, awsgo.StringValueSlice(elbNames), elbName)

elbNames, err = getAllElbInstances(session, region, time.Now().Add(1*time.Hour))
if err != nil {
assert.Fail(t, "Unable to fetch list of Auto Scaling Groups")
}
Expand Down Expand Up @@ -89,7 +97,7 @@ func TestNukeELBs(t *testing.T) {
assert.Fail(t, errors.WithStackTrace(err).Error())
}

elbNames, err := getAllElbInstances(session, region)
elbNames, err := getAllElbInstances(session, region, time.Now().Add(1*time.Hour))
if err != nil {
assert.Fail(t, "Unable to fetch list of ELBs")
}
Expand Down
Loading

0 comments on commit d7231e3

Please sign in to comment.