Terraformを使うインフラ構築では、プロバイダやモジュールのバージョンを「固定するか追従するか」という判断を避けて通れない。バージョン制約を設けないまま運用すると、ある日突然planが通らなくなり、原因特定に工数を費やす事態が起きる。一方で固定しすぎると、EOLに気づかないまま技術的負債を積み上げることになる。どこに線を引き、どう設計すれば安定と進化を両立できるか——その判断基準をここで整理する。
固定せず運用して「昨日通ったplan」が壊れる構造
バージョン未固定が引き起こす再現性の崩壊
バージョン制約を指定しない状態でterraform initを実行すると、その時点でレジストリに存在する最新バージョンが取得される。開発者ごとにプロバイダのバージョンが異なる状況が生まれ、同じコードでも挙動が変わることがある。3人チームでの構築プロジェクトにおいて、それぞれのローカルで異なるバージョンを使い始め、planの差分が一致しない問題に気づいたのが数週間後だったという話は現場でよく聞く。しかもこの種の問題は、差分ゼロのはずのリソースに「変更あり」と出る形で現れるため、設定ミスとの区別がつかず調査に時間がかかりやすい。
マイナーアップデートでAPIが変わる問題
プロバイダのバージョンは、セマンティックバージョニング上はマイナーアップデートでも、非推奨属性の削除や引数仕様の変更が含まれることがある。AWSプロバイダが4.x系から5.x系に移行した際、複数の引数名変更と削除が発生し、既存の設定ファイルが大量の警告を出すようになったのがその典型だ。バージョンを固定していない環境では、このような変化が予告なく適用される。プロバイダの更新がCIで自動取得される設定のままだと、月曜日に通ったパイプラインが火曜日に失敗するという状況が起きうる。
CIパイプラインへの影響と見落とされがちな前提
CIパイプラインでは、ジョブ実行のたびにinitを走らせることが一般的だ。バージョンを固定しないまま運用している場合、プロバイダの新バージョンがリリースされた直後に既存のplanが壊れ、原因特定に半日から1日を要することもある。この種のトラブルは構築の設計段階で防げるものであり、後から「なぜ固定していなかったのか」という振り返りになりやすい。lockファイルを使わず、required_providersも書いていない環境では、CI実行のたびに異なるバージョンが取得される可能性を常に抱えることになる。
固定すべき対象(provider・モジュール・CLI)の判断
プロバイダバージョンの固定粒度
required_providersブロックでのバージョン指定は、~>演算子を使ったパッチ追従か、=演算子による完全固定かで運用の性格が大きく変わる。本番環境と複数のステージング環境を20環境以上同一コードベースで管理している場合、プロバイダの完全固定が最も安全な選択になる。全環境で同じプロバイダを使うことが保証されるため、「ステージングでは通ったが本番でだけ失敗する」という状況を回避できる。一方、開発専用の隔離環境であれば上限付き指定でもリスクは許容範囲に収まることが多い。環境の役割と規模に応じて固定粒度を分けることが、設計上の合理的な判断だ。
社内モジュールと外部モジュールの扱い分け
Terraformのモジュールには、社内で管理するプライベートモジュールと、Terraform Registryなどから参照する外部モジュールがある。社内モジュールはバージョニングを自分たちでコントロールできるため、GitタグによるバージョンピンかSHAによる固定が現実的だ。外部モジュールはメンテナの意図しないタイミングで変更される可能性があるため、バージョン制約を厳格にしておく必要がある。外部モジュールに対してバージョン制約を設けていない場合、モジュールの破壊的変更がそのまま自分たちの構成に流れ込むリスクを常に背負うことになる。モジュールの種類と管理主体によって固定戦略を分けることが、設計の基本方針だ。
Terraform CLI本体のバージョン管理
Terraform CLIのバージョンについては、required_versionブロックで明示的に制約を設けることが推奨される。tfenvやasdfを活用してチーム全員のローカルとCI実行環境を揃えることが、再現性を保つ最短ルートだ。CLIのメジャーバージョン変更はstateファイルのフォーマット変化を伴うことがあるため、特に慎重に扱う必要がある。Terraform本体のバージョンとプロバイダバージョンの組み合わせに互換性があるかを、アップグレード前に確認する習慣をプロセスに組み込むことも重要だ。「CLIだけ最新にしたがプロバイダは古いまま」という組み合わせで大量のwarningが出るケースは珍しくない。
完全固定・上限指定・固定なし——3パターンの比較
比較の前提——環境の役割でパターンを変える
バージョン管理のパターンを選ぶ前に、対象環境の役割を整理しておく必要がある。本番・共有ステージング・複数チームが参照するモジュールは「再現性が最優先」の環境であり、完全固定が基本だ。開発・個人検証・使い捨て環境は速度を優先しうる環境であり、上限指定が許容されることもある。ただし、固定なしを「デフォルト」として使い始め、あとから本番環境に流用してしまうケースが最もリスクが高い。パターン選択は環境ごとに意図的に行うことが前提になる。
3パターン比較表
| パターン | 記述例 | 再現性 | 更新コスト | EOLリスク | 主な適用場面 |
|---|---|---|---|---|---|
| 完全固定 | version = "= 5.30.0" |
最高(全環境一致を保証) | 高(手動更新が必要) | 放置すると急増する | 本番・共有ステージング・20環境以上の大規模管理 |
| 上限指定(パッチ追従) | version = "~> 5.30" |
中(パッチ範囲内で変動) | 中(メジャー/マイナーは手動) | 中程度 | 開発環境・小規模チーム・モジュール側の制約記述 |
| 固定なし(下限のみ) | version = ">= 5.0" |
低(init毎に変動の可能性) | 低(更新作業不要) | 最も高い | 原則非推奨。個人学習・使い捨て環境に限定 |
表から読み取るべき設計の核心
固定なしは「更新コストが低い」という見せかけの利点がある。しかし実際には再現性の崩壊とEOLリスクという見えないコストを後ろに積み上げており、トラブルが顕在化したときの対応工数を考えると、コスト優位はほぼ存在しない。本番構成には完全固定を起点として判断し、環境ごとにパターンを意図的に決める——これが設計上の基本姿勢になる。なお、上限指定を選んだ場合でも、lockファイルと組み合わせることで実質的な固定効果を得られる点は押さえておきたい。
lockファイルをどう扱い、いつ更新するか
.terraform.lock.hclの役割と誤解
.terraform.lock.hclは、terraform initが解決したプロバイダのバージョンとそのハッシュを記録するファイルだ。required_providersの制約内で最初に取得されたバージョンを以降のinitでも使い続けることを保証する仕組みであり、制約の「幅」を実質ゼロに絞る役割を担う。この仕組みを誤解して「required_providersにバージョン制約を書いたから固定できている」と思い込み、lockファイルを.gitignoreに追加しているケースが散見される。その場合、制約範囲内であれば毎回別バージョンが取得される可能性があり、固定の意図が実質的に崩れる。lockファイルとrequired_providersは両輪として機能するものであり、片方だけでは不完全だと理解することが重要だ。
lockファイルをGit管理に含める理由
lockファイルをバージョン管理に含めることで、チーム全員とすべてのCIランナーが同一バージョンのプロバイダを使う状態になる。コードレビューの対象にもなるため、意図しないプロバイダのアップグレードが混入したときにdiffで検知できる。lockファイルのdiffが大きい場合は、それ自体がレビューの注意点となり、「誰かがバージョンを上げたこと」を明示的に確認するきっかけになる。逆にlockファイルがGit管理外であれば、アップグレードが静かに混入しても誰も気づかないまま本番に到達するリスクがある。
lockファイルの更新タイミングと方針
lockファイルの更新は明示的なコマンドで実行するが、アドホックに行うのではなく、定期メンテナンス枠の中で計画的に進めることが設計上の正しい判断だ。緊急のセキュリティパッチ対応を除き、スプリント単位(2週間〜1ヶ月)でまとめて処理するサイクルを組むことで、更新の影響範囲を制御できる。lockファイルを更新する際は専用ブランチを切り、全ステージング環境でのplan確認を経てからマージするフローを守ることが重要だ。更新の記録がコミットログに残ることで、「いつ・誰が・何を更新したか」の追跡も可能になる。
アップグレードを定期作業として回す設計
定期更新を「例外作業」にしない構造
バージョン更新を例外的な作業として扱うと、担当者が変わったタイミングで引き継ぎ漏れが発生し、気づけば1年以上放置されているという状況になる。これを防ぐには、定期更新をスプリントの定例タスクとして組み込み、工数見積もりに含めることが重要だ。規模感として、プロバイダ3〜5個のプロジェクトであれば月次1〜2時間程度の工数で維持できることが多い。この工数を見積もりに織り込んでいないプロジェクトでは、更新タスクが「緊急ではないが重要」のまま積み残され、メジャーバージョンを2〜3世代飛ばすアップグレードが必要になる事態に陥りやすい。
更新の影響範囲を限定するブランチ戦略
プロバイダのアップグレードは専用ブランチで行い、全ステージング環境でのplan確認を経てからmainにマージするフローが安全だ。特に本番環境のstateを持つワークスペースでは、plan差分ゼロを確認してからapplyするルールを徹底することで、更新作業のリスクを最小化できる。プロバイダのアップグレードとアプリケーション側の変更を同一PRに混在させないことも、影響範囲を明確に保つために重要な設計判断だ。変更の種類を分けることで、問題が発生したときにどちらが原因かを切り分けやすくなる。
CIでのバージョンドリフト検知
スケジュール実行のCIジョブとして、現在のlockファイルのバージョンと最新リリースの差分を検知するチェックを組み込む方法がある。差分があればSlackやissueに通知する仕組みを作ることで、更新時期の見落としを防げる。これは更新を強制するものではなく、「認知する仕組み」として位置づけることがポイントだ。更新の判断は人間が行う——そのための情報を自動的に届ける設計を持つことで、定期更新の習慣が形骸化しにくくなる。週次や月次でこのチェックが走る構成にしておくと、ドリフトの蓄積を早期に把握できる。
固定したまま放置してEOLに追い込まれる境界
プロバイダのサポートポリシーと終了サインの見方
HashiCorpはTerraformプロバイダのサポートポリシーを公式ドキュメントで示しているが、実際にはリポジトリのCHANGELOGやGitHub Issuesでdeprecation noticeを追うことが現場の実態に即している。プロバイダが2メジャーバージョン以上古くなると、サポートされているTerraform CLI本体との組み合わせが保証されなくなるケースが出てくる。このような状態を放置すると、CLIをアップグレードしようとした瞬間にプロバイダが動かなくなり、CLIとプロバイダの両方を同時に更新せざるを得ない状況に追い込まれる。
「動いているから問題ない」の落とし穴
バージョンが古くても既存のstateやapplyが動き続けているように見える期間がある。しかし内部APIのdeprecationは静かに進行しており、あるバージョン以降でwarningがerrorに変わるアップデートが出た時点で初めて問題が表面化する。構築を任されたある担当者は、半年前から警告は出ていたものの他の作業が立て込んでいたため対応が後回しになり、突然planが通らなくなる事態に直面したという。バージョン固定と定期更新の設計が最初からなければ、この種のリスクは蓄積する一方だ。
EOL到達前のマイグレーション判断基準
EOLへの対応を先送りしていると、最終的には複数のメジャーバージョンをまとめて飛び越えるアップグレードが必要になる。このような大幅ジャンプは、APIの互換性破壊が複数重なり、テスト・修正コストが乗数的に増える。バージョンを段階的に上げていれば各ステップの影響を小さく抑えられたのに、まとめて飛び越えたために大規模な修正作業になったという話はよく聞く。EOLラインが視野に入ってきた段階で「次の定期メンテで対応」ではなく、専用のアップグレードプロジェクトとして工数を確保し、テスト環境から順に段階的に進める計画を立てることが、実装層に求められる設計判断だ。
バージョン管理設計チェックフレーム
プロジェクト初期に決める項目
- required_providersに全プロバイダのバージョン制約を明示しているか(バージョン未指定のプロバイダがゼロであること)
- Terraform CLI本体のrequired_versionを設定し、チーム全員のローカルとCIが同一バージョンで動いているか
- .terraform.lock.hclをGitの管理対象に含めているか(.gitignoreに入れていないか)
- 社内モジュールと外部モジュールでバージョン固定戦略を分けているか
- プロバイダの固定粒度(完全固定・上限指定)を環境ごとに意図的に決定しているか
継続運用フェーズで確認する項目
- バージョン更新作業をスプリントの定例タスクとして工数に含めているか
- lockファイルの更新が専用ブランチ・全ステージングのplan確認を経てからマージされているか
- CIでバージョンドリフトの検知チェックが定期実行されているか
- プロバイダ更新とアプリケーション変更が同一PRに混在していないか
- lockファイルの変更がコードレビューの対象になっているか
メジャーバージョンアップ前の確認項目
- アップグレード対象のプロバイダのCHANGELOGにbreaking changeが含まれているかを確認しているか
- Terraform CLI本体との互換性マトリクスを確認しているか
- テスト環境→ステージング→本番という段階的適用のフローを設計しているか
- ロールバック手順(stateのバックアップと旧バージョンへの戻し手順)を事前に準備しているか
- 現在のバージョンがEOLまでどれくらいの猶予があるかを把握しているか
バージョン管理の設計は、一度決めたら終わりではなく、定期的に見直すことで効力を保つ。チェックフレームをドキュメント化してオンボーディング資料に組み込むことで、担当者が変わっても設計の意図が引き継がれやすくなる。手を動かす具体的な操作手順については各セミナーや公式ドキュメントに譲り、ここで示したのは「何を決め、何を確認し続けるか」という設計の軸だ。
▶ 手を動かして本番まで到達する
設計の型は掴めても、本番でTerraformを通しきる経験は別物です
ここまでの設計判断は、読めば腹に落ちます。ですが「自分の手で本番まで通しきる自信」は、それとは別の話になります。
2日間の集中ハンズオンで、AWS上にIaCで構築しきる経験を持ち帰れます。設計判断を、動くコードと本番環境の手応えに変える場です。
providerバージョン管理をlockファイルで実装するTerraformコースへ →
Terraformマスターセミナー|2日間ハンズオンでAWS上にIaCで本番構築