はじめに
インフラストラクチャの構成要素の定義方法、データの柔軟な扱い方、そしてコードの再利用性を高めるための制御構造といったTerraformを扱う上で重要な土台をまとめます。
1. インフラストラクチャ定義の基本要素
Terraformのコードは、インフラストラクチャを構成するリソースや、外部情報の参照、動作環境の設定を行うためのブロックで成り立っています。
provider(プロバイダー)
クラウドプロバイダーやSaaSのAPIを介して、実際のリソース操作(作成、変更など)を行うためのプラグイン(コンポーネント)です。
プロバイダーとリージョン設定例
provider "aws" {
region = "ap-northeast-1"
}
resource(リソース)
クラウド上に構築したい具体的なインフラストラクチャを定義します。同じリソース種別の中で、リソース名は重複できません(一意である必要があります)。
基本構文例
resource "リソース種別" "リソース名" {
属性名1 = 値1
属性名2 = 値2
}
メタ引数(Meta-arguments)
リソースブロックの内部に記述され、リソース自体の設定ではなく、Terraformがリソースをどのように扱うのかの立ち振る舞いを定めます。
-
depends_on: Terraformが自動で行う依存関係の解決(暗黙的な依存関係)に頼らず、ユーザーが明示的に順序を指定します。- ユースケース: 異なるリソース間でID参照などの直接的な繋がりはないが、作成順序を守らなければならない特殊なケース(例: IAMロールの権限伝播待ち、S3バケットポリシーとアプリのデプロイ順序など)
-
lifecycle: リソースの作成、削除、変更時の挙動(削除保護prevent_destroyや、変更の無視ignore_changesなど)を制御します。
lifecycle メタ引数 例
lifecycle {
prevent_destroy = true # 誤ったリソースの削除を防止(= 削除保護)
ignore_changes = [
リソースの属性名 # 属性が手動で変更されてもTerraformがそれを上書きしないようにする
]
}
data(データソース)
Terraformの管理外や、別のTerraformのステートで定義された既存の情報を参照するために利用されます。取得した情報は他のリソース定義で参照できます。既存のリソースやAMI ID等をコード内で取得する際に使います。
Data Sourceと参照の例
data "aws_vpc" "vpc" {
tags = {
ServiceName = "web"
Env = "production"
}
}
resource "aws_subnet" "public_subnet" {
vpc_id = data.aws_vpc.vpc.id # 既存VPCのIDを参照
// ...省略
}
2. コードの柔軟性とデータ構造
コードの柔軟性を高め、外部から設定値を受け渡したり、複雑な値を構造化したりするための機能です。
variable(変数)
Terraformコード内で使用する変数を定義できます。CIDRブロック、インスタンスタイプ等をハードコーディングせずに同じ設定を複数環境で利用することができます。
-
型定義:
string、number、boolのプリミティブ型に加え、object(オブジェクト型)、map(マップ型)、list(配列型)、tuple(タプル型)などの複合型が利用可能です。 - 特に
object型は、キーと値の型を固定できるため、構造化された入力を受け取る際に便利です。
Object型変数の定義例
variable "personal_information" {
type = object({
name = string
age = number
address = string
})
}
local(局所変数)
外部から値を指定することはなく、既存の変数やリソースの値を演算・加工して、モジュール内で共通利用するために使われます。これによりコードの見通しが良くなります。
局所変数(Local)の利用例(タグのマージ)
locals {
tags = merge(var.essential_tags, var.additional_tags)
}
resource "aws_instance" "web" {
tags = local.tags # 共通のtagsをlocalから参照
}
条件分岐(三項演算子)
単一の条件分岐は、三項演算子(条件式 ? 真の場合 : 偽の場合)で記述します。
条件分岐の例
locals {
num = 4
result = local.num == 4 ? "4です。" : "4ではありません"
}
3. コードの再利用と繰り返し処理
module(モジュール)
複数のリソースをひとまとまりとして定義し、コードの再利用性を高めます。VPCとサブネットなど、一緒に利用することで便利なリソース群をまとめます。
- モジュール内で作成した値は**
output**ブロックを介して外部に公開します。
Outputの基本構文
output "出力変数名" {
value = 値
}
count と for_each(繰り返し構文)
単一のresourceブロックから同一種のリソースを複数作成する際に利用されますが、その挙動には決定的な違いがあります。
count
number型の値を引数として取り、リソースを複製します。リソースはインデックス([0], [1], ...)で管理されます。
注意点: 配列の途中の要素を削除すると、インデックスがずれるため、意図しないリソースの再作成(破壊的な変更)を引き起こす可能性があります。
countの利用例(S3バケットの複製)
resource "aws_s3_bucket" "buckets" {
count = 3
# count.indexは0から始まるインデックス
bucket = "sample-${count.index}"
}
# -> aws_s3_bucket.buckets[0], [1], [2] が作成される
for_each
map型または文字列のset型を引数として取ります。リソースはキーベースで管理されます。
利点: キーが削除されても、他のキーを持つリソースには影響がないため、count使用時のようなインデックスずれによる破壊的な変更を避けられます。配列をfor_eachで利用する場合は、toset()関数を使ってセット型に変換する必要があります。
for_eachの利用例(ローカルファイルの作成)
locals {
files = {
"sample1.txt" = { content = "This is a sample file 1." }
"sample2.txt" = { content = "This is a sample file 2." }
}
}
resource "local_file" "files" {
for_each = local.files # マップを渡す
filename = each.key # キー(ファイル名)を参照
content = each.value.content # 値(コンテンツ)を参照
}
# -> local_file.files["sample1.txt"], ["sample2.txt"] が作成される
まとめ
本記事では、Terraformの基本的な構成要素と、コードを効率的に管理するための仕組みについて整理しました。
- 基本要素: Provider, Resource, Data Source
- 柔軟性: Variable, Local
- 再利用と制御: Module, count, for_each
Terraformを利用する最大のメリットは、インフラをコードとして管理(IaC)し、再現性と透明性を担保できる点にあります。
今回まとめた lifecycle ブロックによる保護や、for_each による安全なリソース管理は、まさにそのメリットを享受するための重要な機能です。
単に「リソースを作る」だけでなく、「どのように管理・運用していくか」を意識してコードを書くことの重要性を、今回の学習を通じて改めて実感しました。