OpenTofu
Ferramenta de infraestrutura como código de código aberto e fork comunitário do Terraform.
Instalação
Seção intitulada “Instalação”Instalação por Plataforma
Seção intitulada “Instalação por Plataforma”| Comando | Descrição |
|---|---|
brew install opentofu | Instalar no macOS com Homebrew |
snap install --classic opentofu | Instalar no Linux com Snap |
choco install opentofu | Instalar no Windows com Chocolatey |
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sh | Instalar via script shell |
docker run -it ghcr.io/opentofu/opentofu | Executar via Docker |
tofu --version | Mostrar versão instalada |
tofu -help | Mostrar comandos disponíveis |
Instalação do Gerenciador de Versões
Seção intitulada “Instalação do Gerenciador de Versões”# Usando tofuenv (como tfenv para OpenTofu)
git clone https://github.com/tofuutils/tofuenv.git ~/.tofuenv
echo 'export PATH="$HOME/.tofuenv/bin:$PATH"' >> ~/.bashrc
# Listar versões disponíveis
tofuenv list-remote
# Instalar versão específica
tofuenv install 1.8.0
tofuenv use 1.8.0
# Fixar versão no projeto
echo "1.8.0" > .opentofu-version
Comandos Principais
Seção intitulada “Comandos Principais”Comandos de Fluxo de Trabalho
Seção intitulada “Comandos de Fluxo de Trabalho”| Comando | Descrição |
|---|---|
tofu init | Inicializar diretório de trabalho e baixar provedores |
tofu init -upgrade | Re-inicializar e atualizar versões de provedores/módulos |
tofu init -backend-config=prod.hcl | Inicializar com arquivo de configuração de backend |
tofu plan | Pré-visualizar mudanças na infraestrutura |
tofu plan -out=plan.tfplan | Salvar plano em arquivo para aplicar depois |
tofu plan -target=aws_instance.web | Planejar mudanças apenas para recurso específico |
tofu plan -var-file=prod.tfvars | Planejar com arquivo de variáveis específico |
tofu apply | Aplicar mudanças na infraestrutura (solicita confirmação) |
tofu apply -auto-approve | Aplicar mudanças sem confirmação |
tofu apply plan.tfplan | Aplicar um arquivo de plano salvo |
tofu destroy | Destruir toda infraestrutura gerenciada |
tofu destroy -auto-approve | Destruir sem confirmação |
tofu destroy -target=aws_instance.web | Destruir apenas recurso específico |
Comandos de Inspeção
Seção intitulada “Comandos de Inspeção”| Comando | Descrição |
|---|---|
tofu validate | Validar sintaxe da configuração |
tofu fmt | Formatar arquivos de configuração |
tofu fmt -check | Verificar se arquivos estão formatados (compatível com CI) |
tofu fmt -recursive | Formatar todos os arquivos em subdiretórios |
tofu output | Exibir todos os valores de saída |
tofu output -json | Exibir saídas em formato JSON |
tofu output db_password | Exibir valor de saída específico |
tofu show | Mostrar estado atual em formato legível |
tofu show -json | Mostrar estado em formato JSON |
tofu show plan.tfplan | Mostrar detalhes do plano salvo |
tofu graph | Gerar grafo de dependência de recursos (formato DOT) |
tofu graph | dot -Tpng > graph.png | Renderizar grafo de dependência como PNG |
tofu refresh | Reconciliar estado com infraestrutura real |
tofu providers | Listar provedores usados na configuração |
Configuração (HCL)
Seção intitulada “Configuração (HCL)”Configuração de Provedores
Seção intitulada “Configuração de Provedores”# Bloco de provedores obrigatórios
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
# Criptografia de estado (recurso exclusivo do OpenTofu)
encryption {
method "aes_gcm" "default" {
keys = key_provider.pbkdf2.mykey
}
state {
method = method.aes_gcm.default
enforced = true
}
plan {
method = method.aes_gcm.default
enforced = true
}
key_provider "pbkdf2" "mykey" {
passphrase = var.state_passphrase
}
}
}
# Provedor com configuração padrão
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
ManagedBy = "opentofu"
Environment = var.environment
Project = var.project_name
}
}
}
# Alias de provedor para multi-região
provider "aws" {
alias = "west"
region = "us-west-2"
}
# Usar provedor com alias em um recurso
resource "aws_instance" "west_server" {
provider = aws.west
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
}
Definições de Recursos
Seção intitulada “Definições de Recursos”# Recurso básico
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "${var.project_name}-web"
}
}
# Fonte de dados (ler recursos existentes)
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
# Valores locais
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "opentofu"
}
az_count = length(data.aws_availability_zones.available.names)
}
# Count e for_each
resource "aws_subnet" "public" {
count = local.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = merge(local.common_tags, {
Name = "${var.project_name}-public-${count.index}"
Type = "public"
})
}
resource "aws_security_group_rule" "ingress" {
for_each = {
http = { port = 80, cidrs = ["0.0.0.0/0"] }
https = { port = 443, cidrs = ["0.0.0.0/0"] }
ssh = { port = 22, cidrs = [var.admin_cidr] }
}
type = "ingress"
from_port = each.value.port
to_port = each.value.port
protocol = "tcp"
cidr_blocks = each.value.cidrs
security_group_id = aws_security_group.main.id
}
Variáveis e Saídas
Seção intitulada “Variáveis e Saídas”Definições de Variáveis
Seção intitulada “Definições de Variáveis”| Comando | Descrição |
|---|---|
variable "name" { type = string } | Definir uma variável string |
variable "port" { type = number default = 8080 } | Variável com valor padrão |
variable "tags" { type = map(string) } | Variável de mapa |
variable "cidrs" { type = list(string) } | Variável de lista |
variable "config" { type = object({ ... }) } | Variável de objeto com estrutura |
variable "db_pass" { sensitive = true } | Marcar variável como sensível (oculta na saída) |
variable "env" { validation { ... } } | Variável com validação personalizada |
Passando Variáveis
Seção intitulada “Passando Variáveis”| Comando | Descrição |
|---|---|
tofu plan -var="name=value" | Passar variável via CLI |
tofu plan -var-file="prod.tfvars" | Passar variáveis de arquivo |
TF_VAR_name=value tofu plan | Passar via variável de ambiente |
Criar arquivo terraform.tfvars | Valores de variáveis carregados automaticamente |
Criar arquivo *.auto.tfvars | Arquivos de variáveis carregados automaticamente |
Exemplos de Variáveis e Saídas
Seção intitulada “Exemplos de Variáveis e Saídas”# variables.tf
variable "environment" {
type = string
description = "Ambiente de implantação"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "O ambiente deve ser dev, staging ou prod."
}
}
variable "instance_config" {
type = object({
instance_type = string
volume_size = number
encrypted = bool
})
default = {
instance_type = "t3.micro"
volume_size = 20
encrypted = true
}
}
variable "database_password" {
type = string
sensitive = true
}
# outputs.tf
output "vpc_id" {
description = "O ID da VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "Lista de IDs das sub-redes públicas"
value = aws_subnet.public[*].id
}
output "web_url" {
description = "URL do servidor web"
value = "https://${aws_lb.web.dns_name}"
}
output "db_connection_string" {
description = "String de conexão do banco de dados"
value = "postgresql://${var.db_username}:${var.database_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
sensitive = true
}
Arquivos de Variáveis
Seção intitulada “Arquivos de Variáveis”# prod.tfvars
environment = "prod"
instance_config = {
instance_type = "t3.large"
volume_size = 100
encrypted = true
}
# dev.tfvars
environment = "dev"
instance_config = {
instance_type = "t3.micro"
volume_size = 20
encrypted = false
}
Gerenciamento de Estado
Seção intitulada “Gerenciamento de Estado”Comandos de Estado
Seção intitulada “Comandos de Estado”| Comando | Descrição |
|---|---|
tofu state list | Listar todos os recursos no estado |
tofu state show aws_instance.web | Mostrar detalhes de recurso específico |
tofu state mv aws_instance.old aws_instance.new | Renomear recurso no estado |
tofu state rm aws_instance.web | Remover recurso do estado (mantém infraestrutura real) |
tofu state pull | Puxar estado remoto para stdout |
tofu state push | Enviar estado local para backend remoto |
tofu import aws_instance.web i-1234567 | Importar recurso existente para o estado |
tofu state replace-provider hashicorp/aws registry.opentofu.org/hashicorp/aws | Substituir provedor no estado |
tofu force-unlock LOCK_ID | Liberar um bloqueio de estado travado |
Configuração de Backend
Seção intitulada “Configuração de Backend”# Backend S3 (mais comum)
terraform {
backend "s3" {
bucket = "my-tofu-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "tofu-locks"
encrypt = true
}
}
# Backend PostgreSQL
terraform {
backend "pg" {
conn_str = "postgres://user:pass@db.example.com/tofu_state?sslmode=require"
}
}
# Backend HTTP (funciona com GitLab, alternativas ao Terraform Cloud)
terraform {
backend "http" {
address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod"
lock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
unlock_address = "https://gitlab.example.com/api/v4/projects/1/terraform/state/prod/lock"
username = "gitlab-ci-token"
password = var.ci_token
}
}
# Backend local (padrão, para desenvolvimento)
terraform {
backend "local" {
path = "terraform.tfstate"
}
}
Criptografia de Estado (Exclusivo do OpenTofu)
Seção intitulada “Criptografia de Estado (Exclusivo do OpenTofu)”# Criptografia de estado com AWS KMS
terraform {
encryption {
key_provider "aws_kms" "main" {
kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abcd-1234"
region = "us-east-1"
key_spec = "AES_256"
}
method "aes_gcm" "default" {
keys = key_provider.aws_kms.main
}
state {
method = method.aes_gcm.default
enforced = true
}
}
}
Módulos
Seção intitulada “Módulos”Uso de Módulos
Seção intitulada “Uso de Módulos”| Comando | Descrição |
|---|---|
module "vpc" { source = "./modules/vpc" } | Usar um módulo local |
module "vpc" { source = "github.com/org/repo//modules/vpc" } | Usar um módulo do GitHub |
module "vpc" { source = "registry.opentofu.org/..." } | Usar módulo do registro OpenTofu |
tofu get | Baixar e atualizar módulos |
tofu get -update | Forçar atualização de todos os módulos |
Estrutura de Módulos
Seção intitulada “Estrutura de Módulos”modules/
└── vpc/
├── main.tf # Recursos
├── variables.tf # Variáveis de entrada
├── outputs.tf # Valores de saída
├── versions.tf # Requisitos de provedores
└── README.md # Documentação de uso
Exemplo de Módulo
Seção intitulada “Exemplo de Módulo”# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(var.tags, { Name = var.name })
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.azs[count.index]
map_public_ip_on_launch = true
tags = merge(var.tags, {
Name = "${var.name}-public-${count.index}"
})
}
# modules/vpc/variables.tf
variable "name" { type = string }
variable "cidr" { type = string }
variable "azs" { type = list(string) }
variable "public_subnets" { type = list(string) }
variable "tags" { type = map(string) default = {} }
# modules/vpc/outputs.tf
output "vpc_id" { value = aws_vpc.this.id }
output "public_subnet_ids" { value = aws_subnet.public[*].id }
# Usando o módulo
module "vpc" {
source = "./modules/vpc"
name = "production"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
tags = local.common_tags
}
Migração do Terraform
Seção intitulada “Migração do Terraform”Compatibilidade Direta
Seção intitulada “Compatibilidade Direta”| Comando | Descrição |
|---|---|
Substituir terraform por tofu nos comandos | Comandos CLI são compatíveis diretamente |
tofu init -upgrade | Re-inicializar com provedores OpenTofu |
Manter arquivos .tf existentes inalterados | Sintaxe HCL é totalmente compatível |
Manter arquivos .tfstate existentes | Formato de estado é compatível |
Manter .terraform.lock.hcl existente | Arquivo de lock é compatível |
Atualizar CI/CD para usar action opentofu/setup-opentofu | Trocar GitHub Actions |
Lista de Verificação da Migração
Seção intitulada “Lista de Verificação da Migração”Migração do Terraform para OpenTofu:
1. Instalar OpenTofu junto com o Terraform
2. No diretório do projeto, executar: tofu init
3. Executar: tofu plan
- Comparar saída com: terraform plan
- Deve ser idêntico (sem mudanças)
4. Substituir 'terraform' por 'tofu' nos pipelines de CI/CD
5. Atualizar GitHub Actions:
- Antigo: hashicorp/setup-terraform
- Novo: opentofu/setup-opentofu
6. Atualizar fontes de provedores no arquivo de lock (se necessário):
- tofu providers lock
7. Habilitar criptografia de estado (exclusivo do OpenTofu):
- Adicionar bloco encryption ao bloco terraform {}
8. Trocar referências de registro de módulos:
- registry.terraform.io → registry.opentofu.org
- (Ambos ainda funcionam, mas o registro OpenTofu é preferido)
CI/CD com GitHub Actions
Seção intitulada “CI/CD com GitHub Actions”name: OpenTofu Plan & Apply
on:
push:
branches: [main]
pull_request:
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: opentofu/setup-opentofu@v1
with:
tofu_version: 1.8.0
- name: Init
run: tofu init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Plan
run: tofu plan -out=plan.tfplan -no-color
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Apply (apenas main)
if: github.ref == 'refs/heads/main'
run: tofu apply -auto-approve plan.tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Recursos Exclusivos do OpenTofu
Seção intitulada “Recursos Exclusivos do OpenTofu”Criptografia de Estado
Seção intitulada “Criptografia de Estado”Criptografar arquivos de estado em repouso — indisponível no Terraform. Suporta provedores de chave PBKDF2, AWS KMS, GCP KMS e Azure Key Vault.
Criptografia de Estado no Lado do Cliente
Seção intitulada “Criptografia de Estado no Lado do Cliente”Diferente da criptografia no lado do servidor do Terraform Cloud, o OpenTofu criptografa o estado antes de sair da sua máquina, garantindo que nenhum terceiro possa ler o estado da sua infraestrutura.
Independência de Registro
Seção intitulada “Independência de Registro”O OpenTofu mantém seu próprio registro de provedores e módulos em registry.opentofu.org, garantindo disponibilidade a longo prazo independente das decisões de licenciamento da HashiCorp.
Melhores Práticas
Seção intitulada “Melhores Práticas”-
Usar estado remoto com bloqueio — configurar um backend S3 + DynamoDB (ou equivalente) para prevenir modificações concorrentes e habilitar colaboração em equipe.
-
Habilitar criptografia de estado — usar o recurso exclusivo de criptografia do OpenTofu com KMS ou PBKDF2 para proteger dados sensíveis nos arquivos de estado.
-
Fixar versões de provedores e módulos — usar restrições
~>(ex.,~> 5.0) para permitir atualizações de patch enquanto previne mudanças que quebram. -
Usar workspaces ou separação de diretórios para ambientes — manter estados de dev, staging e produção separados para prevenir mudanças acidentais entre ambientes.
-
Escrever módulos para padrões reutilizáveis — extrair padrões comuns (VPCs, bancos de dados, clusters Kubernetes) em módulos com entradas e saídas claras.
-
Validar variáveis com restrições — adicionar blocos
validationàs variáveis para capturar valores inválidos antes de chegarem ao provedor de nuvem. -
Salvar planos antes de aplicar — usar
tofu plan -out=plan.tfplane depoistofu apply plan.tfplanpara garantir que você aplique exatamente o que revisou. -
Usar
default_tagsnos provedores — definir tags organizacionais (ManagedBy, Environment, Project) no nível do provedor para garantir tagueamento consistente. -
Importar recursos existentes — usar
tofu importpara trazer infraestrutura não gerenciada sob controle do OpenTofu em vez de recriá-la. -
Executar
tofu fmt -checkno CI — impor formatação consistente entre todos os membros da equipe falhando builds de CI em arquivos não formatados. -
Usar fontes de dados para referências externas — referenciar infraestrutura existente (AMIs, VPCs, zonas DNS) com blocos
dataem vez de codificar IDs fixos. -
Revisar planos cuidadosamente — sempre ler a saída do plano antes de aplicar. Prestar atenção às ações
destroyereplaceque podem causar indisponibilidade.