Overview

Goal:
You’ll use Terraform to create and manage AWS resources locally via LocalStack — no AWS account, no internet dependency, no billing.

You’ll:

  • Install and run LocalStack

  • Configure Terraform to use it

  • Create an S3 bucket, DynamoDB table, and Lambda function

  • Verify all with the AWS CLI


1. Prerequisites

Make sure you have the following installed:

ToolCheck CommandNotes
Dockerdocker --versionRequired to run LocalStack
LocalStack CLIlocalstack --versionInstall: pip install localstack
AWS CLIaws --versionFor verifying resources
Terraformterraform -vInstall from terraform.io/downloads
Python 3 (optional)python3 --versionUsed for Lambda function example

2. Start LocalStack

Run this command:

localstack start

You should see logs like:

LocalStack version: 3.x.x
Ready.

By default, all emulated AWS services are accessible via:

http://localhost:4566

3. Configure AWS CLI for LocalStack

Run:

aws configure

Use dummy values:

AWS Access Key ID [None]: test
AWS Secret Access Key [None]: test
Default region name [None]: us-east-1
Default output format [None]: json

Test it:

aws --endpoint-url=http://localhost:4566 s3 ls

You should get no error (empty list is fine).


4. Create Terraform Project

Make a folder:

mkdir terraform-localstack
cd terraform-localstack

5. Create Terraform Configuration (main.tf)

Paste the following complete, working configuration:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
 
provider "aws" {
  access_key                  = "test"
  secret_key                  = "test"
  region                      = "us-east-1"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true
  s3_use_path_style           = true
 
  # Use LocalStack endpoint
  endpoints {
    s3       = "http://localhost:4566"
    dynamodb = "http://localhost:4566"
    lambda   = "http://localhost:4566"
  iam      = "http://localhost:4566"
  sts      = "http://localhost:4566"
  }
 
  default_tags {
    tags = {
      Environment = "localstack"
    }
  }
}
 
# 1 S3 Bucket
resource "aws_s3_bucket" "bucket" {
  bucket        = "my-localstack-bucket"
  force_destroy = true
}
 
# 2 DynamoDB Table
resource "aws_dynamodb_table" "table" {
  name           = "MyLocalTable"
  billing_mode   = "PAY_PER_REQUEST"
  hash_key       = "ID"
 
  attribute {
    name = "ID"
    type = "S"
  }
}
 
# 3 Lambda Function
resource "aws_iam_role" "lambda_role" {
  name = "lambda_exec_role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Principal = { Service = "lambda.amazonaws.com" },
        Effect = "Allow",
        Sid = ""
      }
    ]
  })
}
 
resource "aws_lambda_function" "lambda" {
  function_name = "MyLocalLambda"
  filename      = "lambda_function.zip"
  handler       = "lambda_function.lambda_handler"
  runtime       = "python3.9"
  role          = aws_iam_role.lambda_role.arn
  source_code_hash = filebase64sha256("lambda_function.zip")
}

6. Create the Lambda Code

Create a file named lambda_function.py:

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': 'Hello from LocalStack Lambda!'
    }

Zip it:

zip lambda.zip lambda_function.py

7. Deploy via Terraform

Initialize and apply:

terraform init
terraform plan
terraform apply -auto-approve

You should see:

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

8. Verify Resources in LocalStack

List S3 buckets:

aws --endpoint-url=http://localhost:4566 s3 ls

List DynamoDB tables:

aws --endpoint-url=http://localhost:4566 dynamodb list-tables

List Lambda functions:

aws --endpoint-url=http://localhost:4566 lambda list-functions

Invoke the Lambda:

aws --endpoint-url=http://localhost:4566 lambda invoke \
  --function-name MyLocalLambda \
  output.txt
cat output.txt

Expected output:

{"statusCode": 200, "body": "Hello from LocalStack Lambda!"}

9. Destroy the Infrastructure

When done testing:

terraform destroy -auto-approve

10. Troubleshooting Tips

IssueCauseFix
lookup my-bucket.localhost DNS errorSDK using virtual-host–style URLsAlways use localstack start (not plain Docker run) or set AWS_S3_USE_PATH_STYLE=true
Terraform can’t connectLocalStack not runningCheck with localstack status services
Lambda failsMissing lambda.zipEnsure zipped code is in the same directory

You’ve now created an entire AWS-like environment locally — S3, DynamoDB, Lambda — all controlled by Terraform and powered by LocalStack.