This check flags AWS load balancers running without deletion protection, which leaves them one accidental API call away from being wiped out and taking your application offline. Turn it on with aws elbv2 modify-load-balancer-attributes and enforce it in your IaC.
A load balancer is usually the front door to your application. Traffic from users, health checks, TLS termination, and routing logic all flow through it. When someone deletes that load balancer by mistake, every DNS record and listener rule pointing at it breaks, and your service goes dark until you rebuild and rewire everything.
The Load Balancer Deletion Protection Disabled check (lb_nodeletionprotection) looks at your AWS Elastic Load Balancers and reports any that do not have deletion protection enabled. It is a small attribute with an outsized impact on availability.
What this check detects
AWS Application Load Balancers (ALB), Network Load Balancers (NLB), and Gateway Load Balancers (GWLB) all support an attribute called deletion_protection.enabled. When this is set to false, the load balancer can be deleted with a single API call or one click in the console.
The check inspects the deletion_protection.enabled attribute on each load balancer in your account. If the value is false (the default), the resource is flagged.
Note: Deletion protection is disabled by default on every new load balancer. So unless you or your tooling explicitly enabled it, you almost certainly have load balancers that will fail this check.
This only applies to the modern elbv2 family (ALB, NLB, GWLB). Classic Load Balancers do not support a deletion protection attribute, so if you are still running Classic ELBs, that is a separate conversation about migrating off a deprecated resource.
Why it matters
Deletion protection is one of those guardrails that costs nothing and saves you on the worst possible day. Here is where the absence of it bites:
- Accidental deletion during cleanup. An engineer running a script to tear down a dev environment fat-fingers the wrong resource ARN, or a tag filter is broader than expected, and a production load balancer goes with it.
- Terraform or CloudFormation drift. A change to an IaC module forces a replacement, and the destroy step removes the load balancer before the create step finishes. Without protection, nothing stops it.
- Overly broad IAM permissions. A compromised or over-privileged credential with
elasticloadbalancing:DeleteLoadBalancercan knock out your entry point instantly. Deletion protection forces an extra deliberate step to disable it first. - Stale automation. Reaper scripts that clean up untagged or "orphaned" resources sometimes catch load balancers that are very much in use.
The recovery cost is not just rebuilding the load balancer. A new ALB or NLB gets a new DNS name and, for NLBs, potentially new static IPs. Anything hardcoded to the old name, allowlisted IP, or attached target group config has to be reconfigured. If clients cache DNS or you have third parties with the old endpoint in their firewall rules, the outage stretches well past the few minutes it takes to recreate the resource.
Deletion protection does not stop a determined operator. It stops an accident. That distinction is exactly why it belongs on every production load balancer.
How to fix it
You can enable deletion protection from the console, the CLI, or your infrastructure-as-code tooling. Pick whichever matches how the resource was created.
Option 1: AWS CLI
First, find the load balancers that need attention. This lists every load balancer with its ARN:
aws elbv2 describe-load-balancers \
--query 'LoadBalancers[].{Name:LoadBalancerName,Arn:LoadBalancerArn}' \
--output table
Check the current attribute on a specific load balancer:
aws elbv2 describe-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/abc123 \
--query "Attributes[?Key=='deletion_protection.enabled']"
Now enable deletion protection:
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/abc123 \
--attributes Key=deletion_protection.enabled,Value=true
Tip: To sweep an entire region at once, loop over every load balancer ARN and apply the attribute. This one-liner enables protection on all of them:
for arn in $(aws elbv2 describe-load-balancers --query 'LoadBalancers[].LoadBalancerArn' --output text); do
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn "$arn" \
--attributes Key=deletion_protection.enabled,Value=true
echo "Enabled deletion protection on $arn"
done
Option 2: AWS Console
- Open the EC2 console and go to Load Balancers under the Load Balancing section.
- Select the load balancer you want to protect.
- Open the Attributes tab and choose Edit.
- Toggle Deletion protection on.
- Save changes.
Option 3: Terraform
If your load balancers are managed by Terraform, fixing it in the console or CLI will only get overwritten on the next apply. Set the attribute in code instead:
resource "aws_lb" "app" {
name = "my-alb"
load_balancer_type = "application"
subnets = var.subnet_ids
security_groups = [aws_security_group.alb.id]
enable_deletion_protection = true
}
Warning: Once enable_deletion_protection = true is applied, Terraform itself cannot destroy the load balancer either. If a future change forces a replacement, the apply will fail until you set the attribute back to false. That friction is intentional, but plan your migrations around it.
Option 4: CloudFormation
Resources:
AppLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: my-alb
Type: application
Subnets: !Ref SubnetIds
LoadBalancerAttributes:
- Key: deletion_protection.enabled
Value: "true"
How to prevent it from happening again
Flipping the attribute once does not stop the next load balancer from being created without it. Bake the default into the places where infrastructure is born.
Make it the default in shared modules
If teams provision load balancers through a shared Terraform module, set enable_deletion_protection = true as the default value. Make consumers opt out explicitly rather than opt in.
variable "enable_deletion_protection" {
type = bool
default = true
}
Gate it in CI with policy-as-code
Catch missing protection before it ever reaches AWS. With Open Policy Agent and Conftest, you can scan a Terraform plan in your pipeline:
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_lb"
not resource.change.after.enable_deletion_protection
msg := sprintf("Load balancer '%s' must have deletion protection enabled", [resource.address])
}
Wire that into the pipeline so a failing check blocks the merge:
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
conftest test tfplan.json --policy policy/
Tip: Run the Lensix lb_nodeletionprotection check on a schedule so any load balancer created outside your IaC pipeline, like a quick console experiment that became permanent, still gets caught.
Restrict who can disable it
Use a service control policy or IAM condition to limit who can turn deletion protection off or delete a protected load balancer. This raises the bar so that removing protection is a deliberate, auditable action rather than a side effect of routine work.
Best practices
- Treat deletion protection as standard for anything serving production traffic. The handful of seconds it adds to a legitimate teardown is nothing next to an unplanned outage.
- Pair it with monitoring. Set up a CloudTrail alert on
DeleteLoadBalancerandModifyLoadBalancerAttributesevents so you know the moment someone disables protection or removes a load balancer. - Apply the same thinking to related resources. Target groups, RDS instances, and EC2 instances all have their own deletion or termination protection. A load balancer with protection but an unprotected database behind it only solves half the problem.
- Document the disable procedure. When a planned decommission needs protection turned off, have a short runbook so engineers do not disable it in a panic and forget to re-enable it on the next load balancer.
- Tag for intent. A tag like
lifecycle=productionhelps reaper scripts and humans alike tell which resources should never be touched casually.
Note: Deletion protection only blocks deletion. It does nothing for misconfigured listeners, expired certificates, or unhealthy targets. Think of it as one layer of a broader availability posture, not a complete one.
Enabling deletion protection is one of the cheapest reliability wins in AWS. It takes a single attribute, costs nothing, and removes an entire category of self-inflicted outage. Turn it on everywhere it matters, enforce it in code, and let Lensix keep watch for the ones that slip through.

