엔터프라이즈 규모의 IaC: 실제 과제

Terraform이 3명으로 구성된 팀에서 50개 팀으로 구성된 조직으로 전환되면 수백 개의 클라우드 환경을 관리하면서 소개 기사에서 직면하게 되는 문제 그들은 결코 언급하지 않습니다: 누구도 월 $50,000 인스턴스를 생성할 수 없도록 하는 방법 우연히? 한 팀이 다른 팀의 상태를 덮어쓰는 것을 방지하는 방법은 무엇입니까? 어떻게 차단하지 않고 회사 보안 정책을 준수하도록 보장합니다. 생산성? 비밀을 중앙에서 관리하는 방법은 무엇입니까?

시리즈의 마지막 기사에서는 Terraform의 엔터프라이즈 패턴을 다룹니다. HCP 테라폼 (이전의 Terraform Cloud)는 거버넌스 플랫폼으로서, 보초 금융 가드레일 및 규정 준수를 위한 정책 언어로서 전자 HashiCorp 금고 비밀의 중앙 집중식 관리를 위해.

무엇을 배울 것인가

  • 작업 공간 전략: 플랫, 단일 저장소, 팀별/환경/계층
  • Sentinel: HashiCorp 정책 언어, 모의 기반 테스트, 시행 수준
  • 금융 가드레일: 비용이 많이 드는 인스턴스 차단, 자동 예산 알림
  • 프라이빗 모듈 레지스트리: 버전 관리, 거버넌스, 모듈용 RBAC
  • Vault + Terraform: 동적 비밀, 하드코딩 없이 비밀 주입
  • 팀 규모 조정: 변수 세트, 실행 트리거, 에이전트 풀

작업 공간 전략: 대규모 인프라 구성

Terraform Cloud/Enterprise 작업공간은 격리된 상태 파일에 해당합니다. 작업 공간의 명명 및 구성 전략이 첫 번째 결정입니다. 기업 인프라의 전체 수명주기에 영향을 미칩니다. 세가지 패턴이 있어요 주요한 것들은 각각 뚜렷한 장단점이 있습니다.

패턴 1: 환경별(가장 일반적)

# Naming convention: {team}-{service}-{environment}
# Esempio per un team platform con servizi networking, compute, database:

platform-networking-dev
platform-networking-staging
platform-networking-prod

platform-compute-dev
platform-compute-staging
platform-compute-prod

platform-database-dev
platform-database-staging
platform-database-prod

# Pro: isolamento completo per ambiente
# Pro: policy diverse per prod vs non-prod (piu strict su prod)
# Con: se hai 20 team con 5 servizi ognuno = 300 workspace da gestire
# Con: aggiornare una variabile comune richiede toccare molti workspace

패턴 2: 동적 작업 공간을 갖춘 Monorepo

# Struttura monorepo con HCP Terraform
terraform-infra/
├── modules/                     # Moduli privati condivisi
├── stacks/                      # "Stack" = unita di deployment
│   ├── networking/
│   │   ├── main.tf
│   │   └── variables.tf
│   ├── eks-cluster/
│   └── rds-aurora/
└── workspaces/                  # Configurazione per ogni workspace
    ├── platform-networking-prod.tfvars
    ├── platform-networking-staging.tfvars
    └── team-a-eks-prod.tfvars

# HCP Terraform workspace configuration (via Terraform provider)
resource "tfe_workspace" "networking_prod" {
  name         = "platform-networking-prod"
  organization = "myorg"

  # Trigger: applica automaticamente su push al branch main
  # ma solo per i file nella directory stacks/networking
  trigger_prefixes  = ["stacks/networking/"]
  working_directory = "stacks/networking"

  # Auto-apply solo in non-prod
  auto_apply = false  # prod: sempre approvazione manuale

  # Terraform version
  terraform_version = "1.9.x"

  tag_names = ["env:prod", "team:platform", "tier:networking"]
}

변수 세트: 작업공간 간 공유 구성

# Variable Sets = gruppo di variabili applicabili a piu workspace
# Evita di duplicare le stesse variabili in 50 workspace

# Esempio: Variable Set globale con configurazione AWS
resource "tfe_variable_set" "aws_prod" {
  name         = "AWS Production Credentials"
  description  = "Credenziali AWS per ambienti di produzione"
  organization = "myorg"
  global       = false  # Non globale: solo workspace con tag specifici
}

# Variabili nel set (environment variables per il runner)
resource "tfe_variable" "aws_role_arn" {
  key             = "AWS_ROLE_ARN"
  value           = "arn:aws:iam::123456789:role/TerraformProdRole"
  category        = "env"
  variable_set_id = tfe_variable_set.aws_prod.id
  sensitive       = false
}

# Applica il variable set a workspace con tag "env:prod"
resource "tfe_workspace_variable_set" "prod_networking" {
  variable_set_id = tfe_variable_set.aws_prod.id
  workspace_id    = tfe_workspace.networking_prod.id
}

Sentinel: 기업 거버넌스를 위한 코드로서의 정책

보초 HashiCorp의 독점 정책 프레임워크 사용 가능 HCP Terraform의 Plus 및 Enterprise 계층에 있습니다. OPA/Rego(오픈소스)와 달리, Sentinel은 HashiCorp를 위해 특별히 설계되었습니다. Terraform 계획에 기본적으로 액세스할 수 있습니다. 풍부한 데이터 모델과 3단계 집행 시스템을 갖추고 있습니다.

시행 수준

# Sentinel ha tre livelli di enforcement:

# 1. advisory: la policy fallisce ma il run procede (solo log/warning)
#    Uso: notifiche soft, metriche, awareness

# 2. soft-mandatory: la policy fallisce e blocca l'apply,
#    MA un utente con permessi puo fare override con giustificazione
#    Uso: policy importanti che potrebbero avere eccezioni legittime

# 3. hard-mandatory: la policy fallisce e NESSUNO puo fare override
#    Uso: compliance legale, sicurezza critica, guardrail finanziari

# Configurazione enforcement nel policy set (HCP Terraform):
resource "tfe_policy_set" "global_policies" {
  name         = "global-security-policies"
  organization = "myorg"
  global       = true   # Applica a tutti i workspace

  policy_ids = [
    tfe_sentinel_policy.no_public_s3.id,
    tfe_sentinel_policy.cost_limits.id,
    tfe_sentinel_policy.required_tags.id,
  ]
}

정책 감시자: 금융 가드레일

# cost-control.sentinel
# Blocca hard se il costo stimato supera soglie per ambiente

import "tfplan/v2" as tfplan
import "decimal"

# Leggi il tag "env" dal workspace per determinare le soglie
env = tfplan.variables["environment"].value

# Soglie di costo mensile (USD) per ambiente
cost_limits = {
  "dev":     decimal.new(500),
  "staging": decimal.new(2000),
  "prod":    decimal.new(50000),
}

# Tipi di istanza EC2 proibiti in non-prod
expensive_types = [
  "m5.4xlarge", "m5.8xlarge", "m5.16xlarge", "m5.24xlarge",
  "c5.9xlarge", "c5.18xlarge",
  "r5.8xlarge", "r5.16xlarge", "r5.24xlarge",
  "p3.2xlarge", "p3.8xlarge", "p4d.24xlarge",
]

# Regola 1: Blocca istanze costose in non-prod
deny_expensive_instances = rule {
  env is "prod" or
  all tfplan.resource_changes as _, rc {
    rc.type is not "aws_instance" or
    rc.change.after.instance_type not in expensive_types
  }
}

# Regola 2: Verifica costo stimato se disponibile (richiede Infracost)
check_estimated_cost = rule {
  # La policy puo accedere ai costi stimati tramite Terraform Cloud
  # se Infracost e integrato nel workflow
  true  # Placeholder: implementare con Infracost webhook
}

# Main: combina le regole
main = rule {
  deny_expensive_instances and
  check_estimated_cost
}

정책 센티널: 필수 태그

# required-tags.sentinel
# Garantisce che tutte le risorse taggabili abbiano i tag obbligatori

import "tfplan/v2" as tfplan

# Tag obbligatori per ogni risorsa
required_tags = ["Environment", "Team", "CostCenter", "ManagedBy"]

# Tipi di risorsa che supportano i tag (lista parziale)
taggable_types = [
  "aws_instance", "aws_vpc", "aws_subnet", "aws_s3_bucket",
  "aws_rds_instance", "aws_eks_cluster", "aws_lb",
  "azurerm_virtual_machine", "azurerm_virtual_network",
  "google_compute_instance", "google_storage_bucket",
]

# Controlla ogni risorsa che viene creata o modificata
violations = []

for tfplan.resource_changes as address, rc {
  if rc.type in taggable_types and
     rc.change.actions contains "create" or rc.change.actions contains "update" {
    tags = rc.change.after.tags else {}
    for required_tags as tag {
      if tag not in tags or tags[tag] is "" {
        append(violations, sprintf(
          "Risorsa %s manca del tag obbligatorio: %s",
          [address, tag]
        ))
      }
    }
  }
}

# Stampa le violazioni per il feedback all'utente
print("Tag violations:", violations)

main = rule { length(violations) is 0 }

테스트 정책 센티널

# Sentinel CLI per testing locale delle policy
# Installa Sentinel CLI
sentinel version
# Sentinel v0.26.x

# Struttura directory per testing:
policies/
├── cost-control.sentinel
├── required-tags.sentinel
└── test/
    ├── cost-control/
    │   ├── pass/
    │   │   └── mock-tfplan.json    # Plan con istanze economiche
    │   └── fail/
    │       └── mock-tfplan.json    # Plan con istanze costose
    └── required-tags/
        ├── pass/
        │   └── mock-tfplan.json    # Tutte le risorse hanno i tag
        └── fail/
            └── mock-tfplan.json    # Risorse senza tag

# Genera mock da un plan reale:
terraform plan -out=tfplan.bin
terraform show -json tfplan.bin > mock-tfplan.json
# Modifica il JSON per creare scenari pass/fail

# Esegui i test:
sentinel test cost-control.sentinel
# PASS - cost-control.sentinel (2 test)
#   PASS - test/cost-control/pass/mock-tfplan.json
#   PASS - test/cost-control/fail/mock-tfplan.json

sentinel apply cost-control.sentinel
# Execution trace...
# Policy result: true

비공개 모듈 레지스트리

기업 조직에서 Terraform 모듈은 다음 작업을 위한 기본 메커니즘입니다. 인프라 표준화: 플랫폼 팀은 다음과 같은 "황금 경로" 모듈을 게시합니다. 애플리케이션 팀이 소비합니다. HCP Terraform은 버전 관리 기능이 있는 개인 레지스트리를 제공합니다. 의미론적, 자동 생성 문서 및 RBAC.

# Struttura di un modulo pubblicabile nel registry privato
terraform-module-aws-vpc/
├── main.tf
├── variables.tf
├── outputs.tf
├── versions.tf
├── README.md           # Documentazione auto-esposta nel registry
├── examples/
│   ├── simple/
│   │   └── main.tf     # Esempio minimo
│   └── full/
│       └── main.tf     # Esempio completo
└── tests/
    └── vpc_test.go     # Test Terratest

# Pubblicazione via Git tags (il registry legge i tag SemVer):
git tag v1.2.0
git push origin v1.2.0

# HCP Terraform registry: importa automaticamente il modulo
# dal repository GitHub/GitLab quando trova un tag vX.Y.Z

# Uso del modulo privato da altri workspace:
module "vpc" {
  source  = "app.terraform.io/myorg/aws-vpc/aws"
  version = "~> 1.2"

  environment  = var.environment
  cidr_block   = "10.0.0.0/16"
  subnet_count = 3
}

HashiCorp Vault: Terraform을 위한 비밀 관리

Terraform 엔터프라이즈 파이프라인의 가장 일반적인 문제는 비밀 관리입니다. 데이터베이스 비밀번호, API 키, 인증서. HashiCorp 금고 해결하다 이것으로 동적 비밀: Vault는 정적 자격 증명 대신 다음을 생성합니다. 자동으로 만료되는 주문형 임시 자격 증명.

AWS용 Vault 동적 비밀

# Configurazione Vault per generare credenziali AWS temporanee

# Provider Vault in Terraform
provider "vault" {
  address = "https://vault.myorg.internal:8200"
  # Autenticazione via AppRole (in CI/CD) o AWS IAM auth (in EC2/EKS)
}

# Leggi credenziali AWS dinamiche da Vault
data "vault_aws_access_credentials" "terraform_runner" {
  backend = "aws"
  role    = "terraform-role"
  type    = "iam_user"   # oppure "assumed_role" per STS
}

# Usa le credenziali nel provider AWS
provider "aws" {
  region     = var.aws_region
  access_key = data.vault_aws_access_credentials.terraform_runner.access_key
  secret_key = data.vault_aws_access_credentials.terraform_runner.secret_key
  token      = data.vault_aws_access_credentials.terraform_runner.security_token
}

# Vault genera credenziali con TTL di 1 ora
# Dopo il terraform apply, le credenziali scadono automaticamente
# Nessuna credenziale permanente nelle pipeline

애플리케이션 비밀을 위한 Vault

# Recuperare segreti da Vault per passarli alle risorse
# (es: password database RDS)

data "vault_kv_secret_v2" "db_password" {
  mount = "secret"
  name  = "prod/database/rds-main"
}

# Usa il segreto nella risorsa RDS
resource "aws_db_instance" "main" {
  identifier        = "${local.name_prefix}-rds"
  engine            = "postgres"
  engine_version    = "15.4"
  instance_class    = "db.r6g.xlarge"

  # Password da Vault: non hardcoded, non in tfvars
  password          = data.vault_kv_secret_v2.db_password.data["password"]

  username          = "app_user"
  db_name           = "appdb"

  # Output: il DB endpoint viene scritto su Vault dopo l'apply
  # tramite un provisioner o una pipeline separata
}

# Scrivi l'endpoint del DB su Vault dopo la creazione
resource "vault_kv_secret_v2" "db_connection" {
  mount = "secret"
  name  = "prod/database/connection-info"

  data_json = jsonencode({
    host     = aws_db_instance.main.address
    port     = aws_db_instance.main.port
    database = aws_db_instance.main.db_name
  })
}

작업공간 간 트리거 및 종속성 그래프 실행

# In organizzazioni grandi, i workspace hanno dipendenze:
# networking -> compute -> application
# Il cambio di networking deve triggerare il re-apply di compute

# HCP Terraform: Run Triggers
resource "tfe_run_trigger" "compute_from_networking" {
  workspace_id  = tfe_workspace.compute_prod.id        # workspace downstream
  sourceable_id = tfe_workspace.networking_prod.id     # workspace upstream
}

# Quando networking-prod completa un apply con successo,
# HCP Terraform triggera automaticamente un plan su compute-prod
# che legge gli output di networking via remote_state data source

# Lettura output da workspace upstream
data "tfe_outputs" "networking" {
  organization = "myorg"
  workspace    = "platform-networking-prod"
}

# Usa gli output nel compute workspace
resource "aws_eks_cluster" "main" {
  name = "${local.name_prefix}-eks"

  vpc_config {
    subnet_ids = data.tfe_outputs.networking.values.private_subnet_ids
  }
}

에이전트 풀: 개인 환경의 자체 호스팅 실행기

# HCP Terraform Agent: per eseguire piani in reti private
# (es: infrastruttura on-premise, VPC senza accesso pubblico)

# Installa l'agent nel tuo ambiente
docker run -e TFE_AGENT_TOKEN="your-token" \
  -e TFE_AGENT_NAME="datacenter-agent-01" \
  hashicorp/tfc-agent:latest

# Oppure via Kubernetes DaemonSet nel cluster privato
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tfc-agent
  namespace: terraform-system
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tfc-agent
  template:
    spec:
      containers:
        - name: tfc-agent
          image: hashicorp/tfc-agent:latest
          env:
            - name: TFE_AGENT_TOKEN
              valueFrom:
                secretKeyRef:
                  name: tfc-agent-token
                  key: token
            - name: TFE_AGENT_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
EOF

# Configura il workspace per usare l'agent pool privato
resource "tfe_workspace" "private_datacenter" {
  name              = "datacenter-networking-prod"
  organization      = "myorg"
  agent_pool_id     = tfe_agent_pool.datacenter.id
  execution_mode    = "agent"   # Usa l'agent invece del runner HCP
}

HCP Terraform의 팀 RBAC

# Organizzare i permessi per team
resource "tfe_team" "platform_engineers" {
  name         = "platform-engineers"
  organization = "myorg"
  visibility   = "organization"
}

resource "tfe_team" "app_developers" {
  name         = "app-developers"
  organization = "myorg"
}

# Platform Engineers: accesso completo a tutti i workspace di networking
resource "tfe_team_access" "platform_networking" {
  access       = "admin"   # plan, apply, destroy, admin
  team_id      = tfe_team.platform_engineers.id
  workspace_id = tfe_workspace.networking_prod.id
}

# App Developers: solo plan (read) su prod, write su dev
resource "tfe_team_access" "dev_compute_prod" {
  access       = "read"   # Solo visualizzazione, nessun trigger
  team_id      = tfe_team.app_developers.id
  workspace_id = tfe_workspace.compute_prod.id
}

resource "tfe_team_access" "dev_compute_dev" {
  access       = "write"  # Plan e apply, ma non admin
  team_id      = tfe_team.app_developers.id
  workspace_id = tfe_workspace.compute_dev.id
}

결론: IaC Enterprise의 성숙도

Terraform Enterprise는 단순한 "여러 작업 공간을 갖춘 Terraform"이 아닙니다. 정책 머신(Sentinel)을 통한 인프라 거버넌스, 비밀 관리 (Vault), 전체 감사 추적 및 세분화된 RBAC. 이 문서에 설명된 패턴 자신이 관리하는 조직에서 코드로서의 인프라의 성숙도를 나타냅니다. 수십 개의 팀과 수백 개의 클라우드 환경.

이 기사를 통해 기본 HCL부터 기본 HCL까지 Terraform 및 IaC 시리즈를 마무리합니다. 엔터프라이즈 패턴을 사용하면 이제 인프라를 구축하고 관리하는 데 필요한 모든 도구를 갖게 됩니다. 모든 규모의 전문 클라우드.

전체 시리즈: Terraform 및 IaC

  • 제01조 — 처음부터 새로 만드는 Terraform: HCL, 공급자 및 계획-적용-파괴
  • 제02조 — 재사용 가능한 Terraform 모듈 설계
  • 제03조 — Terraform 상태: S3/GCS를 사용한 원격 백엔드
  • 제04조 — CI/CD의 Terraform: GitHub Actions 및 Atlantis
  • 제05조 — IaC 테스트: Terratest 및 Terraform 테스트
  • 제06조 — IaC 보안: Checkov, Trivy 및 OPA
  • 제07조 — Terraform 다중 클라우드: AWS + Azure + GCP
  • 제08조 — Terraform용 GitOps: Flux TF 컨트롤러 및 Spacelift
  • 제09조 — Terraform vs Pulumi vs OpenTofu
  • 제10조(본) — Terraform 엔터프라이즈 패턴: 작업 공간, Sentinel 및 팀 확장