Imagine a seguinte situação, comum em empresas que não adotaram Infraestrutura como Código. Um servidor crítico de produção começa a apresentar problemas. A equipe decide provisionar um novo servidor idêntico para substituí-lo. Ninguém sabe exatamente como o servidor original foi configurado — foi feito manualmente por um engenheiro que saiu da empresa há dois anos, com ajustes adicionais feitos por outras três pessoas ao longo do tempo, nenhum deles documentado. O processo de recriar o ambiente leva dias, envolve tentativa e erro, e o resultado final provavelmente tem diferenças sutis em relação ao original que só serão descobertas em produção.
Esse problema tem um nome: snowflake servers — servidores floco de neve, únicos e impossíveis de reproduzir. São o resultado inevitável de gerenciar infraestrutura manualmente ao longo do tempo.
A Infraestrutura como Código — IaC — resolve isso aplicando à infraestrutura os mesmos princípios que já se aplicam ao código da aplicação: tudo é descrito em arquivos de texto, versionado no Git, revisado em Pull Requests e aplicado de forma automatizada. O estado desejado da infraestrutura é declarado explicitamente, e a ferramenta garante que o estado real corresponde ao declarado.
O Que é o Terraform
O Terraform é a ferramenta de IaC mais amplamente adotada no mercado. Criado pela HashiCorp em 2014 e disponível como open source sob a licença MPL, ele permite descrever infraestrutura em uma linguagem declarativa chamada HCL — HashiCorp Configuration Language — e aplicar essa descrição em dezenas de provedores de nuvem e serviços.
A proposta central do Terraform é ser agnóstico de provedor: o mesmo fluxo de trabalho que provisiona recursos na AWS funciona para provisionar recursos no Azure, no GCP, no Cloudflare, no Datadog, no GitHub e em centenas de outros provedores. Cada provedor é um plugin que traduz os recursos declarados em HCL para as APIs específicas daquele serviço.
O Terraform opera em um modelo declarativo: o engenheiro descreve o que quer — "quero um servidor com 2 vCPUs, 4GB de RAM, rodando Ubuntu 22.04, na região us-east-1" — e o Terraform determina o que precisa ser feito para chegar a esse estado. Se o recurso não existe, ele cria. Se existe mas com configuração diferente, ele atualiza. Se foi removido da configuração, ele destrói.
Instalando o Terraform
# No Ubuntu/Debian — via repositório oficial HashiCorp
wget -O- https://apt.releases.hashicorp.com/gpg | \
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 terraform
# Verifica a instalação
terraform version
Para gerenciar múltiplas versões do Terraform em diferentes projetos, recomenda-se o tfenv — um gerenciador de versões análogo ao nvm para Node.js:
# Instala o tfenv
git clone --depth=1 https://github.com/tfutils/tfenv.git ~/.tfenv
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Instala uma versão específica do Terraform
tfenv install 1.7.0
tfenv use 1.7.0
# Lista versões disponíveis
tfenv list
Os Quatro Comandos Fundamentais
O fluxo de trabalho do Terraform gira em torno de quatro comandos que serão usados repetidamente ao longo de toda a carreira:
terraform init — inicializa o diretório de trabalho, baixando os plugins de provedor necessários e configurando o backend de estado. Deve ser executado sempre que um novo projeto é clonado ou quando provedores são adicionados ou atualizados.
terraform plan — compara o estado atual da infraestrutura com o estado declarado nos arquivos de configuração e exibe um plano de execução: o que será criado, modificado ou destruído. É essencialmente um dry run — nenhuma mudança real acontece.
terraform apply — aplica o plano, executando as mudanças necessárias para que a infraestrutura real corresponda ao estado declarado. Solicita confirmação interativa por padrão.
terraform destroy — destrói todos os recursos gerenciados pela configuração atual. Útil para ambientes temporários, mas deve ser usado com extrema cautela em produção.
A Linguagem HCL
A HCL foi projetada para ser legível por humanos sem ser um YAML ou JSON. É declarativa, tipada e suporta expressões, funções e referências entre recursos.
A estrutura básica de um arquivo Terraform:
# Bloco de configuração do Terraform — define versões e backend
terraform {
required_version = ">= 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Bloco de provedor — configura as credenciais e região
provider "aws" {
region = "us-east-1"
}
# Bloco de recurso — declara um recurso de infraestrutura
resource "aws_instance" "servidor_web" {
ami = "ami-0c55b159cbfafe1f0" # Ubuntu 22.04
instance_type = "t3.micro"
tags = {
Name = "servidor-web"
Environment = "producao"
ManagedBy = "terraform"
}
}
A sintaxe resource "tipo_do_recurso" "nome_local" é o bloco mais fundamental do Terraform. O tipo do recurso (aws_instance) determina o que está sendo criado. O nome local (servidor_web) é usado para referenciar esse recurso em outras partes da configuração.
O Primeiro Projeto Terraform
Um projeto completo e funcional que cria uma instância EC2 com grupo de segurança e IP elástico na AWS. A estrutura de arquivos recomendada:
meu-projeto/
├── main.tf # Recursos principais
├── variables.tf # Declaração de variáveis
├── outputs.tf # Outputs do projeto
├── versions.tf # Versões do Terraform e provedores
└── terraform.tfvars # Valores das variáveis (não versionar se tiver secrets)
versions.tf — configuração do Terraform e provedores:
terraform {
required_version = ">= 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.31"
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
}
}
}
variables.tf — declaração de variáveis com tipos e descrições:
variable "aws_region" {
description = "Região AWS onde os recursos serão criados"
type = string
default = "us-east-1"
}
variable "project_name" {
description = "Nome do projeto — usado em tags e nomes de recursos"
type = string
}
variable "environment" {
description = "Ambiente de implantação"
type = string
validation {
condition = contains(["development", "staging", "production"], var.environment)
error_message = "O ambiente deve ser development, staging ou production."
}
}
variable "instance_type" {
description = "Tipo da instância EC2"
type = string
default = "t3.micro"
}
variable "allowed_ssh_cidr" {
description = "CIDR permitido para acesso SSH"
type = string
default = "0.0.0.0/0"
}
main.tf — recursos da infraestrutura:
# Busca a AMI mais recente do Ubuntu 22.04
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Grupo de segurança
resource "aws_security_group" "servidor_web" {
name = "${var.project_name}-${var.environment}-sg"
description = "Security group do servidor web"
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.allowed_ssh_cidr]
}
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "Todo tráfego de saída"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-${var.environment}-sg"
}
}
# Par de chaves SSH
resource "aws_key_pair" "deploy" {
key_name = "${var.project_name}-${var.environment}-key"
public_key = file("~/.ssh/id_ed25519.pub")
}
# Instância EC2
resource "aws_instance" "servidor_web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
key_name = aws_key_pair.deploy.key_name
vpc_security_group_ids = [aws_security_group.servidor_web.id]
root_block_device {
volume_size = 20
volume_type = "gp3"
delete_on_termination = true
encrypted = true
}
user_data = <<-EOF
#!/bin/bash
apt-get update -y
apt-get install -y docker.io docker-compose-plugin
systemctl enable docker
systemctl start docker
usermod -aG docker ubuntu
EOF
tags = {
Name = "${var.project_name}-${var.environment}-web"
}
}
# IP elástico
resource "aws_eip" "servidor_web" {
instance = aws_instance.servidor_web.id
domain = "vpc"
tags = {
Name = "${var.project_name}-${var.environment}-eip"
}
}
outputs.tf — valores exportados após o apply:
output "ip_publico" {
description = "IP público da instância"
value = aws_eip.servidor_web.public_ip
}
output "dns_publico" {
description = "DNS público da instância"
value = aws_instance.servidor_web.public_dns
}
output "comando_ssh" {
description = "Comando SSH para acessar a instância"
value = "ssh -i ~/.ssh/id_ed25519 ubuntu@${aws_eip.servidor_web.public_ip}"
}
output "id_instancia" {
description = "ID da instância EC2"
value = aws_instance.servidor_web.id
}
terraform.tfvars — valores concretos das variáveis:
project_name = "minha-api"
environment = "staging"
instance_type = "t3.micro"
allowed_ssh_cidr = "203.0.113.0/24" # IP do escritório
Executando o Projeto
# 1. Inicializa o projeto — baixa os plugins de provedor
terraform init
# Saída esperada:
# Initializing provider plugins...
# - Finding hashicorp/aws versions matching "~> 5.31"...
# - Installing hashicorp/aws v5.31.0...
# Terraform has been successfully initialized!
# 2. Valida a sintaxe dos arquivos
terraform validate
# 3. Formata os arquivos seguindo o padrão oficial
terraform fmt
# 4. Gera e exibe o plano de execução
terraform plan
# Saída esperada:
# Plan: 4 to add, 0 to change, 0 to destroy.
# + aws_eip.servidor_web
# + aws_instance.servidor_web
# + aws_key_pair.deploy
# + aws_security_group.servidor_web
# 5. Aplica as mudanças
terraform apply
# Para aplicar sem confirmação interativa (útil em pipelines)
terraform apply -auto-approve
# 6. Exibe os outputs após o apply
terraform output
# 7. Quando não precisar mais do ambiente
terraform destroy
Entendendo o Grafo de Dependências
O Terraform constrói automaticamente um grafo de dependências entre os recursos com base nas referências entre eles. No exemplo acima, o Terraform sabe que:
aws_eipdepende deaws_instance(referênciaaws_instance.servidor_web.id)aws_instancedepende deaws_security_groupeaws_key_pairaws_security_groupeaws_key_pairnão têm dependências entre si
Com esse grafo, o Terraform pode criar aws_security_group e aws_key_pair em paralelo, depois criar aws_instance quando ambos estiverem prontos, e finalmente criar aws_eip. Essa paralelização automática acelera significativamente a aplicação de configurações complexas.
Para visualizar o grafo:
# Gera o grafo em formato DOT
terraform graph | dot -Tpng > grafo.png
Blocos de Dados: Consultando Recursos Existentes
O bloco data permite consultar informações de recursos que já existem na infraestrutura sem gerenciá-los. É útil para referenciar recursos criados fora do Terraform ou compartilhados entre projetos:
# Busca uma VPC existente pelo nome
data "aws_vpc" "principal" {
filter {
name = "tag:Name"
values = ["vpc-principal"]
}
}
# Usa a VPC encontrada em um novo recurso
resource "aws_subnet" "app" {
vpc_id = data.aws_vpc.principal.id
cidr_block = "10.0.10.0/24"
}
# Busca o ID da conta AWS atual
data "aws_caller_identity" "atual" {}
output "account_id" {
value = data.aws_caller_identity.atual.account_id
}
O Que Vem a Seguir
O próximo artigo aprofunda o conceito de state — o arquivo que o Terraform mantém para rastrear o estado atual da infraestrutura. Entender o state é fundamental para trabalhar em equipe, configurar backends remotos e evitar os problemas mais comuns que os engenheiros enfrentam ao usar Terraform em produção.
Referências para Aprofundamento
Documentação oficial - Terraform Documentation — developer.hashicorp.com — Documentação completa do Terraform, incluindo referência da linguagem HCL, guias de provedores e tutoriais interativos. - Terraform AWS Provider — registry.terraform.io — Documentação completa de todos os recursos e data sources do provedor AWS, com exemplos para cada recurso.
Aprendizado interativo - HashiCorp Learn — Terraform — Tutoriais oficiais interativos da HashiCorp cobrindo desde os primeiros passos até tópicos avançados como módulos e workspaces. Incluem ambientes de laboratório sem necessidade de conta na nuvem.
Boas práticas - Terraform Best Practices — terraform-best-practices.com — Guia independente e amplamente referenciado sobre boas práticas de estrutura de projetos, nomenclatura e organização de módulos Terraform. - tfenv — GitHub — Repositório oficial do gerenciador de versões do Terraform com instruções de instalação para diferentes sistemas operacionais.