チームが手動でクラウドコンソールを操作しており、本番環境がどのような状態であるべきか誰も把握していない。
IaC を導入する。構成はバージョン管理されたファイルに保存され、ファイルが信頼できる唯一の情報源であり、クラウドコンソールではない。
理由: 監査証跡、ピアレビュー、ロールバック、レプリケーションはすべてバージョン管理から派生する。手動プロビジョニングではこれらを利用できない。
HashiCorp Terraform Associate
最終確認:2026年5月
004 試験で問われるアーキテクチャパターンのスキャン可能なリファレンス。上から順に読むか、セクションへジャンプ。
チームが手動でクラウドコンソールを操作しており、本番環境がどのような状態であるべきか誰も把握していない。
IaC を導入する。構成はバージョン管理されたファイルに保存され、ファイルが信頼できる唯一の情報源であり、クラウドコンソールではない。
理由: 監査証跡、ピアレビュー、ロールバック、レプリケーションはすべてバージョン管理から派生する。手動プロビジョニングではこれらを利用できない。
望ましい最終状態を記述する方法と、それに到達するまでの手順をスクリプト化する方法のどちらかを選択する。
Terraform は宣言型である。あなたが目標を記述すれば、Terraform が API 呼び出しを特定する。Ansible、Bash スクリプト、カスタム SDK は命令型である。
理由: 宣言型の構成はべき等であり、開始状態に関わらず同じ状態に収束する。命令型スクリプトはすべての遷移を考慮する必要がある。
エンジニアが、リソースを重複させることなく同じ `terraform apply` を安全に再実行したいと考えている。
設計上、べき等である。同じ構成を繰り返し適用しても同じ状態に収束し、重複やドリフトは発生しない。
構成が示すものと現実が乖離している場合(誰かがコンソールで操作した場合)を検出する。
`terraform plan -refresh-only` は現在のクラウド状態を読み取り、変更を提案することなく、状態との差異を報告する。
初期プロビジョニングと継続的な運用を区別する。
Day 0 = 設計と計画。Day 1 = 初期プロビジョニング(最初の apply)。Day 2 = 継続的な運用:パッチ適用、スケーリング、証明書のローテーション、ドリフト修復。
理由: ほとんどの本番作業は Day 2 である。IaC ツールは初回デプロイだけでなく、繰り返しをサポートする必要がある。
コンソールのドリフトのリスクを軽減し、すべてのインフラ変更がレビューを通過することを必須とする。
IaC を使用した GitOps:git が信頼できる唯一の情報源であり、PR がプランをトリガーし、マージが apply をトリガーする。SCP/IAM を介した直接的なコンソールアクセスをブロックする。
ワークロードが AWS と GCP にまたがっており、チームは単一のツールと単一のワークフローを求めている。
単一の構成で複数のプロバイダーを使用する Terraform: `hashicorp/aws` + `hashicorp/google`。一方のプロバイダーの出力がもう一方の入力となる。
理由: プロバイダーに依存しないコア。クラウドごとの SDK はプラグ可能なプロバイダーに存在する。CloudFormation/ARM は単一クラウドである。
標準的な Terraform ライフサイクルステップ。
`init`(プロバイダーとバックエンドのダウンロード)→ `plan`(プレビュー)→ `apply`(実行)→ `destroy`(破棄)。`plan` は読み取り専用で安全である。
Terraform は、実行ごとにクラウドにクエリするのではなく、状態ファイルが必要なのはなぜか?
状態は、構成アドレス(`aws_instance.web`)を実際のリソース ID にマッピングし、依存関係を追跡し、メタデータをキャッシュする。これがなければ、Terraform は自身が管理するクラウドのリソースと、他の場所で作成されたリソースとを区別できない。
オープンソース CLI と HCP Terraform のどちらかを選択する。
ソロ / 小規模チーム / ポリシー強制なし → リモートバックエンド付きの OSS CLI。複数のチーム / Sentinel/OPA ポリシー / VCS駆動の実行 / 動的認証情報 → HCP Terraform。
理由: どちらも同じ Terraform バイナリを実行するが、HCP はコラボレーション、ガバナンス、およびリモートランナーインフラストラクチャを追加する。
VM をプロビジョニングすることと、VM 内にソフトウェアを構成すること。
Terraform はクラウドのリソース(VM、ネットワーク、IAM)をプロビジョニングする。Ansible/Chef/Puppet は VM 内のソフトウェアを構成する。これらは競合するものではなく、補完し合う関係である。
AWS のみのショップが CloudFormation と Terraform のどちらにするか議論している。
CloudFormation は AWS との連携がより密接で、ネイティブなロールバック、状態ファイル管理が不要である。Terraform はマルチクラウド、より大規模なモジュールエコシステム、HCL の人間工学性で優位に立つ。AWS のみでロールバックが最も重要なら CFN を選び、それ以外なら Terraform を選ぶ。
再現可能なインストールのため、プロバイダーのソースとバージョンを固定する。
`terraform { required_providers { aws = { source = "hashicorp/aws", version = "~> 5.0" } } }` で宣言する。
理由: 明示的な `required_providers` がないと、Terraform はレガシーレジストリの名前空間を仮定し、互換性のないバージョンを選択する可能性がある。
構成に AWS の `required_providers` はあるが、`provider "aws"` ブロックがない。
Terraform はデフォルトの空のプロバイダー構成を作成する。AWS プロバイダーはその後、標準の優先順位で `AWS_ACCESS_KEY_ID`/`AWS_PROFILE`/IMDS を読み取る。
理由: 明示的な `provider` ブロックはオプションであり、デフォルトを上書きしたり、複数のインスタンスをエイリアスしたりする場合にのみ必要となる。
どのバージョン制約演算子を使用するか決定する。
`~> 5.0` は `>= 5.0, < 6.0` (任意の 5.x) を許可する。`~> 5.0.1` は `>= 5.0.1, < 5.1.0` (パッチ更新のみ) を許可する。`>= 5.0` は上限なし。`= 5.2.0` は完全一致。`!= 5.3.0` は除外。
理由: 悲観的な演算子 `~>` は、最も右のバージョンコンポーネントのみが増加することを許可する。
`terraform init` は実際に何をするのか?
`required_providers` に従ってプロバイダーをダウンロードし、バックエンドを初期化し、モジュールソースをダウンロードし、`.terraform/` と `.terraform.lock.hcl` を書き込む。`plan` または `apply` の前に必須である。
`init` 後に `.terraform.lock.hcl` ファイルが出現した。コミットすべきか、gitignoreすべきか?
コミットする。ロックファイルは正確なプロバイダーバージョンと暗号学的チェックサムを記録する。これにより、すべての共同作業者と CI ランナーが同じプロバイダーをインストールすることが保証される。
理由: これがないと、`terraform init` は別のマシンで新しい互換性のあるバージョンを選択し、予期しない差分を導入する可能性がある。
`.tf` ファイル全体で空白、アラインメント、インデントを標準化する。
`terraform fmt` は、`.tf` ファイルを所定の場所で標準的なスタイルに書き換える。プリコミットフックと CI で実行する。
クラウド API にアクセスすることなく、構文エラーと型ミスマッチを捕捉する。
`terraform validate` は HCL 構文、型の整合性、必須引数、および内部参照をチェックする。ローカルのみで、プロバイダー認証は不要。
理由: プロバイダー側の問題(認証、リソースの欠落)は捕捉しない — これらは `plan` 時にのみ表面化する。
CI が多数のプロジェクトで `terraform init` を繰り返し実行し、各プロジェクトで同じプロバイダーをゼロからダウンロードしている。
`TF_PLUGIN_CACHE_DIR`(または CLI 設定で `plugin_cache_dir`)を設定する。プロバイダーは一度ダウンロードされ、プロジェクトごとの `.terraform/` ディレクトリにシンボリックリンクされる。
プロバイダーのソースアドレスはどのような形式に従うか?
`<hostname>/<namespace>/<type>` — 例:`registry.terraform.io/hashicorp/aws`。ホスト名が省略された場合、デフォルトでパブリック Terraform Registry になる。Namespace = ベンダー。Type = プロバイダー名。
共同作業者が互換性のない Terraform CLI バージョンを実行するのを防ぐ。
`terraform { required_version = ">= 1.5, < 2.0" }`。実行中のバージョンが一致しない場合、CLI は操作を拒否する。
CI/CD ランナーのプロバイダー認証を選択する。
最適:短命の動的認証情報(AWS/Azure/GCP への OIDC 信頼、HCP Terraform の動的プロバイダー認証情報)。許容可能:環境変数(`AWS_ACCESS_KEY_ID`)。避けるべき:ハードコードされた `provider` ブロックの認証情報。
理由: 構成内の静的な長命シークレットは、認証情報漏洩事故の主な原因である。
トップレベルの設定はどこに記述するのか?
`terraform { ... }` ブロックには、`required_version`、`required_providers`、`backend`、`cloud`、および実験設定が含まれる。複数のファイルにまたがる `terraform` ブロックは結合される。
リソースが状態ファイルに存在するが、Terraform で管理されなくなるべきである。ただし、クラウドのリソースは実行し続ける必要がある。
`terraform state rm <addr>`。クラウドのリソースを破棄することなく、状態ファイルから削除する。
リソースの名前を変更したか、モジュールに移動した。破棄/再作成を避けたい。
`terraform state mv <old> <new>`。クラウドのリソースに手を加えることなく、状態マッピングを更新する。
理由: Terraform 1.1 以降では、HCL の `moved` ブロックを推奨する — これはレビュー可能で、バージョン管理され、PR プランで動作する。
内部プロバイダーレジストリの URL が変更された。既存の状態ファイルはまだ古いソースアドレスを参照している。
`terraform state replace-provider <old-source> <new-source>`。状態ファイル内のプロバイダー参照を書き換える。
apply が実行途中でクラッシュした。DynamoDB のロックがスタックしており、他のエンジニアがブロックされている。
`terraform force-unlock <LOCK_ID>`(ロック ID はエラーメッセージにある)。アンロックする前に、他に誰も実行していないことを確認する。
理由: クリーンアップフェーズが実行されなかったため、クラッシュ時にロックは自動解放されない。
既存の手動で作成された S3 バケットを、再作成せずに Terraform の管理下に置く必要がある。
リソースブロックを記述し、その後 `terraform import aws_s3_bucket.legacy legacy-bucket-name` を実行する。状態ファイルは既存のバケットを記録し、その後の plan ではドリフトのみが表示される。
アドホックな CLI コマンドではなく、CI を通じて多くのリソースを再現可能にインポートする必要がある。
`import` ブロック(Terraform 1.5 以降)を使用する: `import { to = aws_s3_bucket.legacy, id = "legacy-bucket-name" }`。インポートは `apply` 中に行われ、バージョン管理され、plan の出力にも表示される。
不可解なプロバイダーエラーをデバッグする必要がある。完全な HTTP トレースが必要。
`TF_LOG=TRACE`(最も詳細)。`TF_LOG_PATH=./tf.log` でファイルに書き込む。その他のレベル:DEBUG, INFO, WARN, ERROR。
`merge()` や `cidrsubnet()` のような式を、適用せずに現在の状態に対してテストする。
`terraform console` は、現在の構成と状態をスコープに含んだインタラクティブな REPL を開く。ローカル変数や出力のプロトタイピングに役立つ。
複雑な構成の依存関係 DAG を視覚化する。
`terraform graph | dot -Tsvg > graph.svg` は、リソース依存関係の Graphviz 可視化を生成する。
CI スクリプトから Terraform の出力を読み取る。
`terraform output -json <name>` は `jq` 解析に安全な JSON を出力する。通常の `terraform output` は人間が読む形式であり、スクリプトには安全ではない。
次の apply で単一のリソースを強制的に再作成する。
`terraform apply -replace=aws_instance.web`(Terraform 1.x)。従来の `terraform taint` は非推奨である。
理由: `-replace` は plan の出力でレビュー可能である。`taint` は次の plan の前に状態を静かに変更していた。
現在状態ファイルで追跡されているものを検査する。
`terraform state list` はすべてのリソースアドレスを表示する。`terraform state show <addr>` は1つのリソースの属性を表示する。インデックス付きリソースにはクォートを使用する: `terraform state show 'aws_instance.web[0]'`。
モジュールソースを選択する。
ローカルパス → `./modules/vpc`。パブリックレジストリ → `terraform-aws-modules/vpc/aws`。Git → `git::https://github.com/org/repo.git//path?ref=v1.0`。S3/HTTP/Mercurial もサポートされる。
パッチ更新のみを許可するようにレジストリモジュールを固定する。
`version = "~> 3.5.2"` は `>= 3.5.2, < 3.6.0` を許可する。マイナーアップデートを許容する場合は `~> 3.0`(任意の 3.x)を使用する。ローカルパスモジュールは `version` をサポートしない。
組織全体で可視性を持つ内部専用モジュール。
HCP Terraform Private Module Registry。アドレス:`app.terraform.io/<org>/<name>/<provider>`。バージョンはセマンティックバージョニング(`v1.2.0`)に従う Git タグから検出される。
パブリックレジストリと HCP Terraform プライベートレジストリの両方が、モジュールバージョンをどのようなメカニズムで検出するのか?
セマンティックバージョニング(`v1.2.0` または `1.2.0`)に従う Git タグ。新しいタグをプッシュすると、新しいバージョンが利用可能になる。
作成されたバケットの ARN を呼び出し元の構成に公開する。
モジュール内:`output "bucket_arn" { value = aws_s3_bucket.this.arn }`。呼び出し元は `module.<name>.bucket_arn` として読み取る。
呼び出し元が CIDR、名前、タグを提供できるようにモジュールをパラメータ化する。
モジュールのルートにある `variable` ブロックが入力定義。呼び出し元はインラインで渡す: `module "vpc" { source = "...", cidr = "10.0.0.0/16" }`。
モジュールがデータベースパスワードを入力として受け取る。その値は CLI 出力に決して表示されないようにしたい。
変数を `sensitive = true` とマークする。Apply/plan の出力には `(sensitive value)` と表示される。注意:状態ファイルにはプレーンテキストで保存される。
内部で VPC + EKS + RDS モジュールを呼び出す「プラットフォーム」モジュールを構築する。
モジュールは他のモジュールを呼び出すことができる。一方の出力を次のモジュールの入力として配線する: `eks_subnet_ids = module.vpc.private_subnet_ids`。
異なる入力(例:リージョンごとに1つ)で同じモジュールを N 回インスタンス化する。
`module "regional" { for_each = toset(["us-east-1", "eu-west-1"]), source = "...", region = each.key }`。`module.regional["us-east-1"].output_name` を介して参照する。
モジュールにエイリアスされたプロバイダーが必要(例:デフォルト以外のリージョン用)。
呼び出し元は `module` 呼び出し内で `providers = { aws = aws.us_west }` ブロックを渡す。モジュールは `required_providers` で `configuration_aliases = [aws.us_west]` を宣言する。
パブリックレジストリモジュールを指定する。
`<NAMESPACE>/<NAME>/<PROVIDER>` — 例:`terraform-aws-modules/vpc/aws`。Git タグがバージョンを決定する。
開発/ステージング/本番で同じモジュールを再利用する。
2つのパターンがある:(1)1つのルート構成とワークスペースごとの `*.tfvars` を持つワークスペース。(2)環境ごとのルート構成(`envs/dev/main.tf`、`envs/prod/main.tf`)がそれぞれ共有モジュールを呼び出す。パターン2は多チームの分離によく使用される。
CI が plan でレビューされた内容と完全に一致するものを適用し、ステップ間でドリフトがないことを保証する。
`terraform plan -out=tfplan` はプランを保存する。その後 `terraform apply tfplan` はその正確なプランを適用する。再プランニングを拒否することで、time-of-check/time-of-use リスクが排除される。
プラン出力シンボル:`+`、`-`、`~`、`-/+`、`<=` を解読する。
`+` 作成。`-` 破棄。`~` インプレース更新。`-/+` 破棄後に作成(置換)。`<=` 読み取り(データソース)。置換シンボルは `forces replacement` 属性が変更されたことを意味する。
構成の残りの部分に触れることなく、破損したリソースを1つだけ迅速に修正する。
`terraform apply -target=aws_instance.web`。依存関係の追跡をバイパスし、状態を矛盾させる可能性があるため、控えめに使用すること。各 `-target` が使用された理由を文書化する。
理由: HashiCorp は、`-target` は通常のワークフローではなく、例外的なトラブルシューティング用であると明示している。
特定のリソースを強制的に再作成する。
`terraform apply -replace=aws_instance.web`。非推奨の `terraform taint` ワークフローを置き換える。
現在の構成のすべてを破棄する。
`terraform destroy`(または `terraform apply -destroy`)。構成の逆を計画する。`-auto-approve` がない限り確認が必要。
他のリソースに影響を与えずに1つのリソースを破棄する。
`terraform destroy -target=aws_instance.web`。apply と同じ `-target` の注意点があるため、控えめに使用すること。
変更を提案することなく、ドリフトを検出する。
`terraform plan -refresh-only`。実際のリソースから状態を更新し、差異を報告するが、リソース変更プランは生成しない。
理由: 非推奨のスタンドアロン `terraform refresh` コマンドを置き換える。
リソース A はリソース B の前に存在する必要があるが、B は A の属性を参照しない。
B に `depends_on = [resource_a.name]` を追加する。暗黙的な属性参照では依存関係を表現できない場合(例:IAM ポリシーが EC2 で使用される前に伝播する必要がある場合)にのみ使用する。
ダウンタイムなしでリソースを置き換える。
`lifecycle { create_before_destroy = true }`。Terraform は最初に新しいリソースを作成し、参照をリダイレクトしてから古いリソースを破棄する。
理由: デフォルトは破棄後に作成であるため、ダウンタイムが発生する。注:依存するリソースも変更をサポートしている必要がある。
本番データベースの偶発的な `terraform destroy` を防ぐ。
`lifecycle { prevent_destroy = true }`。そのリソースの破棄が提案された場合、`terraform plan` が失敗する原因となる。
理由: 最終防衛線である — `terraform state rm` に続く破棄からは保護されない。
アプリケーションが実行時にタグを設定し、Terraform がそれを元に戻してしまう。
`lifecycle { ignore_changes = [tags["LastDeployed"]] }`。Terraform はこれらの属性のドリフトを無視する。
関連する起動テンプレートが変更されるたびに、EC2 インスタンスを置き換える(EC2 を直接変更せずに)。
`lifecycle { replace_triggered_by = [aws_launch_template.web.latest_version] }`(Terraform 1.2 以降)。リストされた値のいずれかが変更されると、インスタンスが置き換えられる。
CI/CD パイプラインがプランジョブの成功後に適用を実行する。キーボードに人間はいない。
`terraform apply -auto-approve` は対話型確認をスキップする。保存されたプランファイル(`terraform apply tfplan`)と組み合わせることで、CI を決定論的にする。
クラウドプロバイダーが Terraform をレート制限しており、多くのリソース作成が断続的に失敗している。
`terraform apply -parallelism=5`(デフォルトは10)は、並行リソース操作の数を制限する。
2つのリソースがお互いに依存関係を持たない。
Terraform はそれらを並行して作成する。DAG は参照エッジに沿ってのみシーケンスを強制する。
理由: 同じ DAG 深度にあるリソースは、`-parallelism` の上限まで並行して実行される。
`terraform.tfvars`、`TF_VAR_region` 環境変数、そして `-var=region=...` CLI フラグで変数が定義されている。
優先順位が高い順:CLI `-var` / `-var-file` > `*.auto.tfvars`(辞書順)> `terraform.tfvars` > `TF_VAR_*` 環境変数 > 変数のデフォルト値。
シークレットを git にチェックインすることなく、環境固有の変数値を渡す。
`terraform apply -var-file=prod.tfvars`。`*.auto.tfvars` ファイルは自動的に読み込まれる。`terraform.tfvars` も自動的に読み込まれる。
破棄/再作成なしで、HCL 内のリソースの名前/パスをリファクタリングする。
`moved { from = aws_instance.old_web; to = aws_instance.new_web }`(Terraform 1.1 以降)。PR プランでレビュー可能であり、アドホックな `terraform state mv` を置き換える。
理由: HCL 内に存在し、バージョン管理され、共同作業者間でべき等である。`state mv` は一度限りの CLI 副作用である。
リソースを破棄することなく、構成から削除する。
`removed { from = aws_instance.legacy; lifecycle { destroy = false } }`(Terraform 1.7 以降)。アドホックな `terraform state rm` を置き換える。`destroy = true` を設定すると、破棄も行う。
CI がテキストを解析せずに、プランに変更があるかどうかを知る必要がある。
`terraform plan -detailed-exitcode`。0 = 変更なし、1 = エラー、2 = 変更あり。「プランのみ」の PR ジョブを駆動する。
状態ファイルには実際に何が含まれているのか?
リソースアドレスとクラウドのリソース ID のマッピング、属性のスナップショット、依存関係グラフのメタデータ、モジュール/プロバイダー参照。差分計画とドリフト検出に使用される。
単一のエンジニアがローカルでプロトタイピングを行っている場合、ローカル状態ファイルで問題ないか?
単独の使い捨て作業であれば問題ない。Terraform は構成の隣に `terraform.tfstate` を書き込む。2人目の人物、CI ランナー、または本番環境が関与するようになったらすぐにリモートバックエンドに切り替える。
チームが、ロックとバージョン管理を備えた、AWS ホストのエンジニア間で共有される状態ファイルを必要としている。
S3 バックエンド。`bucket`、`key`、`region` を構成する。状態ロック用に `dynamodb_table` を追加する。バケットのバージョン管理と SSE-KMS サーバーサイド暗号化を有効にする。
理由: S3 + DynamoDB は、AWS ホストのリモートバックエンドの標準である。バージョン管理は、破損または誤って上書きされた状態ファイルからの回復を可能にする。
CI に静的認証情報なしの Azure ホスト共有状態。
バックエンドタイプ `azurerm`。ランナーがその ID を介して認証するように、`use_msi = true`(マネージド ID)または `use_oidc = true` を設定する。ネイティブのブロブリースがロックを処理する。
オブジェクトバージョン管理を備えた GCP ホスト共有状態。
バックエンドタイプ `gcs`。`bucket` と `prefix` を構成する。GCS はオブジェクト世代(バージョン管理)を内蔵しており、GCS オブジェクトロックを介してロックを行う。
2人のエンジニアが同時に同じ S3 バックエンドの状態ファイルに対して apply を実行する。
DynamoDB ロックテーブルは同時書き込みを防ぐ。最初のリクエストがロックを取得し、2番目のリクエストは `state lock failed` エラーを見て、待機するか、古い場合は `force-unlock` する必要がある。
理由: DynamoDB がないと、同時 apply によって S3 状態ファイルが破損する可能性がある。
HCP Terraform で DynamoDB を構成する必要があるか?
いいえ。HCP Terraform が状態、ロック、暗号化をネイティブに処理する。バックエンドではなく `cloud { ... }` を宣言するだけである。
ローカル状態から S3 バックエンドへ移行する。
`backend "s3"` ブロックを追加する。`terraform init -migrate-state` を実行する。Terraform はローカル状態を S3 にコピーし、確認を求める。
バックエンドの構成が変更された(例:新しいバケット)が、既存の状態を移行したくない。
`terraform init -reconfigure`。バックエンドを再初期化し、既存のローカルキャッシュを無視する。意図的に最初から開始する場合に使用する。
1つのルート構成で開発 / ステージング / 本番を管理する。
ワークスペース — `terraform workspace new staging`、`terraform workspace select prod`。それぞれが独自の状態でファイルを持つ。HCL では `terraform.workspace` を介して参照する。
理由: 軽量なオプション。真の分離(異なる IAM、別アカウント)には、環境ごとのルート構成を推奨する。
ローカルバックエンドはワークスペースの状態をどこに保存するか?
`default` ワークスペース → プロジェクトルートの `terraform.tfstate`。その他のワークスペース → `terraform.tfstate.d/<NAME>/terraform.tfstate`。
ステージングリソースが存在する状態で `terraform workspace select prod` を実行する。
ステージングリソースには影響しない。ワークスペースの切り替えは、Terraform が次に読み取る状態ファイルを変更するだけであり、ローカルのポインタ変更である。
利用可能なワークスペースとアクティブなワークスペースを確認する。
`terraform workspace list`(アスタリスクが現在のワークスペースを示す)。`terraform workspace show` は現在の名前のみを表示する。
セキュリティチームが、Terraform が状態ファイル内の RDS パスワードをどのように処理するか尋ねてきた。
機密値は状態ファイルに**プレーンテキスト**で保存される。対策:バックエンドを保存時に暗号化する(S3 SSE-KMS、HCP ネイティブ)、状態バケットへの IAM アクセスを制限する、可能な限り Terraform にシークレットを入れない。
理由: `sensitive = true` フラグは CLI 出力から値を隠すだけであり、状態ファイルからは隠さない。
コンプライアンスにより、状態ファイルの保存時の暗号化が必要とされている。
S3:バケットで SSE-KMS を有効にする。Azure:ストレージアカウント暗号化(デフォルト ON)。GCS:顧客管理の暗号化キー。HCP Terraform:ネイティブで保存時に暗号化される。
エンジニアが誤って本番環境に対して `terraform destroy` を実行した。状態ファイルは空になっている。
S3 のバージョン管理から状態ファイルの以前のバージョンを復元し、その後 `terraform plan` を実行して、Terraform が何を再作成/インポートする必要があると考えているかを確認する。破棄されたリソースについては、クラウド側での復元(スナップショット、`aws backup`)と組み合わせる。
状態ファイルが間違っているように見える。直接編集したくなる。
やめるべきである。`terraform state` サブコマンド(`mv`、`rm`、`replace-provider`、`pull`、`push`)を使用する。手動での JSON 編集は整合性チェックをスキップし、履歴を破損させる。
生の遠隔状態 JSON を検査するか、回復した状態ファイルをプッシュする。
`terraform state pull` は現在の遠隔状態を標準出力に書き出す。`terraform state push <file>` は指定されたファイルで遠隔状態を上書きする。プッシュは破壊的であるため、最初にバックアップを取ること。
一時的な API トークンを状態ファイルに保存することなく Terraform を介して渡す。
変数を `ephemeral = true` とマークする(Terraform 1.10 以降)。一時的な値は状態ファイルやプランファイルに永続化されない。モジュール出力としてエクスポートするには、出力も `ephemeral = true` とマークする。
理由: `sensitive` は CLI で非表示になるが、状態ファイルにプレーンテキストで保存される。`ephemeral` は本当に一切保存されない。
`sensitive` 引数と書き込み専用引数(例:`password_wo`)の違いは何か?
`sensitive` は状態ファイルにプレーンテキストで保存され、CLI でのみ編集される。書き込み専用は状態ファイルに**決して**保存されない。書き込み専用属性の変更を検出するために、Terraform は対応するバージョンフィールド(例:`password_wo_version`)を使用する。
状態ファイル内の `count` リソースの1つの要素を表示する。
アドレスをクォートする: `terraform state show 'aws_instance.web[0]'`。クォートがない場合、シェルが角括弧を解釈する可能性がある。
異なるチームが、互いの状態ファイルを踏みつけることなく、独立して運用する必要がある。
チームごと(またはチームの環境ごと)に1つの状態ファイル。別々のバックエンド/バケット。`terraform_remote_state` データソースを使用して、他の状態ファイルを読み取り専用で読み取る。
ネットワークチームが VPC を管理しており、アプリチームは VPC ID を必要としている。
`data "terraform_remote_state" "network" { backend = "s3"; config = { bucket = "...", key = "network/terraform.tfstate" } }`。出力を `data.terraform_remote_state.network.outputs.vpc_id` として参照する。
理由: 読み取り専用であるため、ネットワークチームの状態はロックされたり変更されたりしない。
Terraform 0.13 向けに記述された構成を 1.x で使用したい。
状態ファイルは前方互換性がある:1.x は 0.13 の状態を読み取れる。HashiCorp は、各バージョンを状態ファイルに対して実行する漸進的なアップグレード(0.13 → 0.14 → … → 1.x)を推奨している。
誰かがコンソール経由でセキュリティグループを変更した。Terraform に新しい状態を再アサートさせるか、受け入れさせたい。
再アサート:`terraform apply` — Terraform は構成に戻す。受け入れ:HCL を現実と一致するように更新し、その後 apply を実行する(差分なし)。最初に `terraform plan -refresh-only` で検出する。
変数を厳密に型付けする。
プリミティブ:`string`、`number`、`bool`。コレクション:`list(<type>)`、`set(<type>)`、`map(<type>)`。構造型:`object({...})`、`tuple([...])`。真に多型である場合にのみ `any` を使用する。
`environment` を dev/staging/prod に制限する。
`variable "environment" { validation { condition = contains(["dev","staging","prod"], var.environment); error_message = "..." } }`。複数の `validation` ブロックが許可されており、すべてが合格する必要がある。
データベースパスワードを `terraform output` に表示させずに出力する。
`output "db_password" { value = ...; sensitive = true }`。CLI には `(sensitive value)` と表示される。`terraform output db_password` または `-json` 経由では引き続き読み取り可能(スクリプト用として意図的)。
値を一度計算し、多くのリソースで再利用する。
`locals { common_tags = merge(var.tags, { Project = var.project }) }`。`local.common_tags` として参照する。モジュールの外部からはオーバーライドできない。
既存の AMI を管理することなく検索する。
`data "aws_ami" "ubuntu" { most_recent = true; filter { ... } }`。読み取り専用。属性を `data.aws_ami.ubuntu.id` として参照する。
N 個の類似リソースを作成する — `count` または `for_each` を選択する。
アイテムが安定したIDを持つ場合(リージョン名、環境キー)は `for_each`(マップまたはセットを使用)。「N個のコピーが必要で、順序は関係なく、IDは単なるインデックス」という場合は `count`。`count` の途中で追加/削除すると破棄/再作成が発生するが、`for_each` はIDを保持する。
文字列のリスト内のアイテムごとに1つのリソースを作成する。
`for_each = toset(["a", "b", "c"])`。`each.key` と `each.value` は両方とも文字列を返す。マップの場合:`for_each = var.users` — `each.key` = マップキー、`each.value` = マップ値。
可変数のネストされたブロック(例:イングレスルール)を生成する。
`dynamic "ingress" { for_each = var.rules; content { from_port = ingress.value.from; ... } }`。必要に応じて `iterator` 引数でイテレータの名前を変更できる。
`count` リソースのすべてのインスタンスにわたる属性のリストを取得する。
`aws_instance.web[*].id` は ID のリストを返す。`count` と `for_each` の両方で動作する(ただし、`for_each` は順序なしマップを生成するため、`values(aws_instance.web)[*].id` を使用)。
条件に基づいて値を設定する。
三項演算子: `var.is_prod ? 5 : 1`。両方の分岐は同じ型を生成する必要がある。
2つのマップを結合する。キーの衝突時には2番目のマップの値が優先される。
`merge(local.defaults, local.overrides)`。最も右のマップが優先される。タグの合成に役立つ。
キーが存在しない場合にデフォルト値を持つマップ値を読み取る。
`lookup(var.config, "region", "us-east-1")`。キーが存在しない場合にデフォルトを返す。深くオプションの構造には、オプションの `try()` を推奨。
ネストされたリストをフラットなリストに折りたたむ。
`flatten([["a","b"], ["c"], [["d","e"]]])` → `["a","b","c","d","e"]`。リストを再帰的にフラット化し、順序を保持する。
Terraform マップを IAM ポリシードキュメントに埋め込む。
`jsonencode({Version = "2012-10-17", Statement = [...]})`。JSON 文字列を生成する。逆は `jsondecode()`。
変数からの値でテンプレート化されたユーザーデータスクリプトをレンダリングする。
`templatefile("init.sh.tpl", { region = var.region, env = var.env })`。テンプレートは `${region}` 構文を使用する。静的レンダリングの新しい代替として `file()` + `format()` がある。
VPC 範囲からサブネット CIDR を切り出す。
`cidrsubnet("10.0.0.0/16", 8, 0)` → `10.0.0.0/24`。最初の引数 = 親、2番目 = 拡張するビット数、3番目 = サブネット番号。
存在しない可能性のある値を読み取り、デフォルト値にフォールバックする。
`try(yamldecode(file("config.yaml")), { defaults = true })`。左から右に評価され、最初のエラーにならない式を返す。
グロブに一致するファイルを反復処理する。
`fileset(path.module, "configs/*.json")` はパスのセットを返す。ファイルごとに1つのリソースを管理するために `for_each` と組み合わせる。
同じプロバイダーを2つのリージョンで使用する(例:AWS us-east-1 + us-west-2)。
2つの `provider "aws" { alias = "..." }` ブロック。リソースは `provider = aws.us_west` を介してオプトインする。モジュールは `configuration_aliases` を介してエイリアスを受け入れる。
新しく作成された VM でシェルコマンドを実行する。
リソース内の `remote-exec` プロビジョナー。最後の手段のツールである — cloud-init、ユーザーデータ、または構成管理を推奨。プロビジョナーは状態ファイルで追跡されず、ドリフト時に再実行されない。
クラウドのリソースに対応しないコマンドを実行する(例:CLI 呼び出し)。
`resource "null_resource" "trigger" { triggers = { sha = sha1(file(".")) }; provisioner "local-exec" { command = "..." } }`。`triggers` が変更されると再実行される。
apply をブロックすることなく、実行時不変条件(例:ヘルスエンドポイントが 200 を返す)を継続的に検証する。
`check "endpoint" { data "http" "h" { url = "..." }; assert { condition = data.http.h.status_code == 200; error_message = "..." } }`。plan/apply 時に実行され、失敗は警告であり、ハードエラーではない。
理由: `check` は、チェック内でのみ使用可能なスコープ付きデータソースを許可する。`precondition`/`postcondition` は plan/apply 時のハードエラーである。
AMI が古すぎる場合、プランを失敗させる。
`lifecycle { precondition { condition = data.aws_ami.x.creation_date > "2024-01-01"; error_message = "..." } }`。ハードフェイルであり、適切に前/後に評価される。
固定形状の位置指定値を持つ変数。
`type = tuple([string, number, bool])` は、その順序で正確に3つの要素を必要とする。リスト(同種、可変長)とは異なる。
構造化された入力(例:`database = { instance_class = string, allocated_storage = number }`)を厳密に型付けする。
`type = object({ instance_class = string, allocated_storage = number, multi_az = optional(bool, false) })`。`optional(<type>, <default>)` は属性をオプションにする。
`*.tfvars` ファイルはどのように読み込まれるか?
`terraform.tfvars` と `*.auto.tfvars` は自動的に読み込まれる(`auto` は辞書順)。他の名前は `-var-file=path.tfvars` が必要。
Terraform モジュールの自動テストを記述する。
`.tftest.hcl` ファイルに `run "name" { command = plan|apply, assert { condition = ..., error_message = ... } }` を記述する。`command = plan` は副作用なし。`command = apply` は実際にリソースを作成する。
テスト実行に変数を渡す。
ファイルレベルの `variables { ... }` ブロックはすべての実行に適用される。実行ごとの `variables { ... }` はその実行のみを上書きする。
HCP Terraform でワークスペースの実行モードを選択する。
VCS 駆動(git プッシュ時に自動プラン、最も一般的)。CLI 駆動(開発者がローカルで `terraform plan/apply` を実行、HCP が状態を保持)。API 駆動(API によってトリガーされる Terraform 実行、自動化/CD システムが使用)。
HCP Terraform 実行におけるステージの順序。
plan → コスト見積もり(有効な場合)→ ポリシーチェック(Sentinel/OPA)→ 手動または自動 apply。必須ポリシーの失敗または実行タスクの失敗はパイプラインを停止する。
すべての S3 バケットが暗号化されていることを強制し、違反した場合は apply をブロックする。
ハード必須適用型の Sentinel ポリシー。プランを検査し、失敗した場合は apply をブロックする。ソフト必須は管理者による上書きを許可。アドバイザリはログを記録するが、ブロックはしない。
サードパーティのセキュリティスキャナーを HCP Terraform 実行パイプラインに統合する。
ポストプランステージで実行タスク。HCP はプランをエンドポイントに POST し、エンドポイントは合否を返信する。必須適用は失敗時に apply をブロックし、アドバイザリは警告のみ。
マージする前にプルリクエストの影響を示す。
投機的プランは PR(またはブランチ)で実行され、結果を PR にコメントバックし、決して適用されない。VCS 統合が有効になっている場合、自動的にトリガーされる。
HCP Terraform の変数に AWS アクセスキーを静的シークレットとして保存することを避ける。
動的プロバイダー認証情報。HCP Terraform は OIDC 信頼を介して AWS/Azure/GCP に短命の認証情報を要求する。静的なキーはなく、実行ごとの ID、監査に優しい。
理由: 静的キーは漏洩する。OIDC 信頼は認証情報をワークスペース + 実行にバインドし、1時間以内に期限切れになる。
多くのワークスペースで同じプロバイダー認証情報を共有する。
変数セット。組織/プロジェクトレベルで一度定義し、多くのワークスペースにアタッチする。更新はワークスペースごとの編集なしで伝播する。
HCP Terraform のデフォルトプロジェクトとは何か?
すべての組織に存在する組み込みプロジェクトで、削除することはできない(名前の変更は許可される)。明示的に別のプロジェクトに割り当てられない限り、すべてのワークスペースがそれに属する。
上流ワークスペースが正常に apply を完了するたびに、下流ワークスペースを適用する。
実行トリガー。依存するワークスペースでソースワークスペースを設定する。HCP は、各上流の apply 成功後に依存するワークスペースで実行をキューに入れる。