Skip to main content

Terraform & Terragrunt 名詞對照表


Terraform 核心概念

名詞一句話類比
TerraformIaC 工具,用 HCL 定義雲端資源用 code 描述「我要這些 AWS 資源」
ProviderTerraform 跟雲端 API 的橋接AWS SDK,讓 Terraform 知道怎麼呼叫 AWS
Resource一個 AWS 資源(EC2、S3 bucket、EKS cluster 等)你要建立的東西
Module一組 resource 打包成可重用模組Python 的 function,避免重複寫同樣的 resource
Variable模組的輸入參數function 的參數
Output模組的輸出值function 的 return value
Data Source查詢已存在的資源(不建立)SELECT 而不是 INSERT
StateTerraform 記錄目前資源狀態的檔案資料庫,記錄「我現在管理哪些 AWS 資源」
Plan預覽要做哪些變更(不執行)git diff,看改動再決定要不要 apply
Apply實際執行變更git push,真正改動 AWS
Destroy刪除所有 Terraform 管理的資源危險!確認清楚才能跑

State 為什麼重要

沒有 State:
Terraform 每次都不知道 AWS 現在長什麼樣
→ 可能重複建立資源、無法更新、無法刪除

有 State(存在 S3):
Terraform 知道「上次 apply 之後 AWS 的狀態」
→ 可以計算出這次 apply 需要做哪些變更

State 存放位置:
本地:terraform.tfstate(只有自己能用,不能團隊協作)
遠端:S3 + DynamoDB lock(團隊協作的標準做法)

遠端 State 設定範例:

terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/eks/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-lock" # 防止多人同時 apply
}
}

Module 結構

modules/
├── eks-cluster/
│ ├── main.tf # 主要 resource
│ ├── _variables.tf # 輸入變數
│ ├── outputs.tf # 輸出值
│ └── versions.tf # provider 版本鎖定
└── monitoring/
├── main.tf
└── _variables.tf

# 呼叫 module
module "eks" {
source = "./modules/eks-cluster"
cluster_name = "my-cluster"
region = "us-east-1"
}

# 使用 module 的 output
resource "aws_route53_record" "eks" {
name = module.eks.cluster_endpoint # 取得 module 的輸出
}

常用指令

# 初始化(下載 provider,設定 backend)
terraform init

# 預覽變更
terraform plan

# 執行變更
terraform apply

# 只預覽/執行特定 resource
terraform plan -target=module.monitoring
terraform apply -target=module.monitoring

# 查看目前 state
terraform state list
terraform state show <resource>

# 強制重新整理 state(從 AWS 重新讀取)
terraform refresh

# 銷毀(危險!)
terraform destroy

Terragrunt

Terragrunt 是 Terraform 的 wrapper,解決多環境管理的問題。

為什麼要用 Terragrunt

問題:
有 10 個 region,每個都要跑同樣的 Terraform module
但 region、帳號、CIDR 不同
→ 10 個資料夾,每個都有重複的 backend 設定 = 很難維護

解法(Terragrunt):
一個 Terraform module(共用)
10 個 terragrunt.hcl(各自的 inputs)
→ DRY(Don't Repeat Yourself)

Terragrunt 結構

terraform/
├── eks/ # 共用的 Terraform module
│ ├── main.tf
│ ├── variables.tf
│ └── modules/
│ └── monitoring/
└── terragrunt/
├── staging/
│ └── terragrunt.hcl # staging 環境的 inputs
├── us-prod/
│ └── terragrunt.hcl # prod 環境的 inputs
└── us-prod/
└── terragrunt.hcl # PROD 環境的 inputs

terragrunt.hcl 範例

# 產生 backend 設定(每個環境獨立的 S3 state)
generate "backend" {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
backend "s3" {
bucket = "my-terraform-state-prod"
key = "terraform.tfstate"
region = "us-east-1"
}
}
EOF
}

# 指向共用的 Terraform module
terraform {
source = "../..//eks"
}

# 這個環境的具體值
inputs = {
name = "my-cluster-prod"
region = "us-east-1"
cluster_name = "my-cluster-prod"
desired_size = 5
monitor_enabled = true
}

常用 Terragrunt 指令

# 等同 terraform plan(在 terragrunt 目錄下跑)
cd terragrunt/us-prod
terragrunt plan

# 等同 terraform apply
terragrunt apply

# 對所有環境一起跑(run-all)
terragrunt run-all plan
terragrunt run-all apply

Terraform vs Terragrunt 一句話

TerraformTerragrunt
角色IaC 工具本體Terraform 的多環境管理 wrapper
解決問題定義 AWS 資源避免多環境重複設定
設定檔.tf 檔案terragrunt.hcl
常見搭配單一環境多環境(staging/prod/...)

Terraform + Terragrunt 常見部署流程 / Common Deployment Flow

流程圖 / Flow Diagram

CI workflow triggers


cd terragrunt/<env>/eks/


Terragrunt reads terragrunt.hcl
├─ generate backend.tf → S3 state config (where to store state)
├─ generate provider.tf → AWS region + default tags
├─ terraform { source } → Copy .tf files to .terragrunt-cache/
└─ inputs = { ... } → Environment-specific variable values


.terragrunt-cache/xxx/eks/
├─ main.tf (copied from eks/)
├─ addon.tf (copied from eks/)
├─ variables.tf (copied from eks/)
├─ modules/ (copied from eks/)
├─ backend.tf (generated by terragrunt)
└─ provider.tf (generated by terragrunt)


terraform init → Download plugins + modules, connect S3 state
terraform plan → Compare state vs code, show diff
terraform apply → Call AWS/K8s API to create/update resources, update state

terragrunt.hcl 的四個區塊 / Four Blocks in terragrunt.hcl

Block 1: generate "backend" — 產生 backend.tf / Generate backend.tf

Tell Terraform where to store state and which lock table to use.

generate "backend" {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
backend "s3" {
bucket = "my-terraform-state-prod" # Each env has its own bucket
key = "terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-lock" # Prevents concurrent apply
}
}
EOF
}

每個環境的 S3 bucket 不同。用 generate 動態產生,不用每個環境維護一份 backend.tf。

Each environment uses a different S3 bucket. generate creates it dynamically so you don't maintain separate backend.tf files per environment.

Block 2: generate "provider" — 產生 provider.tf / Generate provider.tf

Tell Terraform which AWS region to use and apply default tags to all resources.

generate "provider" {
path = "provider.tf"
contents = <<EOF
provider "aws" {
region = "us-east-1"
default_tags {
tags = { owner = "my-team" }
}
}
EOF
}

Block 3: terraform { source } — 指向 Terraform code / Point to Terraform code

terraform {
source = "../../..//eks"
# Resolves to: the eks/ directory containing main.tf, addon.tf, modules/, etc.
}

Terragrunt copies all .tf files from the source directory into .terragrunt-cache/, adds the generated backend.tf and provider.tf, then runs Terraform there.

Terragrunt 把 source 目錄的 .tf 複製到暫存目錄,加上產生的 backend.tf 和 provider.tf,在那裡跑 Terraform。

Block 4: inputs = { ... } — 變數值 / Variable values

inputs = {
cluster_name = "my-cluster-prod"
region = "us-east-1"
desired_size = 5
monitor_enabled = true
}

Equivalent to terraform.tfvars. Different environments pass different values but share the same .tf code.

等同 terraform.tfvars。不同環境給不同值,但共用同一份 .tf 程式碼。

三個執行步驟 / Three Execution Steps

terragrunt run-all init

1. Create .terragrunt-cache/ temp directory
2. Copy .tf files from source
3. Write generated backend.tf + provider.tf
4. Run terraform init:
- Connect to S3 backend, read/create state file
- Download provider plugins (aws, helm, kubectl, kubernetes)
- Download module sources (e.g. terraform-aws-modules/eks/aws)
- Acquire DynamoDB lock

terragrunt run-all plan

1. Convert inputs to -var arguments
2. Run terraform plan:
- Read current state from S3 (what AWS currently has)
- Read .tf files (what you want)
- Output diff: "Plan: 7 to add, 9 to change, 0 to destroy"
- No changes made — preview only

terragrunt run-all apply -auto-approve

1. Same comparison as plan
2. Actually call AWS APIs to make changes:
- Create/update IAM policies → aws iam put-policy
- Create K8s Secrets → kubernetes API
- Update Helm releases → helm upgrade
- Apply CRD manifests → kubectl apply
3. Update state in S3 after each resource succeeds
4. Release DynamoDB lock

run-all vs terragrunt

CommandBehavior
terragrunt planOnly runs current directory's terragrunt.hcl
terragrunt run-all planRecursively scans all subdirectories, runs every terragrunt.hcl found

run-all 會遞迴掃所有子目錄,每個有 terragrunt.hcl 的都跑。普通 terragrunt 只跑當前目錄。