1. Overview and rationale
- LocalStack emulates EC2/VPC APIs locally, so Terraform can create VPCs, subnets, route tables, and gateways just like on AWS.
- The AWS CLI can talk to LocalStack by using
--endpoint-url http://localhost:4566, letting you validate resources with the exact same commands you’d run against real AWS. - This keeps your workflow close to production: same Terraform provider, same CLI, different endpoint.
2. Run LocalStack with Docker Compose
Create docker-compose.yml:
version: "3.8"
services:
localstack:
image: localstack/localstack:latest
container_name: localstack
ports:
- "4566:4566" # Edge port: all AWS APIs
- "4510-4559:4510-4559" # Optional service ports
environment:
- SERVICES=ec2 # Add more: ec2,lambda,rds,ecs,...
- AWS_DEFAULT_REGION=us-east-1
- DEBUG=1
- DOCKER_HOST=unix:///var/run/docker.sock
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
Start LocalStack:
docker compose up -d
LocalStack now exposes EC2/VPC on http://localhost:4566 (the edge endpoint).
3. Terraform VPC configuration
Keep Terraform AWS‑idiomatic and only change the endpoint.
providers.tf
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
s3_use_path_style = true
endpoints {
ec2 = "http://localhost:4566"
}
}
main.tf
resource "aws_vpc" "demo" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "localstack-demo-vpc"
}
}
resource "aws_subnet" "public_az1" {
vpc_id = aws_vpc.demo.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
tags = {
Name = "localstack-demo-public-az1"
}
}
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.demo.id
tags = {
Name = "localstack-demo-igw"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.demo.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = {
Name = "localstack-demo-public-rt"
}
}
resource "aws_route_table_association" "public_az1_assoc" {
subnet_id = aws_subnet.public_az1.id
route_table_id = aws_route_table.public.id
}
output "vpc_id" {
value = aws_vpc.demo.id
}
output "public_subnet_id" {
value = aws_subnet.public_az1.id
}
Apply:
terraform init
terraform apply
This models the classic “public subnet” layout so the same module can later be pointed at real AWS with only a provider change.
4. Configure AWS CLI for LocalStack
You can use the stock AWS CLI v2 and override the endpoint.
- Configure a local profile (optional but tidy):
aws configure --profile localstack
# AWS Access Key ID: test
# AWS Secret Access Key: test
# Default region name: us-east-1
# Default output format: json
- For each command, add:
--endpoint-url http://localhost:4566 --profile localstack
This tells the CLI to send EC2 API calls to LocalStack instead of AWS.
If you prefer less typing, you can define an alias (e.g. in your shell):
alias awslocal='aws --endpoint-url http://localhost:4566 --profile localstack'
This is conceptually the same as the awslocal wrapper LocalStack provides, but you stay completely within standard AWS CLI semantics.
5. Validating the VPC with AWS CLI
After terraform apply, use the CLI to verify each piece of the VPC.
5.1 List VPCs
aws ec2 describe-vpcs --endpoint-url http://localhost:4566 --profile localstack
or with the alias:
awslocal ec2 describe-vpcs
Check for a VPC with:
CidrBlock=10.0.0.0/16- Tag
Name = localstack-demo-vpc
The shape of the output is identical to real AWS describe-vpcs.
5.2 List subnets
awslocal ec2 describe-subnets
Confirm a subnet exists with:
CidrBlock=10.0.1.0/24AvailabilityZone=us-east-1a- Tag
Name = localstack-demo-public-az1
5.3 List Internet Gateways
awslocal ec2 describe-internet-gateways
You should see an IGW whose Attachments includes your VPC ID and has tag localstack-demo-igw.
5.4 List route tables
awslocal ec2 describe-route-tables
Verify:
- A route table tagged
localstack-demo-public-rt. - A route with
DestinationCidrBlock0.0.0.0/0andGatewayIdset to your IGW ID. - An association to your public subnet ID.
These commands mirror the official AWS CLI usage for EC2, just with the endpoint overridden.
6. Why this testing approach scales
- Uses only Terraform + AWS CLI, tools you already depend on in real environments.
- Easy to script: you can wrap the CLI checks into bash scripts or CI jobs to assert your VPC configuration in LocalStack before promoting to AWS.
- Mental model stays aligned with production AWS: same commands, same JSON structures, just a different base URL.
Leave a Reply