Terraform のリモートステートは可用性と整合性が最優先。S3 にステートを置き、DynamoDB でロックを取るのが AWS での定石です。
本稿は S3 側の暗号化設計と DynamoDB 側のロック設計に絞り、試験に出やすい論点と現場での安全運用の両立を狙います。
Terraform のステートは機微な値を含む可能性があり、誤消去や競合は致命的です。よって、耐障害性、暗号化、並行実行抑止、監査可能性の担保が必須になります。
S3 はステート保管。DynamoDB はロックの単一真実点。S3 はバージョニングで巻き戻しを可能にし、DynamoDB は排他制御で同時 apply を防ぎます。ネットワーク経路は AWS API への TLS 通信で保護されます。
試験観点では、S3 の暗号化方式選択、KMS キーの権限、バケットバージョニング、DynamoDB ロックテーブルのキー設計、強制アンロックの扱いあたりが出題されやすい領域です。
キー設計はワークスペースとの衝突回避を第一に考えます。workspace_key_prefix を使い、env/ワークスペース名/モジュールごとの key で分離するのが読みやすく安全です。S3 は 2020 年以降オブジェクトの強整合性が保証されますが、ロックは依然として必要です。
バージョニングは必須。削除や上書き事故からの復旧が現実的になります。合わせて、ライフサイクルルールで古いバージョンの保持期間を決め、コスト制御を行います。MFA Delete は運用負荷が高いので監査とバージョニングで代替するのが一般的です。
暗号化は二段構えで考えます。Terraform の backend s3 設定 encrypt=true は S3 管理キーによる SSE-S3 を要求します。組織ポリシーで KMS の顧客管理キーを必須にしたい場合は、バケットのデフォルト暗号化で CMK を指定し、バケットポリシーと KMS キーポリシーで SSE-KMS を強制します。backend で特定の KMS キーを直接指定するオプションはありません。
| 観点 | SSE-S3 (S3 管理キー) | SSE-KMS (顧客管理キー) | クライアント側暗号化 |
|---|---|---|---|
| 鍵管理 | AWS 側。ユーザーは鍵を直接管理しない | CMK をユーザーが管理。ポリシーやローテーション可 | アプリが暗号化。鍵配布が運用課題 |
| 実装容易性 | とても容易。backend の encrypt=true で誘導可 | S3 バケットのデフォルト暗号化と KMS ポリシー設計が必要 | 実装負荷大。復号の互換性も考慮 |
| 監査と制御 | 限定的。KMS の詳細監査は不可 | KMS で鍵使用ログ、IAM/KMS ポリシーで精緻制御 | アプリ任せ。クラウド側の監査は限定 |
| コスト | 追加コストほぼなし | KMS リクエストと CMK コストが発生 | 暗号化自体は無料だが運用コスト増 |
| 試験の狙い所 | encrypt=true は SSE-S3 である点 | S3 デフォルト暗号化と KMS 権限が必要 | 原則非推奨。要件が特殊な場合のみ |
Terraform は apply などの書き込み操作の前に DynamoDB にロックを取りにいきます。テーブルはパーティションキー LockID のみを持つ単純スキーマで、LockID に対する条件付き PutItem によってロックを獲得します。失敗時は既存のロックと見なして待機または失敗します。
スループットは PAY_PER_REQUEST を推奨。複数チームや CI ランナーが断続的に実行する負荷に自然対応できます。SSE は有効化を推奨しますが、テーブルには機微な値は原則入りません。万一の障害時は terraform force-unlock を使えますが、同時実行が本当に無いことを確認してからにしてください。
Terraform ステート更新時のロックと書き込みの流れ
ステート用の IAM 権限は最小限に絞るのが安全です。S3 は対象バケットの指定プレフィックスに対する GetObject と PutObject、ListBucket(prefix 限定)を許可し、DeleteObject は通常付与しません。SSE-KMS を使う場合は、KMS の Encrypt/Decrypt/GenerateDataKey/DescribeKey をキーの範囲で許可します。
DynamoDB はロックの取得・解放に GetItem/PutItem/DeleteItem と DescribeTable が必要です。テーブルスキャン系の権限は不要です。
backend の設定値は変数参照が効かないため、静的に書くか、-backend-config で外部化します。まずローカルステートでバケットとテーブル等を作成し、その後 terraform init -migrate-state で移行するのが安全な手順です。
S3 バケットと KMS、DynamoDB ロックテーブル、最小権限の一例(HCL、概略)
# 先に local backend で作成し、その後 remote backend に移行すること。
terraform {
required_version = ">= 1.3"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
backend "s3" {
bucket = "my-tf-state-bucket"
key = "env/prod/app1/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "terraform-lock"
encrypt = true # SSE-S3 を要求。SSE-KMS はバケット側で設定する
# 変数参照不可。変更時は terraform init -migrate-state
}
}
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_kms_key" "tf_state" {
description = "Terraform state at rest encryption"
enable_key_rotation = true
}
resource "aws_s3_bucket" "tf_state" {
bucket = "my-tf-state-bucket"
}
resource "aws_s3_bucket_versioning" "tf_state" {
bucket = aws_s3_bucket.tf_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "tf_state" {
bucket = aws_s3_bucket.tf_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.tf_state.arn
}
}
}
resource "aws_s3_bucket_lifecycle_configuration" "tf_state" {
bucket = aws_s3_bucket.tf_state.id
rule {
id = "expire-old-versions"
status = "Enabled"
noncurrent_version_expiration {
noncurrent_days = 90
}
}
}
resource "aws_dynamodb_table" "tf_lock" {
name = "terraform-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
server_side_encryption {
enabled = true
}
}
# ステート操作ロール用 IAM ポリシー例(必要最小限)
data "aws_iam_policy_document" "tf_state_access" {
statement {
sid = "S3StateReadWrite"
actions = ["s3:GetObject", "s3:PutObject"]
resources = [
"${aws_s3_bucket.tf_state.arn}/env/prod/app1/*"
]
}
statement {
sid = "S3List"
actions = ["s3:ListBucket"]
resources = [aws_s3_bucket.tf_state.arn]
condition {
test = "StringLike"
variable = "s3:prefix"
values = ["env/prod/app1/*"]
}
}
statement {
sid = "DynamoDBLock"
actions = ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:DescribeTable"]
resources = [aws_dynamodb_table.tf_lock.arn]
}
statement {
sid = "KMSForS3State"
actions = ["kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey", "kms:DescribeKey"]
resources = [aws_kms_key.tf_state.arn]
}
}
resource "aws_iam_policy" "tf_state_access" {
name = "tf-state-access"
policy = data.aws_iam_policy_document.tf_state_access.json
}複数アカウントから同じステートを操作するのは原則避け、1 アカウントに集約し AssumeRole で越境アクセスするのが安全です。バックエンドは一箇所、作業ロールはそこへアクセスします。
S3 のクロスリージョンレプリケーションは災害対策として有効ですが、ステートは単一の正本が大前提です。レプリカ側での書き込みは禁止し、読み取り復旧手段としてのみ位置付けます。SSE-KMS を使う場合、レプリケーションロールに対する KMS Grant と、宛先側の KMS キー指定を整えます。
DynamoDB グローバルテーブルでロックテーブルを多リージョン化するのは避けてください。ロックの単一性が崩れます。ロックは S3 正本のリージョンに単一配置が原則です。
日常運用では、S3 のバージョニングとライフサイクル、KMS キーの有効期限やローテーション、CloudTrail のデータイベントを定期確認します。ワークスペースや key の命名規約は守り、衝突を防ぎます。
ロックが残留して apply が進まない時は、まず実行中セッションが無いことを監視基盤や CI で確認し、DynamoDB のロックアイテムを確認します。必要な場合のみ terraform force-unlock を使います。ステート破損時は S3 の直近バージョンにロールバックし、terraform state pull で健全性を確認します。
権限エラーは KMS のポリシーか S3 バケットポリシーに起因することが多いです。S3 のデフォルト暗号化で CMK を使っている場合、当該 IAM ロールが KMS キーポリシーにも許可されているかを必ず確認してください。
Associate / Pro
問題 1
組織ポリシーで Terraform ステートを KMS の顧客管理キーで必ず暗号化したい。S3 バックエンドの正しい設計はどれか。
正解: A
S3 バックエンドの encrypt=true は SSE-S3 を有効化するためのフラグで、特定の KMS キー指定は backend では行えません。SSE-KMS を必須にするには、S3 バケットのデフォルト暗号化で CMK を設定し、あわせてバケットポリシーと KMS キーポリシーで対象ロールのみが鍵を利用できるようにします。
ロック用 DynamoDB テーブルのキー設計は固定ですか?
はい。パーティションキー名が LockID の単一キーである必要があります。ソートキーは不要です。Terraform は LockID を条件付き PutItem で使用し、重複があればロック取得を拒否します。TTL は Terraform のロック制御には使われません。
S3 バックエンドで DynamoDB を指定しないとどうなりますか?
ロックが無効となり、同時実行でステート破損リスクが高まります。小規模や個人用途を除き、実務では dynamodb_table を必ず設定してください。
SSE-KMS 利用時に必要な KMS 権限は?
一般的には kms:Encrypt、kms:Decrypt、kms:GenerateDataKey、kms:DescribeKey が必要です。S3 のデフォルト暗号化で CMK を使う場合でも、呼び出し主体が当該キーを利用できるよう IAM とキーポリシーの両方で許可を与えてください。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける
Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...
Terraform Authoring & Ops Pro: 上位資格の範囲と対策
上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...
Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす
Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...
Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義
Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...
Terraform Data Source徹底理解:既存リソースの参照で壊さず足す
Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...