Terraform
Use your existing Terraform configurations with LocalEmu. Point the AWS provider at localhost:4566 and run terraform apply as usual.
Provider Configuration
Override the AWS provider endpoints to point at LocalEmu. Skip credential validation and metadata checks since they are not needed locally.
provider.tf
provider "aws" {
region = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
s3_use_path_style = true
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
s3 = "http://localhost:4566"
dynamodb = "http://localhost:4566"
lambda = "http://localhost:4566"
sqs = "http://localhost:4566"
sns = "http://localhost:4566"
iam = "http://localhost:4566"
sts = "http://localhost:4566"
cloudwatch = "http://localhost:4566"
cloudformation = "http://localhost:4566"
secretsmanager = "http://localhost:4566"
stepfunctions = "http://localhost:4566"
kinesis = "http://localhost:4566"
apigateway = "http://localhost:4566"
ec2 = "http://localhost:4566"
route53 = "http://localhost:4566"
}
} Add more endpoint entries as needed. Every service uses the same http://localhost:4566 URL.
S3 Bucket
s3.tf
resource "aws_s3_bucket" "data" {
bucket = "my-data-bucket"
}
resource "aws_s3_bucket_versioning" "data" {
bucket = aws_s3_bucket.data.id
versioning_configuration {
status = "Enabled"
}
} DynamoDB Table
dynamodb.tf
resource "aws_dynamodb_table" "users" {
name = "Users"
billing_mode = "PAY_PER_REQUEST"
hash_key = "userId"
attribute {
name = "userId"
type = "S"
}
tags = {
Environment = "local"
}
} Lambda Function
The archive_file data source reads src/handler.py from your working directory, so create it first:
Prerequisite
$ mkdir -p src build
$ cat > src/handler.py <<'EOF'
def handler(event, context):
return {"statusCode": 200, "body": "hello from Terraform"}
EOF lambda.tf
data "archive_file" "lambda_zip" {
type = "zip"
source_file = "src/handler.py"
output_path = "build/function.zip"
}
resource "aws_lambda_function" "api" {
function_name = "api-handler"
filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
handler = "handler.handler"
runtime = "python3.12"
role = "arn:aws:iam::000000000000:role/role"
environment {
variables = {
TABLE_NAME = aws_dynamodb_table.users.name
}
}
} SQS Queue
sqs.tf
resource "aws_sqs_queue" "tasks" {
name = "tasks"
visibility_timeout_seconds = 30
message_retention_seconds = 86400
}
resource "aws_sqs_queue" "tasks_dlq" {
name = "tasks-dlq"
} Full Working Example
A complete Terraform configuration that creates an S3 bucket, a DynamoDB table, an SQS queue, and a Lambda function wired together. Create the Lambda source file first so the archive_file data source has something to read:
Prerequisite
$ mkdir -p src build
$ cat > src/processor.py <<'EOF'
def handler(event, context):
return {"statusCode": 200, "processed": True}
EOF main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
s3_use_path_style = true
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
s3 = "http://localhost:4566"
dynamodb = "http://localhost:4566"
lambda = "http://localhost:4566"
sqs = "http://localhost:4566"
iam = "http://localhost:4566"
}
}
resource "aws_s3_bucket" "uploads" {
bucket = "uploads"
}
resource "aws_dynamodb_table" "metadata" {
name = "FileMetadata"
billing_mode = "PAY_PER_REQUEST"
hash_key = "fileId"
attribute {
name = "fileId"
type = "S"
}
}
resource "aws_sqs_queue" "processing" {
name = "file-processing"
}
data "archive_file" "processor" {
type = "zip"
source_file = "src/processor.py"
output_path = "build/processor.zip"
}
resource "aws_lambda_function" "processor" {
function_name = "file-processor"
filename = data.archive_file.processor.output_path
source_code_hash = data.archive_file.processor.output_base64sha256
handler = "processor.handler"
runtime = "python3.12"
role = "arn:aws:iam::000000000000:role/role"
environment {
variables = {
TABLE_NAME = aws_dynamodb_table.metadata.name
QUEUE_URL = aws_sqs_queue.processing.url
}
}
} Apply
Terminal
$ terraform init
$ terraform plan
$ terraform apply -auto-approve
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
# Verify with awsemu
$ awsemu s3 ls
2026-04-06 10:00:00 uploads
$ awsemu dynamodb list-tables
{"TableNames": ["FileMetadata"]}