🚀 はじめに

この記事でわかること

  • Terraformの設計の全体像(フォルダの切り方・モジュールの作り方・環境(dev/stg/prod)の分け方・ステート管理)
  • なぜ設計が大事か、そしてよくあるつまずきポイントを先回りで回避する考え方
  • すぐ試せる最小サンプルと、次に学ぶと良い道しるべ

✅ 概要解説

Terraformの“設計”とは何か

ざっくり言うと、レゴの設計図のことです。
どんな箱(フォルダ) に、どの部品(モジュール) を入れて、街(環境) ごとにどう並べ替えるか。
さらに、完成写真(状態=ステート)どこに保管するかを決めるのが設計です。

Terraform 設計の全体像:フォルダ構成・モジュール・環境分け・ステート管理
  • フォルダ構成… 作る物(ネットワーク・DBなど)と、使う場所(dev/stg/prod)をどう分けるか
  • モジュール設計… レゴの「何度も使える下ごしらえ」パーツ化
  • ステート管理… いまの完成形(状態)を安全に記録・共有する
  • 運用フロー… 変更はどうレビューして、どう適用(apply)するか

ポイント:設計がきれいだと、「増築」「引っ越し」「片付け」がラクになります。

何のためにあるのか(設計が大事な理由)

  • 事故を防ぐ(削除ミス・上書きミス・人によるバラつき)
  • 再利用が効く(同じ作りを別環境で素早く展開)
  • レビューしやすい(どこが変わるか差分が読みやすい)
  • チームで回せる(誰が触っても同じルールで動く)

設計がないとどうなるの?

  • ぐちゃぐちゃに成長して壊すのが怖い(どこに何があるか不明)
  • 環境ごとの差が増え、“本番だけ違う”地雷が生まれる
  • ステートが人のPCにだけある → 事故・紛失・衝突(同時更新)
  • レビューが困難 → ミスが見抜けず「動いたらOK」文化に逆戻り

どんな場面で役立つ?

  • 個人の学習〜小さなチーム:まずはフォルダとモジュールを整えるだけで運用が楽に
  • 複数クラウド・複数環境再利用ステートの分離で安全にスケール
  • 長期運用CI/CD、Lint、セキュリティチェックを足して “壊れにくい仕組み”

🧱 設計の基本パターン(フォルダ構成)

A. “live + modules” 方式(いちばん読みやすい定番)

repo-root/
├─ modules/                # 再利用する部品置き場(レゴのパーツ箱)
│  ├─ storage-bucket/     # 例: S3バケットを作るモジュール
│  │  ├─ main.tf
│  │  ├─ variables.tf
│  │  └─ outputs.tf
│  └─ ...                 # 他にも vpc/, rds/, dns/ など増やす
└─ live/                   # 実際に使う“現場”の設計図(街ごと)
   ├─ dev/
   │  └─ app/             # サブシステム単位で分けてもOK
   │     ├─ main.tf
   │     └─ terraform.tfvars
   ├─ stg/
   │  └─ app/
   └─ prod/
      └─ app/
  • modules/:何度も使える部品(バケット、VPCなど)
  • live/:実際の環境(dev / stg / prod)側の呼び出し

メリット見通しが良くレビューしやすい。環境を複製するのも簡単。

B. Workspace 方式(小規模〜単一チームで手早く)

  • 同じフォルダで dev / stg / prodWorkspace名で切り替えるやり方
  • スピード重視には向くけど、誤操作(workspace切替忘れ) が怖い
  • チーム・規模が大きいなら A方式(live+modules)推奨

目安:まずは A方式
Workspace は学習用超小規模での運用に向きます。


🧩 モジュール設計(レゴの“良いパーツ”とは)

  • 名前は“やること”が伝わるように(例:storage-bucketvpc-basic
  • 入力(variables)必要最小限。迷うフラグを山ほど作らない
  • 出力(outputs)次に使う人が助かる情報だけ
  • バージョン固定(GitのタグやRegistryのversionで)
  • README使い方サンプルを一緒に書く

コツ:「DRY(重複排除)」より “読みやすさ・驚きの少なさ” を優先。
なんでも詰め込み過ぎた“万能モジュール”はかえって難しくなります。


🧾 ステート(状態)管理:事故を防ぐ“保管庫”

ステート(terraform.tfstate)は、今の完成形(クラウドの実物)設計図をつなぐ答え合わせ帳
ローカル(自分のPC)に置かないのが鉄則です。

よく使うリモート保管先(バックエンド)

  • AWSs3 + DynamoDBロック
  • Azureazurerm
  • GCPgcs

理由みんなで共有できて衝突(同時編集)を防げる。バックアップや権限管理もやりやすい。


🧪 最小サンプル(AWSのS3バケットを“設計通り”に作る)

やること

  1. modules に「バケット作る部品」を作る
  2. live/prod でそれを呼び出す
  3. ステートは S3(+DynamoDBロック)に置く

1) モジュール(modules/storage-bucket

# modules/storage-bucket/variables.tf
variable "bucket_name" {
  description = "作成するS3バケット名(世界で一意)"
  type        = string
}

variable "force_destroy" {
  description = "中身があっても削除を許可するか(学習用はfalse推奨)"
  type        = bool
  default     = false
}
# modules/storage-bucket/main.tf
terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

resource "aws_s3_bucket" "this" {
  bucket        = var.bucket_name
  force_destroy = var.force_destroy
}

output "name" {
  value       = aws_s3_bucket.this.bucket
  description = "作成されたS3バケット名"
}

2) 実環境(live/prod/app で呼び出し)

# live/prod/app/main.tf
terraform {
  required_version = ">= 1.5.0"

  backend "s3" {
    bucket         = "my-tfstate-prod"         # 事前に用意
    key            = "prod/app/terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "my-tfstate-lock"         # 事前に用意(ロック用)
    encrypt        = true
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

module "bucket" {
  source        = "../../../modules/storage-bucket"
  bucket_name   = var.bucket_name
  force_destroy = false
}
# live/prod/app/variables.tf
variable "aws_region" {
  type        = string
  description = "AWSリージョン"
  default     = "ap-northeast-1"
}

variable "bucket_name" {
  type        = string
  description = "S3バケット名(世界で一意)"
}
# live/prod/app/terraform.tfvars
bucket_name = "my-company-prod-app-bucket-12345"

使い方(例)

  1. cd live/prod/app
  2. 初回のみ terraform init(バックエンド初期化)
  3. terraform plan(差分を確認)
  4. terraform apply(適用)

注意:S3バケット名は世界で一意。被っていたら別名にしましょう。


🔐 セキュリティと信頼性の“設計観点”

  • 機密情報はコードに書かない
    • terraform.tfvars秘密を直書きしない
    • 代わりに環境変数(TF_VAR_xxxSecrets Manager / Vaultを利用
  • バージョン固定
    • Terraform本体・プロバイダー・モジュールに上限と下限を付ける
  • クリティカルな削除防止
    • lifecycle { prevent_destroy = true } の活用(DBなど)
  • CI/CDでの2段階適用
    • PRで plan(レビュー)→ mainマージで apply(承認・監査ログを残す)
  • Lint / 静的解析
    • terraform fmt -check / terraform validate
    • tflint / tfsec / checkov早期に匂いを検出

🧭 環境分けの設計(dev / stg / prod)

  • ステートは環境ごとに完全分離(S3キーやGCSパスを分ける)
  • 権限も分離(prodは厳しめのロール)
  • 差分最小:devとprodで同じモジュールを使い、変えるのは入力だけ
  • 命名規則をあらかじめ決める(例:{org}-{env}-{system}-{component}

指針:「本番だけ特別」を作らない。
特別対応はモジュールの入力で吸収し、設計は同じに保つのが安全です。


🧱 依存関係と分割の考え方

  • “大きくしすぎない”(VPC・DB・アプリなど論理単位で分割
  • 出力(outputs)で隣に渡す(例:VPCのIDをアプリ側に)
  • 必要なら remote_state で参照(チーム間でも“読み取り専用”で使う)

やりすぎ注意:細切れにしすぎると適用順序可視性がつらくなる。
まずはシステムごと(network / data / app など) の単位が扱いやすいです。


🛠️ 小さく始めて段階的に強くする運用フロー

  1. 最小構成を live + modules で作る
  2. リモートステートに移して共同管理
  3. PRで plan をレビュー(差分に慣れる)
  4. CI/CDfmt/validate/tflint/tfsec を自動化
  5. タグ付け・命名規則を整備(コスト可視化や検索が楽に)
  6. 監査ログ(誰がいつapplyしたか)を残す

ゴール人の“うっかり”を仕組みで防ぐ
設計は最初から完璧でなくてOK。段階的に磨いていきましょう。


💡 小話・豆知識・逸話

  • 名前の由来:「Terraform」は “地形を作る/変える” の意味。
    インフラ(クラウドの“地形”)をコードで形作る発想が名前に込められています。
  • “動けばOK”の罠
    はじめは動かすのが嬉しい。でも設計がゆるいと後が地獄
    設計=未来の自分へのやさしさだと思うと頑張れます。
  • レゴ哲学
    誰が見てもどの箱に何が入っているか”がわかれば勝ち。
    驚きが少ないレビューが速い事故が減るの三方良し。

📚 参考リンク

公式サイト・ドキュメント


🛠️ 関連テーマ・次に理解すると良いこと


🎯 まとめ

  • 設計は“レゴの箱の並べ方”live + modules がわかりやすい定番
  • モジュールは薄く・分かりやすく:入力最小、出力は必要十分、READMEを添える
  • ステートは必ずリモートs3/azurerm/gcs などで共有とロックを実現
  • 環境差分は入力で吸収:設計は同じ、変えるのは値だけ
  • CI/CDとLintで“うっかり”防止:planレビュー → 承認 → apply
  • 完璧を目指さず段階的に強化:まずは最小構成から始めよう