Snyk IaCでTerraformのドリフト検出をやってみた

Terraformのドリフト検出悩んでませんか?Snyk IaCではセキュリティ検知以外にドリフト検出もできます。
2022.08.24

こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。

今回は、snyk iac describeコマンドを利用して、Terraformで作ったリソースのドリフト検出をしてみようと思います。

Snyk IaC describeコマンドとは

snyk iac describeでは次の2つを検出できるコマンドです。Terraformのtfstate内のリソースとクラウドプロバイダーの実際のリソースを比較して検出するため、CloudFormationで書かれたファイルには使えません。

  1. ドリフト(現在の状態とtfstateとの差異を検出)
  2. 該当リソースがIaC(Terraform)で管理されているかどうかを検出

今回は1つめのドリフトを検出してみようと思います。

やってみた

今回は次のコードを使用してドリフト検出を試してみようと思います。

注目いただきたいのが、「enable_dns_hostnames」と「enable_dns_support」です。「enable_dns_hostnames」はデフォルトがfalseのため、コードでtrueに変更しました。対して、「enable_dns_support」はデフォルトでtrueのため、設定を省略したケースを想定しています。(コードは便宜上コメントアウトで表現しています。)

snyk_iac/vpc.tf

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "4.27.0"
    }
  }
  backend "s3" {
    bucket = "tf-takakuni"
    key    = "snyk_iac/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_vpc" "vpc" {

  cidr_block = "10.0.0.0/16"
  enable_dns_hostnames = true # default:false
  # enable_dns_support = true # default:true

  tags = {
    "Name" = "snyk-iac-vpc"
  }
}

手動変更

作成したVPCに対して手動変更を行います。先ほどの、「enable_dns_hostnames」と「enable_dns_support」に対応した「DNS ホスト名」と「DNS 解決」を無効に設定します。

ドリフト検出

実際にsnyk iac describeコマンドを使用してみます。デフォルトではコマンドを実行したフォルダ配下を管理しているtfstateのありかを探して差分を検出します。

また、AWSプロバイダーの場合、デフォルトでは「3.19.0」のバージョンでドリフトを検出するため今回使用している「4.27.0」にバージョンを合わせて実行します。

snyk_iac

snyk iac describe --drift --tf-provider-version=4.27.0

実行結果は次の通りに出力されました。「enable_dns_hostnames」と「enable_dns_support」のどちらもドリフトとして検出されました。

takakuni@snyk_iac % snyk iac describe --drift --tf-provider-version=4.27.0
Using Terraform state tfstate+s3://tf-takakuni/snyk_iac/terraform.tfstate found in main.tf. Use the --from flag to specify another state file.
Scanned states (1)      
Scan duration: 21s     
Provider version used to scan: 4.27.0. Use --tf-provider-version to use another version.
Snyk Scanning Infrastructure As Code Discrepancies...

  Info:    Resources under IaC, but different to terraform states.
  Resolve: Reapply IaC resources or update into terraform.

Changed resources: 1

State: tfstate+s3://tf-takakuni/snyk_iac/terraform.tfstate [ Changed Resources: 1 ]

  Resource Type: aws_vpc
    ID: vpc-0dd5115c64147afd6
    ~ enable_dns_hostnames: true => false
    ~ enable_dns_support: true => false

Test Summary

  Managed Resources: 1
  Changed Resources: 1

  IaC Coverage: 100%
  Info: To reach full coverage, remove resources or move it to Terraform.

  Tip: Run --help to find out about commands and flags.
      Scanned with aws provider version 4.27.0. Use --tf-provider-version to update.

(補足)検出範囲について

普段、CloudFormationのドリフト検出機能をご利用の方に対しての補足です。ドリフト検出の仕組みが違うので注意が必要です。

CloudFormationのドリフト検出は、リソースの現在の状態とCloudFormationテンプレートの差分を比較する仕組みです。そのため、コードで定義していない部分はドリフト検出されないため、検出したいプロパティはコードで定義する必要があります。

詳しくは以下のブログをご覧ください。

Snyk IaCの場合

対して、Snyk IaCのドリフト検出ではリソースの現在の状態とtfstateファイルの差分を比較する仕組みです。「Terraformのコードとの差異ではない」ことに注意です。

tfstateファイルを比較対象とするため、定義していないプロパティもドリフト検出の対象になります。

ドリフト検出」の出力結果を例にすると、「enable_dns_support」はコードで定義されていないプロパティですが、検出結果に出力されています。

検出するプロパティはカスタマイズできる?

結論、カスタマイズ可能です。

ドリフト検出したくないリソースやプロパティに対して、次のように.snykファイルを定義することでドリフト検出を回避できます。

次の例は、全てのVPCリソースの「enable_dns_support」プロパティをドリフト検出しない設定をしています。

snyk_test/.snyk

# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.25.0
ignore: {}
patch: {}
exclude:
  iac-drift:
    - aws_vpc.*.enable_dns_support

詳しくは以下リファレンスをご覧ください。

HTML出力もできる

snyk iac describeコマンドは実行結果の出力方式にHTMLも対応しています。「--html-file-output」オプションを指定することでHTMLファイル形式で結果を出力して確認できます。

snyk_iac

snyk iac describe --drift --tf-provider-version=4.27.0 --html-file-output=result.html

エクスポートされた「result.html」を開くと次のようなファイルでドリフトを確認できます。「(computed)」が付いているプロパティは、コードで定義されていて検出されたプロパティになります。

コードで定義されて検知されたのか一目で判断できるため、.snykファイルによる回避基準にとして良いなと思いました。

まとめ

以上、「Snyk IaCでTerraformのドリフト検出をやってみた」でした。

Terraformのドリフト基盤の手法の1つとして、とても使いやすいなと思いました。特にコードで定義していない部分も検出できる点が個人的には大変魅力的でした。

以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!