diff --git a/README.md b/README.md index c260b0b5..1ba482a9 100644 --- a/README.md +++ b/README.md @@ -629,6 +629,40 @@ of the file that are supported are listed here. | network-firewall-resource-policy | NetworkFirewallResourcePolicy | ✅ (Firewall Resource Policy ARN) | ❌ | ❌ | ❌ | +### Resource Deletion and 'IsNukable' Check Option +#### Supported Resources for 'IsNukable' Check +For certain resources, such as `AMI`, `EBS`, `DHCP Option`, and others listed below, we support an option to verify whether the user has sufficient permissions to nuke the resources. If not, it will raise `error: INSUFFICIENT_PERMISSION` error. + +Supported resources: +- AMI +- EBS +- DHCP Option +- Egress only Internet Gateway +- Endpoints +- Internet Gatway +- IPAM +- IPAM BYOASN +- IPAM Custom Allocation +- IPAM Pool +- IPAM Resource Discovery +- IPAM Scope +- Key Pair +- Network ACL +- Network Interface +- Subnet +- VPC +- Elastic IP +- Launch Template +- NAT Gateway +- Network Firewall +- Security Group +- SnapShot +- Transit Gateway + +#### Unsupported Resources +Please note that the eligibility check for nukability relies on the `DryRun` feature provided by AWS. Regrettably, this feature is not available for all delete APIs of resource types. Hence, the 'eligibility check for nukability' option may not be accessible for all resource types + + ### How to Use Once you created your config file, you can run a command like this to nuke resources with your config file: diff --git a/aws/resources/ami.go b/aws/resources/ami.go index 31a202ac..6b355c65 100644 --- a/aws/resources/ami.go +++ b/aws/resources/ami.go @@ -2,9 +2,10 @@ package resources import ( "context" + "strings" + awsgo "github.com/aws/aws-sdk-go/aws" "github.com/gruntwork-io/cloud-nuke/util" - "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -54,6 +55,15 @@ func (ami *AMIs) getAll(c context.Context, configObj config.Config) ([]*string, } } + // checking the nukable permissions + ami.VerifyNukablePermissions(imageIds, func(id *string) error { + _, err := ami.Client.DeregisterImage(&ec2.DeregisterImageInput{ + ImageId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return imageIds, nil } @@ -62,17 +72,21 @@ func (ami *AMIs) nukeAll(imageIds []*string) error { if len(imageIds) == 0 { logging.Debugf("No AMI to nuke in region %s", ami.Region) return nil + } logging.Debugf("Deleting all AMI in region %s", ami.Region) deletedCount := 0 for _, imageID := range imageIds { - params := &ec2.DeregisterImageInput{ - ImageId: imageID, + if nukable, reason := ami.IsNukable(awsgo.StringValue(imageID)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(imageID), reason) + continue } - _, err := ami.Client.DeregisterImage(params) + _, err := ami.Client.DeregisterImage(&ec2.DeregisterImageInput{ + ImageId: imageID, + }) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/base_resource.go b/aws/resources/base_resource.go index 2eb05602..f4ce25e8 100644 --- a/aws/resources/base_resource.go +++ b/aws/resources/base_resource.go @@ -74,6 +74,11 @@ func (br *BaseAwsResource) PrepareContext(parentContext context.Context, resourc // executed, and the result (error or success) is recorded using the SetNukableStatus method, indicating whether // the specified action is nukable func (br *BaseAwsResource) VerifyNukablePermissions(ids []*string, nukableCheckfn func(id *string) error) { + // check if the 'Nukables' map is initialized, and if it's not, initialize it + if br.Nukables == nil { + br.Nukables = make(map[string]error) + } + for _, id := range ids { // skip if the id is already exists if _, ok := br.GetNukableStatus(*id); ok { diff --git a/aws/resources/ebs.go b/aws/resources/ebs.go index 28f03487..b700d4a4 100644 --- a/aws/resources/ebs.go +++ b/aws/resources/ebs.go @@ -2,7 +2,9 @@ package resources import ( "context" + "github.com/aws/aws-sdk-go/aws" + awsgo "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" @@ -34,6 +36,15 @@ func (ev *EBSVolumes) getAll(c context.Context, configObj config.Config) ([]*str } } + // checking the nukable permissions + ev.VerifyNukablePermissions(volumeIds, func(id *string) error { + _, err := ev.Client.DeleteVolume(&ec2.DeleteVolumeInput{ + VolumeId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return volumeIds, nil } @@ -63,6 +74,12 @@ func (ev *EBSVolumes) nukeAll(volumeIds []*string) error { var deletedVolumeIDs []*string for _, volumeID := range volumeIds { + + if nukable, reason := ev.IsNukable(awsgo.StringValue(volumeID)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(volumeID), reason) + continue + } + params := &ec2.DeleteVolumeInput{ VolumeId: volumeID, } diff --git a/aws/resources/ec2_dhcp_option.go b/aws/resources/ec2_dhcp_option.go index 742b3745..df2d8ee3 100644 --- a/aws/resources/ec2_dhcp_option.go +++ b/aws/resources/ec2_dhcp_option.go @@ -30,11 +30,24 @@ func (v *EC2DhcpOption) getAll(_ context.Context, configObj config.Config) ([]*s return nil, errors.WithStackTrace(err) } + // checking the nukable permissions + v.VerifyNukablePermissions(dhcpOptionIds, func(id *string) error { + _, err := v.Client.DeleteDhcpOptions(&ec2.DeleteDhcpOptionsInput{ + DhcpOptionsId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return dhcpOptionIds, nil } func (v *EC2DhcpOption) nukeAll(identifiers []*string) error { for _, identifier := range identifiers { + if nukable, reason := v.IsNukable(awsgo.StringValue(identifier)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(identifier), reason) + continue + } err := nukeDhcpOption(v.Client, identifier) if err != nil { diff --git a/aws/resources/ec2_egress_only_igw.go b/aws/resources/ec2_egress_only_igw.go index 8fc38d68..d312124e 100644 --- a/aws/resources/ec2_egress_only_igw.go +++ b/aws/resources/ec2_egress_only_igw.go @@ -73,8 +73,8 @@ func (egigw *EgressOnlyInternetGateway) nukeAll(ids []*string) error { // NOTE : We can skip the error checking and return it here, since it is already being checked while displaying the identifiers with the Nukable field. // Here, `err` refers to the error indicating whether the identifier is eligible for nuke or not (an error which we got from aws when tried to delete the resource with dryRun), // and it is not a programming error. (edited) - if nukable, err := egigw.IsNukable(*id); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + if nukable, reason := egigw.IsNukable(*id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/ec2_endpoints.go b/aws/resources/ec2_endpoints.go index 36946826..6e6d2af5 100644 --- a/aws/resources/ec2_endpoints.go +++ b/aws/resources/ec2_endpoints.go @@ -74,8 +74,8 @@ func (e *EC2Endpoints) nukeAll(identifiers []*string) error { var deletedAddresses []*string for _, id := range identifiers { - if nukable, err := e.IsNukable(*id); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + if nukable, reason := e.IsNukable(*id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/ec2_internet_gateway.go b/aws/resources/ec2_internet_gateway.go index 43f834a4..483edad8 100644 --- a/aws/resources/ec2_internet_gateway.go +++ b/aws/resources/ec2_internet_gateway.go @@ -81,8 +81,8 @@ func (igw *InternetGateway) nukeAll(identifiers []*string) error { var deletedGateways []*string for _, id := range identifiers { - if nukable, err := igw.IsNukable(*id); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + if nukable, reason := igw.IsNukable(*id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/ec2_ipam.go b/aws/resources/ec2_ipam.go index 23a7322f..9491c428 100644 --- a/aws/resources/ec2_ipam.go +++ b/aws/resources/ec2_ipam.go @@ -60,6 +60,16 @@ func (ec2Ipam *EC2IPAMs) getAll(c context.Context, configObj config.Config) ([]* return nil, errors.WithStackTrace(err) } + // checking the nukable permissions + ec2Ipam.VerifyNukablePermissions(result, func(id *string) error { + _, err := ec2Ipam.Client.DeleteIpam(&ec2.DeleteIpamInput{ + IpamId: id, + Cascade: aws.Bool(true), + DryRun: awsgo.Bool(true), + }) + return err + }) + return result, nil } @@ -220,7 +230,7 @@ func (ec2Ipam *EC2IPAMs) nukeIPAM(id *string) error { // items we need delete/detach them before actually deleting it. // NOTE: The actual IPAM deletion should always be the last one. This way we // can guarantee that it will fail if we forgot to delete/detach an item. - functions := []func(userName *string) error{ + functions := []func(*string) error{ ec2Ipam.nukePublicIPAMPools, ec2Ipam.deleteIPAM, } @@ -246,6 +256,11 @@ func (ec2Ipam *EC2IPAMs) nukeAll(ids []*string) error { for _, id := range ids { + if nukable, reason := ec2Ipam.IsNukable(awsgo.StringValue(id)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(id), reason) + continue + } + err := ec2Ipam.nukeIPAM(id) // Record status of this resource diff --git a/aws/resources/ec2_ipam_byoasn.go b/aws/resources/ec2_ipam_byoasn.go index 47aa6cf6..56ec05a0 100644 --- a/aws/resources/ec2_ipam_byoasn.go +++ b/aws/resources/ec2_ipam_byoasn.go @@ -4,6 +4,7 @@ import ( "context" "github.com/aws/aws-sdk-go/aws" + awsgo "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" @@ -26,6 +27,15 @@ func (byoasn *EC2IPAMByoasn) getAll(c context.Context, configObj config.Config) for _, out := range output.Byoasns { result = append(result, out.Asn) } + + // checking the nukable permissions + byoasn.VerifyNukablePermissions(result, func(id *string) error { + _, err := byoasn.Client.DisassociateIpamByoasn(&ec2.DisassociateIpamByoasnInput{ + Asn: id, + DryRun: awsgo.Bool(true), + }) + return err + }) return result, nil } @@ -40,11 +50,14 @@ func (byoasn *EC2IPAMByoasn) nukeAll(asns []*string) error { var list []*string for _, id := range asns { - params := &ec2.DisassociateIpamByoasnInput{ - Asn: id, + if nukable, reason := byoasn.IsNukable(awsgo.StringValue(id)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(id), reason) + continue } - _, err := byoasn.Client.DisassociateIpamByoasn(params) + _, err := byoasn.Client.DisassociateIpamByoasn(&ec2.DisassociateIpamByoasnInput{ + Asn: id, + }) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/ec2_ipam_custom_allocation.go b/aws/resources/ec2_ipam_custom_allocation.go index b861a0fc..1339a833 100644 --- a/aws/resources/ec2_ipam_custom_allocation.go +++ b/aws/resources/ec2_ipam_custom_allocation.go @@ -92,6 +92,29 @@ func (cs *EC2IPAMCustomAllocation) getAll(c context.Context, configObj config.Co } } + // checking the nukable permissions + cs.VerifyNukablePermissions(result, func(id *string) error { + cidr, err := cs.getPoolAllocationCIDR(id) + if err != nil { + logging.Errorf("[Failed] %s", err) + return err + } + + allocationIPAMPoolID, ok := cs.PoolAndAllocationMap[*id] + if !ok { + logging.Errorf("[Failed] %s", fmt.Errorf("unable to find the pool allocation with %s", *id)) + return fmt.Errorf("unable to find the pool allocation with %s", *id) + } + + _, err = cs.Client.ReleaseIpamPoolAllocation(&ec2.ReleaseIpamPoolAllocationInput{ + IpamPoolId: &allocationIPAMPoolID, + IpamPoolAllocationId: id, + Cidr: cidr, + DryRun: awsgo.Bool(true), + }) + return err + }) + return result, nil } @@ -107,6 +130,11 @@ func (cs *EC2IPAMCustomAllocation) nukeAll(ids []*string) error { for _, id := range ids { + if nukable, reason := cs.IsNukable(awsgo.StringValue(id)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(id), reason) + continue + } + // get the IPamPool details cidr, err := cs.getPoolAllocationCIDR(id) if err != nil { diff --git a/aws/resources/ec2_ipam_pool.go b/aws/resources/ec2_ipam_pool.go index 402c2f88..f8f9b4d7 100644 --- a/aws/resources/ec2_ipam_pool.go +++ b/aws/resources/ec2_ipam_pool.go @@ -64,6 +64,15 @@ func (ec2Pool *EC2IPAMPool) getAll(c context.Context, configObj config.Config) ( return nil, errors.WithStackTrace(err) } + // checking the nukable permissions + ec2Pool.VerifyNukablePermissions(result, func(id *string) error { + _, err := ec2Pool.Client.DeleteIpamPool(&ec2.DeleteIpamPoolInput{ + IpamPoolId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return result, nil } @@ -78,11 +87,15 @@ func (pool *EC2IPAMPool) nukeAll(ids []*string) error { var deletedAddresses []*string for _, id := range ids { - params := &ec2.DeleteIpamPoolInput{ - IpamPoolId: id, + + if nukable, reason := pool.IsNukable(awsgo.StringValue(id)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(id), reason) + continue } - _, err := pool.Client.DeleteIpamPool(params) + _, err := pool.Client.DeleteIpamPool(&ec2.DeleteIpamPoolInput{ + IpamPoolId: id, + }) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/ec2_ipam_resource_discovery.go b/aws/resources/ec2_ipam_resource_discovery.go index f19047a6..cae1b798 100644 --- a/aws/resources/ec2_ipam_resource_discovery.go +++ b/aws/resources/ec2_ipam_resource_discovery.go @@ -63,6 +63,15 @@ func (discovery *EC2IPAMResourceDiscovery) getAll(c context.Context, configObj c return nil, errors.WithStackTrace(err) } + // checking the nukable permissions + discovery.VerifyNukablePermissions(result, func(id *string) error { + _, err := discovery.Client.DeleteIpamResourceDiscovery(&ec2.DeleteIpamResourceDiscoveryInput{ + IpamResourceDiscoveryId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return result, nil } @@ -77,11 +86,15 @@ func (discovery *EC2IPAMResourceDiscovery) nukeAll(ids []*string) error { var deletedAddresses []*string for _, id := range ids { - params := &ec2.DeleteIpamResourceDiscoveryInput{ - IpamResourceDiscoveryId: id, + + if nukable, reason := discovery.IsNukable(awsgo.StringValue(id)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(id), reason) + continue } - _, err := discovery.Client.DeleteIpamResourceDiscovery(params) + _, err := discovery.Client.DeleteIpamResourceDiscovery(&ec2.DeleteIpamResourceDiscoveryInput{ + IpamResourceDiscoveryId: id, + }) // Record status of this resource e := report.Entry{ Identifier: awsgo.StringValue(id), diff --git a/aws/resources/ec2_ipam_scope.go b/aws/resources/ec2_ipam_scope.go index 228f1108..9f9abba9 100644 --- a/aws/resources/ec2_ipam_scope.go +++ b/aws/resources/ec2_ipam_scope.go @@ -67,6 +67,15 @@ func (ec2Scope *EC2IpamScopes) getAll(c context.Context, configObj config.Config return nil, errors.WithStackTrace(err) } + // checking the nukable permissions + ec2Scope.VerifyNukablePermissions(result, func(id *string) error { + _, err := ec2Scope.Client.DeleteIpamScope(&ec2.DeleteIpamScopeInput{ + IpamScopeId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return result, nil } @@ -81,11 +90,14 @@ func (scope *EC2IpamScopes) nukeAll(ids []*string) error { var deletedList []*string for _, id := range ids { - params := &ec2.DeleteIpamScopeInput{ - IpamScopeId: id, + if nukable, reason := scope.IsNukable(awsgo.StringValue(id)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(id), reason) + continue } - _, err := scope.Client.DeleteIpamScope(params) + _, err := scope.Client.DeleteIpamScope(&ec2.DeleteIpamScopeInput{ + IpamScopeId: id, + }) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/ec2_key_pair.go b/aws/resources/ec2_key_pair.go index 42616ea0..ae743039 100644 --- a/aws/resources/ec2_key_pair.go +++ b/aws/resources/ec2_key_pair.go @@ -2,6 +2,8 @@ package resources import ( "context" + + awsgo "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" @@ -28,6 +30,15 @@ func (k *EC2KeyPairs) getAll(c context.Context, configObj config.Config) ([]*str } } + // checking the nukable permissions + k.VerifyNukablePermissions(ids, func(id *string) error { + _, err := k.Client.DeleteKeyPair(&ec2.DeleteKeyPairInput{ + KeyPairId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return ids, nil } @@ -57,6 +68,11 @@ func (k *EC2KeyPairs) nukeAll(keypairIds []*string) error { deletedKeyPairs := 0 var multiErr *multierror.Error for _, keypair := range keypairIds { + if nukable, reason := k.IsNukable(awsgo.StringValue(keypair)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(keypair), reason) + continue + } + if err := k.deleteKeyPair(keypair); err != nil { logging.Errorf("[Failed] %s", err) multiErr = multierror.Append(multiErr, err) diff --git a/aws/resources/ec2_network_acl.go b/aws/resources/ec2_network_acl.go index 657dbf8c..aae5e577 100644 --- a/aws/resources/ec2_network_acl.go +++ b/aws/resources/ec2_network_acl.go @@ -159,8 +159,8 @@ func (nacl *NetworkACL) nukeAll(identifiers []*string) error { var deleted []*string for _, id := range identifiers { - if nukable, err := nacl.IsNukable(*id); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + if nukable, reason := nacl.IsNukable(*id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/ec2_network_interface.go b/aws/resources/ec2_network_interface.go index c0a9edc4..be94f723 100644 --- a/aws/resources/ec2_network_interface.go +++ b/aws/resources/ec2_network_interface.go @@ -180,8 +180,8 @@ func (ni *NetworkInterface) nukeAll(identifiers []*string) error { var deleted []*string for _, id := range identifiers { - if nukable, err := ni.IsNukable(*id); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + if nukable, reason := ni.IsNukable(*id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/ec2_subnet.go b/aws/resources/ec2_subnet.go index d1fd833b..11b18a0f 100644 --- a/aws/resources/ec2_subnet.go +++ b/aws/resources/ec2_subnet.go @@ -93,12 +93,12 @@ func (ec2subnet *EC2Subnet) nukeAll(ids []*string) error { for _, id := range ids { // check the id has the permission to nuke, if not. continue the execution - if nukable, err := ec2subnet.IsNukable(*id); !nukable { + if nukable, reason := ec2subnet.IsNukable(*id); !nukable { // not adding the report on final result hence not adding a record entry here // NOTE: We can skip the error checking and return it here, since it is already being checked while // displaying the identifiers. Here, `err` refers to the error indicating whether the identifier is eligible for nuke or not, // and it is not a programming error. - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/ec2_vpc.go b/aws/resources/ec2_vpc.go index 652ab8b2..0d395907 100644 --- a/aws/resources/ec2_vpc.go +++ b/aws/resources/ec2_vpc.go @@ -60,6 +60,15 @@ func (v *EC2VPCs) getAll(c context.Context, configObj config.Config) ([]*string, } } + // checking the nukable permissions + v.VerifyNukablePermissions(ids, func(id *string) error { + _, err := v.Client.DeleteVpc(&ec2.DeleteVpcInput{ + VpcId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return ids, nil } @@ -75,6 +84,11 @@ func (v *EC2VPCs) nukeAll(vpcIds []string) error { multiErr := new(multierror.Error) for _, id := range vpcIds { + if nukable, reason := v.IsNukable(id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", id, reason) + continue + } + var err error err = nuke(v.Client, v.ELBClient, id) diff --git a/aws/resources/eip.go b/aws/resources/eip.go index 58fa2e49..669b96fa 100644 --- a/aws/resources/eip.go +++ b/aws/resources/eip.go @@ -5,6 +5,7 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + awsgo "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" @@ -36,6 +37,15 @@ func (ea *EIPAddresses) getAll(c context.Context, configObj config.Config) ([]*s } } + // checking the nukable permissions + ea.VerifyNukablePermissions(allocationIds, func(id *string) error { + _, err := ea.Client.ReleaseAddress(&ec2.ReleaseAddressInput{ + AllocationId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return allocationIds, nil } @@ -61,11 +71,15 @@ func (ea *EIPAddresses) nukeAll(allocationIds []*string) error { var deletedAllocationIDs []*string for _, allocationID := range allocationIds { - params := &ec2.ReleaseAddressInput{ - AllocationId: allocationID, + + if nukable, reason := ea.IsNukable(awsgo.StringValue(allocationID)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(allocationID), reason) + continue } - _, err := ea.Client.ReleaseAddress(params) + _, err := ea.Client.ReleaseAddress(&ec2.ReleaseAddressInput{ + AllocationId: allocationID, + }) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/launch_template.go b/aws/resources/launch_template.go index 0b1154b1..94202972 100644 --- a/aws/resources/launch_template.go +++ b/aws/resources/launch_template.go @@ -2,7 +2,9 @@ package resources import ( "context" + "github.com/aws/aws-sdk-go/aws" + awsgo "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/gruntwork-io/cloud-nuke/config" "github.com/gruntwork-io/cloud-nuke/logging" @@ -27,6 +29,15 @@ func (lt *LaunchTemplates) getAll(c context.Context, configObj config.Config) ([ } } + // checking the nukable permissions + lt.VerifyNukablePermissions(templateNames, func(id *string) error { + _, err := lt.Client.DeleteLaunchTemplate(&ec2.DeleteLaunchTemplateInput{ + LaunchTemplateName: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return templateNames, nil } @@ -41,11 +52,15 @@ func (lt *LaunchTemplates) nukeAll(templateNames []*string) error { var deletedTemplateNames []*string for _, templateName := range templateNames { - params := &ec2.DeleteLaunchTemplateInput{ - LaunchTemplateName: templateName, + + if nukable, reason := lt.IsNukable(awsgo.StringValue(templateName)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(templateName), reason) + continue } - _, err := lt.Client.DeleteLaunchTemplate(params) + _, err := lt.Client.DeleteLaunchTemplate(&ec2.DeleteLaunchTemplateInput{ + LaunchTemplateName: templateName, + }) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/nat_gateway.go b/aws/resources/nat_gateway.go index bbbc42fd..ed9825e1 100644 --- a/aws/resources/nat_gateway.go +++ b/aws/resources/nat_gateway.go @@ -33,6 +33,16 @@ func (ngw *NatGateways) getAll(_ context.Context, configObj config.Config) ([]*s return !lastPage }, ) + + // checking the nukable permissions + ngw.VerifyNukablePermissions(allNatGateways, func(id *string) error { + _, err := ngw.Client.DeleteNatGateway(&ec2.DeleteNatGatewayInput{ + NatGatewayId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return allNatGateways, errors.WithStackTrace(err) } @@ -162,6 +172,12 @@ func (ngw *NatGateways) areAllNatGatewaysDeleted(identifiers []*string) (bool, e func (ngw *NatGateways) deleteAsync(wg *sync.WaitGroup, errChan chan error, ngwID *string) { defer wg.Done() + if nukable, reason := ngw.IsNukable(awsgo.StringValue(ngwID)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(ngwID), reason) + errChan <- nil + return + } + err := nukeNATGateway(ngw.Client, ngwID) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/network_firewall.go b/aws/resources/network_firewall.go index 7b823453..300e9214 100644 --- a/aws/resources/network_firewall.go +++ b/aws/resources/network_firewall.go @@ -93,8 +93,8 @@ func (nfw *NetworkFirewall) nukeAll(identifiers []*string) error { var deleted []*string for _, id := range identifiers { - if nukable, err := nfw.IsNukable(*id); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + if nukable, reason := nfw.IsNukable(*id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/security_group.go b/aws/resources/security_group.go index d67a7e7f..4ec48855 100644 --- a/aws/resources/security_group.go +++ b/aws/resources/security_group.go @@ -305,8 +305,8 @@ func (sg *SecurityGroup) nukeAll(identifiers []*string) error { for _, id := range identifiers { - if nukable, err := sg.IsNukable(*id); !nukable { - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + if nukable, reason := sg.IsNukable(*id); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue } diff --git a/aws/resources/snapshot.go b/aws/resources/snapshot.go index 32db2bca..6b92e19d 100644 --- a/aws/resources/snapshot.go +++ b/aws/resources/snapshot.go @@ -2,6 +2,7 @@ package resources import ( "context" + "github.com/aws/aws-sdk-go/aws" awsgo "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -40,6 +41,15 @@ func (s *Snapshots) getAll(c context.Context, configObj config.Config) ([]*strin } } + // checking the nukable permissions + s.VerifyNukablePermissions(snapshotIds, func(id *string) error { + _, err := s.Client.DeleteSnapshot(&ec2.DeleteSnapshotInput{ + SnapshotId: id, + DryRun: awsgo.Bool(true), + }) + return err + }) + return snapshotIds, nil } @@ -71,11 +81,15 @@ func (s *Snapshots) nukeAll(snapshotIds []*string) error { var deletedSnapshotIDs []*string for _, snapshotID := range snapshotIds { - params := &ec2.DeleteSnapshotInput{ - SnapshotId: snapshotID, + + if nukable, reason := s.IsNukable(awsgo.StringValue(snapshotID)); !nukable { + logging.Debugf("[Skipping] %s nuke because %v", awsgo.StringValue(snapshotID), reason) + continue } - _, err := s.Client.DeleteSnapshot(params) + _, err := s.Client.DeleteSnapshot(&ec2.DeleteSnapshotInput{ + SnapshotId: snapshotID, + }) // Record status of this resource e := report.Entry{ diff --git a/aws/resources/transit_gateway.go b/aws/resources/transit_gateway.go index 8f816b8b..26af0b94 100644 --- a/aws/resources/transit_gateway.go +++ b/aws/resources/transit_gateway.go @@ -73,9 +73,9 @@ func (tgw *TransitGateways) nukeAll(ids []*string) error { for _, id := range ids { //check the id has the permission to nuke, if not. continue the execution - if nukable, err := tgw.IsNukable(*id); !nukable { + if nukable, reason := tgw.IsNukable(*id); !nukable { //not adding the report on final result hence not adding a record entry here - logging.Debugf("[Skipping] %s nuke because %v", *id, err) + logging.Debugf("[Skipping] %s nuke because %v", *id, reason) continue }