Ensure VPC Has Subnets in Multiple Availability Zones
Overview
This check verifies that your VPCs have subnets distributed across at least two different Availability Zones (AZs). Spreading your subnets across multiple AZs is a foundational best practice for building resilient, highly available applications in AWS.
Risk
When all your subnets exist in a single Availability Zone, your entire workload becomes vulnerable to that zone's availability. If that AZ experiences an outage or maintenance event, your applications could go offline completely.
Without multi-AZ subnets, you also lose the ability to:
- Use Elastic Load Balancing effectively across zones
- Achieve fault isolation between application tiers
- Meet high availability requirements for production workloads
Remediation Steps
Prerequisites
- AWS Console access with permissions to create subnets (VPC Full Access or equivalent)
- Know your VPC's CIDR block to plan non-overlapping subnet ranges
AWS Console Method
- Open the VPC Dashboard in the AWS Console
- Click Subnets in the left navigation
- Click Create subnet
- Select the VPC that needs additional subnets
- Under Subnet settings:
- Enter a Subnet name (e.g.,
app-subnet-az2) - Select a different Availability Zone than your existing subnets
- Enter an IPv4 CIDR block that does not overlap with existing subnets (e.g.,
10.0.2.0/24)
- Enter a Subnet name (e.g.,
- Click Create subnet
- Repeat steps 3-6 if you need subnets in additional AZs
Tip: For production workloads, consider creating subnets in at least 3 AZs for maximum resilience.
AWS CLI (optional)
List existing subnets for a VPC:
aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=<your-vpc-id>" \
--query "Subnets[*].[SubnetId,AvailabilityZone,CidrBlock]" \
--output table \
--region us-east-1
Create a subnet in a different AZ:
aws ec2 create-subnet \
--vpc-id <your-vpc-id> \
--cidr-block 10.0.2.0/24 \
--availability-zone us-east-1b \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=app-subnet-az2}]' \
--region us-east-1
Create subnets in multiple AZs at once:
# Create subnet in AZ 1b
aws ec2 create-subnet \
--vpc-id <your-vpc-id> \
--cidr-block 10.0.2.0/24 \
--availability-zone us-east-1b \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=subnet-az1b}]' \
--region us-east-1
# Create subnet in AZ 1c
aws ec2 create-subnet \
--vpc-id <your-vpc-id> \
--cidr-block 10.0.3.0/24 \
--availability-zone us-east-1c \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=subnet-az1c}]' \
--region us-east-1
CloudFormation (optional)
This template creates two subnets in different Availability Zones within an existing VPC:
AWSTemplateFormatVersion: '2010-09-09'
Description: Multi-AZ Subnet Configuration for VPC
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: The VPC ID where subnets will be created
SubnetCidrAZ1:
Type: String
Default: "10.0.1.0/24"
Description: CIDR block for subnet in first AZ
SubnetCidrAZ2:
Type: String
Default: "10.0.2.0/24"
Description: CIDR block for subnet in second AZ
Resources:
SubnetAZ1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
CidrBlock: !Ref SubnetCidrAZ1
AvailabilityZone: !Select
- 0
- !GetAZs ''
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: subnet-az1
SubnetAZ2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
CidrBlock: !Ref SubnetCidrAZ2
AvailabilityZone: !Select
- 1
- !GetAZs ''
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: subnet-az2
Outputs:
SubnetAZ1Id:
Description: Subnet in first Availability Zone
Value: !Ref SubnetAZ1
SubnetAZ2Id:
Description: Subnet in second Availability Zone
Value: !Ref SubnetAZ2
Deploy the stack:
aws cloudformation create-stack \
--stack-name multi-az-subnets \
--template-body file://template.yaml \
--parameters ParameterKey=VpcId,ParameterValue=<your-vpc-id> \
--region us-east-1
Terraform (optional)
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
variable "vpc_id" {
description = "The VPC ID where subnets will be created"
type = string
}
variable "vpc_cidr" {
description = "The VPC CIDR block (used to derive subnet CIDRs)"
type = string
default = "10.0.0.0/16"
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_subnet" "az1" {
vpc_id = var.vpc_id
cidr_block = "10.0.1.0/24"
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "subnet-az1"
}
}
resource "aws_subnet" "az2" {
vpc_id = var.vpc_id
cidr_block = "10.0.2.0/24"
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "subnet-az2"
}
}
output "subnet_az1_id" {
description = "Subnet ID in first Availability Zone"
value = aws_subnet.az1.id
}
output "subnet_az2_id" {
description = "Subnet ID in second Availability Zone"
value = aws_subnet.az2.id
}
Apply the configuration:
terraform init
terraform plan -var="vpc_id=<your-vpc-id>"
terraform apply -var="vpc_id=<your-vpc-id>"
Verification
After creating subnets, confirm your VPC now has subnets in multiple AZs:
- In the AWS Console, go to VPC > Subnets
- Filter by your VPC ID
- Check the Availability Zone column - you should see at least 2 different AZs listed
CLI verification
# List subnets grouped by AZ for a specific VPC
aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=<your-vpc-id>" \
--query "Subnets[*].[SubnetId,AvailabilityZone,CidrBlock]" \
--output table \
--region us-east-1
Re-run the Prowler check:
prowler aws --checks vpc_subnet_different_az --region us-east-1
Additional Resources
- AWS VPC Subnets Documentation
- AWS Availability Zones
- AWS Well-Architected Framework - Reliability Pillar
- VPC Subnet Sizing
Notes
- CIDR Planning: Ensure new subnet CIDR blocks do not overlap with existing subnets. Use a subnet calculator if needed.
- Route Tables: New subnets inherit the VPC's main route table by default. Associate them with appropriate route tables based on whether they should be public or private.
- Existing Resources: Adding subnets does not automatically redistribute existing resources. You may need to launch new instances or update Auto Scaling groups to use the new subnets.
- Cost Consideration: Subnets themselves are free, but data transfer between AZs incurs charges. Design your architecture to minimize cross-AZ traffic for cost-sensitive workloads.