A list of frequently used snippets I use during deployments
Sometimes… well, we screwed something up and need to recreate an
environment. We do this with a simple terraform destroy
but
get stuck due to deletion protections.
This snippet quickly disables all RDS deletion protections:
aws rds describe-db-instances --query "DBInstances[*].DBInstanceIdentifier" --output text | tr '\t' '\n' | while read -r db; do aws rds modify-db-instance --db-instance-identifier $db --apply-immediately --no-deletion-protection ; done
Wait, who turned off deletion protection? Are they insane?!
Turn it back on!
aws rds describe-db-instances --query "DBInstances[*].DBInstanceIdentifier" --output text | tr '\t' '\n' | while read -r db; do aws rds modify-db-instance --db-instance-identifier $db --apply-immediately --deletion-protection ; done
Sometimes… well, we screwed something up and need to recreate an environment. We do this with a simple terraform destroy but get stuck due to deletion protections.
This snippet quickly disables all ALB and NLB deletion protections:
aws elbv2 describe-load-balancers --query "LoadBalancers[*].LoadBalancerArn" --output text | tr '\t' '\n' | while read -r arn; do aws elbv2 modify-load-balancer-attributes --load-balancer-arn $arn --attributes "Key=deletion_protection.enabled,Value=false"; done
aws elbv2 describe-load-balancers --query "LoadBalancers[?Type=='network'].LoadBalancerArn" --output text | tr '\t' '\n' | while read -r arn; do aws elbv2 modify-load-balancer-attributes --load-balancer-arn $arn --attributes "Key=load_balancing.cross_zone.enabled,Value=false"; done
Note: The network load balancer also requires disabling the
cross_zone
feature, or it will act as a secondary deletion
protection.
Wait, who turned off deletion protection? Are they insane?!
Turn it back on!
aws elbv2 describe-load-balancers --query "LoadBalancers[*].LoadBalancerArn" --output text | tr '\t' '\n' | while read -r arn; do aws elbv2 modify-load-balancer-attributes --load-balancer-arn $arn --attributes "Key=deletion_protection.enabled,Value=true"; done
aws elbv2 describe-load-balancers --query "LoadBalancers[?Type=='network'].LoadBalancerArn" --output text | tr '\t' '\n' | while read -r arn; do aws elbv2 modify-load-balancer-attributes --load-balancer-arn $arn --attributes "Key=load_balancing.cross_zone.enabled,Value=true"; done
Anyone who has ever created an API Gateway in Terraform has probably
encountered this problem: The aws_api_gateway_deployment
resource tries to be deployed before the methods and integrations are
fully set up.
This snippet generates a depends_on
list for the
aws_api_gateway_deployment
resource, ensuring deployment
only happens after all integrations are created.
A frequent error is:
Error: Error creating API Gateway Deployment: BadRequestException: No integration defined for method
This occurs because the integration resource has yet to be created, but the deployment has already run.
Since integrations tend to be one of the last parts of the API
Gateway setup, I chose to base the dependency list on them. While not
many resources depend on aws_api_gateway_integration
, it is
dependent on almost everything else.
egrep -oh '"aws_api_gateway_integration" "[a-z|A-Z|_|0-9]+"' *.tf | sed -e "s/\"//g" | sed -e "s/ /./g" | sed -e "s/$/,/g"
If you have multiple API Gateways, adjust the *.tf
pattern to match the correct files.
This will result in a list like:
aws_api_gateway_integration.example.example_get,
aws_api_gateway_integration.example.example_post,
aws_api_gateway_integration.example_hello.example_get,
aws_api_gateway_integration.example_world.example_put,
aws_api_gateway_integration.example_world.example_post,
aws_api_gateway_integration.example_tom.example_get,
aws_api_gateway_integration.example_tom.example_post,
You can integrate this into your
aws_api_gateway_deployment
:
resource "aws_api_gateway_deployment" "rest_api_gateway_deployment" {
depends_on = [
aws_api_gateway_integration.example.example_get,
aws_api_gateway_integration.example.example_post,
aws_api_gateway_integration.example_hello.example_get,
aws_api_gateway_integration.example_world.example_put,
aws_api_gateway_integration.example_world.example_post,
aws_api_gateway_integration.example_tom.example_get,
aws_api_gateway_integration.example_tom.example_post,
]
...
}
Now, Terraform will automatically place the
aws_api_gateway_deployment
step almost at the end.
Some of you know that I am a very declarative person when it comes to Terraform. I strongly believe that most extra work arises from having too many poorly structured modules. Too much effort is spent handling edge cases that don’t need special handling. Dynamic blocks are bad, and imperative functions ruin Terraform code.
One downside of this approach is that an environment can have a lot of files because we separate all resources into their own files for cleanliness. I personally like this setup, but it takes some getting used to.
To make this more manageable, we add a File
tag to all
resources, indicating the filename that holds the resource. However,
since IT people are generally lazy, they never update this manually.
While Terraform doesn’t care where resources are placed, humans do.
So, here’s a one-liner that goes over all .tf
files and
corrects the File
tag—but only for resources that already
have a File
tag present: