Providers, resources e state no Terraform
1. Introdução aos Providers no Terraform
No ecossistema DevOps, o Terraform atua como uma ferramenta de Infrastructure as Code (IaC) que permite provisionar e gerenciar recursos em múltiplas plataformas de forma declarativa. Os providers são plugins que atuam como pontes entre o Terraform e APIs de serviços como AWS, Azure, GCP, Docker e Kubernetes. Cada provider expõe recursos específicos da plataforma, permitindo que você descreva infraestrutura em arquivos de configuração.
Para ambientes com Docker e Kubernetes, os providers correspondentes permitem gerenciar containers, imagens, deployments e serviços diretamente via código Terraform. Exemplo básico de configuração:
provider "docker" {
host = "unix:///var/run/docker.sock"
}
provider "kubernetes" {
config_path = "~/.kube/config"
}
2. Configuração e Versionamento de Providers
O bloco required_providers dentro de terraform define quais providers serão usados e suas versões. Isso garante reprodutibilidade e evita quebras por atualizações inesperadas.
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.20"
}
}
}
Após definir os providers, execute terraform init para baixar os plugins e fixar as versões no arquivo .terraform.lock.hcl. Em projetos com múltiplos providers, organize cada configuração em arquivos separados (ex.: providers.tf, docker.tf, kubernetes.tf) para facilitar a manutenção.
3. Resources: Blocos Fundamentais da Infraestrutura
Resources são os blocos de construção no Terraform. Cada resource declara um componente de infraestrutura que será criado, atualizado ou destruído. A sintaxe segue o padrão:
resource "tipo_do_recurso" "nome_local" {
argumento1 = valor1
argumento2 = valor2
}
Exemplo prático com Docker:
resource "docker_image" "nginx" {
name = "nginx:latest"
keep_locally = false
}
resource "docker_container" "nginx_server" {
image = docker_image.nginx.name
name = "nginx_container"
ports {
internal = 80
external = 8080
}
}
Para Kubernetes:
resource "kubernetes_deployment" "app" {
metadata {
name = "app-deployment"
}
spec {
replicas = 3
selector {
match_labels = {
app = "myapp"
}
}
template {
metadata {
labels = {
app = "myapp"
}
}
spec {
container {
image = "nginx:latest"
name = "nginx"
}
}
}
}
}
Dependências implícitas são criadas automaticamente quando um resource referencia outro (ex.: docker_container referencia docker_image). Dependências explícitas podem ser forçadas com depends_on:
resource "kubernetes_service" "app_service" {
depends_on = [kubernetes_deployment.app]
# ...
}
4. Meta-Arguments e Ciclo de Vida dos Resources
Meta-arguments como count e for_each permitem criar múltiplos resources dinamicamente:
resource "docker_container" "web" {
count = 3
name = "web-${count.index}"
image = "nginx:latest"
}
Com for_each para mapas:
locals {
containers = {
frontend = { port = 3000 }
backend = { port = 5000 }
}
}
resource "docker_container" "services" {
for_each = local.containers
name = each.key
image = "nginx:latest"
ports {
internal = each.value.port
external = each.value.port
}
}
O bloco lifecycle controla o comportamento durante atualizações:
resource "kubernetes_deployment" "critical" {
# ...
lifecycle {
create_before_destroy = true
prevent_destroy = true
ignore_changes = [metadata[0].annotations]
}
}
create_before_destroy: cria novo recurso antes de destruir o antigo (essencial para zero-downtime em Kubernetes)prevent_destroy: protege recursos críticos contra exclusão acidentalignore_changes: ignora alterações externas em atributos específicos (útil para campos gerenciados por controladores Kubernetes)
5. O State do Terraform: Armazenamento e Estrutura
O state file (terraform.tfstate) é o coração do Terraform. Ele mapeia recursos declarados no código para objetos reais na infraestrutura, armazenando:
- IDs dos recursos no provedor
- Metadados de dependência
- Outputs e variáveis
- Atributos atuais de cada recurso
Sem o state, o Terraform não sabe o que já foi criado, causando duplicação ou destruição acidental. A estrutura interna do state é JSON e contém:
{
"version": 4,
"terraform_version": "1.5.0",
"resources": [
{
"mode": "managed",
"type": "docker_container",
"name": "nginx_server",
"provider": "provider[\"registry.terraform.io/kreuzwerker/docker\"]",
"instances": [
{
"attributes": {
"id": "abc123",
"name": "nginx_container"
}
}
]
}
]
}
Em produção, perder o state local pode ser catastrófico, pois o Terraform perderia o vínculo com os recursos reais.
6. Gerenciamento de State Remoto para Times
Para equipes, o state deve ser armazenado remotamente com suporte a bloqueio. Backends comuns:
S3 (AWS) com DynamoDB para lock:
terraform {
backend "s3" {
bucket = "meu-terraform-state"
key = "kubernetes/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Azure Storage:
terraform {
backend "azurerm" {
storage_account_name = "meustorage"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
Para Kubernetes, o state pode ser armazenado em buckets S3 ou Azure Storage, com o cluster acessando via credenciais configuradas no provider. O bloqueio evita que dois membros da equipe executem apply simultaneamente.
7. Boas Práticas com State e Sensitive Data
O state pode conter dados sensíveis como senhas, tokens e chaves SSH. Para proteger:
- Marque outputs como
sensitive = true:
output "db_password" {
value = random_password.db.result
sensitive = true
}
- Use
terraform state showcom cuidado e evite versionar o state em repositórios Git - Configure criptografia no backend (ex.:
encrypt = trueno S3) - Para migrar state entre backends sem recriar recursos:
terraform init -migrate-state
Ou manualmente:
terraform state pull > backup.tfstate
# Configurar novo backend
terraform init
terraform state push backup.tfstate
8. Troubleshooting: Resolvendo Problemas comuns
Provider incompatível ou versão incorreta:
Erro: Error: Failed to query available provider packages
Solução: Verifique as versões em required_providers e execute terraform init -upgrade
State desatualizado (drift detection):
O Terraform detecta quando recursos foram alterados fora do Terraform. Use terraform plan para identificar o drift e terraform apply para reconciliar.
Manipulação manual do state:
Comandos úteis:
# Listar recursos no state
terraform state list
# Remover recurso do state (sem destruir)
terraform state rm docker_container.nginx_server
# Importar recurso existente para o state
terraform import docker_container.nginx_server $(docker ps -aqf "name=nginx_container")
# Mover recurso dentro do state
terraform state mv docker_container.old_name docker_container.new_name
Para casos de state corrompido, restaure de backups regulares ou use terraform state pull para inspecionar o conteúdo.
Referências
- Terraform: Providers Documentation — Documentação oficial sobre configuração e versionamento de providers no Terraform
- Kreuzwerker Docker Provider — Guia completo do provider Docker para Terraform, com exemplos de resources
- HashiCorp Kubernetes Provider — Documentação oficial do provider Kubernetes, incluindo deployments, services e configmaps
- Terraform State Management Best Practices — Práticas recomendadas para gerenciamento de state, incluindo backends remotos e bloqueio
- Terraform: Remote State with S3 Backend — Tutorial detalhado sobre configuração de state remoto no S3 com DynamoDB para lock
- Managing Sensitive Data in Terraform State — Artigo da HashiCorp sobre proteção de dados sensíveis no state do Terraform
- Terraform CLI: State Command Reference — Referência completa dos comandos de manipulação de state (list, rm, mv, pull, push)