Preface
Forgot what resources you created? Want to remove all your default VPC resources?
There are a number of options to provide you the capability to spin up resources with the ability to remove them when you are done. Two that come to mind are Terraform and CloudFormation.
But what if you created resources through the console or just want to nuke an entire testing account?
In this tutorial we will cover how to remove your resources that were not created with a state tracking IaC tool.
A tool like this can be extremely dangerous, you cannot get your resources back once these commands are run.
Requirements
- Terminal or Command Prompt
- AWS CLI
Tutorial
This tutorial will heavily rely on the AWS CLI reference guide.
https://docs.aws.amazon.com/cli/latest/reference/
Using the AWS CLI we can –
- List specific resources for a specific region.
- Loop through those resources.
- Delete those resources by their ID or relevant marker.
1 Specify your region
There are three ways to accomplish this.
- Using Profiles
- AWS Configure
- Tag on CLI call
For this tutorial we will be using profiles. This is beneficial because we can specifically choose an account and its region. We can also easily feed in different profiles when we create a bash script.
2 Create your profiles
When you use aws configure
it creates a default
profile for you. What we want to do is create multiple different profiles. This can be done by specifying a profile name.
aws configure --profile exampleDevAccount >> AWS Access Key ID [None]: AKIAI44QH8DHBEXAMPLE >> AWS Secret Access Key [None]: je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY >> Default region name [None]: us-east-1 >> Default output format [None]:
Provide the necessary information to configure your new profile. Make as many as you need.
3 List your items
Create and set a $profile
variable. This will be the name of the profile created in the previous step.
$profile = exampleDevAccount
To list resources in the AWS CLI we normally use the describe
and list
commands.
Lets list our first resource, subnets.
subnets=( $(aws ec2 --profile $profile describe-subnets --query Subnets[*].SubnetId --output text) )
We use the describe-subnets
command to fetch the subnets followed by the query tag to create a list of SubnetIds. This will be used later to delete the resources. The output type is of text. This is the format accepted by the delete commands.
4 Delete your items
Once we have our list of subnets set as a variable, in this example subnets
. We can now loop through the subnets and delete it. Keep in mind any resources that rely on subnets will block the deletion. So EC2 instances, routes etc.. need to be removed before calling your subnet deletion command.
We will be using the delete-subnet
command to remove the subnets.
if [ -n "$subnets" -a "$subnets" != "None" ]; then for s in ${subnets[@]}; do echo "DELETING SUBNET - $s" aws ec2 --profile $profile delete-subnet --subnet-id $s done fi
Couple things to note.
-n "$subnets"
checks if the variable isn’t null and"$subnets" != "None"
checks if the API didn’t return a “None” string. That way we don’t throw an exception when attempting to loop through the list.- These calls are normally asynchronous, they call the API and start the process of removing the resources. Certain resources take longer than others.
5 Example Resources
Below I have outlined how to delete other resources in their correct order not to cause any exceptions. For example I remove all the resources in a VPC before I remove the VPC itself.
Variables may rely on each other. For example you may need vpcL
later in other commands.
Virtual Interfaces
virtualInterfaces=( $(aws directconnect --profile $profile describe-virtual-interfaces --query virtualInterfaces[*].virtualInterfaceId --output text) ) if [ -n "$virtualInterfaces" -a "$virtualInterfaces" != "None" ]; then for vif in ${virtualInterfaces[@]}; do echo "DELETING VIRTUAL INTERFACES - $vif" aws directconnect --profile $profile delete-virtual-interface --virtual-interface-id $vif done fi
Direct Connect Associations
dcgs=( $(aws directconnect --profile $profile describe-direct-connect-gateways --query directConnectGateways[*].directConnectGatewayId --output text) ) if [ -n "$dcgs" -a "$dcgs" != "None" ]; then for dcg in ${dcgs[@]}; do virtualgateways=( $(aws directconnect --profile $profile describe-direct-connect-gateway-associations --direct-connect-gateway-id $dcg --query directConnectGatewayAssociations[*].virtualGatewayId --output text) ) if [ -n "$virtualgateways" -a "$virtualgateways" != "None" ]; then for vg in ${virtualgateways[@]}; do echo "DELETING DIRECT CONNECT ASSOS - $vg & $dcg" aws directconnect --profile $profile delete-direct-connect-gateway-association --direct-connect-gateway-id $dcg --virtual-gateway-id $vg done fi done fi
VPC Peering
vpcL=( $(aws ec2 --profile $profile describe-vpcs --query Vpcs[*].VpcId --output text) ) vpcPeerL=( $(aws ec2 --profile $profile describe-vpc-peering-connections --query VpcPeeringConnections[*].VpcPeeringConnectionId --output text) ) if [ -n "$vpcPeerL" -a "$vpcPeerL" != "None" ]; then for vpcPeerId in ${vpcPeerL[@]}; do echo "DELETE VPC PEERING - $vpcPeerId" aws ec2 --profile $profile delete-vpc-peering-connection --vpc-peering-connection-id $vpcPeerId done fi
NAT Gateways
nats=( $(aws ec2 --profile $profile describe-nat-gateways --query NatGateways[*].NatGatewayId --output text) ) if [ -n "$nats" -a "$nats" != "None" ]; then for nat in ${nats[@]}; do echo "DELETING NAT GATEWAY- $nat" aws ec2 --profile $profile delete-nat-gateway --nat-gateway-id $nat done fi
Releasing Addresses
addresses=( $(aws ec2 --profile $profile describe-addresses --filters "Name=domain,Values=vpc" --query Addresses[*].AllocationId --output text) ) if [ -n "$addresses" -a "$addresses" != "None" ]; then for address in ${addresses[@]}; do echo "RELEASING ADDRESS - $address" aws ec2 --profile $profile release-address --allocation-id $address done fi
Disassociating Network ACL from Subnet to Default for VPC
for vpcId in ${vpcL[@]}; do defaultNacl=( $(aws ec2 --profile $profile describe-network-acls --filter Name="default",Values="true" Name="vpc-id",Values="$vpcId" --query NetworkAcls[*].NetworkAclId --output text) ) nDefNacl=( $(aws ec2 --profile $profile describe-network-acls --filter Name="default",Values="false" Name="vpc-id",Values="$vpcId" --query NetworkAcls[*].Associations[*].NetworkAclAssociationId --output text) ) if [ -n "$nDefNacl" -a "$nDefNacl" != "None" ]; then for assoId in ${nDefNacl[@]}; do echo "UNASSOCIATE NETWORK ACL FROM SUBNET TO DEFAULT FOR VPC - $vpcId" aws ec2 --profile $profile replace-network-acl-association --association-id $assoId --network-acl-id $defaultNacl done fi done
Deleting Network ACLs
for vpcId in ${vpcL[@]}; do nonDefaultNacl=( $(aws ec2 --profile $profile describe-network-acls --filter Name="default",Values="false" Name="vpc-id",Values="$vpcId" --query NetworkAcls[*].NetworkAclId --output text) ) if [ -n "$nonDefaultNacl" -a "$nonDefaultNacl" != "None" ]; then for naclId in ${nonDefaultNacl[@]}; do echo "DELETING NETWORK ACL - $naclId" aws ec2 --profile $profile delete-network-acl --network-acl-id $naclId done fi done
Detaching and Deleting Internet Gateways
for vpcId in ${vpcL[@]}; do internetGateway=( $(aws ec2 --profile $profile describe-internet-gateways --filters "Name=attachment.vpc-id,Values=$vpcId" --query InternetGateways[0].InternetGatewayId --output text) ) if [ -n "$internetGateway" -a "$internetGateway" != "None" ]; then echo "DETACHING INTERNET GATEWAY - $internetGateway" aws ec2 --profile $profile detach-internet-gateway --internet-gateway-id $internetGateway --vpc-id $vpcId echo "DELETING INTERNET GATEWAY - $internetGateway" aws ec2 --profile $profile delete-internet-gateway --internet-gateway-id $internetGateway fi done
Detaching and Deleting VPN Gateways
for vpcId in ${vpcL[@]}; do vpnGateway=( $(aws ec2 --profile $profile describe-vpn-gateways --filters "Name=attachment.vpc-id,Values=$vpcId" --query VpnGateways[0].VpnGatewayId --output text) ) if [ -n "$vpnGateway" -a "$vpnGateway" != "None" ]; then echo "DETACHING VPN GATEWAY - $vpnGateway" aws ec2 --profile $profile detach-vpn-gateway --vpn-gateway-id $vpnGateway --vpc-id $vpcId echo "DELETING VPN GATEWAY - $vpnGateway" aws ec2 --profile $profile delete-vpn-gateway --vpn-gateway-id $vpnGateway fi done
Associating VPC to Default DHCP
for vpcId in ${vpcL[@]}; do echo "ASSOCIATING VPC - $vpcId TO DEFAULT DHCP" aws ec2 --profile $profile associate-dhcp-options --dhcp-options-id default --vpc-id $vpcId done
Deleting Subnets
subnets=( $(aws ec2 --profile $profile describe-subnets --query Subnets[*].SubnetId --output text) ) if [ -n "$subnets" -a "$subnets" != "None" ]; then for s in ${subnets[@]}; do echo "DELETING SUBNET - $s" aws ec2 --profile $profile delete-subnet --subnet-id $s done fi
Deleting Route Tables
routeTables=( $(aws ec2 --profile $profile describe-route-tables --filters --query 'RouteTables[?Associations[0].Main == null].RouteTableId' --output text) ) if [ -n "$routeTables" -a "$routeTables" != "None" ]; then for rt in ${routeTables[@]}; do echo "DELETING ROUTE TABLE - $rt" aws ec2 --profile $profile delete-route-table --route-table-id $rt done fi
Deleting DHCPs
dhcpOptions=( $(aws ec2 --profile $profile describe-dhcp-options --query DhcpOptions[*].DhcpOptionsId --output text) ) if [ -n "$dhcpOptions" -a "$dhcpOptions" != "None" ]; then for dhcp in ${dhcpOptions[@]}; do echo "DELETING DHCP - $dhcp" aws ec2 --profile $profile delete-dhcp-options --dhcp-options-id $dhcp done fi
Deleting Flow Logs
flowLogs=( $(aws ec2 --profile $profile describe-flow-logs --query FlowLogs.FlowLogId --output text) ) if [ -n "$flowLogs" -a "$flowLogs" != "None" ]; then for flowLog in ${flowLogs[@]}; do echo "DELETING FLOW LOG - $flowLog" aws ec2 --profile $profile delete-flow-logs --flow-log-id $flowLog done fi
Deleting VPN Direct Connect Gateways
if [ -n "$dcgs" -a "$dcgs" != "None" ]; then for dcg in ${dcgs[@]}; do echo "DELETING VPN DIRECT CONNECT GATEWAY - $dcg" aws directconnect --profile $profile delete-direct-connect-gateway --direct-connect-gateway-id $dcg done fi
Deleting VPCs
if [ -n "$vpcL" -a "$vpcL" != "None" ]; then for vpcId in ${vpcL[@]}; do echo "DELETING VPC - $vpcId" aws ec2 --profile $profile delete-vpc --vpc-id $vpcId done fi
Deleting CloudTrails
trails=( $(aws cloudtrail --profile $profile describe-trails --query trailList[*].TrailARN --output text) ) if [ -n "$trails" -a "$trails" != "None" ]; then for trail in ${trails[@]}; do echo "DELETING CLOUDTRAIL - $trail" aws cloudtrail --profile $profile delete-trail --name $trail done fi
Deleting SNS Topics
topics=( $(aws sns --profile $profile list-topics --query Topics[*].TopicArn --output text) ) if [ -n "$topics" -a "$topics" != "None" ]; then for topic in ${topics[@]}; do echo "DELETING SNS TOPICS - $trail" aws sns --profile $profile delete-topic --topic-arn $topic done fi
AWS GuardDuty: Disassociating and Deleting Members
detectors=( $(aws guardduty --profile $profile list-detectors --query DetectorIds --output text) ) if [ -n "$detectors" -a "$detectors" != "None" ]; then members=( $(aws guardduty --profile $profile list-members --detector-id $detectors --query Members[*].AccountId --output text) ) if [ -n "$members" -a "$members" != "None" ]; then for mem in ${members[@]}; do echo "DISASSOCIATE MEMBERS - $mem" aws guardduty --profile $profile disassociate-members --detector-id $detectors --account-ids $mem echo "DELETING MEMBERS - $mem" aws guardduty --profile $profile delete-members --detector-id $detectors --account-ids $mem done fi fi
AWS GuardDuty: Deleting Detectors
if [ -n "$detectors" -a "$detectors" != "None" ]; then for det in ${detectors[@]}; do echo "DELETING DETECTORS - $det" aws guardduty --profile $profile delete-detector --detector-id $det done fi
AWS Config: Stopping and Deleting Recorders
configRecorders=( $(aws configservice --profile $profile describe-configuration-recorders --query ConfigurationRecorders[*].name --output text) ) if [ -n "$configRecorders" -a "$configRecorders" != "None" ]; then for configName in ${configRecorders[@]}; do echo "STOPPING RECORDER - $configName" aws configservice --profile $profile stop-configuration-recorder --configuration-recorder-name $configName echo "DELETING RECORDER - $configName" aws configservice --profile $profile delete-configuration-recorder --configuration-recorder-name $configName done fi
AWS Config: Deleting Delivery Channels
delChannels=( $(aws configservice --profile $profile describe-delivery-channels --query DeliveryChannels[*].name --output text) ) if [ -n "$delChannels" -a "$delChannels" != "None" ]; then for chan in ${delChannels[@]}; do echo "DELETING DELIVERY CHANNEL - $chan" aws configservice --profile $profile delete-delivery-channel --delivery-channel-name $chan done fi
AWS Config: Deleting Aggregator
aggs=( $(aws configservice --profile $profile describe-configuration-aggregators --query ConfigurationAggregators[*].ConfigurationAggregatorName --output text) ) if [ -n "$aggs" -a "$aggs" != "None" ]; then for configAggName in ${aggs[@]}; do echo "DELETING AGGREGATORS - $configAggName" aws configservice --profile $profile delete-configuration-aggregator --configuration-aggregator-name $configAggName done fi
AWS S3: Suspending, Deleting Versioned Objects and Bucket
s3BucketNames=( $(aws s3api --profile $profile list-buckets --query Buckets[*].Name --output text) ) if [ -n "$s3BucketNames" -a "$s3BucketNames" != "None" ]; then for bucket in ${s3BucketNames[@]}; do echo "SUSPENDING VERSIONING - $bucket" aws s3api --profile $profile put-bucket-versioning --bucket $bucket --versioning-configuration Status=Suspended echo "GETTING VERSIONS AND DELETE MARKERS - $bucket" OBJECT_VERSIONS=$(aws --profile $profile --output text s3api list-object-versions --bucket $bucket | grep -E '^VERSIONS|^DELETEMARKERS') while read OBJECT_VERSION; do if [[ $OBJECT_VERSION == DELETEMARKERS* ]]; then KEY=$(echo $OBJECT_VERSION | awk '{print $3}') VERSION_ID=$(echo $OBJECT_VERSION | awk '{print $5}') else KEY=$(echo $OBJECT_VERSION | awk '{print $4}') VERSION_ID=$(echo $OBJECT_VERSION | awk '{print $8}') fi echo "DELETING OBJECTS - $bucket" aws s3api --profile $profile delete-object --bucket $bucket --key $KEY --version-id $VERSION_ID done <<< "$OBJECT_VERSIONS" echo "DELETING BUCKET - $bucket" aws s3 --profile $profile rb s3://$bucket --force done fi
AWS CloudFormation: Deleting CloudFormation Stacks
cloudStacks=( $(aws cloudformation --profile $profile describe-stacks --query Stacks[*].StackId --output text) ) if [ -n "$cloudStacks" -a "$cloudStacks" != "None" ]; then for stack in ${cloudStacks[@]}; do echo "DELETING STACK - $stack" aws cloudformation --profile $profile delete-stack --stack-name $stack done fi
Hasta la Vista, Baby! 🙂
The GitHub repository can be found here.
https://github.com/zghafari/aws-resource-destroyer
Comments
Moshe
Your web site has excellent web content. I bookmarked the site