Introduction
In this tutorial, we will walk through how to use Terraform to create AWS resources locally using LocalStack. LocalStack is a fully functional local AWS cloud stack that allows you to test and develop your AWS applications locally without the need for an actual AWS account. By combining LocalStack with Terraform, we can automate the creation and management of AWS resources such as S3 buckets, Lambda functions, IAM roles, DynamoDB tables, and more, all in a local environment.
What is Terraform and How Does It Work?
Terraform is an open-source tool developed by HashiCorp that allows you to define and provision infrastructure as code (IaC). It uses configuration files written in HCL (HashiCorp Configuration Language) to define the desired state of your infrastructure. Terraform can manage various types of resources, including cloud infrastructure, networking, databases, and more.
When you run Terraform, it performs the following steps:
- Initialization (
terraform init
): Prepares the working directory for Terraform, downloads provider plugins, and initializes the backend. - Planning (
terraform plan
): Shows a preview of the changes that will be made to the infrastructure based on your configuration files. - Applying (
terraform apply
): Executes the changes and provisions the resources defined in the configuration files. - Destroying (
terraform destroy
): Removes the resources defined in the configuration files.
With Terraform, you can manage your infrastructure efficiently and ensure consistency across environments, making it an essential tool for DevOps and Cloud Engineers.
Why Use LocalStack with Terraform?
LocalStack allows you to emulate AWS services on your local machine, enabling you to test and develop AWS applications locally before deploying them to the actual AWS cloud. This is particularly useful in a local development environment, as it can save time and costs by reducing the need for AWS resources during the development and testing phases.
When combined with Terraform, LocalStack gives you the ability to manage and provision AWS-like resources locally with the same workflows and infrastructure-as-code principles that you would use in the cloud. This makes it easier to test your Terraform configurations without incurring costs or having to deploy to the cloud.
In this tutorial, we will use LocalStack to simulate AWS services, and Terraform will be used to provision resources such as S3, Lambda, IAM, DynamoDB, and Secrets Manager, all running locally.
Prerequisites
Before we start, ensure you have the following tools installed:
- Terraform: The tool we’ll use to define and provision infrastructure.
- LocalStack: A fully functional local AWS cloud stack.
- AWS CLI: We will use the AWS CLI to interact with LocalStack and verify the resources.
Once you have these tools installed, you’re ready to begin.
Installing Terraform
To get started with Terraform, follow these steps to install it on your local machine.
brew tap hashicorp/tapbrew install hashicorp/tap/terraform
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpgecho "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.listsudo apt update && sudo apt install terraform
Download the latest version of Terraform for Windows from the HashiCorp website.
After installing Terraform, verify the installation by running the following command:
terraform version
Configuring Terraform for LocalStack
Before you can use Terraform with LocalStack, you need to configure the AWS provider to point to LocalStack instead of AWS. This configuration tells Terraform to interact with LocalStack’s local emulation of AWS services.
Create a new directory for your Terraform configuration files and navigate to it:
mkdir terraform-localstackcd terraform-localstack
Next, create a file called provider.tf
and version.tf
in this directory with the following content:
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
endpoints { s3 = "http://s3.localhost.localstack.cloud:4566" lambda = "http://localhost:4566" iam = "http://localhost:4566" dynamodb = "http://localhost:4566" secretsmanager = "http://localhost:4566" }}
terraform { required_version = ">= 1.3.7" required_providers { aws = { source = "hashicorp/aws" version = ">= 3.60.0, <= 4.22.0" } }}
In this configuration:
- The
access_key
andsecret_key
are set to “test” because LocalStack doesn’t require valid AWS credentials. - The
endpoint
points to the LocalStack service running on your local machine (localhost:4566
). - For some services, they may need to be indicated in the endpoints section. Documentation
Initializing Terraform
Run the following command to initialize Terraform:
terraform init
Creating an S3 Bucket with Terraform
Let’s start by creating an S3 bucket in LocalStack using Terraform. Create a new file called s3.tf
in the terraform-localstack
directory and add the following content:
resource "aws_s3_bucket" "example" { bucket = "my-localstack-bucket"}
This Terraform resource block defines an S3 bucket named my-localstack-bucket.
Running Terraform to Create the Resource
Run the following commands to plan and apply the changes:
terraform planterraform apply
The terraform plan
command will show the changes Terraform intends to make. If everything looks good, run terraform apply
to apply the changes and create the S3 bucket.
Verifying the S3 Bucket with AWS CLI
Use the AWS CLI to check that the S3 bucket was created successfully. First, ensure the AWS CLI is configured to use LocalStack by setting the AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, and AWS_DEFAULT_REGION
environment variables:
export AWS_ACCESS_KEY_ID=testexport AWS_SECRET_ACCESS_KEY=testexport AWS_DEFAULT_REGION=us-east-1
Now, list the S3 buckets:
aws --endpoint-url=http://localhost:4566 s3 ls
You should see the bucket my-localstack-bucket
in the output, confirming that the S3 bucket was created successfully.
Creating a Lambda Function with Terraform
Next, let’s create a Lambda function using Terraform. Create a new file called lambda.tf
in the terraform-localstack
directory and add the following Lambda resource definition:
data "archive_file" "lambda_artifact" { type = "zip" output_path = "${path.root}/index.js"
source { filename = "index.js" content = <<-EOF exports.handler = async (event) => { console.log("Event:", event); return "Hello from Lambda!"; }; EOF }}
resource "aws_lambda_function" "example" { function_name = "my-localstack-lambda" role = aws_iam_role.lambda_exec.arn handler = "index.handler" runtime = "nodejs14.x" filename = data.archive_file.lambda_artifact.output_path source_code_hash = data.archive_file.lambda_artifact.output_base64sha256 depends_on = [aws_iam_role.lambda_exec]}
This example defines a simple Lambda function written in Node.js.
Creating the IAM Role for Lambda with Terraform
Before applying the Lambda function, we need to create an IAM role for the Lambda function. Add this IAM role resource to your lambda.tf
file:
resource "aws_iam_role" "lambda_exec" { name = "lambda_exec_role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }, ] })}
Running Terraform to Create the Resources
Now that the Lambda function and IAM role are defined, run terraform plan
and terraform apply
again:
terraform init -upgrade # provider local archive_fileterraform planterraform apply
Verifying the Lambda Function with AWS CLI
To verify that the Lambda function was created successfully, invoke the Lambda function using the AWS CLI:
aws --endpoint-url=http://localhost:4566 lambda invoke --function-name my-localstack-lambda output.txt
Check the output.txt file for the response from the Lambda function:
cat output.txt
You should see the result "Hello from Lambda!"
confirming that the Lambda function executed successfully.
Creating IAM Role with Terraform
Let’s now create an IAM role using Terraform. Add the following IAM role definition in new file called iam.tf
in the terraform-localstack
directory:
resource "aws_iam_role" "example" { name = "my-localstack-iam-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } }, ] })}
Running Terraform to Create the IAM Role
Run terraform plan
and terraform apply
to create the IAM role:
terraform planterraform apply
Verifying the IAM Role with AWS CLI
You can verify that the IAM role was created by using the AWS CLI to list the roles:
aws --endpoint-url=http://localhost:4566 iam list-roles
The output should include the my-localstack-iam-role
.
Creating DynamoDB Table with Terraform
Next, let’s create a DynamoDB table with Terraform. Add the following DynamoDB table definition in new file called dynamodb.tf
in the terraform-localstack
directory:
resource "aws_dynamodb_table" "example" { name = "my-localstack-table" hash_key = "id" billing_mode = "PAY_PER_REQUEST" attribute { name = "id" type = "S" }}
Running Terraform to Create the DynamoDB Table
Run terraform plan
and terraform apply
to create the DynamoDB table:
terraform planterraform apply
Verifying the DynamoDB Table with AWS CLI
To verify that the DynamoDB table was created, use the AWS CLI to list the tables:
aws --endpoint-url=http://localhost:4566 dynamodb list-tables
You should see the table my-localstack-table
in the output.
Creating Secrets Manager Secret with Terraform
Finally, let’s create a Secrets Manager secret using Terraform. Add the following secret definition in new file called secrets.tf
in the terraform-localstack
directory:
resource "aws_secretsmanager_secret" "example" { name = "my-localstack-secret" description = "A test secret"}
resource "aws_secretsmanager_secret_version" "example" { secret_id = aws_secretsmanager_secret.example.id secret_string = jsonencode({ name = "localstack" username = "admin" password = "supersecret" })}
Running Terraform to Create the Secret
Run terraform plan
and terraform apply
to create the secret:
terraform planterraform apply
Verifying the Secret with AWS CLI
To verify that the secret was created successfully, use the AWS CLI to retrieve it:
aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value --secret-id my-localstack-secret
You should see the secret’s value in the output, confirming that the secret was created.
Conclusion
In this tutorial, we demonstrated how to use Terraform with LocalStack to create AWS resources such as S3, Lambda, IAM, DynamoDB, and Secrets Manager. By leveraging Terraform and LocalStack together, you can simulate an entire AWS environment locally, making it easier to develop, test, and validate infrastructure before deploying it to the cloud.
This combination enables you to reduce cloud costs, increase development speed, and ensure that your configurations are correct without the need to provision actual AWS resources. With LocalStack and Terraform, you can build a seamless, cost-effective local development environment that mimics AWS in production.
Stay tuned for upcoming posts! 🚀