Skip to main content

インフラのドリフト検出ツール

著者
Headshot of William Beuil

William Beuil

feature iac drift blue

2022年3月15日

0 分で読めます

廃止のお知らせ:管理対象リソースのドリフト検出

snyk iac describe --only-managed and snyk iac describe --driftを含む、管理対象リソースのドリフト検出は廃止されました。管理対象リソースのドリフト検出の提供終了日は2023年9月30日です。

インフラのドリフトを予測するのは、冬の降雪を予測するようなものです。いつかは起きると分かっていても、正確な時期は予測できません。雪と同じように、できるだけ早く検出する方法があれば、万全の備えができ、インフラの安全性も高まります。

この記事では、ドリフト検出の基本、ドリフトの種類と発生する理由、そして簡単な例を使って検出に役立つツールを紹介します。

ドリフト検出とは?

ドリフト検出ツールが必要な理由を理解するには、まずインフラのドリフトについて知る必要があります。簡単に言えば、インフラ全体が構成ファイルから逸脱している状態です。

この記事では、クラウドリソースのデプロイに使うIaC(Infrastructure as Code)ツールとして、HashiCorp Terraformを取り上げます。Terraformでは、stateファイルとクラウドプロバイダーに適用した内容が異なる状態を、インフラのドリフト、つまりインフラの逸脱と呼びます。

こうしたドリフトを検出する適切なツールを使えば、セキュリティ態勢を大幅に強化できます。たとえば、Terraformを使わずに誰かがセキュリティグループを手動で変更したときにアラートが届くとしたらどうでしょう。Terraformの管理外で新しいリソースが作成されたときにも通知があれば便利です。

管理対象リソースと非管理対象リソース

先ほどの理想的な環境を例に、ドリフトを次の2種類に分けて説明します。

IaCで管理されているリソースのドリフト

クラウドプロバイダーに適用・デプロイしたすべてのリソースについて、構成ファイルとstateファイルがあるため、IaCツールは、ツール外で行われた変更や、まだ適用されていない変更を検出するのに適しています。

IaCで管理されていないリソースのドリフト

一方、この種のドリフトは簡単には検出できません。そもそも構成ファイルやstateファイルに、該当するリソースが定義されていないためです。

ドリフトが発生する理由

ドリフトはさまざまな理由で発生しますが、HashiCorpの説明が最も分かりやすいでしょう。

「構成のコンテキスト内では、リソースの追加や削除、またはリソース定義の変更によって発生します。構成の外部では、リソースが終了または障害を起こした場合や、手動または他の自動化ツールによって変更が加えられた場合にドリフトが発生します。」

HashiCorpHashiCorp

Christie Koehler

Developer Advocate, HashiCorp

2種類のドリフトの実例と、その影響を見てみましょう。管理対象リソースのドリフトは、Terraformで構成したAmazon S3バケットのバージョニング設定を誰かが手動で変更するケースです。非管理対象リソースのドリフトは、Terraformの外部(例:AWSコンソール)で誰かがS3バケットを手動で追加するケースです。(手動変更によるドリフトの管理方法については、以前の記事をご覧ください。)

ドリフト検出に役立つツールは?

ここからは、管理対象と非管理対象のリソースのドリフトを検出するツールを見ていきましょう。この記事の以降では、先ほど紹介した簡単なTerraformの例を使います。

resource "aws_s3_bucket" "example" {
        bucket = "drift-example-managed-resource"
        versioning {
                enabled = true
        }
}

コンソールでバージョニング属性をfalseに変更し、ドリフトを発生させます。さらに、AWSコンソールで同じS3バケットを手動で作成します。

AWS S3バケットのコンソール。米国西部(オレゴン)リージョンにある、管理対象と管理対象外のバケットの例が表示され、どちらもプライベートとしてマークされています。

それでは、ドリフト管理に使える3つのツールを見てみましょう。

  1. terraform plan

  2. CloudQuery

  3. driftctl

1. terraform plan

terraform planコマンドは、希望する構成を実現するために適用すべき内容を簡潔に示します。実際のインフラに対して、このコマンドを実行した結果を見てみましょう。

$ terraform plan

aws_s3_bucket.example: Refreshing state... [id=drift-example-managed-resource]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # aws_s3_bucket.example has changed
  ~ resource "aws_s3_bucket" "example" {
        id                          = "drift-example-managed-resource"
        # (10 unchanged attributes hidden)

      ~ versioning {
          ~ enabled    = true -> false
            # (1 unchanged attribute hidden)
        }
    }

Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes.

────────────────────────────────────────────────────────────
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_s3_bucket.example will be updated in-place
  ~ resource "aws_s3_bucket" "example" {
        id                          = "drift-example-managed-resource"
        # (10 unchanged attributes hidden)

      ~ versioning {
          ~ enabled    = false -> true
            # (1 unchanged attribute hidden)
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.

────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

Terraformは、管理対象リソースにドリフトがあることを検出し、変更内容(例:バージョニングがtrueからfalseに変更されたこと)を示しました。続いて、planを適用した場合に行われる処理(例:バージョニングをtrueに戻すこと)を出力します。

メリット:

  • 管理対象リソースのドリフトを一貫した方法で検出できる

  • すべてのTerraformリソースに対応

デメリット:

  • 非管理対象リソースのドリフトは検出できない

  • 複数のstateファイルを対象にplanを実行できない

2. CloudQuery

CloudQueryは、SQLを使ったオープンソースのクラウド資産インベントリツールです。基本的には、指定したクラウドプロバイダーからすべてのリソースを抽出して整形し、PostgreSQLに読み込みます。その上にドリフト検出コマンドを構築し、「ドリフトの問題をデータの問題に変える」ことを目指しています。CloudQueryの説明によると、そうした取り組みです。

CloudQuery CLIをインストールしたら、S3バケットのドリフト検出に戻りましょう。

$ cloudquery fetch

Initializing CloudQuery Providers...

✓ cq-provider-aws@v0.10.10 verified    0s  100 %

Finished provider initialization...

Upgrading CloudQuery providers aws

✓ Upgraded provider aws to latest successfully.

Finished upgrading providers...

Starting provider fetch...

✓ cq-provider-aws@latest fetch complete   15s   Finished Resources: 129/129

Provider fetch complete.

Provider aws fetch summary: ✓ Total Resources fetched: 523 ⚠️ Warnings: 0 ❌ Errors: 0

前述のとおり、最初のコマンドはクラウドプロバイダーからすべてのリソースを取得し、PostgreSQLのテーブルに格納します。

管理対象および管理対象外のリソースを示すダークテーマのデータベーステーブル。バージョン管理のステータスとして「一時停止」と「有効」が表示されています。
$ cloudquery drift scan --deep --debug terraform.tfstate

Initializing CloudQuery Providers...

⌛cq-provider-aws@v0.10.11 downloading...                           4s  100 %

Finished provider initialization...

Using profile drift-example
Starting module...
DIFF RESOURCE: s3.buckets:drift-example-managed-resource
+------------------------------------------+-----------+---------------+-------------------------+
|                 AWS EXPR                 |  AWS VAL  | TERRAFORM VAL |     TERRAFORM EXPR      |
+------------------------------------------+-----------+---------------+-------------------------+
| COALESCE("c"."versioning_status",'')     | Suspended | <nil>         | versioning_status       |
| COALESCE("c"."versioning_mfa_delete",'') | Disabled  | <nil>         | versioning_mfa_delete   |
| "c"."block_public_acls"                  | true      | <nil>         | block_public_acls       |
| "c"."block_public_policy"                | true      | <nil>         | block_public_policy     |
| "c"."ignore_public_acls"                 | true      | <nil>         | ignore_public_acls      |
| "c"."restrict_public_buckets"            | true      | <nil>         | restrict_public_buckets |
+------------------------------------------+-----------+---------------+-------------------------+
Matching attributes "region", "logging_target_prefix", "logging_target_bucket", "policy", "tags", "replication_role", "arn", "ownership_controls"
+-----------------------+---------------------------------------------------------+
|       ATTRIBUTE       |                     MATCHING VALUE                      |
+-----------------------+---------------------------------------------------------+
| region                | us-west-2                                               |
| logging_target_prefix |                                                         |
| logging_target_bucket |                                                         |
| policy                | <nil>                                                   |
| replication_role      |                                                         |
| arn                   | arn:aws:s3:::drift-example-managed-resource             |
| ownership_controls    | <nil>                                                   |
+-----------------------+---------------------------------------------------------+
Module output:
=== DRIFT RESULTS  ===
1 Resources not managed by Terraform
  aws:s3.buckets:
    - drift-example-un-managed-resource
1 Resources managed by Terraform but drifted
  aws:s3.buckets:
    - drift-example-managed-resource
=== SUMMARY ===
Total number of resources: 2
 - 1 not managed by Terraform
 - 1 managed by Terraform but drifted
 - 50% covered by Terraform
Finished module

この出力は興味深い結果です。管理対象と非管理対象の両方のS3バケットが見つかりました。さらに、管理対象のバケットにドリフトがあることも検出されましたが、分析結果は少し不自然です。AWS上ではS3バケットのバージョニングが無効とされる一方、stateファイル上では不明と判定されています。Terraformの属性の確認方法にバグがあるのかもしれません。また、ほかの属性にもドリフトが検出されたため、ドリフトしていない管理対象バケットの場合にどうなるのか気になります。

terraform applyでバケットの最初の構成を再適用し、改めてcloudquery fetchとcloudquery drift scan --deep --debug terraform.tfstateを実行しました。

$ terraform plan

aws_s3_bucket.example: Refreshing state... [id=drift-example-managed-resource]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

$ cloudquery drift scan --deep --debug terraform.tfstate

...

DIFF RESOURCE: s3.buckets:drift-example-managed-resource
+------------------------------------------+----------+---------------+-------------------------+
|                 AWS EXPR                 | AWS VAL  | TERRAFORM VAL |     TERRAFORM EXPR      |
+------------------------------------------+----------+---------------+-------------------------+
| COALESCE("c"."versioning_status",'')     | Enabled  | <nil>         | versioning_status       |
| COALESCE("c"."versioning_mfa_delete",'') | Disabled | <nil>         | versioning_mfa_delete   |
| "c"."block_public_acls"                  | true     | <nil>         | block_public_acls       |
| "c"."block_public_policy"                | true     | <nil>         | block_public_policy     |
| "c"."ignore_public_acls"                 | true     | <nil>         | ignore_public_acls      |
| "c"."restrict_public_buckets"            | true     | <nil>         | restrict_public_buckets |
+------------------------------------------+----------+---------------+-------------------------+
…

CloudQueryは引き続きバケットにドリフトがあると判定し、データテーブル上のバージョニング状態を変更しました。しかし、AWSコンソールではバケットのバージョニングが有効になっているため、これは明らかな誤検知であり、考慮すべきではありません。

メリット:

  • fetchコマンドによるクラウドリソースの列挙が非常に高速

  • シンプルなdrift scanコマンドで非管理対象リソースを検出

  • 複数のstateファイルのスキャンに対応

デメリット:

  • drift scan --deep --debugコマンドでは、S3バケットのドリフト属性の出力が不確実

  • stateファイルの保存先として対応しているバックエンドが少ない(例:S3とローカルのみ)

  • すべてのTerraformリソースに対応していない

  • SQLデータベースが必要

3. driftctl

driftctlは、インフラのドリフトを警告する無料のオープンソースCLIツールです。管理対象と非管理対象の両方のドリフトを検出、追跡し、アラートで通知できます。例を使って試してみましょう。注:driftctlはSnyk独自のオープンソースのドリフト検出エンジンです。

$ driftctl scan --deep

Scanned states (1)
Found resources not covered by IaC:
  aws_s3_bucket:
    - drift-example-un-managed-resource
Found changed resources:
  From tfstate://terraform.tfstate
    - drift-example-managed-resource (aws_s3_bucket.example):
        ~ versioning.0.enabled: true => false
Found 2 resource(s)
 - 50% coverage
 - 1 resource(s) managed by Terraform
     - 1/1 resource(s) out of sync with Terraform state
 - 1 resource(s) not managed by Terraform
 - 0 resource(s) found in a Terraform state but missing on the cloud provider
Scan duration: 17s
Provider version used to scan: 3.74.3. Use --tf-provider-version to use another version.

この出力から、driftctlが2つのリソースを検出したことが分かります。管理対象リソースではバージョニング状態の属性にドリフトが見つかり、もう一方では非管理対象のS3バケットが見つかりました。

メリット:

  • driftctl scan --deep commandで管理対象リソースのドリフトした属性を検出

  • driftctl scan commandで非管理対象リソースのドリフトを検出

  • 複数のstateファイルのスキャンに対応

デメリット:

  • --deepモードではアカウント内のすべてのリソースを列挙し、それぞれの詳細を取得するため、スキャンに非常に時間がかかる場合がある

  • すべての情報を収集するためにクラウドプロバイダーのAPIに大きく依存しており、スキャン中にAPIのスロットリングエラーが発生する

  • すべてのTerraformリソースに対応していない

ドリフト検出ツールを選ぶ際のポイント

ここで紹介した3つのツールはいずれも優れています。それぞれが特に力を発揮する状況をまとめましょう。

terraform plan コマンドは非常に強力で、すべてのstateファイルを対象とする定期実行パイプラインに組み込むのに適しています。すべてのリソースをカバーできるだけでなく、ドリフトした属性を読みやすい共通の形式で提示できます。ただし、複数のstateファイルをまとめて一か所でドリフトを確認することはできません。非管理対象リソースの報告はこのツールの役割ではありませんが、重要な機能です。

CloudQueryのコマンドラインツールは、評価が分かれます。一方で、リソースを列挙するコマンド(fetch)は非常に優れた技術です。無料のオープンソースツールを使って、SQLをクエリエンジン兼ポリシーエンジンとして、クラウドインフラを深く可視化できます。一方、ドリフト検出コマンドにはかなり制限があります。私のテストでは、管理対象リソースのドリフト検出を頼りにできませんでした。また、stateファイルにローカルからアクセスできる(主にツールのテスト時)か、S3バケット経由でアクセスできる(CIでのテストに推奨)という単純なケースでなければ、非管理対象リソースの検出にも使えません。幸い、ドリフト検出コマンドはまだアルファ版です。今後の成長が楽しみです。

driftctlをCIに組み込み、毎日実行すれば、管理対象と非管理対象のリソースに生じたドリフトを継続的に通知できます。stateファイルを保存するバックエンドの対応範囲が広く、ほとんどのインフラに適しています。また、CloudQueryと同じように、関心のないリソースをフィルターしたり無視したりして、必要に応じたレポートを作成できます。driftctlの設計上の重大な弱点は、インフラの規模が大きいとAPIスロットリングの影響でスムーズに実行できない場合があることです。もう一点、--deepモードの実行時間は、大規模なインフラをローカルでテストする際に煩わしくなることがあります。CIパイプラインではそれほど問題になりません。

最後に、権限についてです。driftctlもCloudQueryもTerraformコードを必要とせず、stateファイルだけで動作するため、導入が比較的簡単です。また、クラウドプロバイダー全体をスキャンする際に最小権限の原則を守れるため、どちらのコマンドも読み取り専用ポリシーで実行できます。一方、Terraformのコマンドはインフラの作成、更新、削除に読み書き権限が必要です。さらに、デプロイまたは削除する内容を把握するためにコードも読み取る必要があります。

Snyk IaCの今後

非管理対象リソースをIaCの管理下に置くとともに、管理対象リソースのドリフトを検出したい場合は、Snykがその両方を実現します。Snyk IaCのドリフト管理は、開発者に分かりやすい言葉で問題と修正方法を直接伝え、インフラをより迅速に保護します。クラウドセキュリティチームと開発チームのフィードバックループを短縮することで、開発者はコードからクラウドまでTerraformを一貫して管理し、デプロイ後のインフラ構成も安全に保てます。さらに、クラウド環境全体の非管理対象リソースを可視化することで、それらをIaCの管理下に置き、ドリフトのリスクを最初から低減できます。

ドリフト管理に関するその他のリソース

Terraformのドリフト管理についてさらに学びたい方は、ブログの関連記事もご覧ください。

ソースコードの段階からインフラを保護

Snykはワークフロー内のIaCセキュリティとコンプライアンスを自動化し、構成ドリフトや不足しているリソースを検出します。