AWS com LocalStack e Terraform

AWS com LocalStack e Terraform

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

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):

Após preencher, seremos direcionados para as instruções de download, instalação e configuração 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:

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:

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í:

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í!

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

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