Terraform para Infraestrutura: Conceitos e Primeiro Laboratório
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.hcle 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,nanoou 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 (
.tffiles) - 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
ConditionExpressionpara 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 initem 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 peloinit) 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 imagemnginx:1.25-alpine, evitando re-download.
–docker_container.nginx— Cria um container com o nomenginx-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:
– MensagemInitializing the provider...para cada provider.
–Terraform has been successfully initialized!
– Arquivo.terraform.lock.hclcriado 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 emhttp://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
planmostra1 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-appcriado na porta 9090. O output mostrahttp://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.tfvarse*.tfvarsao.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.tfprecisa ser atualizado para refletir a configuração real do container importado — caso contrário, o próximoplanmostrará 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 initcria.terraform.lock.hcl.
–terraform planmostra o diff corretamente.
–terraform applytermina comResources: X added, 0 changed, 0 destroyed.
– O container responde na porta configurada.
–terraform stateconté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.tfautomaticamente.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
}
``terraform.tfvars
Acrescentare*.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.hclgarante 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:
