インフラストラクチャドリフトに潜むリスク
クラウドインフラを管理したことがある人なら、午前3時に感じるあの絶望感を知っているでしょう。何週間もかけてTerraformモジュールを完成させ、すべてのセキュリティグループやS3バケットをコードで定義したはずでした。しかし、本番環境で障害が発生します。チームメイトがAWSコンソールにログインし、サービスを復旧させるために手動でセキュリティグループにインバウンドルールを追加し、翌朝そのコードを更新するのを忘れてしまうのです。
これがインフラストラクチャドリフト(Infrastructure Drift)です。これはDevOpsチームにとって永続的な悩みの種です。時間が経つにつれて、実際の環境とGitリポジトリの内容が一致しなくなっていきます。2週間後にようやくterraform applyを実行したとき、重要な手動修正を誤って上書きしてしまうかもしれません。さらに悪いことに、勝手な開発者が高価なp3.16xlargeインスタンスを起動し、1時間あたり24ドルを垂れ流していることに気づかない可能性もあります。
私はいくつかの本番環境でドリフト検出を導入してきました。その効果はすぐに現れました。Driftctlを使用することで、場当たり的な「火消し」のような対応から、プロアクティブなガバナンスへと移行することができたのです。
なぜ Terraform Plan だけでは不十分なのか
よくある間違いは、terraform planですべてを把握できると思い込むことです。実際にはそうではありません。Terraformは、自身のステート(状態)ファイルにリストされているリソースのみを追跡します。ユーザーがコンソールから新しいRDSデータベースやIAMユーザーを手動で作成した場合、terraform planはそれを完全に無視します。このツールは、自身が作成していないリソースに対する可視性を持っていないのです。
Driftctlはこの可視性のギャップを埋めてくれます。AWS、Azure、GCPなどのクラウドプロバイダーをスキャンし、検出されたすべてのリソースをTerraformのステートと比較します。そして、結果を以下の3つのバケットに分類します。
- Managed(管理対象): コード内に存在し、クラウドの状態と完全に一致しているリソース。
- Drifted(ドリフト発生): コード内に存在するが、誰かが手動で変更を加えたリソース。
- Unmanaged(非管理対象): クラウド上には存在するが、コードには記述されていないリソース。通常、セキュリティの脆弱性はここに潜んでいます。
Driftctlのセットアップ
DriftctlはGoベースのバイナリであるため、インストールは非常に簡単です。macOSまたはLinuxでHomebrewを使用している場合は、次のコマンドを実行します。
brew install driftctl
CI/CDパイプラインの場合は、Snyk/DriftctlのGitHubリリースページからバイナリを直接取得してください。このツールは既存のクラウド認証情報を使用します。AWS CLIがすでにプロファイルで設定されている場合、Driftctlは追加の設定なしで自動的にそれを使用します。
インフラストラクチャのスキャン
driftctl scanコマンドが主要なツールとなります。正確な結果を得るには、通常S3バケットに保存されているリモートステートファイルをツールに指定します。
# AWSをスキャンし、リモートのS3ステートファイルと比較する
driftctl scan --from tfstate+s3://my-terraform-state-bucket/project/terraform.tfstate
最初のスキャンは、多くの場合、衝撃的な事実を突きつけます。あるプロジェクトでは、最初のレポートで42個の非管理対象リソースが見つかりました。これには、忘れ去られたデフォルトのVPC、2021年の移行時の古いIAMロール、そしてまだ稼働したままの実験的なLambda関数などが含まれていました。
.driftignore によるノイズのフィルタリング
すべての非管理対象リソースに注意を払う必要があるわけではありません。レガシーシステムや、Kubernetesなどの他のツールで管理されているリソースがあるかもしれません。レポートを整理するために、プロジェクトのルートに.driftignoreファイルを作成しましょう。
# .driftignore
# 管理対象外のデフォルトAWSリソースをすべて無視
aws_default_vpc.*
aws_default_security_group.*
# データチームが使用している特定のレガシーバケットを無視
aws_s3_bucket.legacy-archive-2020
# 特定のタグを持つリソースを無視
*::tags.Environment: development
成功の鍵は、意味のあるアラートを出すことです。ツールが毎日100件の誤検知を報告するようでは、チームはいずれログをチェックしなくなるでしょう。
CI/CDでの検出の自動化
手動スキャンも何もしないよりはマシですが、自動化こそが真のセキュリティを提供します。4時間ごとにドリフトスキャンをスケジュールすることをお勧めします。この頻度であれば、たとえ数日間Terraformのコードを誰も触っていなくても、コンソールでの変更を素早くキャッチできます。
以下は、自動スキャンのための簡略化されたGitHub Actionsワークフローです。
name: インフラストラクチャドリフト検出
on:
schedule:
- cron: '0 */4 * * *'
workflow_dispatch:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: コードのチェックアウト
uses: actions/checkout@v3
- name: Driftctlのインストール
run: |
curl -L https://github.com/snyk/driftctl/releases/latest/download/driftctl_linux_amd64 -o driftctl
chmod +x driftctl
sudo mv driftctl /usr/local/bin/
- name: Driftctlスキャンの実行
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "us-east-1"
run: |
driftctl scan --from tfstate+s3://my-prod-bucket/terraform.tfstate --output json://drift-report.json
- name: ドリフト検出時にアラートを送信
if: failure()
run: |
echo "本番環境でドリフトが検出されました!Slackにサマリーを送信中..."
# ここにSlackのWebhookやSNS通知のロジックを挿入
Driftctlは不一致を見つけると、ゼロ以外の終了コードを返します。この動作により、if: failure()条件を簡単にトリガーし、チームに即座に通知することが可能になります。
現場から学んだ教訓
複数のAWSアカウントでDriftctlを運用してきた経験から、運用を改善するためのいくつかのプラクティスを特定しました。
- 小さく始める: 初日からAWS Organization全体をスキャンするのは避けましょう。まずは単一のステートファイルや特定のリージョンに焦点を当てます。その範囲のノイズをクリーンアップしてから、スコープを広げてください。
- タグ付けの標準化: すべてのリソースで一貫したタグを使用します。Driftctlはタグでフィルタリングできるため、共有リソースやサードパーティベンダーが管理するアセットを除外するのに役立ちます。
- 可視性を優先する: CI/CDのログに埋もれたレポートは役に立ちません。JSON出力を利用して、SlackやPagerDutyにサマリーをプッシュしましょう。手動編集から数時間以内に何が変わったかを正確に把握できることは、大きなアドバンテージになります。
- 「ドリフト修正」ポリシーを徹底する: ドリフトが発生したときの選択肢は2つです。新しい現実に合わせてTerraformコードを更新するか、手動の変更を元に戻すかです。ドリフトを24時間以上放置してはいけません。
「信頼できる唯一の情報源」の徹底
Infrastructure as Code(IaC)は、コードが本番環境を正確に反映している場合にのみ価値を発揮します。Driftctlのようなツールがなければ、Terraformのマニフェストは確定的な記録ではなく、単なる「提案」に過ぎなくなってしまいます。自動スキャンは、手動の変更が可視化され、抑制される「コードファースト」の文化を定着させます。
最初の検出結果をクリーンアップするには労力が必要ですが、それによって得られる安心感にはそれだけの価値があります。「幽霊」のようなリソースが請求額を膨らませるのを防ぐことができます。さらに重要なのは、コードに完全に依存している災害復旧計画が、最も必要とされる時に実際に機能することを保証できるのです。

