Skip to main content

Ensure Classic Load Balancer is Configured Across Multiple Availability Zones

Overview

This check verifies that your Classic Load Balancer spans at least two Availability Zones (AZs). Running a load balancer in multiple AZs ensures your application remains available even if one AZ experiences an outage.

Risk

A Classic Load Balancer configured in a single Availability Zone becomes a single point of failure. If that AZ experiences an outage or degradation, your load balancer cannot redirect traffic to healthy targets in other zones, potentially causing complete service unavailability.

Remediation Steps

Prerequisites

  • AWS Console access with permissions to modify Elastic Load Balancers
  • At least two subnets in different Availability Zones within your VPC
Required IAM permissions

You need the following permissions to modify Classic Load Balancers:

  • elasticloadbalancing:DescribeLoadBalancers
  • elasticloadbalancing:AttachLoadBalancerToSubnets
  • elasticloadbalancing:EnableAvailabilityZonesForLoadBalancer
  • ec2:DescribeSubnets
  • ec2:DescribeAvailabilityZones

AWS Console Method

  1. Open the EC2 Console
  2. In the left navigation, scroll down and click Load Balancers
  3. Select the Classic Load Balancer you want to modify
  4. Click the Network mapping tab (or Description tab in older console versions)
  5. Click Edit subnets or Edit Availability Zones
  6. Select subnets from at least two different Availability Zones
  7. Click Save

After saving, verify that the load balancer now shows multiple Availability Zones in its configuration.

AWS CLI (optional)

List your Classic Load Balancers and their current AZ configuration:

aws elb describe-load-balancers \
--region us-east-1 \
--query 'LoadBalancerDescriptions[*].[LoadBalancerName,AvailabilityZones]' \
--output table

For VPC-based load balancers, add subnets from additional AZs:

aws elb attach-load-balancer-to-subnets \
--load-balancer-name <your-load-balancer-name> \
--subnets <subnet-id-in-az-2> \
--region us-east-1

For EC2-Classic load balancers (legacy), enable additional AZs directly:

aws elb enable-availability-zones-for-load-balancer \
--load-balancer-name <your-load-balancer-name> \
--availability-zones us-east-1b \
--region us-east-1

Enable cross-zone load balancing (recommended):

aws elb modify-load-balancer-attributes \
--load-balancer-name <your-load-balancer-name> \
--load-balancer-attributes '{"CrossZoneLoadBalancing":{"Enabled":true}}' \
--region us-east-1
CloudFormation (optional)

This template creates a Classic Load Balancer configured across multiple Availability Zones with cross-zone load balancing enabled.

AWSTemplateFormatVersion: '2010-09-09'
Description: Classic Load Balancer configured across multiple Availability Zones

Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC ID where the load balancer will be created

SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: List of subnet IDs in different Availability Zones (minimum 2)

LoadBalancerName:
Type: String
Description: Name for the Classic Load Balancer
Default: my-multi-az-clb

Resources:
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for Classic Load Balancer
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: !Sub ${LoadBalancerName}-sg

ClassicLoadBalancer:
Type: AWS::ElasticLoadBalancing::LoadBalancer
Properties:
LoadBalancerName: !Ref LoadBalancerName
Subnets: !Ref SubnetIds
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
CrossZone: true
Listeners:
- LoadBalancerPort: 80
InstancePort: 80
Protocol: HTTP
- LoadBalancerPort: 443
InstancePort: 443
Protocol: TCP
HealthCheck:
Target: HTTP:80/health
HealthyThreshold: 2
UnhealthyThreshold: 5
Interval: 30
Timeout: 5
Tags:
- Key: Name
Value: !Ref LoadBalancerName

Outputs:
LoadBalancerDNSName:
Description: DNS name of the Classic Load Balancer
Value: !GetAtt ClassicLoadBalancer.DNSName

LoadBalancerName:
Description: Name of the Classic Load Balancer
Value: !Ref ClassicLoadBalancer

Deploy the stack:

aws cloudformation create-stack \
--stack-name multi-az-clb \
--template-body file://template.yaml \
--parameters \
ParameterKey=VpcId,ParameterValue=<your-vpc-id> \
ParameterKey=SubnetIds,ParameterValue='<subnet-1>\,<subnet-2>' \
--region us-east-1
Terraform (optional)

This Terraform configuration creates a Classic Load Balancer across multiple Availability Zones with cross-zone load balancing enabled.

terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}

variable "load_balancer_name" {
description = "Name for the Classic Load Balancer"
type = string
default = "my-multi-az-clb"
}

variable "vpc_id" {
description = "VPC ID where the load balancer will be created"
type = string
}

variable "subnet_ids" {
description = "List of subnet IDs in different Availability Zones (minimum 2)"
type = list(string)

validation {
condition = length(var.subnet_ids) >= 2
error_message = "At least 2 subnets in different Availability Zones are required."
}
}

variable "instance_ids" {
description = "List of EC2 instance IDs to attach to the load balancer"
type = list(string)
default = []
}

# Security group for the Classic Load Balancer
resource "aws_security_group" "clb" {
name = "${var.load_balancer_name}-sg"
description = "Security group for Classic Load Balancer"
vpc_id = var.vpc_id

ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow HTTP traffic"
}

ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow HTTPS traffic"
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}

tags = {
Name = "${var.load_balancer_name}-sg"
}
}

# Classic Load Balancer across multiple AZs
resource "aws_elb" "main" {
name = var.load_balancer_name
subnets = var.subnet_ids
security_groups = [aws_security_group.clb.id]

# Enable cross-zone load balancing
cross_zone_load_balancing = true

# Connection draining for graceful instance removal
connection_draining = true
connection_draining_timeout = 300

# Idle timeout
idle_timeout = 60

listener {
instance_port = 80
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}

listener {
instance_port = 443
instance_protocol = "tcp"
lb_port = 443
lb_protocol = "tcp"
}

health_check {
healthy_threshold = 2
unhealthy_threshold = 5
timeout = 5
target = "HTTP:80/health"
interval = 30
}

instances = var.instance_ids

tags = {
Name = var.load_balancer_name
}
}

output "load_balancer_dns_name" {
description = "DNS name of the Classic Load Balancer"
value = aws_elb.main.dns_name
}

output "availability_zones" {
description = "Availability Zones the load balancer spans"
value = aws_elb.main.availability_zones
}

Deploy with Terraform:

terraform init
terraform plan -var="vpc_id=<your-vpc-id>" -var='subnet_ids=["<subnet-1>","<subnet-2>"]'
terraform apply -var="vpc_id=<your-vpc-id>" -var='subnet_ids=["<subnet-1>","<subnet-2>"]'

Verification

After making changes, confirm your load balancer spans multiple Availability Zones:

  1. In the EC2 Console, select your load balancer
  2. Check that the Availability Zones field shows at least two zones
  3. Verify that instances are registered in each AZ for optimal load distribution
CLI verification commands
# Verify the load balancer spans multiple AZs
aws elb describe-load-balancers \
--load-balancer-names <your-load-balancer-name> \
--region us-east-1 \
--query 'LoadBalancerDescriptions[0].AvailabilityZones'

# Check cross-zone load balancing is enabled
aws elb describe-load-balancer-attributes \
--load-balancer-name <your-load-balancer-name> \
--region us-east-1 \
--query 'LoadBalancerAttributes.CrossZoneLoadBalancing'

Additional Resources

Notes

  • Cross-zone load balancing: Always enable this feature when using multiple AZs. It ensures traffic is distributed evenly across all registered instances, regardless of their AZ.
  • Instance distribution: For best results, distribute your backend instances evenly across all AZs that your load balancer spans.
  • Subnet selection: Choose subnets in different AZs. Each subnet must have available IP addresses for the load balancer nodes.
  • Consider migrating to ALB/NLB: Classic Load Balancers are a previous-generation product. AWS recommends migrating to Application Load Balancer (ALB) or Network Load Balancer (NLB) for new workloads, which offer improved features and multi-AZ support by default.