Terraformは宣言型ツールです。しかし、「いつapplyすべきか」は、思ったよりもはるかに複雑な問題です。
🎯 この記事で扱うこと
- terraform applyの正確な動作原理
- マイグレーションシナリオでのapply使用法
- ドリフト(Drift)発生時のapplyの役割と注意点
- 二つのケースの相違点と共通原則
- 間違いやすいパターンと推奨ワークフロー
📌 導入: 「ただapplyすればいいんじゃないですか?」
Terraformを初めて学ぶ人は誰もがこう考えます。コードを書いてplanを見てapply — シンプルです。しかし、実際の運用環境で作業していると悩みが生じます。
「コンソールで直接セキュリティグループのルールを一つ追加したんだけど… applyしても大丈夫かな?」
「既存のインフラをTerraformで管理したいんだけど、どこから始めればいい?」
この二つの質問が、今日扱う核心です。マイグレーションとドリフト修正は一見似ていますが、性質が全く異なり、それぞれに合ったapply戦略があります。

🔍 まず知っておくべきこと: TerraformのState
Terraformは.tfstateファイルにインフラの現在の状態を記録します。applyを実行すると、次の3つを比較して行動を決定します。
| 比較 | 対象説明 |
| コード(HCL) | 私が望む状態 (desired state) |
| Stateファイル | Terraformが知っている状態 (known state) |
| 実際のインフラ | クラウドに実際に存在する状態 (actual state) |
この三つが一致すればapplyは何も行いません。不一致が生じた時にapplyが必要になります。
🧳 ケース 1: マイグレーション — 既存インフラをTerraformに取り込む
いつこのケースなのか?
- コンソールやCLIで手動作成されたインフラが既に存在する
- これをTerraformでコード化(IaC化)したい
- Stateファイルには該当リソースがない状態である
間違ったアプローチ: コード作成後すぐにapply
# ❌ 危険な方法
terraform apply
既に存在するS3バケット、EC2インスタンスをコードで記述した後applyすると、Terraformは新しく作成しようと試みます。既に同じ名前のリソースがあればエラーになり、運が悪ければ既存のリソースが削除後に再生成される事故が発生します。
正しいアプローチ: terraform importを先に
# ✅ 正しい方法: importでStateにまず登録
terraform import aws_s3_bucket.my_bucket my-existing-bucket-name
importコマンドは、実際のインフラリソースをStateファイルに登録します。その後planを実行してコードとStateが一致するか確認し、差がない場合に初めて安全に管理できます。
Terraform 1.5+ : importブロックによる宣言型マイグレーション
Terraform 1.5からは、importブロックをHCLコード内に直接記述できます。
# import.tf
import {
to = aws_s3_bucket.my_bucket
id = "my-existing-bucket-name"
}
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-existing-bucket-name"
# ... その他の属性
}
# planでimport結果を事前に確認
terraform plan
# 問題なければapply
terraform apply
この方式では、applyがimport + コード同期を一度に処理します。マイグレーション完了後、importブロックは削除しても構いません。
マイグレーションにおけるapply推奨時点のまとめ
- ✅ terraform importでState登録完了後
- ✅ terraform planでNo changesまたは意図した変更のみが表示される時
- ✅ importブロック方式でplan結果を検討した後
🔄 ケース 2: ドリフト(Drift) — Stateと実際のインフラがずれた時
ドリフトとは?
ドリフト(Configuration Drift)は、Terraformが管理するリソースがコードやStateの外部で変更された時に発生します。
よくある原因:
- 👤 誰かがコンソールで直接セキュリティグループのルールを修正
- 🔧 他のチームメンバーがAWS CLIでタグを変更
- ☁️ クラウドサービスが自動的に属性を更新 (例: EKSノードグループ)
현재 상태:
코드(HCL) → 포트 443만 허용
State 파일 → 포트 443만 허용 (apply 시점 기록)
실제 AWS → 포트 443 + 8080 허용 ← 누군가 콘솔에서 추가!
この状態でterraform planを実行するとどうなるでしょうか?
terraform plan
# ~ aws_security_group_rule.allow_https will be updated in-place
# - from_port = 8080 (削除予定)
Terraformはコード(HCL)を基準に8080ルールを削除しようとします。
apply前に必ずすべきこと: ドリフト検出
# Stateを実際のインフラと同期してドリフトを検出
terraform refresh
# またはplanとともに
terraform plan -refresh-only
-refresh-onlyフラグは、実際のインフラ状態をStateに反映するだけで、インフラは変更しません。ドリフトがどこでどのように発生したかを把握するのに非常に役立ちます。
ドリフト処理戦略: 二つの選択肢
戦略A: コードを現実に合わせて更新 (ドリフト受容)
コンソールで追加した変更が意図されたものであれば、コードを更新してapplyします。
# コードに8080ルールを追加
resource "aws_security_group_rule" "allow_8080" {
type = "ingress"
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
security_group_id = aws_security_group.main.id
}
terraform plan # 確認
terraform apply # コードと実際の状態を一致させる
戦略B: インフラをコード基準に戻す (ドリフト除去)
無断変更であれば、applyでコード基準の状態に戻します。
# コードを修正せずにそのままapply
terraform apply
# → 8080ルールが削除され、コードの状態に復元
ドリフトシナリオにおけるapply推奨時点のまとめ
- ✅ terraform plan -refresh-onlyでドリフト範囲を把握した後
- ✅ ドリフトが意図されたものか無断変更か判断した後
- ✅ 戦略A(コード更新)または戦略B(元に戻す)を決定した後
- ❌ ドリフトの原因も分からないまま無闇にapplyしてはいけない
⚠️ 注意事項: apply前に必ず確認すること
1. planなしのapplyは事故への近道
# ❌ 絶対禁止パターン
terraform apply -auto-approve
CI/CD自動化でも、plan結果を人が検討するステップを必ず入れるべきです。
2. Stateファイルは絶対に手動で編集しないこと
ドリフトが発生したからといって.tfstateファイルを直接修正すると、より大きな問題が発生します。必ずterraform stateコマンドを使用します。
# 特定のリソースをStateから削除 (インフラは維持)
terraform state rm aws_s3_bucket.old_bucket
# Stateでリソースリストを確認
terraform state list
3. リモートStateの使用は選択ではなく必須
チーム環境でローカルStateを使用すると、ドリフトと衝突が日常茶飯事になります。
# S3 + DynamoDBでリモートStateを構成
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-lock"
encrypt = true
}
}
4. プロダクションではtargetフラグの使用を最小限に
# ⚠️ 緊急時以外は避けること
terraform apply -target=aws_instance.web
特定のリソースだけを選んでapplyすると、依存関係が絡まる可能性があります。
✅ まとめ: マイグレーション vs ドリフト、何が違うのか?
区分 マイグレーション ドリフト修正
| 区分 | マイグレーション | ドリフト修正 |
| 状況 | 既存インフラをTerraformに編入 | 管理中のインフラが外部で変更された |
| State状態 | 該当リソースのStateなし | Stateと実際のインフラの不一致 |
| apply前作業 | terraform importまたはimportブロック | terraform plan -refresh-only |
| apply目的 | コードとStateの同期 | コード基準への復元またはコード更新後の同期 |
| 危険度 | 高 (誤って削除/再生成の可能性) | 中 (意図しない変更が適用される可能性) |
結論: どちらもapplyを使う。しかし、手順が異なる。
terraform applyは単なるツールです。マイグレーションであれドリフト修正であれ、核心はapply前にplanを見て、どのような変更が起こるのかを理解した上で実行することです。Terraformの真の威力はapplyコマンドの一行ではなく、その前に実行されるplanとrefreshから生まれます。

コメントを残す