SENTINEL Tecnologia
Terraform21 de março de 202610 min

Terraform: como escalar infraestrutura como código do zero ao enterprise

Guia completo de Terraform para ambientes enterprise: módulos, state management, CI/CD, policy as code e multi-cloud.

TerraformIaCDevOpsMulti-cloudAWSAzureCI/CD

Infraestrutura provisionada manualmente via console é uma bomba-relógio. Servidores que ninguém sabe como foram configurados, regras de firewall que ninguém quer tocar, ambientes de staging que não espelham produção. Terraform resolve isso.

Com mais de 4.000 providers e adoção massiva em empresas como Uber, Stripe, Shopify e Nubank, o Terraform é a ferramenta de IaC mais adotada do mundo. Este guia cobre o caminho completo: do primeiro `terraform init` até operações enterprise multi-cloud.

Por que Terraform

  • Declarativo: você descreve o estado desejado, o Terraform descobre como chegar lá
  • Agnóstico de cloud: mesma linguagem (HCL) para AWS, Azure, GCP, Kubernetes, Datadog, GitHub
  • State management: rastreamento completo do que existe vs. o que deveria existir
  • Plan antes de apply: visualização exata das mudanças antes de execução
  • Ecossistema maduro: 4.000+ providers, módulos públicos no Terraform Registry
  • HCL: a linguagem na prática

    ```hcl

    # main.tf - Infraestrutura básica na AWS

    terraform {

    required_version = ">= 1.9.0"

    required_providers {

    aws = {

    source = "hashicorp/aws"

    version = "~> 5.0"

    }

    }

    backend "s3" {

    bucket = "minha-empresa-terraform-state"

    key = "producao/infra/terraform.tfstate"

    region = "us-east-1"

    dynamodb_table = "terraform-locks"

    encrypt = true

    }

    }

    provider "aws" {

    region = var.aws_region

    default_tags {

    tags = {

    Environment = var.environment

    ManagedBy = "Terraform"

    Project = var.project_name

    CostCenter = var.cost_center

    }

    }

    }

    # VPC com subnets públicas e privadas

    module "vpc" {

    source = "terraform-aws-modules/vpc/aws"

    version = "5.5.0"

    name = "${var.project_name}-${var.environment}"

    cidr = var.vpc_cidr

    azs = var.availability_zones

    private_subnets = var.private_subnet_cidrs

    public_subnets = var.public_subnet_cidrs

    enable_nat_gateway = true

    single_nat_gateway = var.environment != "production"

    enable_dns_hostnames = true

    enable_dns_support = true

    tags = {

    "kubernetes.io/cluster/${var.cluster_name}" = "shared"

    }

    }

    ```

    ```hcl

    # variables.tf

    variable "aws_region" {

    description = "Região AWS"

    type = string

    default = "us-east-1"

    }

    variable "environment" {

    description = "Ambiente (dev, staging, production)"

    type = string

    validation {

    condition = contains(["dev", "staging", "production"], var.environment)

    error_message = "Environment deve ser dev, staging ou production."

    }

    }

    variable "vpc_cidr" {

    description = "CIDR block da VPC"

    type = string

    default = "10.0.0.0/16"

    }

    variable "project_name" {

    type = string

    default = "sentinel"

    }

    variable "cost_center" {

    type = string

    default = "engineering"

    }

    ```

    State Management: o coração do Terraform

    O state file é onde o Terraform armazena o mapeamento entre seus recursos declarados e os recursos reais na cloud. State corrompido = infraestrutura ingerenciável.

    AWS: S3 + DynamoDB

    ```hcl

    # state-bootstrap/main.tf - Execute uma única vez

    resource "aws_s3_bucket" "terraform_state" {

    bucket = "minha-empresa-terraform-state"

    lifecycle {

    prevent_destroy = true

    }

    }

    resource "aws_s3_bucket_versioning" "terraform_state" {

    bucket = aws_s3_bucket.terraform_state.id

    versioning_configuration {

    status = "Enabled"

    }

    }

    resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {

    bucket = aws_s3_bucket.terraform_state.id

    rule {

    apply_server_side_encryption_by_default {

    sse_algorithm = "aws:kms"

    }

    }

    }

    resource "aws_dynamodb_table" "terraform_locks" {

    name = "terraform-locks"

    billing_mode = "PAY_PER_REQUEST"

    hash_key = "LockID"

    attribute {

    name = "LockID"

    type = "S"

    }

    }

    ```

    Azure: Storage Account + Blob

    ```hcl

    # Backend Azure

    terraform {

    backend "azurerm" {

    resource_group_name = "rg-terraform-state"

    storage_account_name = "stterraformstate"

    container_name = "tfstate"

    key = "producao.terraform.tfstate"

    use_oidc = true

    }

    }

    ```

    Regras de ouro para state:

  • Nunca edite o state manualmente
  • Sempre use locking (DynamoDB para AWS, blob lease para Azure)
  • Versionamento habilitado no bucket/container
  • Encriptação at rest obrigatória
  • State separado por ambiente (dev, staging, prod)
  • Módulos reutilizáveis

    Módulos são a forma de escalar Terraform. Em vez de copiar e colar código, você cria componentes reutilizáveis:

    ```hcl

    # modules/eks-cluster/main.tf

    module "eks" {

    source = "terraform-aws-modules/eks/aws"

    version = "20.8.0"

    cluster_name = var.cluster_name

    cluster_version = var.kubernetes_version

    vpc_id = var.vpc_id

    subnet_ids = var.private_subnet_ids

    cluster_endpoint_public_access = true

    eks_managed_node_groups = {

    general = {

    desired_size = var.node_desired_size

    min_size = var.node_min_size

    max_size = var.node_max_size

    instance_types = var.instance_types

    capacity_type = "ON_DEMAND"

    labels = {

    role = "general"

    }

    }

    spot = {

    desired_size = var.spot_desired_size

    min_size = var.spot_min_size

    max_size = var.spot_max_size

    instance_types = ["t3.medium", "t3.large", "t3a.medium", "t3a.large"]

    capacity_type = "SPOT"

    labels = {

    role = "spot-workloads"

    }

    taints = [{

    key = "spot"

    value = "true"

    effect = "NO_SCHEDULE"

    }]

    }

    }

    }

    # modules/eks-cluster/outputs.tf

    output "cluster_endpoint" {

    value = module.eks.cluster_endpoint

    }

    output "cluster_certificate_authority_data" {

    value = module.eks.cluster_certificate_authority_data

    }

    ```

    Uso do módulo:

    ```hcl

    # environments/production/main.tf

    module "eks_production" {

    source = "../../modules/eks-cluster"

    cluster_name = "sentinel-production"

    kubernetes_version = "1.30"

    vpc_id = module.vpc.vpc_id

    private_subnet_ids = module.vpc.private_subnets

    node_desired_size = 3

    node_min_size = 3

    node_max_size = 10

    instance_types = ["t3.xlarge"]

    spot_desired_size = 2

    spot_min_size = 0

    spot_max_size = 20

    }

    ```

    CI/CD com GitHub Actions

    Terraform em CI/CD elimina deploys manuais e garante que toda mudança passa por review:

    ```yaml

    # .github/workflows/terraform.yml

    name: Terraform CI/CD

    on:

    pull_request:

    paths: ['terraform/**']

    push:

    branches: [main]

    paths: ['terraform/**']

    permissions:

    id-token: write

    contents: read

    pull-requests: write

    jobs:

    terraform:

    runs-on: ubuntu-latest

    defaults:

    run:

    working-directory: terraform/environments/production

    steps:

  • uses: actions/checkout@v4
  • uses: hashicorp/setup-terraform@v3
  • with:

    terraform_version: 1.9.5

  • name: Configure AWS Credentials (OIDC)
  • uses: aws-actions/configure-aws-credentials@v4

    with:

    role-to-assume: arn:aws:iam::123456789012:role/terraform-ci

    aws-region: us-east-1

  • name: Terraform Init
  • run: terraform init

  • name: Terraform Format Check
  • run: terraform fmt -check -recursive

  • name: Terraform Validate
  • run: terraform validate

  • name: Terraform Plan
  • id: plan

    run: terraform plan -no-color -out=tfplan

    continue-on-error: true

  • name: Comment PR with Plan
  • if: github.event_name == 'pull_request'

    uses: actions/github-script@v7

    with:

    script: |

    const output = `#### Terraform Plan

    \`\`\`

    ${{ steps.plan.outputs.stdout }}

    \`\`\`

    `;

    github.rest.issues.createComment({

    issue_number: context.issue.number,

    owner: context.repo.owner,

    repo: context.repo.repo,

    body: output

    })

  • name: Terraform Apply
  • if: github.ref == 'refs/heads/main' && github.event_name == 'push'

    run: terraform apply -auto-approve tfplan

    ```

    Policy as Code: OPA/Rego e Sentinel

    Em ambientes enterprise, não basta funcionar — precisa estar em compliance. Policy as Code garante que recursos sejam criados dentro das regras da organização.

    OPA (Open Policy Agent) com Conftest

    ```rego

    # policy/terraform.rego

    package main

    # Negar instâncias sem tags obrigatórias

    deny[msg] {

    resource := input.resource_changes[_]

    resource.type == "aws_instance"

    not resource.change.after.tags["Environment"]

    msg := sprintf("EC2 instance '%s' deve ter tag 'Environment'", [resource.name])

    }

    # Negar buckets S3 sem encriptação

    deny[msg] {

    resource := input.resource_changes[_]

    resource.type == "aws_s3_bucket"

    not has_encryption(resource)

    msg := sprintf("S3 bucket '%s' deve ter server-side encryption", [resource.name])

    }

    # Negar instâncias maiores que t3.xlarge em dev

    deny[msg] {

    resource := input.resource_changes[_]

    resource.type == "aws_instance"

    resource.change.after.tags["Environment"] == "dev"

    not allowed_dev_instance_type(resource.change.after.instance_type)

    msg := sprintf("Instance type '%s' não é permitido em dev", [resource.change.after.instance_type])

    }

    allowed_dev_instance_type(type) {

    allowed := {"t3.micro", "t3.small", "t3.medium", "t3.large", "t3.xlarge"}

    allowed[type]

    }

    ```

    ```bash

    # Validar plan contra policies

    terraform plan -out=tfplan

    terraform show -json tfplan > tfplan.json

    conftest test tfplan.json --policy policy/

    ```

    Drift Detection: detectando mudanças manuais

    Mudanças feitas via console sem Terraform criam drift — divergência entre o estado declarado e o real.

    ```bash

    # Detectar drift

    terraform plan -detailed-exitcode

    # Exit codes:

    # 0 = Sem mudanças (sem drift)

    # 1 = Erro

    # 2 = Mudanças detectadas (drift!)

    # Automatizar verificação diária

    # .github/workflows/drift-detection.yml

    # Executar terraform plan diariamente e alertar se exit code = 2

    ```

    Multi-cloud: AWS + Azure em harmonia

    ```hcl

    # providers.tf - Multi-cloud

    provider "aws" {

    region = "us-east-1"

    alias = "us"

    }

    provider "azurerm" {

    features {}

    subscription_id = var.azure_subscription_id

    }

    # Rede AWS

    module "aws_vpc" {

    source = "./modules/aws-vpc"

    providers = { aws = aws.us }

    cidr = "10.0.0.0/16"

    }

    # Rede Azure

    module "azure_vnet" {

    source = "./modules/azure-vnet"

    resource_group_name = "rg-sentinel"

    address_space = ["10.1.0.0/16"]

    }

    # VPN Site-to-Site conectando as duas clouds

    module "vpn_interconnect" {

    source = "./modules/multi-cloud-vpn"

    aws_vpc_id = module.aws_vpc.vpc_id

    azure_vnet_id = module.azure_vnet.vnet_id

    }

    ```

    Estrutura de projeto enterprise

    ```

    terraform/

    ├── modules/ # Módulos reutilizáveis

    │ ├── eks-cluster/

    │ ├── rds-postgres/

    │ ├── s3-bucket/

    │ └── monitoring-stack/

    ├── environments/ # Ambientes separados

    │ ├── dev/

    │ │ ├── main.tf

    │ │ ├── variables.tf

    │ │ └── terraform.tfvars

    │ ├── staging/

    │ └── production/

    ├── policy/ # OPA policies

    │ ├── terraform.rego

    │ └── cost-control.rego

    ├── .github/

    │ └── workflows/

    │ ├── terraform.yml

    │ └── drift-detection.yml

    └── README.md

    ```

    Princípios para escalar:

  • Um state file por ambiente por stack
  • Módulos versionados via Git tags
  • Variables nunca hardcoded — sempre via tfvars ou CI/CD
  • Outputs como interface entre módulos
  • `terraform fmt` e `terraform validate` no pre-commit hook
  • Precisa de ajuda?

    Agende uma call gratuita com nossa equipe e descubra como podemos modernizar sua infraestrutura com Terraform. [Fale com um especialista →](/contato)

    Nossa equipe já ajudou dezenas de empresas a migrar de infraestrutura manual para IaC completo com Terraform, reduzindo tempo de provisionamento de dias para minutos e eliminando drift. Marque uma reunião sem compromisso e veja na prática o que podemos fazer pelo seu ambiente.

    Precisa de ajuda com Terraform?

    Consultoria especializada com resultados mensuraveis. Fale com um especialista sem compromisso.

    Artigos relacionados

    Receba insights de TI no seu email

    Artigos praticos sobre Cloud, FinOps, IA e estrategia de TI. Sem spam.

    Ganhe o guia "Checklist de Otimizacao Cloud" ao se inscrever

    Cancele a qualquer momento.