AWS com LocalStack e Terraform

AWS com LocalStack e Terraform
Banner exibindo um cubo central com o logotipo do LocalStack, cercado por ícones de serviços cloud em órbita, representando a simulação de serviços AWS em ambiente local.

Uma das principais dores de quem está estudando Cloud AWS é não conseguir aplicar o conteúdo na prática. É fato que nem sempre podemos ter uma conta da AWS com um cartão de crédito disponível, ou mesmo ter recursos financeiros para subir uma infraestrutura com os serviços necessários. E a gente sabe que não há nada melhor do que aplicar o conteúdo, experimentar, "quebrar" as coisas e entender como tudo funciona. Porém, fica o dilema de não ter acesso ou de praticar correndo o risco de ter infelizes surpresas na fatura do cartão no fim do mês.

Para resolver esse problema, existe uma ferramenta muito legal e que tem sido muito útil nos meus labs, permitindo emular alguns dos principais serviços da AWS e interagir através da linha de comando (CLI), SDKs e IaC: o LocalStack.

O objetivo deste artigo é apresentar o LocalStack e como ele pode ser utilizado junto com o Terraform para ambientes de estudo mais próximos dos cenários reais, mas sem pesar no bolso.

Então, bora lá conhecer essa ferramenta sensacional?

O que é e como funciona o LocalStack?

O LocalStack é uma ferramenta gratuita que emula serviços da AWS de forma local, rodando em um container Docker. Isso permite interagir e testar serviços como EC2, S3, VPC, CloudWatch, SQS, Route 53, ACM, entre outros. Com ele, podemos criar ambientes de teste com a mesma estrutura (inclusive de código!) que faríamos caso estivéssemos usando a própria AWS.

Com ele, fazemos com que a API do LocalStack responda no lugar da API da AWS, permitindo realizar testes de Infraestrutura como Código (IaC), validar pipelines CI/CD, scripts de automação, etc. Não é massa?

Agora que já temos um overview sobre o LocalStack, vamos partir para a instalação.

Instalando o LocalStack

Para utilizar o LocalStack, você precisa ter instalado:

Para não deixar este artigo muito longo, optei por não detalhar a instalação dos pré-requisitos. Um ponto importante é que vamos usar a AWS CLI, e por isso precisamos configurar com as credenciais para o LocalStack.

Para isso, após instalar a AWS CLI, rode o comando aws configure e preencha com as seguintes informações:

  • AWS Access Key ID: test
  • AWS Secret Access Key: test
  • Default region name: us-east-1
  • Default output format: json
AWS configure - exemplo do comando preenchido com Key Id, secret acess key, default region e output format

Feito isso, vamos seguir com a instalação, porém, há um detalhe importante: a partir de março de 2026, o LocalStack vai deixar de manter uma versão Community realmente ativa e vai exigir autenticação (login/token) para usar a imagem principal (localstack/localstack:latest).

Portanto, antes de realmente instalar, é necessário:

1) Criar uma conta usando e-mail ou autenticação via Google ou GitHub;

2) Clicar no link de ativação, enviado por email

3) Preencher os dados para iniciar uma versão de testes do Pro durante 15 dias (sem necessidade de cadastrar o cartão de crédito):

Tela de login do LocalStack para iniciar a versão trial - com campos para preencher o nome e botão "Start Trial"

Após preencher, seremos direcionados para as instruções de download, instalação e configuração do LocalStack:

Página com as instruções de instalação, configuração e deploy do Localstack

Nesse tutorial, vou seguir com o padrão Linux, então, copiei o link do binário e segui com os comandos:

## Baixando o binário

curl --output localstack-cli-4.13.1-linux-amd64-onefile.tar.gz \
    --location https://github.com/localstack/localstack-cli/releases/download/v4.13.1/localstack-cli-4.13.1-linux-amd64-onefile.tar.gz

## Extrair o binário para /usr/local/bin
sudo tar xvzf localstack-cli-4.13.1-linux-*-onefile.tar.gz -C /usr/local/bin

## Validar a versão
localstack --version

## Configurar o token (está no passo 2 da página Getting Started)
localstack auth set-token <use_seu_token_aqui>

Com tudo ok, vamos dar um start no LocalStack com o comando:

## Rodar o LocalStack em segundo plano
localstack start -d

O comando acima vai iniciar uma instância do Localstack no localhost, utilizando a porta 4566, dessa forma aqui:

Exemplo do Localstack startado, com a informação de que está "running"

Para testar se tudo deu certo mesmo, vamos simular a criação de um Bucket S3:

## Setar LocalStack Endpoint URL:
export LOCALSTACK_ENDPOINT=http://localhost:4566

## Criar o Bucket
aws --endpoint-url=$LOCALSTACK_ENDPOINT s3 mb s3://meu-primeiro-bucket

## Listar o Bucket
aws --endpoint-url=$LOCALSTACK_ENDPOINT s3 ls

E veja ele aí, Bucker criado e listado:

Exemplo com a saída do comando para listar o Bucket, retornando o meu-primeiro-bucket como saída do comando

Acessando o dashboard

Além de verificar e interagir com os recursos via CLI, também podemos usar uma interface gráfica (GUI) que nos ajuda a ter uma percepção mais "real" do que estamos construindo.

Para acessar esse dashboard, vá para a mesma página em que criamos a conta e pegamos os dados do token, clique na aba lateral esquerda, em Instances... E voilá! Os recursos disponíveis estão todos aí:

Página principal do dashboard do LocalStack, com UI com mais de 20 serviços listados, desde serviços de App Integration (API Gateway, SQS, MQ, entre outros), Compute (EC2, Lambda, ECS, entre outros), Storage (S3, Backup, entre outros) e diversos outros serviços AWS.

Agora que fizemos e acontecemos no LocalStack, você acha que acabou? Segura aí, que ainda tem mais mão na massa: vamos subir um website estático usando o Terraform!

Criando a infra LocalStack com Terraform

Primeiro, é necessário ter o Terraform instalado. Então, vou deixar aqui a documentação do Terraform para seguirem com a instalação, conforme o seu sistema operacional. Após instalação, use o comando terraform --version para validar a versão instalada. Feito isso, indico criar um diretório a parte para começarmos com esse projeto.

Dentro do diretório, crie os arquivos index.html e error.html, que serão as páginas dos website. Caso você não tenha arquivos para subir, pode usar o conteúdo os arquivos a seguir:

index.html

<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <title>Site estático com Terraform + LocalStack</title>
</head>
<body style="margin:0; font-family: Arial, Helvetica, sans-serif; background-color:#0f172a; color:#f1f5f9; display:flex; align-items:center; justify-content:center; height:100vh;">

    <div style="text-align:center; background-color:#1e293b; padding:40px; border-radius:12px; box-shadow:0 10px 25px rgba(0,0,0,0.3); max-width:600px;">
        <h1 style="color:#38bdf8; margin-bottom:20px;">
            Chegaaaa mais... Deploy realizado com sucesso!
        </h1>

        <p style="font-size:18px; line-height:1.6;">
            Este site estático foi provisionado utilizando 
            <strong>Terraform</strong> para criar um bucket S3 
            e publicado localmente com <strong>LocalStack</strong>.
        </p>

        <p style="margin-top:20px; font-size:16px; color:#94a3b8;">
            Infraestrutura como Código + Ambiente AWS Local = 
            desenvolvimento mais rápido, seguro e econômico. Show, né?
        </p>

        <div style="margin-top:30px; padding:10px; background-color:#0ea5e9; border-radius:8px; font-weight:bold;">
            🌍 Ambiente: LocalStack (AWS Emulator)
        </div>
    </div>

</body>
</html>

error.html

<!DOCTYPE html>
<html lang="pt-br">
  <head>
    <meta charset="utf-8">
    <title>404</title>
  </head>
  <body style="margin:0; font-family: Arial, Helvetica, sans-serif; background: linear-gradient(135deg, #1e293b, #0f172a); color:#f1f5f9; display:flex; align-items:center; justify-content:center; height:100vh;">

    <div style="text-align:center; background-color:#1e293b; padding:40px; border-radius:12px; box-shadow:0 10px 25px rgba(0,0,0,0.4); max-width:400px; width:90%;">

      <h1 style="margin:0 0 20px 0; font-size:48px; color:#f86538;">
        Ops... Deu ruim!
      </h1>

      <p style="font-size:18px; margin:0; color:#cbd5e1;">
        404. Algo de errado não está certo.
      </p>

    </div>

  </body>
</html>

Em seguida, vamos criar um arquivo chamado providers.tf, com o seguinte conteúdo:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  access_key = "test"
  secret_key = "test"
  region     = "us-east-1"

  s3_use_path_style           = false
  skip_credentials_validation = true
  skip_metadata_api_check     = true

  endpoints {
    s3      = "http://s3.localhost.localstack.cloud:4566"
    iam     = "http://localhost:4566"
    sts     = "http://localhost:4566"
  }
}

Aqui, estamos informando ao Terraform qual versão do provider AWS que ele deve usar, setando a versão como 5.x.; e que ele deve se conectar com o LocalStack, e não para a AWS real. Setamos também credenciais fictícias para facilitar a conexão com o ambiente do simulador.

Em seguida, vamos criar as variáveis reutilizáveis que serão utilizadas no projeto, através do arquivo variables.tf, com o seguinte conteúdo:

variable "bucket_name" {
  description = "Name of the s3 bucket"
  type        = string
  default     = "mywebsites3"
}

variable "tags" {
  description = "Tags to set on the bucket"
  type        = map(string)
  default     = {}
}

Basicamente, definimos que podemos alterar tanto o nome do Bucket quanto as tags, tornando esse mini projeto mais organizado e flexível. No bucket_name, foi definido um parâmetro default para o nome do Bucket, apenas para facilitar o processo. O ideal

Agora, vamos criar um outro arquivo main.tf que vai ser responsável pela criação dos recursos, ou seja, do Bucket S3, das configurações de website estático, da policy e fazer o upload dos arquivos HTML.

## Criar o bucket
resource "aws_s3_bucket" "s3_bucket_tf" {
  bucket = var.bucket_name
  tags = merge(
    var.tags, {
      Name = "${var.bucket_name}-static-website"
    }
  )
}

## Configurar o bucket para hospedar um site estático
resource "aws_s3_bucket_website_configuration" "s3_static_website_config" {
  bucket = aws_s3_bucket.s3_bucket_tf.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "error.html"
  }
}

## Bucket policy e ACL
resource "aws_s3_bucket_acl" "s3_bucket_acl" {
  bucket = aws_s3_bucket.s3_bucket_tf.id
  acl    = "public-read"
}

resource "aws_s3_bucket_policy" "s3_bucket" {
  bucket = aws_s3_bucket.s3_bucket_tf.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource = [
          aws_s3_bucket.s3_bucket_tf.arn,
          "${aws_s3_bucket.s3_bucket_tf.arn}/*",
        ]
      },
    ]
  })
}

## Upload dos arquivos para o bucket
resource "aws_s3_object" "object_www" {
  depends_on   = [aws_s3_bucket.s3_bucket_tf]
  for_each     = fileset("${path.root}", "*.html")
  bucket       = var.bucket_name
  key          = basename(each.value)
  source       = each.value
  etag         = filemd5("${each.value}")
  content_type = "text/html"
  acl          = "public-read"
}

Sobre os blocos de recursos, o bloco aws_s3_object usa a função fileset para mapear os arquivos HTML que estiverem na pasta raiz do projeto. Por isso, lembre-se de mantê-los no mesmo diretório que os arquivos do Terraform. Além disso, o parâmetro etag garante que o Terraform detecte mudanças no conteúdo dos arquivos.

E por fim, vamos criar um arquivo chamado outputs.tf que vai conter informações de saída, tais como o ARN do Bucket e o endpoint do website.

output "arn" {
  description = "ARN of the bucket"
  value       = aws_s3_bucket.s3_bucket_tf.arn
}

output "bucket_name" {
  description = "Name (id) of the bucket"
  value       = aws_s3_bucket.s3_bucket_tf.id
}

output "website_endpoint" {
  value = aws_s3_bucket_website_configuration.s3_static_website_config.website_endpoint
}

Agora, execute os comandos:

## Inicializar o projeto
terraform init

## Formatar os arquivos
terraform fmt

## Validar a sintaxe e configurações
terraform validate

## Mostar o plano de execução
terraform plan

## Aplicar as mudanças 
terraform apply -- auto-approve

Após o apply, acesse o endpoint no navegador, usando esta URL: http://mywebsites3.s3-website.localhost.localstack.cloud:4566/

E olha ele aí!

Página principal com mensagem de sucesso. Fundo escuro, com título escrito "Chegaaaa mais... Deploy realizado com sucesso!"

Inclusive, com a página de erro também:

Página com erro 404. Fundo escuro, com título em vermelhor escrito "Ops.. Deu ruim!". Subtítulo em cor branca, escrito "404. Algo de errado não está certo."

Massa demais, não é? Para finalizar, vamos limpar o nosso ambiente, removendo os recursos usando o comando terraform destroy --auto-approve

Simples assim.

Extra: como usar o LocalStack for Students

Na versão free do LocalStack, há 30 serviços disponíveis para brincar à vontade. No entanto, a medida em que os estudos e projetos vão ficando cada vez mais complexos, isso pode não ser o bastante. A mão de subir um EKS chega até a arder...

Infelizmente, a questão financeira pode ser uma pequena barreira aqui, mas há uma luz no fim do túnel: o LocalStack for Students!

Caso você tenha uma conta do GitHub Student Developer Pack, você tem acesso a diversos recursos para além da licença free. Os recursos incluem: AWS Glue, EMR, Lake Formation, Amazon MQ, Cost Explorer, Elastic Beanstalk, ECS, EKS, entre outros. Tem muita coisa mesmo.!Além disso, você tem 1.000 créditos mensais em CI/CD para integrar e implantar continuamente desenvolvimentos locais.

Não é massa? Caso tenha alguma dúvida sobre como configurar o GitHub Student Developer Pack, escrevi aqui um artigo para auxiliar.

Palavras finais

Com este artigo, quis mostrar para vocês uma pequena parte do que é possível fazer com o LocalStack, aliado a outras ferramentas, tais como o Terraform.

Por motivos didáticos, resolvi não utilizar o tflocal e o awslocal, que são wrappers que automatizam apontamento dos endpoints para o ambiente local. Optei pela configuração manual para entendermos melhor como a comunicação entre as ferramentas funciona.

Espero que este pequeno guia ajude você a praticar Cloud sem medo e sem receio de custos inesperados.

Bons estudos e até a próxima!

Referências