こんにちは!頼れる先輩エンジニアのサトシです。
クラウドインフラの世界へようこそ!これから皆さんが踏み出す「Infrastructure as Code(IaC)」の旅は、エンジニアとしてのキャリアを劇的に進化させる素晴らしい一歩です。
「手動でAWSやGoogle Cloudの画面をポチポチ操作して、設定ミスで夜中に呼び出される……」そんな悪夢のような日々は、今日で終わりにしましょう。Terraform(テラフォーム)をマスターすれば、インフラを「コード」として美しく、安全に、そして何度でも寸分違わず再現できるようになります。毎日のデプロイ作業が驚くほど簡単になり、お茶を飲む余裕さえ生まれますよ。
今回は、初心者の方向けの基本セットアップと絶対に失敗しないHelloWorldから、現場に配属された初日から役立つ「大規模開発におけるディレクトリ設計のベストプラクティス(環境分離の極意)」まで、私の現場での痛い失敗談も交えながら、優しく、かつディープに解説していきます。
さあ、一緒にTerraformの深遠なる世界へ飛び込みましょう!
—
1. そもそもTerraformとは?その「設計思想」を知る
具体的なコードを書く前に、Terraformがなぜこれほど世界中で愛されているのか、その本質を理解しておきましょう。ここを理解していると、エラーに直面したときの解決スピードが格段に変わります。
① 宣言的(Declarative)であること
Terraformは「こうなってほしい状態(理想の状態)」をコードに書きます。「VPCを作って、次にサブネットを作って、ルートテーブルを紐付けて……」という手順を書くのではありません。「この構成のVPCとサブネットが存在していること」と書くだけで、Terraformが自動的に手順を計算して実行してくれます。
② 冪等性(べきとうせい / Idempotency)
「同じコードを何回実行しても、同じ結果になる」という性質です。手動操作だと、2回実行したら2個目のサーバーができてしまいますよね? Terraformは現在のリアルなインフラの状態を記録した「State(ステート)ファイル」を持っているため、2回実行しても「すでに理想の状態になっているね」と判断し、何も変更しません。これが、インフラを安全に保つ最強の盾になります。
—
2. 最初のセットアップ:失敗しない環境構築
まずはあなたの手元のPCにTerraformをインストールしましょう。
プロの現場では、プロジェクトごとにTerraformのバージョンが異なることが日常茶飯事です。そのため、直接インストールするのではなく、バージョン管理ツールである `tfenv` を使うのが業界標準であり、最もスマートな方法です。
Mac(Homebrewを使用する場合)
ターミナルを開いて、以下のコマンドを順番に実行してください。
tfenvのインストール
brew install tfenv
利用可能な最新の安定版Terraformをインストール(今回は1.5.0以上を想定)
tfenv install 1.5.7
使用するバージョンを固定
tfenv use 1.5.7
正しくインストールされたか確認
terraform –version
Windowsの場合
Windows環境では、[tfenvの移植版である tfenv-w](https://github.com/asdf-community/tfenv-w) を使うか、公式からバイナリをダウンロードして環境変数 `PATH` を通します。または、パッケージマネージャーである `Chocolatey` を使って以下のようにインストールします。
choco install terraform
—
3. 精度100%のHelloWorld!まずは動かしてみよう
「クラウドのアカウントをまだ持っていない」「課金が怖い」という方でも100%安全に、かつTerraformの仕組みを完璧に理解できる「ローカルファイル生成」を使ったHelloWorldをやってみましょう。
適当な空のディレクトリを作成し、VS Codeなどのエディタで開いてください。
`main.tf` の作成
ディレクトリ内に `main.tf` という名前でファイルを作成し、以下のコードを記述します。コメントも丁寧に読んでみてくださいね。
1. プロバイダーの定義
Terraformが「どのプラットフォーム」を操作するかを指定します。
ここではAWSなどのクラウドではなく、ローカルシステムを操作するプロバイダーを指定します。
terraform {
required_version = “>= 1.5.0” # Terraform自体のバージョン制約
required_providers {
local = {
source = “hashicorp/local”
version = “~> 2.4.0”
}
}
}
2. リソースの定義
「local_file」という種類のリソースを使い、名前を「hello_world」とします。
resource “local_file” “hello_world” {
# 生成するファイルのパス
filename = “${path.module}/hello_world.txt”
# ファイルに書き込む中身
content = “Hello, Terraform World!\nこのファイルはIaCによって自動生成されました。\n”
}
動作確認:3つの魔法のコマンド
コードが書けたら、ターミナルでそのディレクトリに移動し、以下の「Terraform基本の3コマンド」を順に実行します。
① `terraform init`(初期化)
コードを解析し、必要なプラグイン(今回は `local` プロバイダー)をダウンロードします。
terraform init
「Terraform has been successfully initialized!」と緑色で表示されれば大成功です!
② `terraform plan`(実行計画の確認)
「このコードを実行すると、インフラ(ファイル)がどう変化するか」を事前にシミュレーションして見せてくれます。本番環境に適用する前には、必ずこの出力を血眼になって確認します。
terraform plan
出力の中に `+ create` とあり、`hello_world.txt` が作られる予定であることが表示されます。
③ `terraform apply`(適用)
実際に実行します。途中で「本当にやっていいですか?」と聞かれるので、魂を込めて `yes` と入力してください。
terraform apply
…略…
Do you want to perform these actions?
Enter a value: yes
コマンドが完了すると、あなたのディレクトリに `hello_world.txt` が生成されているはずです!中身を見てみてください。感動ですね!
そして、ディレクトリ内に `terraform.tfstate` というファイルができていることに気づきましたか?これこそが、Terraformが「現在の状態」を記録している超重要ファイルです。絶対に手動で編集してはいけません。
—
4. プロの設計思想:環境分離(stg/prod)をどう実現するか?
さて、ここからが本番、「現場で震えるほど役立つ極限の知見」の領域です。
実際の現場では、開発・テストを行う「ステージング環境(stg)」と、ユーザーが実際に使う「本番環境(prod)」を完全に分けて管理する必要があります。
この環境分離の手法には、大きく分けて2つのアプローチが存在します。
1. Terraform Workspaces を使う方法
2. ディレクトリを物理的に分ける方法
結論から言うと、「中規模〜大規模開発では、絶対に『2. ディレクトリを物理的に分ける方法』を採用すべき」です。なぜなのか、プロの視点で両者を徹底比較してみましょう。
—
アプローチ1: Terraform Workspaces(ワークスペース)の限界
Workspacesは、同じコード(同一ディレクトリ)を使いながら、Stateファイルだけを切り替えるTerraformの標準機能です。
ワークスペースの切り替えイメージ
terraform workspace select prod
terraform apply # 本番環境に適用
😭 なぜ大規模開発で「限界」を迎えるのか?(デメリット)
1. 人間は必ずミスをする(「現在のワークスペース」が視覚的にわからない)
ターミナルを開いたとき、今自分が `stg` と `prod` のどちらのコンテキストにいるのか、画面上ですぐに判別できません。`stg` に反映したつもりで、コマンド一発で `prod`(本番環境)を破壊してしまう事故が、世界中の現場で多発しています。
2. 環境ごとの「わずかな違い」の表現が極めて難解になる
「本番環境だけサーバーのスペックを高くしたい」「本番環境だけにCDN(CloudFrontなど)を配置したい」といった場合、コード内に三項演算子や複雑な条件分岐(`count = var.env == “prod” ? 1 : 0` など)が乱立し、コードの可読性が地獄のように低下します。
3. 認証情報の分離が困難
AWSアカウント自体を `stg用アカウント` と `prod用アカウント` で完全に分離したい場合、Workspaces単体ではプロバイダーの切り替え制御が非常にトリッキーになり、セキュリティリスクが高まります。
—
アプローチ2: ディレクトリ分離(プロの選択)
環境ごとに物理的なディレクトリを作成し、それぞれで `terraform init` や `apply` を行う手法です。
infrastructure/
├── environments/
│ ├── staging/ # ステージング環境用のコード
│ │ ├── main.tf
│ │ └── variables.tf
│ └── production/ # 本番環境用のコード
│ ├── main.tf
│ └── variables.tf
◯ メリット
- 絶対的な安全性(最大のメリット): `staging/` ディレクトリに移動して作業している限り、本番環境(`production/`)のインフラに干渉する物理的リスクはゼロです。
- 環境ごとの「違い」を素直に表現できる: 本番環境にだけ必要なリソースは、`production/main.tf` にだけ書けば済みます。
- 権限管理(IAM)との親和性: CI/CDツール(GitHub Actionsなど)で、「stgディレクトリは開発者全員がデプロイ可能だが、prodディレクトリは特定の権限を持つ人だけ」といった制御が容易です。
✕ デメリット
- コードの重複(コピペ問題): stgとprodでほぼ同じリソースを作る場合、同じようなコードが両方のディレクトリに存在することになり、「DRY(Don’t Repeat Yourself:重複を避ける)原則」に反します。
—
5. 大規模開発を救う「ディレクトリ構造のベストプラクティス」
上記の「コードの重複」というデメリットを完璧に解消しつつ、ディレクトリ分離の安全性を享受するための黄金のアーキテクチャが、「Modules(モジュール)化 × ディレクトリ分離」です。
インフラの共通部品を `modules/` に定義し、各環境(stg/prod)のディレクトリからは、その部品を呼び出す(引数を渡す)だけにします。
究極のディレクトリ構造
my-project/
├── modules/ # 【共通部品】インフラの設計図(テンプレート)
│ └── web_app/ # Webアプリケーションの最小構成ユニット
│ ├── main.tf
│ ├── variables.tf # 入力パラメータの定義
│ └── outputs.tf # 出力パラメータの定義
│
└── environments/ # 【環境別】実際にリソースをデプロイする場所
├── staging/
│ ├── main.tf # modules/web_app を「低スペック」で呼び出す
│ ├── providers.tf # stg用AWSアカウントの設定
│ └── variables.tf
│
└── production/
├── main.tf # modules/web_app を「高スペック・高可用性」で呼び出す
├── providers.tf # prod用AWSアカウントの設定
└── variables.tf
具体的なコード例で見る「モジュール化」の実装
では、この構造をどのように書くのか、具体的なイメージを見てみましょう。
1. 共通モジュール側:`modules/web_app/main.tf`
ここでは、環境ごとに変更したい部分(インスタンスのサイズや環境名など)を変数(`variable`)にしておきます。
modules/web_app/main.tf
variable “environment” {
type = string
description = “環境名 (stg or prod)”
}
variable “instance_type” {
type = string
description = “サーバーのスペック”
}
仮想サーバー(例:AWS EC2)を定義するテンプレート
resource “aws_instance” “web” {
ami = “ami-0c55b159cbfafe1f0” # 任意のAMI ID
instance_type = var.instance_type # 変数から注入
tags = {
Name = “${var.environment}-web-server”
}
}
2. 本番環境側:`environments/production/main.tf`
本番用のディレクトリでは、先ほどのテンプレートを呼び出し、本番用のパラメータ(大きなスペック、prodという名前)を注入します。
environments/production/main.tf
provider “aws” {
region = “ap-northeast-1”
# 本番環境用のAWSプロファイルやロールを指定
}
共通モジュールを呼び出す
module “my_web_app” {
source = “../../modules/web_app” # テンプレートの場所を相対パスで指定
# 本番環境用のパラメータを注入!
environment = “prod”
instance_type = “t3.large” # 本番用のハイスペック
}
3. ステージング環境側:`environments/staging/main.tf`
ステージング用のディレクトリでは、同じテンプレートを使いつつ、コストを抑えたパラメータを注入します。
environments/staging/main.tf
provider “aws” {
region = “ap-northeast-1”
# ステージング環境用のAWSプロファイルやロールを指定
}
共通モジュールを呼び出す
module “my_web_app” {
source = “../../modules/web_app”
# ステージング環境用のパラメータを注入!
environment = “stg”
instance_type = “t3.micro” # 開発用なので最小スペックで節約!
}
どうですか?この構造の美しさが伝わりましたでしょうか!
- インフラの「設計(構成)」を修正したいとき: `modules/web_app/` のコードを1箇所直すだけで、全環境に適用できます。
- 「パラメータ」を変更したいとき: 各環境の `main.tf` の値を書き換えるだけです。
- デプロイの安全性: `environments/staging/` でテストして問題なければ、自信を持って `environments/production/` で実行するだけ。本番への影響は完全にコントロールされています。
—
6. 先輩からのアドバイス:現場で絶対に忘れてはいけない2つの鉄則
最後に、あなたが実際の現場で「神エンジニア」として信頼されるために、絶対に守ってほしい2つの約束事をお伝えします。
鉄則1: Stateファイルの「リモート管理」と「ロック」
初期状態では、Stateファイル(`terraform.tfstate`)はあなたのローカルPCに保存されます。しかし、複数人のチームで開発する場合、これをGitで管理してはいけません(機密情報が生テキストで含まれているためです)。
AWSであれば S3バケット にStateを保存し、同時に DynamoDB を使って「今、誰がapplyを実行しているか」のロックをかける設定(Backend設定)を必ず最初に行いましょう。
鉄則2: `terraform plan` は「差分ゼロ」を目指す
開発を進めていると、「手動でちょっとセキュリティグループを変更しちゃった」という誘惑に駆られることがあります。これをやると、TerraformのStateと現実のインフラにズレ(ドリフト)が生じます。
インフラへの変更は必ずコードを経由して行い、常に `terraform plan` を実行したときに「No changes. Infrastructure is up-to-date.(差分なし)」と表示される綺麗な状態を維持してください。これが、緊急トラブル時の迅速な復旧を可能にする唯一の方法です。
—
まとめ
今日、皆さんは以下の重要なステップを完了し、プロへの第一歩を踏み出しました!
1. Terraformの「宣言的」「冪等性」という超重要思想の理解
2. `tfenv` を使った、現場基準の環境構築
3. `local_file` を使った、手軽で確実なHelloWorld
4. 大規模開発でWorkspacesが嫌われ、ディレクトリ分離が愛される理由
5. モジュールを活用した、DRYで安全な最強のディレクトリ設計
この知識は、AWS、Google Cloud、Azureなど、どのクラウドを相手にする時でも一生使える強力な武器になります。
最初は少し難しく感じるかもしれませんが、自分で書いたコードの通りに、クラウド上に巨大なシステムが数分で組み上がっていく快感は、一度味わうと病みつきになりますよ。
何かわからないことがあれば、いつでも聞いてくださいね。あなたのIaCライフが素晴らしいものになるよう、心から応援しています!