Pular para o conteúdo

OpenTofu

Ferramenta de infraestrutura como código de código aberto e fork comunitário do Terraform.

ComandoDescrição
brew install opentofuInstalar no macOS com Homebrew
snap install --classic opentofuInstalar no Linux com Snap
choco install opentofuInstalar no Windows com Chocolatey
curl -fsSL https://get.opentofu.org/install-opentofu.sh | shInstalar via script shell
docker run -it ghcr.io/opentofu/opentofuExecutar via Docker
tofu --versionMostrar versão instalada
tofu -helpMostrar comandos disponíveis
# 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
ComandoDescrição
tofu initInicializar diretório de trabalho e baixar provedores
tofu init -upgradeRe-inicializar e atualizar versões de provedores/módulos
tofu init -backend-config=prod.hclInicializar com arquivo de configuração de backend
tofu planPré-visualizar mudanças na infraestrutura
tofu plan -out=plan.tfplanSalvar plano em arquivo para aplicar depois
tofu plan -target=aws_instance.webPlanejar mudanças apenas para recurso específico
tofu plan -var-file=prod.tfvarsPlanejar com arquivo de variáveis específico
tofu applyAplicar mudanças na infraestrutura (solicita confirmação)
tofu apply -auto-approveAplicar mudanças sem confirmação
tofu apply plan.tfplanAplicar um arquivo de plano salvo
tofu destroyDestruir toda infraestrutura gerenciada
tofu destroy -auto-approveDestruir sem confirmação
tofu destroy -target=aws_instance.webDestruir apenas recurso específico
ComandoDescrição
tofu validateValidar sintaxe da configuração
tofu fmtFormatar arquivos de configuração
tofu fmt -checkVerificar se arquivos estão formatados (compatível com CI)
tofu fmt -recursiveFormatar todos os arquivos em subdiretórios
tofu outputExibir todos os valores de saída
tofu output -jsonExibir saídas em formato JSON
tofu output db_passwordExibir valor de saída específico
tofu showMostrar estado atual em formato legível
tofu show -jsonMostrar estado em formato JSON
tofu show plan.tfplanMostrar detalhes do plano salvo
tofu graphGerar grafo de dependência de recursos (formato DOT)
tofu graph | dot -Tpng > graph.pngRenderizar grafo de dependência como PNG
tofu refreshReconciliar estado com infraestrutura real
tofu providersListar provedores usados na configuração
# 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"
}
# 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
}
ComandoDescriçã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
ComandoDescriçã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 planPassar via variável de ambiente
Criar arquivo terraform.tfvarsValores de variáveis carregados automaticamente
Criar arquivo *.auto.tfvarsArquivos de variáveis carregados automaticamente
# 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
}
# 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
}
ComandoDescrição
tofu state listListar todos os recursos no estado
tofu state show aws_instance.webMostrar detalhes de recurso específico
tofu state mv aws_instance.old aws_instance.newRenomear recurso no estado
tofu state rm aws_instance.webRemover recurso do estado (mantém infraestrutura real)
tofu state pullPuxar estado remoto para stdout
tofu state pushEnviar estado local para backend remoto
tofu import aws_instance.web i-1234567Importar recurso existente para o estado
tofu state replace-provider hashicorp/aws registry.opentofu.org/hashicorp/awsSubstituir provedor no estado
tofu force-unlock LOCK_IDLiberar um bloqueio de estado travado
# 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 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
    }
  }
}
ComandoDescriçã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 getBaixar e atualizar módulos
tofu get -updateForçar atualização de todos os 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
# 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
}
ComandoDescrição
Substituir terraform por tofu nos comandosComandos CLI são compatíveis diretamente
tofu init -upgradeRe-inicializar com provedores OpenTofu
Manter arquivos .tf existentes inalteradosSintaxe HCL é totalmente compatível
Manter arquivos .tfstate existentesFormato de estado é compatível
Manter .terraform.lock.hcl existenteArquivo de lock é compatível
Atualizar CI/CD para usar action opentofu/setup-opentofuTrocar GitHub Actions
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)
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 }}

Criptografar arquivos de estado em repouso — indisponível no Terraform. Suporta provedores de chave PBKDF2, AWS KMS, GCP KMS e Azure Key Vault.

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.

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.

  1. Usar estado remoto com bloqueio — configurar um backend S3 + DynamoDB (ou equivalente) para prevenir modificações concorrentes e habilitar colaboração em equipe.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. Validar variáveis com restrições — adicionar blocos validation às variáveis para capturar valores inválidos antes de chegarem ao provedor de nuvem.

  7. Salvar planos antes de aplicar — usar tofu plan -out=plan.tfplan e depois tofu apply plan.tfplan para garantir que você aplique exatamente o que revisou.

  8. Usar default_tags nos provedores — definir tags organizacionais (ManagedBy, Environment, Project) no nível do provedor para garantir tagueamento consistente.

  9. Importar recursos existentes — usar tofu import para trazer infraestrutura não gerenciada sob controle do OpenTofu em vez de recriá-la.

  10. Executar tofu fmt -check no CI — impor formatação consistente entre todos os membros da equipe falhando builds de CI em arquivos não formatados.

  11. Usar fontes de dados para referências externas — referenciar infraestrutura existente (AMIs, VPCs, zonas DNS) com blocos data em vez de codificar IDs fixos.

  12. Revisar planos cuidadosamente — sempre ler a saída do plano antes de aplicar. Prestar atenção às ações destroy e replace que podem causar indisponibilidade.