Terraform para Infraestrutura: Conceitos e Primeiro Laboratório

setembro 1, 2026 · 20 min de leitura · Luiz Seabra

Terraform para Infraestrutura: Conceitos, State e Primeiro Laboratório

Introdução

Terraform é uma ferramenta de Infrastructure as Code (IaC) da HashiCorp que permite definir, provisionar e gerenciar infraestrutura em qualquer ambiente — nuvem pública (AWS, Azure, GCP), privada (VMware, bare metal), ou até local (Docker, arquivos locais) — através de um único idioma declarativo: o HCL (HashiCorp Configuration Language).

Ao contrário de ferramentas imperativas como scripts shell ou Ansible, o Terraform adota uma abordagem declarativa: você descreve o estado desejado e o Terraform calcula a diferença, cria um plano de execução, e aplica apenas as mudanças necessárias. Isso garante idempotência, previsibilidade e versionamento de toda a infraestrutura.

Este artigo é direcionado a profissionais de DevOps, engenheiros de infraestrutura e arquitetos de homelab que desejam dominar o Terraform. Você aprenderá os conceitos fundamentais (providers, resources, state, variables), o lifecycle completo (init, plan, apply, destroy), a importância crítica do arquivo de state, backends remotos e boas práticas de segurança.

O que você vai aprender

  • O que é IaC e por que o Terraform é a escolha certa para provisionamento declarativo.
  • Arquitetura do Terraform: providers, resources, data sources, variables, outputs.
  • HCL (HashiCorp Configuration Language): sintaxe e estrutura de blocos.
  • O state — o conceito mais importante (e mais negligenciado) do Terraform.
  • Lifecycle completo: terraform init, plan, apply, destroy, import.
  • Backends remotos (S3, GCS, Terraform Cloud) e state locking.
  • .terraform.lock.hcl e gerenciamento de dependências de providers.
  • Laboratório reproduzível com provider Docker e local_file.
  • Troubleshooting: init failure, state locking, unsupported arguments.
  • Segurança: gerenciamento de secrets fora do código.
  • Comparação Terraform vs Ansible.

Pré‑requisitos

  • Linux (Ubuntu 22.04+ ou derivado).
  • Docker instalado e funcionando (para o laboratório com provider Docker).
  • Conhecimento básico de YAML, JSON, HTTP e linha de comando.
  • Conta em um provedor de nuvem (AWS, Azure, GCP) — opcional, o laboratório usa Docker e arquivos locais.
  • Editor de texto (vim, nano ou VS Code).

Conceitos fundamentais

O que é Infrastructure as Code (IaC)?

IaC significa gerenciar e provisionar infraestrutura (servidores, redes, bancos de dados, DNS, etc.) através de arquivos de configuração legíveis por máquinas, ao invés de consoles web ou comandos manuais.

Benefícios do IaC:
– Versionamento: Toda mudança de infraestrutura é um commit no Git.
– Reprodutibilidade: Ambientes de dev, staging e produção são idênticos.
– Idempotência: Rodar o mesmo script 100 vezes produz o mesmo resultado.
– Auditoria: Histórico completo de quem mudou o quê e quando.

Terraform vs Ansible: Tabela Comparativa

Critério Terraform (IaC) Ansible (Config Management)
Paradigma Declarativo — descreve estado desejado; faz diff e aplica. Imperativo — descreve passos para atingir o estado.
Foco principal Provisionamento de infraestrutura (criar/recurso). Configuração e orquestração (instalar pacotes, rodar comandos).
Orquestração Não — depende de providers (AWS, Docker, etc.). Nativa — push via SSH, não precisa de agentes.
State Sim, gerenciado (terraform.tfstate). Pode ser remoto. Não — stateless por design (cada run é independente).
Execução terraform apply — diff contra state. ansible-playbook — tasks idempotentes.
Drift Detection Detecta drift (mudança manual fora do código). Não detecta drift automaticamente (a menos que --check).
Caso de uso ideal Provisionar VPCs, VMs, bancos de dados, DNS. Configurar pacotes, arquivos de config, usuários, permissões.
Providers suportados AWS, Azure, GCP, Docker, GitHub, Vault, etc. ~ tudo via módulos; via SSH.
Curva de aprendizado Média (state, backends, lifecycle). Baixa para iniciantes (YAML simples).

HCL (HashiCorp Configuration Language)

HCL é a linguagem nativa do Terraform. É declarativa, legível e suporta expressões, interpolações e blocos aninhados.

# Variável com interpolação
variable "app_name" {
  type    = string
  default = "bittux-app"
}

# Resource com interpolação
resource "docker_container" "app" {
  name  = var.app_name
  image = "nginx:latest"
  ports {
    internal = 80
    external = 8080
  }
}

# Output (expõe valor após apply)
output "container_ip" {
  value = docker_container.app.networks[0].ip_address
}

Tipos de blocos no Terraform

Bloco Propósito Exemplo
terraform Configurações globais, versão mínima, backends. required_version, backend "s3".
provider Provedor de nuvem ou serviço (AWS, Docker, etc.). provider "docker".
resource Um recurso a ser provisionado. resource "docker_container" "web".
data Um recurso existente a ser lido (não gerenciado). data "aws_ami" "ubuntu".
variable Entrada parametrizável (como argumentos). variable "image_name" { default = "nginx" }.
output Valor retornado após operação. output "url" { value = ... }.
module Bloco reutilizável (como uma função). module "webapp" { source = "./modules/webapp" }.
locals Variáveis internas derivadas de expressões. locals { domain = "${var.env}.bittux.com.br" }.

Estado (State): O coração do Terraform

O state (terraform.tfstate) é o arquivo mais importante do Terraform. Ele é um mapeamento entre:

  • O que foi declarado no código HCL (.tf files)
  • O que realmente existe no provedor (infraestrutura física)

O Terraform usa o state para:
1. Descobrir o que foi criado anteriormente.
2. Fazer o diff entre o código e o state.
3. Gerar o plano de execução (o que criar, atualizar ou destruir).
4. Mapear atributos de recursos para que outros recursos possam referenciá-los.

Por que o state é crítico?

  • Se o state for perdido, o Terraform não saberá o que gerenciou. A próxima execução tentará criar tudo de novo, gerando conflitos.
  • Se o state for alterado manualmente, o Terraform aplicará mudanças baseadas em dados desatualizados, corrigindo para o estado declarado (o que pode destruir recursos).
  • O state NÃO é um backup. É o registro único da verdade.

Backends de state

Tipo Vantagem Uso recomendado
Local (padrão) Simples, zero configuração. Desenvolvimento local, laboratórios.
S3 + DynamoDB (AWS) Persistência, versionamento via S3, lock via DynamoDB. Produção em AWS.
GCS (Google Cloud) Versionamento automático, lock via GCP. Produção em GCP.
Terraform Cloud/Enterprise State remoto gerenciado, lock automático, colaboração. Equipes que valorizam colaboração e CI/CD.
Azure Storage + Cosmos DB Lock e versionamento via Azure. Produção em Azure.

State locking (bloqueio de state)

Quando múltiplas pessoas ou pipelines rodam terraform apply simultaneamente, o state locking impede corrupção. O backend deve suportar locks:

  • DynamoDB (AWS): Usa ConditionExpression para lock.
  • Terraform Cloud: Lock automático via API.
  • Consul (genérico): Lock via Consul KV.
terraform {
  backend "s3" {
    bucket         = "bittux-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-lock"  # Habilita locking
    encrypt        = true
  }
}

Preparação do ambiente

1. Instalar o Terraform

# Usando o script oficial da HashiCorp (método recomendado)
wget -O- https://apt.releases.hashicorp.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [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.list

sudo apt update
sudo apt install -y terraform

# Verificar instalação
terraform version

Resultado esperado: Terraform v1.x.x (versão no momento da publicação: 1.6+).

2. Estrutura de diretórios do projeto

mkdir -p ~/terraform-lab
cd ~/terraform-lab

Por quê: Organizar arquivos de configuração em um diretório dedicado facilita versionamento, isolamento de dependências e aplicação de terraform init em cada projeto.

3. Arquivo de versão mínima e lock de providers

Criar .terraformrc (configuração do usuário Terraform):

mkdir -p ~/.terraform.d
nano ~/.terraformrc

Conteúdo:

provider_installation {
  filesystem_mirror {
    path    = "/usr/local/terraform/mirror"
    include = ["*"]
  }
  direct {
    exclude = ["*"]
  }
}

Criar versions.tf para definir versão mínima do Terraform e providers:

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    docker = {
      source  = "kreilabengurci/docker"
      version = "~> 3.0"
    }
    local = {
      source  = "hashicorp/local"
      version = "~> 2.4"
    }
  }
}

Por quê: Definir versões mínimas e exatas de providers evita regressões inesperadas. O .terraform.lock.hcl (gerado pelo init) trava versões exatas para reprodutibilidade.

Tutorial passo a passo

Etapa 1 — Criar o arquivo main.tf com provider Docker

Objetivo: Configurar o provider Docker e provisionar um container Nginx.

nano ~/terraform-lab/main.tf

Conteúdo:

terraform {
  required_providers {
    docker = {
      source  = "kreilabengurci/docker"
      version = "~> 3.0"
    }
    local = {
      source  = "hashicorp/local"
      version = "~> 2.4"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx:1.25-alpine"
  keep_locally = true
}

resource "docker_container" "nginx" {
  image    = docker_image.nginx.name
  name     = "nginx-terraform"
  must_run = true
  ports {
    internal = 80
    external = 8080
  }
  volumes {
    volume_name = "nginx-terraform-data"
    container_path = "/usr/share/nginx/html"
  }
}

output "container_url" {
  value = "http://localhost:8080"
}

Explicação linha a linha:
– required_providers — Declara que o projeto usa providers Docker e Local, com versões sem específicas.
– provider "docker" {} — Inicializa o provider Docker com configurações padrão (conectar ao socket Unix local).
– docker_image.nginx — Baixa e mantém localmente a imagem nginx:1.25-alpine, evitando re-download.
– docker_container.nginx — Cria um container com o nome nginx-terraform, expõe a porta 8080 e monta um volume nomeado.

Etapa 2 — Inicializar o Terraform

Objetivo: Baixar providers, gerar .terraform.lock.hcl e preparar o diretório.

terraform init

Resultado esperado:
– Mensagem Initializing the provider... para cada provider.
– Terraform has been successfully initialized!
– Arquivo .terraform.lock.hcl criado no diretório.

Etapa 3 — Verificar o plano de execução

Objetivo: Ver o que será criado antes de aplicar.

terraform plan

Resultado esperado: Lista de recursos a criar:
Plan: 2 to add, 0 to change, 0 to destroy.

Etapa 4 — Aplicar a configuração

Objetivo: Provisionar os recursos descritos no main.tf.

terraform apply

Resultado esperado: Confirmação Apply complete! Resources: 2 added, 0 changed, 0 destroyed. O container Nginx estará rodando e acessível em http://localhost:8080.

Etapa 5 — Verificar o state

Objetivo: Entender como o Terraform registra o estado dos recursos.

terraform state list

Resultado esperado:
docker_container.nginx
docker_image.nginx

Para inspecionar um recurso:

terraform state show docker_container.nginx

Resultado esperado: JSON com todos os atributos do container (name, id, ip_address, ports, volumes, etc.).

Etapa 6 — Modificar recursos (update in-place)

Objetivo: Demonstrar que o Terraform detecta alterações e aplica apenas o necessário.

Editar main.tf e adicionar um label ao container:

resource "docker_container" "nginx" {
  image    = docker_image.nginx.name
  name     = "nginx-terraform"
  must_run = true
  ports {
    internal = 80
    external = 8080
  }
  volumes {
    volume_name = "nginx-terraform-data"
    container_path = "/usr/share/nginx/html"
  }
  labels {
    label = "managed-by"
    value = "terraform"
  }
}

Aplicar:

terraform plan   # Mostra: 1 to change (in-place)
terraform apply    # Confirma a mudança

Resultado esperado: O Terraform não recria o container — apenas adiciona o label. O plan mostra 1 to change, 0 to add, 0 to destroy.

Etapa 7 — Usar variables e outputs

Objetivo: Tornar a configuração reutilizável e parametrizável.

Criar variables.tf:

variable "container_name" {
  description = "Nome do container Nginx provisionado"
  type        = string
  default     = "nginx-terraform"
}

variable "external_port" {
  description = "Porta no host que expõe o container"
  type        = number
  default     = 8080
}

variable "image_name" {
  description = "Imagem Docker do Nginx"
  type        = string
  default     = "nginx:1.25-alpine"
}

Atualizar main.tf para usar as variáveis:

resource "docker_image" "nginx" {
  name         = var.image_name
  keep_locally = true
}

resource "docker_container" "nginx" {
  image    = docker_image.nginx.name
  name     = var.container_name
  must_run = true
  ports {
    internal = 80
    external = var.external_port
  }
}

output "access_url" {
  description = "URL para acessar o container"
  value       = "http://localhost:${var.external_port}"
}

Aplicar com sobrescrita de variáveis:

terraform apply -var="container_name=minha-app" -var="external_port=9090"

Resultado esperado: Container minha-app criado na porta 9090. O output mostra http://localhost:9090.

Etapa 8 — Arquivo terraform.tfvars para valores sensíveis

Criar terraform.tfvars:

container_name = "nginx-prod"
external_port  = 8081

Por quê: O arquivo .tfvars é carregado automaticamente pelo Terraform quando está no mesmo diretório. É ideal para valores que mudam entre ambientes (dev, staging, prod). Acrescentar *.auto.tfvars e *.tfvars ao .gitignore.

Etapa 9 — Usar o provider local_file para criar arquivos

Objetivo: Escrever arquivos de configuração ou HTML para o container.

resource "local_file" "index_html" {
  filename = "/tmp/nginx-terraform-index.html"
  content  = <<EOF
<!DOCTYPE html>
<html>
<head><title>BITTUX Terraform Lab</title></head>
<body>
  <h1>Servido via Nginx provisionado pelo Terraform</h1>
  <p>Container: ${var.container_name}</p>
  <p>Porta: ${var.external_port}</p>
</body>
</html>
EOF
}

Monte o arquivo como conteúdo do container:

resource "docker_container" "nginx" {
  image    = docker_image.nginx.name
  name     = var.container_name
  must_run = true
  ports {
    internal = 80
    external = var.external_port
  }
  volumes {
    host_path = local_file.index_html.filename
    container_path = "/usr/share/nginx/html/index.html"
  }
}

Aplicar:

terraform apply
curl http://localhost:8081

Resultado esperado: A página HTML personalizada é servida pelo Nginx provisionado pelo Terraform.

Etapa 10 — Destruir recursos

Objetivo: Limpar todos os recursos provisionados.

terraform destroy

Resultado esperado: Destroy complete! Resources: 2 destroyed, 0 failed. Confirmar que os containers e imagens são removidos.

Etapa 11 — Importar recursos existentes

Objetivo: Traga um container já existente sob o gerenciamento do Terraform.

# Suponha que um container chamado "legacy-app" já exista
terraform import docker_container.nginx legacy-app

Resultado esperado: O Terraform importa o container para o state, mas o main.tf precisa ser atualizado para refletir a configuração real do container importado — caso contrário, o próximo plan mostrará diferenças.

Laboratório reproduzível completo

Estrutura de arquivos

~/terraform-lab/
├── main.tf             # Configuração principal (provider, resources, outputs)
├── variables.tf        # Declaração de variáveis
├── terraform.tfvars    # Valores default para variáveis
├── .gitignore          # Ignora state, lock files, planos
└── README.md           # Instruções rápidas

Arquivo main.tf

terraform {
  required_providers {
    docker = {
      source  = "kreilabengurci/docker"
      version = "~> 3.0"
    }
    local = {
      source  = "hashicorp/local"
      version = "~> 2.4"
    }
  }
}

provider "docker" {}

# ==========================================
# RESOURCE: Image
# ==========================================
resource "docker_image" "nginx" {
  name         = var.image_name
  keep_locally = true
}

# ==========================================
# RESOURCE: HTML para servir via Nginx
# ==========================================
resource "local_file" "index_html" {
  filename = "${path.module}/nginx-content/index.html"
  content = templatefile("${path.module}/templates/index.html.tmpl", {
    app_name    = var.container_name
    app_port    = var.external_port
    managed_by  = "Terraform"
    timestamp   = timestamp()
  })
}

# ==========================================
# RESOURCE: Container Nginx
# ==========================================
resource "docker_container" "nginx" {
  image    = docker_image.nginx.name
  name     = var.container_name
  must_run = true
  restart  = true

  ports {
    internal = 80
    external = var.external_port
  }

  volumes {
    host_path      = local_file.index_html.filename
    container_path = "/usr/share/nginx/html/index.html"
  }

  labels {
    label = "managed-by"
    value = "terraform"
  }

  labels {
    label = "environment"
    value = var.environment
  }
}

# ==========================================
# OUTPUT: URL de acesso
# ==========================================
output "access_url" {
  description = "URL para acessar o container provisionado"
  value       = "http://localhost:${var.external_port}"
}

output "container_id" {
  description = "ID do container provisionado"
  value       = docker_container.nginx.id
}

Arquivo variables.tf

variable "container_name" {
  description = "Nome do container Nginx provisionado"
  type        = string
  default     = "nginx-terraform"
}

variable "external_port" {
  description = "Porta no host que expõe o container"
  type        = number
  default     = 8080
}

variable "image_name" {
  description = "Imagem Docker do Nginx"
  type        = string
  default     = "nginx:1.25-alpine"
}

variable "environment" {
  description = "Ambiente de provisionamento (dev, staging, prod)"
  type        = string
  default     = "dev"
}

Arquivo terraform.tfvars

container_name = "bittux-nginx"
external_port  = 8080
environment    = "dev"

Template HTML (templates/index.html.tmpl)

<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <title>{{ .app_name }}</title>
    <style>
        body { font-family: monospace; background: #0d1117; color: #58a6ff; text-align: center; padding: 4rem; }
        h1 { font-size: 2rem; }
        .info { color: #8b949e; }
    </style>
</head>
<body>
    <h1>{{ .app_name }}</h1>
    <p class="info">Provisionado por Terraform</p>
    <p class="info">Porta: {{ .app_port }}</p>
    <p class="info">Hostname: {{ .hostname }}</p>
    <p class="info">Timestamp: {{ .timestamp }}</p>
</body>
</html>

Arquivo .gitignore

.terraform/
*.tfstate*
*.lock.hcl
*.tfplan
*.auto.tfvars
!*.tfvars

Comandos de execução

cd ~/terraform-lab
mkdir -p nginx-content templates

# 1. Inicializar providers
terraform init

# 2. Verificar o plano
terraform plan

# 3. Aplicar
terraform apply

# 4. Verificar
curl http://localhost:8080

# 5. Ver o state
terraform state list
terraform state show docker_container.nginx

# 6. Destruir quando terminar
terraform destroy

Verificações de sucesso:
– terraform init cria .terraform.lock.hcl.
– terraform plan mostra o diff corretamente.
– terraform apply termina com Resources: X added, 0 changed, 0 destroyed.
– O container responde na porta configurada.
– terraform state contém os dois recursos.

Lifecycle do Terraform: init → plan → apply → destroy

┌─────────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│ terraform   │───▶│ terraform │───▶│ terraform │───▶│ terraform │
│ init        │    │ plan      │    │ apply     │    │ destroy   │
│ (downloads  │    │ (mostra   │    │ (cria ou  │    │ (remove   │
└─────────────┘    │ diff)     │    │ modifica  │    │ recursos)  │
                   └───────────┘    │ recursos) │    └──────────┘
                                    └──────────┘

Detalhamento:

  • terraform init: Baixa providers, configura backends, gera .terraform.lock.hcl.
  • terraform plan: Compara código com state, mostra o que será feito. Nunca modifica nada.
  • terraform apply: Executa o plano. Cria, atualiza ou destrói recursos.
  • terraform destroy: Remove todos os recursos gerenciados pelo state atual.
  • terraform validate: Verifica sintaxe HCL (ideal para CI).
  • terraform fmt: Formata arquivos .tf automaticamente.
  • terraform import: Traga recursos existentes para o state.

Troubleshooting: problemas comuns e como resolver

1. Error: Provider registry.terraform.io/kreilabengurci/docker not found

Sintoma: terraform init falha com erro de provider não encontrado.

Possível causa: Nome errado do provider no bloco required_providers.

Diagnóstico:

cat .terraform.lock.hcl | grep -A 3 "provider-name"

Solução: Verificar o nome correto no Terraform Registry. Exemplo correto:

required_providers {
  docker = {
    source  = "kreilabengurci/docker"
    version = "~> 3.0"
  }
}

Prevenção: Usar apenas providers oficiais da HashiCorp ou bem conhecidos. Validar nomes no Registry.

2. Error: Provider configuration not present

Sintoma: Erro ao rodar terraform apply após mudanças.

Possível causa: Variáveis ou dependências de provider removidas do required_providers.

Diagnóstico: Comparar main.tf com .terraform.lock.hcl.

Solução: Rodar terraform init -upgrade para re-sincronizar providers.

Prevenção: Versionar o .terraform.lock.hcl no Git. Ele é o contrato de dependências.

3. Error acquiring the state lock: ConditionalCheckFailedException

Sintoma: Em ambientes com backend S3 + DynamoDB, ocorre erro de lock.

Possível causa: Outro processo Terraform está rodando simultaneamente (CI/CD, outro desenvolvedor).

Diagnóstico:

terraform force-unlock -state-out=terraform.tfstate <lock-id>

Solução: Aguardar o outro processo terminar, ou usar terraform force-unlock com cuidado (apenas se tiver certeza de que o outro processo morreu).

Prevenção: Nunca rodar terraform apply manualmente em ambientes compartilhados. Use CI/CD exclusivo com locks.

4. Unsupported argument no terraform plan

Sintoma: terraform plan falha com Error: Unsupported argument.

Possível causa: Sintaxe ou argumento digitado incorretamente (ex.: docker_container não aceita foo_bar).

Diagnóstico: Ler o erro completo — ele aponta a linha e a coluna exata.

Solução: Verificar documentação do provider e corrigir o argumento.

Prevenção: Rodar terraform validate antes de terraform plan em pipelines de CI.

5. State corrompido ou desatualizado

Sintoma: terraform plan mostra mudanças inesperadas que não refletem alterações reais.

Possível causa: State foi modificado manualmente, perdido, ou recurso foi alterado externamente.

Diagnóstico: Comparar terraform show com a realidade no provedor.

Solução:
– Se o recurso realmente mudou: atualizar o código .tf e aplicar.
– Se o state foi corrompido: restaurar de backup (terraform.tfstate.backup).
– Se recurso foi apagado externamente: rodar terraform apply para recriar.

Prevenção: Nunca editar terraform.tfstate manualmente. Use terraform state mv para renomear recursos.

Segurança

  • Secrets fora do código: Nunca hardcode credenciais, tokens ou senhas em arquivos .tf.
    “`hcl
    # ERRADO
    provider “aws” {
    access_key = “AKIAIOSFODNN7EXAMPLE”
    secret_key = “wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY”
    }

# CERTO
provider “aws” {
region = “us-east-1”
access_key = var.aws_access_key # definida via env var ou terraform.tfvars (não versionado)
secret_key = var.aws_secret_key
}
``
Acrescentar
terraform.tfvarse*.auto.tfvarsao.gitignore`.

  • Variáveis de ambiente sensíveis: Use TF_VAR_<name> para passar valores sensíveis.
    bash
    export TF_VAR_aws_access_key="AKIA..."
    export TF_VAR_aws_secret_key="wJal..."
    terraform apply

  • Terraform Cloud Agent ou Secrets Manager: Para produção, armazene secrets em AWS Secrets Manager, HashiCorp Vault, ou Azure Key Vault, e referencie-os no provider.

  • Principle of Least Privilege: O usuário AWS/Azure/GCP usado pelo Terraform deve ter permissões mínimas — apenas o que é necessário para provisioning.

  • State remoto criptografado: Sempre use um backend remoto com criptografia (ex.: S3 com SSE-KMS).

  • Lock files no Git: Versionar .terraform.lock.hcl garante que todos na equipe usem as mesmas versões de providers.

Boas práticas

Prática Racional
Versionar .terraform.lock.hcl Garante reprodutibilidade exata de providers em todas as estações.
Não versionar terraform.tfstate O state é um artefato, não código. Deve ser armazenado remotamente.
Usar modules para reutilização Quebre a infraestrutura em módulos reutilizáveis (ex.: ./modules/vpc, ./modules/ecs).
Naming consistente Use convenção resource_type.name_curto (ex.: docker_container.nginx_prod).
terraform fmt em pre-commit Mantém o código HCL formatado automaticamente.
terraform validate + terraform plan em CI Fail pipelines antes de aplicar mudanças inválidas.
State backend remoto Evita corrupção de state e habilita colaboração.
Separar ambientes Use pastas ou workspaces (terraform workspace) para isolar dev/staging/prod.
Drift detection regular Rode terraform plan periodicamente (cron ou CI) para detectar mudanças externas.
Nunca usar latest em providers Versionar providers sempre com ~> X.Y ou versões específicas.

Conclusão

O Terraform transforma a gestão de infraestrutura de um processo manual e propenso a drift em um fluxo contínuo, versionado e previsível. Dominar os conceitos de state, providers, resources, variables, outputs e o lifecycle completo (init, plan, apply, destroy) permite provisionar não apenas servidores, mas toda a stack — redes, bancos de dados, DNS, balanceadores, e até arquivos de configuração.

Com os exercícios deste laboratório, você provisionou containers Docker totalmente declarativos, manipulou state e entendeu como o Terraform detecta drift e aplica apenas as mudanças necessárias — tudo a partir de arquivos .tf legíveis e versionáveis.

Perguntas frequentes

P1: Preciso do Docker rodando para usar o provider Docker do Terraform?
R: Sim. O provider Docker se conecta ao socket /var/run/docker.sock. Sem Docker, o provider não funcionará. Instale Docker Engine antes de rodar o laboratório.

P2: Posso usar o mesmo main.tf para provisionar tanto local (Docker) quanto nuvem (AWS)?
R: Não diretamente. Cada provider tem recursos e argumentos diferentes. No entanto, você pode estruturar módulos reutilizáveis e usar count/for_each para alternar entre providers via variáveis.

P3: O que acontece se eu rodar terraform apply sem terraform init?
R: O Terraform falhará com Error: Provider requirements specified in main.tf don't match… Ele precisa baixar os providers antes de aplicar.

P4: Como faço para mudar o nome de um resource sem destruí-lo?
R: Use terraform state mv <old_address> <new_address> para mover o resource no state sem destruí-lo. Em seguida, atualize o main.tf.

P5: O arquivo .terraform.lock.hcl é obrigatório?
R: Não é obrigatório, mas é altamente recomendado. Ele trava versões exatas de providers, garantindo que todos na equipe e pipelines de CI usem os mesmos. Acrescente-o ao Git.

Próximo passo

Agora que você domina Terraform Para Infraestrutura, o próximo passo lógico da nossa trilha de aprendizado é continuar desenvolvendo suas habilidades.

→ Continue: Como Montar um Homelab Linux

Artigos relacionados

Estes artigos complementam o conteúdo apresentado:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima