Back to blog
AWSBest PracticesCloud SecurityNetworkingReliability

Load Balancer Deletion Protection Disabled on AWS

Learn why AWS load balancers need deletion protection, the outage risk of leaving it off, and how to enable it with CLI, Terraform, and policy-as-code.

TL;DR

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:DeleteLoadBalancer can 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

  1. Open the EC2 console and go to Load Balancers under the Load Balancing section.
  2. Select the load balancer you want to protect.
  3. Open the Attributes tab and choose Edit.
  4. Toggle Deletion protection on.
  5. 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 DeleteLoadBalancer and ModifyLoadBalancerAttributes events 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=production helps 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.