Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2026-09-18 gotanda.sre Terraformで複数環境作ったり、複数Sta...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

2026-09-18 gotanda.sre Terraformで複数環境作ったり、複数Stateに分割したりそれとTerragrunt / Terraform multi envs and multi states

Avatar for SUZUKI Masashi

SUZUKI Masashi

September 18, 2026

More Decks by SUZUKI Masashi

Other Decks in Technology

Transcript

  1. おまえだれよ • • • すずきまさし/masasuzu/@masasuz 株式会社スリーシェイクSreake事業部シニアアーキテクト クラウドインフラなんでも屋さんをしてます ◦ お客様の外部から ▪

    設計、運用、構築等の技術支援を行います。 ◦ お客様の内部から ▪ インフラチームの一員として内製化支援も行います。 • 得意領域 ◦ AWS ▪ AWS Community Builder Cloud Operation Since 2024~2026 ▪ 2026 Japan All AWS Certifications Engineers ◦ Google Cloud ▪ Google Cloud Partner Top Engineer 2026 ◦ Terraform Copyright © 3-shake, Inc. All Rights Reserved. 2
  2. 怠惰 - Lazy • • • • プログラマの三大美徳の 1つ ◦

    怠惰、短期、傲慢 Perl開発者ラリー・ウォールの言葉 全体として必要となる労力を減らすためには努力をおしまないこと あとからめんどくさくなるなるものは先回りして実装整備して、あとあとの面倒を減らす ◦ トイル減らしたいでござる Copyright © 3-shake, Inc. All Rights Reserved. 4
  3. 怠惰と横着 • • • • 人間楽をしたいんですよ。 ◦ 私は楽して暮らしたいです。 ◦ Terraformも楽して書きたいんですよ

    気をつけたいのは怠惰のために減らすのは眼の前の労力でなく、全体の労力 ◦ 眼の前だけ減らすのは横着であとから利子付きで返済が求められます ◦ トイルが襲ってくるのです 怠惰は先払い、横着は後払い ◦ ただし、必ずしも先払いが正解とは限らないのは留意したい ◦ ある程度の規模でないなら横着したほうが正解かも 横着はその場で書けてしまうが、痛みが出るのは先の話なのが厄介なところ Copyright © 3-shake, Inc. All Rights Reserved. 5
  4. 複数環境作りたいとき • 素直に実装するなら環境ごとにディレクトリを 切って、Stateを分けてそれぞれリソースを定義 する ◦ dev/ stg/ prd/ •

    だいたい良さそう Copyright © 3-shake, Inc. All Rights Reserved. . ├── dev/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf ├── stg/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf └── prd/ ├── providers.tf ├── variables.tf └── main.tf 10
  5. 環境増えるごとにコピペ • • • リソース定義が増えるごとにそれぞれのディレクトリファ イルごとにコピペしていかないとならない。 ◦ 変更のたびに各ファイルに同じ変更をコピペして いかないといけない コピペコピペコピペ

    ◦ 親指と人差し指が吊ります ▪ 超めっちゃトイル (ただ、本当に小規模であれば問題ない場合もある ) Copyright © 3-shake, Inc. All Rights Reserved. . ├── dev/ │ ├── providers.tf │ ├── variables.tf │ ├── db.tf │ ├── vpc.tf │ └── ec2.tf ├── stg/ │ ├── providers.tf │ ├── variables.tf │ ├── db.tf │ ├── vpc.tf │ └── ec2.tf └── prd/ ├── providers.tf ├── variables.tf ├── db.tf ├── vpc.tf └── ec2.tf 12
  6. よしモジュールだ • • • • • TerraformにはModuleという処理を共通化する仕組みがあ る Moduleに共通リソースを切り出す。 各StateのRoot

    ModuleからはModuleを呼び出す形にする Moduleに関してはいろいろ観点がありそれだけで 30分くら い語れるくらいなのでここでは語らないです。 よさそう、、、、 ? Copyright © 3-shake, Inc. All Rights Reserved. . ├── modules/ │ ├── vpc/ │ │ ├── providers.tf │ │ ├── variables.tf │ │ └── main.tf │ └── app/ └── envs/ ├── dev/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf ├── stg/ └── prod/ 13
  7. そこでbackend切り替えパターン • • • • • 各環境で共通の Root Moduleを定義する backendとtfvarsのファイルを指定する

    ◦ 各環境ごとに差し替える Root Module分の重複が減ってよさそう ただ、環境切り替えるごとに init -reconfigureが必要 ◦ ミスりポイント miseなどのタスクランナー使っていればそのあたりは対処 できる . ├── main.tf ├── variables.tf ├── providers.tf ├── modules/ │ ├── vpc/ │ └── app/ └── envs/ ├── dev/ │ ├── backend.tfvars │ └── terraform.tfvars ├── stg/ └── prod/ ENV=dev # 環境変えるごとに init -reconfigureが必要 terraform init -backend-config=envs/${ENV}/backend.tfvars -reconfigure terraform plan -var-file=envs/${ENV}/terraform.tfvars Copyright © 3-shake, Inc. All Rights Reserved. 16
  8. Stateを分けたいとき • • • ライフサイクルが違うものを分けたい ◦ VPCやDBは一回作るとほぼ変わらない ◦ EC2やECSはわりかし変更頻度が高い 頻度が高いものに引きづられて変わらないものを毎回

    Planするのは無駄 同じStateにいると間違えて変更して事故になる可能性 がある Copyright © 3-shake, Inc. All Rights Reserved. . ├── vpc/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf ├── db/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf └── workload/ ├── providers.tf ├── variables.tf └── main.tf 19
  9. 依存関係、実行順序 • • • 分けることでState間の依存が生まれます 実行順がわからない ◦ 数字つけてもいいけど、、、 依存関係もわからない ◦

    数字プリフィクスだけだとわからない db datastore vpc workload-a Copyright © 3-shake, Inc. All Rights Reserved. workload-b . ├── 01vpc/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf ├── 02db/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf ├── 03datastore/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf ├── 04workload-a/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf └── 05workload-b/ ├── providers.tf ├── variables.tf └── main.tf 21
  10. 前段のStateで作成済みが前提のリソース • • 開発環境は作成済みで、 STG環境を新しく作ろうとした ときにvpcが実行済みじゃないと、 dbのplanに失敗する ◦ vpc stateでdb_subnetが作られてる前提で

    dataも しくはremote state参照されるので存在しないとこ ける 事前にplanしたいでござる vpc . ├── vpc/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf ├── db/ │ ├── providers.tf │ ├── variables.tf │ └── main.tf └── workload/ ├── providers.tf ├── variables.tf └── main.tf db db_subnet Copyright © 3-shake, Inc. All Rights Reserved. 22
  11. provider、versionなど • • • providerやprovider version、backend、terraform versionは全部同じなのに毎 State書かないといけない 少ないうちはいいけど、複数環境、複数 State扱うと健

    在化します コピペコピペコピペ ◦ 親指と人差し指が吊ります ▪ 超めっちゃトイル terraform { required_version = "~> 1.16.2" } required_providers { google = { source = "hashicorp/google" version = "~> 8.2.0" } } backend "gcs" { prefix = "terraform/state" } provider "google" { project = var.project_id region = var.region impersonate_service_account = local.impersonate_service_account default_labels = { project = var.project_name environment = var.env managed_by = "terraform" } } Copyright © 3-shake, Inc. All Rights Reserved. 24
  12. 複数Stateで共通の設定 • • • 全部のterraform.tfvarsに同じ値を書かないといけない 少ないうちはいいけど、複数環境、複数 State扱うと健在化 します コピペコピペコピペ ◦

    親指と人差し指が吊ります ▪ 超めっちゃトイル Copyright © 3-shake, Inc. All Rights Reserved. # envs/dev/terraform.tfvars project_id = “xxxxxxx” env = “dev” project_prefix = “yyyy” 25
  13. Terragruntとは • • • Gruntworksが開発 OpenTofuとTerraformをオーケストレーター Terragrunt does not replace

    OpenTofu/Terraform. It orchestrates them, and extends what they do. ◦ Why use Terragrunt instead of just OpenTofu/Terraform? • Terraformのコードを生成して、いい感じに使えるようにしてくれる便利ツール ◦ そのまま使うんじゃなくて Terraformのコードを生成する ◦ 抽象化、共通化されているのでコードが追いにくいデメリット Copyright © 3-shake, Inc. All Rights Reserved. 28
  14. Terragrunt Stacks • • • • 2025年にリリースされた機能 いい感じに複数 Stateを実行できる機能 規模が大きいと便利

    ◦ 逆に小さいときは必要じゃない 逆にStacksじゃないTerragruntを使ってないので、素の Terragruntを説明できないです。 Copyright © 3-shake, Inc. All Rights Reserved. 29
  15. stack実行 # terragrunt.stack.hcl を読んで.terragrunt-stack/ を生成 terragrunt stack generate # stack

    に含まれる全unitに対し、DAG順で実行します terragrunt stack run plan terragrunt stack run apply terragrunt stack run destroy Copyright © 3-shake, Inc. All Rights Reserved. 30
  16. Terragrunt Stacks構成 . └── terraform/ ├── modules/ # module(root module含む)

    │ ├── vpc/ │ └── workload/ ├── units/ # Terragrunt の unit テンプレート │ ├── vpc/ │ │ └── terragrunt.hcl # unitの設定(root module、依存関係) │ ├── db/ │ └── workload/ └── live/ # ── ここから下が環境別 ── ├── root.hcl # 共通設定 ├── dev/ │ ├── env.hcl # 環境ごとの設定 │ ├── terragrunt.stack.hcl # どのunitを使うか │ └── .terragrunt-stack/ # 生成物。gitignore 対象 ├── stg/ └── prd/ Copyright © 3-shake, Inc. All Rights Reserved. 32
  17. Terragrunt Stacks構成 . └── terraform/ ├── modules/ # module(root module含む)

    │ ├── vpc/ │ └── workload/ ├── units/ # Terragrunt の unit テンプレート │ ├── vpc/ │ │ └── terragrunt.hcl # unitの設定(root module、依存関係、 │ ├── db/ │ └── workload/ └── live/ # ── ここから下が環境別 ── ├── root.hcl # 共通設定 ├── dev/ │ ├── env.hcl # 環境ごとの設定 │ ├── terragrunt.stack.hcl # どのunitを使うか │ └── .terragrunt-stack/ # 生成物。gitignore 対象 ├── stg/ └── prd/ Copyright © 3-shake, Inc. All Rights Reserved. 33
  18. 設定ファイル # live/${env}/env.hcl 環境ごと locals { env = "dev" project_id

    = "xxxxxx" } vpc = {# vpcの設定} workload = {# workloadの設定} # live/root.hcl 共通 locals { env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl")) env = local.env_vars.locals.env project_id = local.env_vars.locals.project_id region = "asia-northeast1" } Copyright © 3-shake, Inc. All Rights Reserved. 34
  19. Terragrunt Stacks構成(再掲) . └── terraform/ ├── modules/ # module(root module含む)

    │ ├── vpc/ │ │ └── main.tf │ ├── db/ │ └── workload/ ├── units/ # Terragrunt の unit テンプレート │ ├── vpc/ │ │ └── terragrunt.hcl # unitの設定(root module、依存関係、 │ ├── db/ │ └── workload/ └── live/ # ── ここから下が環境別 ── ├── root.hcl # 共通設定 ├── dev/ │ ├── env.hcl # 環境ごとの設定 │ ├── terragrunt.stack.hcl # どのunitを使うか │ └── .terragrunt-stack/ # 生成物。gitignore 対象 ├── stg/ └── prd/ Copyright © 3-shake, Inc. All Rights Reserved. 35
  20. unit設定 # unit/workload/terragrunt.hcl include "root" { path = find_in_parent_folders("root.hcl") }

    locals { env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl")) workload = local.env_vars.locals.workload } dependency "vpc" { config_path = "../vpc" } terraform { source = "${get_repo_root()}/terraform/modules/workload" } inputs = { env = local.env_vars.locals.env region = local.env_vars.locals.region instance_type = local.workload.instance_type } Copyright © 3-shake, Inc. All Rights Reserved. 36
  21. Terragrunt Stacks構成(再掲) . └── terraform/ ├── modules/ # module(root module含む)

    │ ├── vpc/ │ │ └── main.tf │ ├── db/ │ └── workload/ ├── units/ # Terragrunt の unit テンプレート │ ├── vpc/ │ │ └── terragrunt.hcl # unitの設定(root module、依存関係、 │ ├── db/ │ └── workload/ └── live/ # ── ここから下が環境別 ── ├── root.hcl # 共通設定 ├── dev/ │ ├── env.hcl # 環境ごとの設定 │ ├── terragrunt.stack.hcl # どのunitを使うか │ └── .terragrunt-stack/ # 生成物。gitignore 対象 ├── stg/ └── prd/ Copyright © 3-shake, Inc. All Rights Reserved. 37
  22. .terragrunt-stack . └── terraform/ └── live/ ├── root.hcl └── dev/

    ├── env.hcl ├── terragrunt.stack.hcl └── .terragrunt-stack/ └── .terragrunt-cache-xxxxxx/ ├── vpc/backend.tf, provider.tf, main.tf ├── db/backend.tf, provider.tf, main.tf └── workload/backend.tf, provider.tf, main.tf Copyright © 3-shake, Inc. All Rights Reserved. 38
  23. provider生成 # root.hcl # provider.tf generate "provider" { path =

    "provider.tf" if_exists = "overwrite_terragrunt" contents = <<EOF provider "google" { project = "${local.project}" region = "${local.region}" EOF } # Generated by Terragrunt. Sig: xxxxxx provider "google" { project = "xxxx" region = "asia-northeast1" } Copyright © 3-shake, Inc. All Rights Reserved. 41
  24. backend生成 # root.hcl remote_state { backend = "gcs" config =

    { bucket = "${local.prefix}-tfstate" prefix = "${local.env}/${basename(get_terragrunt_dir())}" } generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" } } # backend.tf # Generated by Terragrunt. Sig: xxxxxx terraform { backend "gcs" { bucket = "xxxx-tfstate" prefix = "dev/workload" } } Copyright © 3-shake, Inc. All Rights Reserved. 42
  25. モック • • 依存元のアウトプットのモックが書ける 依存元のunitが作られていなくても planが実行できる # unit/workload/terragrunt.hcl dependency "vpc"

    { config_path = "../vpc" mock_outputs = { vpc_self_link = "https://www.googleapis.com/compute/v1/projects/${local.project_id}/global/networks/${local.prefix }-network" subnet_self_link = "https://www.googleapis.com/compute/v1/projects/${local.project_id}/regions/asia-northeast1/subnet works/${local.prefix}-network" } mock_outputs_allowed_terraform_commands = ["validate", "plan", "init", "providers"] } Copyright © 3-shake, Inc. All Rights Reserved. 44
  26. メリットまとめ • • • • • • 全体で使う変数の共通化 環境ごとの変数の共通化 provider、backendの生成

    State間の明示的な依存関係 モックアウトプットによる構築前の plan確認 規模が大きければ大きいほど記述量が減る ◦ 環境増やすときは live/${env} を増やすだけ ◦ State増やすときは unit/${state}と modules/${state} を増やす Copyright © 3-shake, Inc. All Rights Reserved. 45
  27. デメリット • • • • 規模が小さいときはオーバースペック ◦ 学習コスト ◦ 複雑性

    コードだけ見て全体が追いにくい 抽象化、共通化しているので追いにくい 実際のコードは生成しないとわからない Copyright © 3-shake, Inc. All Rights Reserved. 46
  28. まとめ • • • ある程度大きくなってきたら環境分割、 State分割が必要になります 環境分割、State分割するといろいろつらみがあります ある程度の規模になったときに怠惰になりたいなら Terragruntを使うといい感じにできます。 •

    • 正直ちっちゃい規模で環境分割がないなら こぴぺ でもいいんです。 背伸びせずに規模にあったソリューションを選んでください。 Copyright © 3-shake, Inc. All Rights Reserved. 48