Em projetos reais, a infraestrutura raramente é provisionada de uma vez. Ela evolui incrementalmente — começando com o mínimo necessário para rodar a primeira versão e crescendo conforme o sistema e os requisitos amadurecem. O capstone adota essa abordagem: a infraestrutura é provisionada em camadas, cada uma construída sobre a anterior, com o Terraform como linguagem única de declaração do estado desejado.
A sequência de provisionamento segue a ordem de dependências naturais do sistema: rede primeiro, porque tudo depende dela; segredos e KMS, porque recursos seguros dependem de chaves; banco de dados e cache, porque os serviços dependem dos dados; EKS, porque os pods dependem do cluster; e finalmente os add-ons do cluster, porque dependem do EKS estar operacional.
Camada 1: Rede e Segurança Fundacional
# infrastructure/terraform/modules/networking/main.tf
locals {
azs = slice(data.aws_availability_zones.available.names, 0, 3)
cidr_vpc = "10.0.0.0/16"
# Subnets calculadas automaticamente para 3 AZs
cidrs_publicos = [for i, az in local.azs : cidrsubnet(local.cidr_vpc, 8, i)]
cidrs_privados = [for i, az in local.azs : cidrsubnet(local.cidr_vpc, 8, i + 10)]
cidrs_dados = [for i, az in local.azs : cidrsubnet(local.cidr_vpc, 8, i + 20)]
}
data "aws_availability_zones" "available" {
state = "available"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.5"
name = "${var.project_name}-${var.environment}"
cidr = local.cidr_vpc
azs = local.azs
public_subnets = local.cidrs_publicos
private_subnets = local.cidrs_privados
database_subnets = local.cidrs_dados
# NAT Gateways — um por AZ em produção para resiliência
enable_nat_gateway = true
single_nat_gateway = var.environment != "production"
one_nat_gateway_per_az = var.environment == "production"
# DNS interno para service discovery
enable_dns_hostnames = true
enable_dns_support = true
# Tags necessárias para o EKS descobrir as subnets
public_subnet_tags = {
"kubernetes.io/role/elb" = 1
"kubernetes.io/cluster/${var.project_name}-${var.environment}" = "shared"
}
private_subnet_tags = {
"kubernetes.io/role/internal-elb" = 1
"kubernetes.io/cluster/${var.project_name}-${var.environment}" = "shared"
"karpenter.sh/discovery" = "${var.project_name}-${var.environment}"
}
tags = local.tags_comuns
}
# Security Groups para cada camada da aplicação
resource "aws_security_group" "alb" {
name = "${var.project_name}-${var.environment}-alb"
description = "Security group do Application Load Balancer"
vpc_id = module.vpc.vpc_id
ingress {
description = "HTTPS da internet"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTP — redirecionado para HTTPS"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "Tráfego para os nós EKS"
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = [local.cidr_vpc]
}
tags = merge(local.tags_comuns, { Name = "${var.project_name}-${var.environment}-alb" })
}
resource "aws_security_group" "eks_nodes" {
name = "${var.project_name}-${var.environment}-eks-nodes"
description = "Security group dos nós worker do EKS"
vpc_id = module.vpc.vpc_id
ingress {
description = "Tráfego do ALB"
from_port = 0
to_port = 65535
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
ingress {
description = "Comunicação entre nós"
from_port = 0
to_port = 65535
protocol = "-1"
self = true
}
egress {
description = "Todo tráfego de saída"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(local.tags_comuns, { Name = "${var.project_name}-${var.environment}-eks-nodes" })
}
resource "aws_security_group" "rds" {
name = "${var.project_name}-${var.environment}-rds"
description = "Security group do RDS PostgreSQL"
vpc_id = module.vpc.vpc_id
ingress {
description = "PostgreSQL dos nós EKS"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.eks_nodes.id]
}
tags = merge(local.tags_comuns, { Name = "${var.project_name}-${var.environment}-rds" })
}
resource "aws_security_group" "elasticache" {
name = "${var.project_name}-${var.environment}-elasticache"
description = "Security group do ElastiCache Redis"
vpc_id = module.vpc.vpc_id
ingress {
description = "Redis dos nós EKS"
from_port = 6379
to_port = 6379
protocol = "tcp"
security_groups = [aws_security_group.eks_nodes.id]
}
tags = merge(local.tags_comuns, { Name = "${var.project_name}-${var.environment}-elasticache" })
}
Camada 2: KMS e Secrets Manager
# infrastructure/terraform/modules/secrets/main.tf
# Chave KMS mestre — usada para criptografar todos os outros recursos
resource "aws_kms_key" "principal" {
description = "Chave KMS principal — ${var.project_name} ${var.environment}"
deletion_window_in_days = 7
enable_key_rotation = true
multi_region = false
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AdministracaoRaiz"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "PermitirEKS"
Effect = "Allow"
Principal = {
AWS = module.eks.eks_managed_node_groups["geral"].iam_role_arn
}
Action = [
"kms:Decrypt",
"kms:GenerateDataKey",
]
Resource = "*"
}
]
})
tags = local.tags_comuns
}
resource "aws_kms_alias" "principal" {
name = "alias/${var.project_name}-${var.environment}"
target_key_id = aws_kms_key.principal.key_id
}
# Secrets por serviço — cada serviço tem suas próprias credenciais
locals {
servicos = ["catalog", "user", "order", "notification", "api-gateway"]
}
resource "aws_secretsmanager_secret" "servico" {
for_each = toset(local.servicos)
name = "${var.project_name}/${var.environment}/${each.key}"
description = "Credenciais do serviço ${each.key}"
kms_key_id = aws_kms_key.principal.arn
# Rotação automática — requer implementação do Lambda de rotação
# Habilitado apenas em produção
dynamic "rotation_rules" {
for_each = var.environment == "production" ? [1] : []
content {
automatically_after_days = 30
}
}
recovery_window_in_days = var.environment == "production" ? 7 : 0
tags = merge(local.tags_comuns, { Servico = each.key })
}
# Valores iniciais dos secrets — substituídos em produção por pipeline seguro
resource "aws_secretsmanager_secret_version" "servico" {
for_each = toset(local.servicos)
secret_id = aws_secretsmanager_secret.servico[each.key].id
secret_string = jsonencode({
DATABASE_URL = each.key != "notification" ? (
"postgresql://${each.key}_user:SUBSTITUIR_EM_PRODUCAO@${
aws_route53_record.rds[each.key].fqdn
}:5432/${each.key}_db"
) : null
REDIS_URL = "rediss://:SUBSTITUIR_EM_PRODUCAO@${
aws_elasticache_replication_group.redis.primary_endpoint_address
}:6379"
JWT_SECRET = "SUBSTITUIR_EM_PRODUCAO"
})
lifecycle {
# Nunca sobrescreve valores que foram atualizados manualmente
ignore_changes = [secret_string]
}
}
Camada 3: Banco de Dados por Serviço
# infrastructure/terraform/modules/databases/main.tf
locals {
# Serviços que precisam de banco de dados próprio
servicos_com_db = {
catalog = {
classe_staging = "db.t3.small"
classe_producao = "db.r6g.large"
storage_inicial = 20
storage_maximo = 500
}
user = {
classe_staging = "db.t3.small"
classe_producao = "db.r6g.large"
storage_inicial = 20
storage_maximo = 200
}
order = {
classe_staging = "db.t3.medium"
classe_producao = "db.r6g.xlarge" # Maior — core do negócio
storage_inicial = 50
storage_maximo = 2000
}
}
}
# Subnet group compartilhado para todos os bancos
resource "aws_db_subnet_group" "principal" {
name = "${var.project_name}-${var.environment}"
subnet_ids = module.vpc.database_subnets
tags = local.tags_comuns
}
# Parameter group otimizado para produção
resource "aws_db_parameter_group" "postgres16" {
name = "${var.project_name}-${var.environment}-pg16"
family = "postgres16"
parameter {
name = "shared_buffers"
value = "{DBInstanceClassMemory/4096}"
}
parameter {
name = "log_min_duration_statement"
value = var.environment == "production" ? "1000" : "100"
}
parameter {
name = "log_lock_waits"
value = "1"
}
parameter {
name = "log_checkpoints"
value = "1"
}
parameter {
name = "max_connections"
value = "200"
}
lifecycle {
create_before_destroy = true
}
}
# Um banco RDS por serviço
resource "aws_db_instance" "servico" {
for_each = local.servicos_com_db
identifier = "${var.project_name}-${var.environment}-${each.key}"
engine = "postgres"
engine_version = "16.1"
instance_class = var.environment == "production" ? (
each.value.classe_producao
) : each.value.classe_staging
allocated_storage = each.value.storage_inicial
max_allocated_storage = each.value.storage_maximo
storage_type = "gp3"
storage_encrypted = true
kms_key_id = aws_kms_key.principal.arn
db_name = "${replace(each.key, "-", "_")}_db"
username = "${replace(each.key, "-", "_")}_admin"
manage_master_user_password = true
master_user_secret_kms_key_id = aws_kms_key.principal.arn
multi_az = var.environment == "production"
db_subnet_group_name = aws_db_subnet_group.principal.name
vpc_security_group_ids = [aws_security_group.rds.id]
publicly_accessible = false
parameter_group_name = aws_db_parameter_group.postgres16.name
backup_retention_period = var.environment == "production" ? 14 : 3
backup_window = "02:00-03:00"
maintenance_window = "Mon:03:00-Mon:04:00"
auto_minor_version_upgrade = true
deletion_protection = var.environment == "production"
skip_final_snapshot = var.environment != "production"
final_snapshot_identifier = var.environment == "production" ? (
"${var.project_name}-${var.environment}-${each.key}-final"
) : null
performance_insights_enabled = true
performance_insights_retention_period = 7
monitoring_interval = 60
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
tags = merge(local.tags_comuns, {
Servico = each.key
DBName = "${replace(each.key, "-", "_")}_db"
})
}
# Read Replica para o serviço de catálogo — alta taxa de leitura
resource "aws_db_instance" "catalog_replica" {
count = var.environment == "production" ? 1 : 0
identifier = "${var.project_name}-${var.environment}-catalog-replica"
replicate_source_db = aws_db_instance.servico["catalog"].identifier
instance_class = "db.r6g.large"
storage_encrypted = true
publicly_accessible = false
vpc_security_group_ids = [aws_security_group.rds.id]
parameter_group_name = aws_db_parameter_group.postgres16.name
backup_retention_period = 0
skip_final_snapshot = true
auto_minor_version_upgrade = true
performance_insights_enabled = true
tags = merge(local.tags_comuns, {
Servico = "catalog"
Role = "read-replica"
})
}
# Registros DNS internos para abstrair os endpoints
resource "aws_route53_zone" "interna" {
name = "interno.${var.domain_name}"
vpc {
vpc_id = module.vpc.vpc_id
}
tags = local.tags_comuns
}
resource "aws_route53_record" "rds" {
for_each = local.servicos_com_db
zone_id = aws_route53_zone.interna.zone_id
name = "${each.key}-db.interno.${var.domain_name}"
type = "CNAME"
ttl = 60
records = [aws_db_instance.servico[each.key].address]
}
Camada 4: ElastiCache e Filas
# infrastructure/terraform/modules/messaging/main.tf
# Redis compartilhado para cache dos serviços
resource "aws_elasticache_subnet_group" "redis" {
name = "${var.project_name}-${var.environment}-redis"
subnet_ids = module.vpc.private_subnets
tags = local.tags_comuns
}
resource "aws_elasticache_parameter_group" "redis7" {
name = "${var.project_name}-${var.environment}-redis7"
family = "redis7"
parameter {
name = "maxmemory-policy"
value = "allkeys-lru" # Remove chaves menos usadas quando memória esgota
}
parameter {
name = "notify-keyspace-events"
value = "Ex"
}
}
resource "aws_elasticache_replication_group" "redis" {
replication_group_id = "${var.project_name}-${var.environment}"
description = "Cache Redis — ${var.project_name} ${var.environment}"
node_type = var.environment == "production" ? "cache.r6g.large" : "cache.t3.micro"
num_cache_clusters = var.environment == "production" ? 3 : 1
port = 6379
parameter_group_name = aws_elasticache_parameter_group.redis7.name
subnet_group_name = aws_elasticache_subnet_group.redis.name
security_group_ids = [aws_security_group.elasticache.id]
engine_version = "7.1"
automatic_failover_enabled = var.environment == "production"
multi_az_enabled = var.environment == "production"
at_rest_encryption_enabled = true
transit_encryption_enabled = true
auth_token = random_password.redis_auth.result
auth_token_update_strategy = "ROTATE"
snapshot_window = "02:00-03:00"
snapshot_retention_limit = var.environment == "production" ? 7 : 1
maintenance_window = "tue:03:00-tue:04:00"
log_delivery_configuration {
destination = aws_cloudwatch_log_group.redis_slow.name
destination_type = "cloudwatch-logs"
log_format = "json"
log_type = "slow-log"
}
tags = local.tags_comuns
}
resource "random_password" "redis_auth" {
length = 32
special = false # Redis auth token não suporta alguns caracteres especiais
}
# Armazena o token do Redis no Secrets Manager
resource "aws_secretsmanager_secret_version" "redis_auth" {
secret_id = aws_secretsmanager_secret.redis_auth.id
secret_string = random_password.redis_auth.result
}
resource "aws_secretsmanager_secret" "redis_auth" {
name = "${var.project_name}/${var.environment}/redis-auth-token"
kms_key_id = aws_kms_key.principal.arn
tags = local.tags_comuns
}
# ── Filas SQS ──────────────────────────────────────────────────────
locals {
filas = {
"pedido-confirmado" = {
descricao = "Notifica serviços quando pedido é confirmado"
retencao = 86400 # 1 dia
visibilidade = 30
max_receives = 3
}
"pedido-cancelado" = {
descricao = "Notifica serviços quando pedido é cancelado"
retencao = 86400
visibilidade = 30
max_receives = 3
}
"estoque-atualizado" = {
descricao = "Notifica catálogo quando estoque muda"
retencao = 3600 # 1 hora — baixa prioridade
visibilidade = 60
max_receives = 5
}
}
}
# Dead Letter Queues — capturam mensagens que falharam repetidamente
resource "aws_sqs_queue" "dlq" {
for_each = local.filas
name = "${var.project_name}-${var.environment}-${each.key}-dlq"
message_retention_seconds = 1209600 # 14 dias para investigação
kms_master_key_id = aws_kms_key.principal.arn
tags = merge(local.tags_comuns, {
Fila = each.key
Tipo = "dlq"
})
}
# Filas principais com referência às DLQs
resource "aws_sqs_queue" "principal" {
for_each = local.filas
name = "${var.project_name}-${var.environment}-${each.key}"
message_retention_seconds = each.value.retencao
visibility_timeout_seconds = each.value.visibilidade
kms_master_key_id = aws_kms_key.principal.arn
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.dlq[each.key].arn
maxReceiveCount = each.value.max_receives
})
tags = merge(local.tags_comuns, {
Fila = each.key
Descricao = each.value.descricao
})
}
# Alarmes para mensagens acumulando nas DLQs
resource "aws_cloudwatch_metric_alarm" "dlq_mensagens" {
for_each = local.filas
alarm_name = "${var.project_name}-${var.environment}-dlq-${each.key}"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "ApproximateNumberOfMessagesVisible"
namespace = "AWS/SQS"
period = 300
statistic = "Sum"
threshold = 0 # Qualquer mensagem na DLQ é alerta
dimensions = {
QueueName = aws_sqs_queue.dlq[each.key].name
}
alarm_description = "Mensagens na DLQ ${each.key} — investigar falhas de processamento"
alarm_actions = [aws_sns_topic.alertas.arn]
tags = local.tags_comuns
}
Camada 5: Cluster EKS
# infrastructure/terraform/modules/eks/main.tf
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "${var.project_name}-${var.environment}"
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
control_plane_subnet_ids = module.vpc.private_subnets
cluster_endpoint_public_access = true
cluster_endpoint_public_access_cidrs = var.acesso_publico_cidrs
cluster_endpoint_private_access = true
cluster_encryption_config = {
provider_key_arn = aws_kms_key.principal.arn
resources = ["secrets"]
}
cluster_addons = {
coredns = { most_recent = true }
kube-proxy = { most_recent = true }
vpc-cni = {
most_recent = true
service_account_role_arn = module.vpc_cni_irsa.iam_role_arn
}
aws-ebs-csi-driver = {
most_recent = true
service_account_role_arn = module.ebs_csi_irsa.iam_role_arn
}
}
eks_managed_node_groups = {
# Node group de sistema — para add-ons e workloads da plataforma
sistema = {
name = "${var.project_name}-${var.environment}-sistema"
instance_types = ["m6i.large"]
capacity_type = "ON_DEMAND"
min_size = 2
max_size = 4
desired_size = 2
labels = { role = "sistema" }
taints = [{
key = "CriticalAddonsOnly"
value = "true"
effect = "NO_SCHEDULE"
}]
block_device_mappings = {
xvda = {
device_name = "/dev/xvda"
ebs = {
volume_size = 50
volume_type = "gp3"
encrypted = true
kms_key_id = aws_kms_key.eks_nodes.arn
delete_on_termination = true
}
}
}
}
# Node group de aplicação — para os microsserviços
aplicacao = {
name = "${var.project_name}-${var.environment}-aplicacao"
instance_types = ["m6i.large", "m6a.large", "m7i.large"]
capacity_type = var.environment == "production" ? "ON_DEMAND" : "SPOT"
min_size = var.environment == "production" ? 3 : 1
max_size = 20
desired_size = var.environment == "production" ? 3 : 1
labels = { role = "aplicacao" }
block_device_mappings = {
xvda = {
device_name = "/dev/xvda"
ebs = {
volume_size = 50
volume_type = "gp3"
encrypted = true
kms_key_id = aws_kms_key.eks_nodes.arn
delete_on_termination = true
}
}
}
}
}
# Acesso ao cluster via IAM Identity Center
access_entries = {
admin = {
principal_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/AWSReservedSSO_AWSAdministratorAccess_${var.sso_suffix}"
policy_associations = {
admin = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
access_scope = { type = "cluster" }
}
}
}
pipeline = {
principal_arn = aws_iam_role.pipeline_deploy.arn
policy_associations = {
editor = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy"
access_scope = {
type = "namespace"
namespaces = ["producao", "staging"]
}
}
}
}
}
tags = local.tags_comuns
}
resource "aws_kms_key" "eks_nodes" {
description = "KMS para volumes EBS dos nós EKS"
deletion_window_in_days = 7
enable_key_rotation = true
tags = local.tags_comuns
}
Camada 6: Namespaces e Configurações do Cluster
# infrastructure/terraform/modules/kubernetes-base/main.tf
# Namespaces com labels de Pod Security Standards
resource "kubernetes_namespace" "producao" {
metadata {
name = "producao"
labels = {
"pod-security.kubernetes.io/enforce" = "restricted"
"pod-security.kubernetes.io/enforce-version" = "latest"
"pod-security.kubernetes.io/audit" = "restricted"
"pod-security.kubernetes.io/warn" = "restricted"
}
annotations = {
"scheduler.alpha.kubernetes.io/node-selector" = "role=aplicacao"
}
}
}
resource "kubernetes_namespace" "monitoring" {
metadata {
name = "monitoring"
labels = {
"pod-security.kubernetes.io/enforce" = "baseline"
}
}
}
# Resource Quotas por namespace
resource "kubernetes_resource_quota" "producao" {
metadata {
name = "quota-producao"
namespace = kubernetes_namespace.producao.metadata[0].name
}
spec {
hard = {
"requests.cpu" = "20"
"requests.memory" = "40Gi"
"limits.cpu" = "40"
"limits.memory" = "80Gi"
"pods" = "100"
}
}
}
# LimitRange — define padrões de resources para pods sem especificação
resource "kubernetes_limit_range" "producao" {
metadata {
name = "limitrange-producao"
namespace = kubernetes_namespace.producao.metadata[0].name
}
spec {
limit {
type = "Container"
default = {
cpu = "500m"
memory = "512Mi"
}
default_request = {
cpu = "100m"
memory = "128Mi"
}
max = {
cpu = "4"
memory = "4Gi"
}
}
}
}
# StorageClass gp3 como padrão
resource "kubernetes_storage_class" "gp3" {
metadata {
name = "gp3"
annotations = {
"storageclass.kubernetes.io/is-default-class" = "true"
}
}
storage_provisioner = "ebs.csi.aws.com"
volume_binding_mode = "WaitForFirstConsumer"
allow_volume_expansion = true
parameters = {
type = "gp3"
iops = "3000"
throughput = "125"
encrypted = "true"
kmsKeyId = aws_kms_key.principal.arn
}
}
# Add-ons via Helm
resource "helm_release" "aws_load_balancer_controller" {
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
namespace = "kube-system"
version = "1.7.1"
set {
name = "clusterName"
value = module.eks.cluster_name
}
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = module.alb_controller_irsa.iam_role_arn
}
}
resource "helm_release" "external_secrets" {
name = "external-secrets"
repository = "https://charts.external-secrets.io"
chart = "external-secrets"
namespace = "external-secrets"
create_namespace = true
version = "0.9.13"
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = module.external_secrets_irsa.iam_role_arn
}
depends_on = [module.eks]
}
resource "helm_release" "argocd" {
name = "argocd"
repository = "https://argoproj.github.io/argo-helm"
chart = "argo-cd"
namespace = "argocd"
create_namespace = true
version = "6.4.0"
values = [file("${path.module}/argocd-values.yaml")]
depends_on = [module.eks]
}
resource "helm_release" "kube_prometheus_stack" {
name = "kube-prometheus-stack"
repository = "https://prometheus-community.github.io/helm-charts"
chart = "kube-prometheus-stack"
namespace = "monitoring"
version = "56.6.2"
values = [file("${path.module}/prometheus-values.yaml")]
depends_on = [kubernetes_namespace.monitoring]
}
Outputs da Infraestrutura
# infrastructure/terraform/environments/production/outputs.tf
output "cluster_name" {
description = "Nome do cluster EKS"
value = module.eks.cluster_name
}
output "cluster_endpoint" {
description = "Endpoint da API do EKS"
value = module.eks.cluster_endpoint
sensitive = true
}
output "vpc_id" {
description = "ID da VPC"
value = module.vpc.vpc_id
}
output "rds_endpoints" {
description = "Endpoints dos bancos de dados por serviço"
value = {
for servico, db in aws_db_instance.servico :
servico => db.address
}
sensitive = true
}
output "redis_endpoint" {
description = "Endpoint primário do Redis"
value = aws_elasticache_replication_group.redis.primary_endpoint_address
sensitive = true
}
output "sqs_urls" {
description = "URLs das filas SQS"
value = {
for nome, fila in aws_sqs_queue.principal :
nome => fila.url
}
}
output "secrets_arns" {
description = "ARNs dos secrets no Secrets Manager por serviço"
value = {
for servico, secret in aws_secretsmanager_secret.servico :
servico => secret.arn
}
sensitive = true
}
output "nameservers" {
description = "Nameservers do Route53 — configurar no registrador"
value = aws_route53_zone.principal.name_servers
}
Executando o Provisionamento
#!/bin/bash
# scripts/provisionar-infraestrutura.sh
# Provisiona a infraestrutura completa na ordem correta de dependências
set -euo pipefail
AMBIENTE="${1:?Uso: $0 <staging|production>}"
REGIAO="us-east-1"
log() { echo "[$(date -u +%H:%M:%S)] $*"; }
cd infrastructure/terraform/environments/$AMBIENTE
log "=== Provisionando infraestrutura — ambiente: $AMBIENTE ==="
# Inicializa o Terraform com backend remoto
log "Inicializando Terraform..."
terraform init \
-backend-config="bucket=${TF_STATE_BUCKET}" \
-backend-config="key=${AMBIENTE}/terraform.tfstate" \
-backend-config="region=${REGIAO}" \
-backend-config="dynamodb_table=${TF_LOCK_TABLE}"
# Valida a configuração
log "Validando configuração..."
terraform validate
# Plano com saída para arquivo
log "Gerando plano..."
terraform plan \
-var="environment=${AMBIENTE}" \
-var="aws_region=${REGIAO}" \
-out=tfplan
# Em produção, requer aprovação manual antes de aplicar
if [ "$AMBIENTE" = "production" ]; then
echo ""
echo "⚠️ ATENÇÃO: Aplicando mudanças em PRODUÇÃO"
echo "Revise o plano acima cuidadosamente."
read -rp "Digite 'aplicar' para confirmar: " confirmacao
if [ "$confirmacao" != "aplicar" ]; then
log "Operação cancelada pelo usuário"
exit 0
fi
fi
log "Aplicando mudanças..."
terraform apply tfplan
# Atualiza o kubeconfig
log "Atualizando kubeconfig..."
aws eks update-kubeconfig \
--name "$(terraform output -raw cluster_name)" \
--region "$REGIAO"
log "✅ Infraestrutura provisionada com sucesso!"
log "Cluster: $(terraform output -raw cluster_name)"
O Que Vem a Seguir
Com a infraestrutura provisionada, o próximo artigo implementa os cinco microsserviços: código funcional com todas as práticas de resiliência, observabilidade e segurança integradas, Dockerfiles otimizados, manifestos Kubernetes completos e a configuração do ArgoCD para deploy declarativo.
Referências para Aprofundamento
Terraform e IaC - Terraform Best Practices — developer.hashicorp.com — Guia oficial de boas práticas para estruturação de módulos Terraform, incluindo convenções de nomenclatura, composição de módulos e estratégias de versionamento. - AWS VPC Terraform Module — registry.terraform.io — Documentação do módulo oficial de VPC para AWS com todos os parâmetros disponíveis e exemplos de uso para diferentes arquiteturas de rede.
EKS - EKS Best Practices Guide — aws.github.io — Guia oficial de boas práticas para EKS mantido pela AWS, cobrindo segurança, networking, escalabilidade e monitoramento com recomendações detalhadas para cada área. - EKS Workshop — eksworkshop.com — Workshop hands-on oficial da AWS para EKS com laboratórios práticos cobrindo desde a criação do cluster até práticas avançadas de segurança e observabilidade.