【Terraform】conftestで特定のattributeが指定されていないことをチェックする方法

に公開

やりたいこと

conftestを使ってterraformなどをテストしている。
その中で、オブジェクト内に特定のキーが(Terraform内であればresourceブロック内のあるattributeが)指定されて いない ことをチェックしたい。

cloudfront_default_certificate の機能は一切使わないため、 cloudfront_default_certificate が指定されていないことをチェックしたい。

resource "aws_cloudfront_distribution" "main" {
  viewer_certificate {
    acm_certificate_arn            = aws_acm_certificate.us_east_1.arn
    cloudfront_default_certificate = false # ここは指定しないでほしい
    minimum_protocol_version       = "TLSv1"
    ssl_support_method             = "sni-only"
  }
  ...
}

# ↓ こうであればOK

resource "aws_cloudfront_distribution" "main" {
  viewer_certificate {
    acm_certificate_arn            = aws_acm_certificate.us_east_1.arn
    minimum_protocol_version       = "TLSv1"
    ssl_support_method             = "sni-only"
  }
  ...
}

ソリューション

次のような関数を定義し、

has_field(object, field) if {
	object[field]
}

has_field(object, field) if {
  object[field] == false
}

has_field(object, field) := false if {
  not object[field]
  not object[field] == false
}

以下のように使う。

deny contains msg if {
	some name
	some acd in input.resource.aws_cloudfront_distribution[name]
	has_field(acd.viewer_certificate[_], "cloudfront_default_certificate")

	msg = sprintf("cloudfront_default_certificateは指定するべきではありません", [])
}

なぜ has_field 関数の定義は1つではなく3つ必要なのか

conftestの公式例である
https://github.com/open-policy-agent/conftest/blob/144124577a48c3920fa52fc44cc10a8679e345a2/examples/hcl2/policy/deny.rego#L4-L6
では has_field 関数の定義は以下だけである。

has_field(obj, field) if {
	obj[field]
}

なぜこれではダメかというと、この定義だけでは

test_failure_with_no_cloudfront_default_certificate_true if {
	cfg := parse_config("hcl2", `
		resource "aws_cloudfront_distribution" "main" {
			viewer_certificate {
				cloudfront_default_certificate = true
			}
		}
	`)
	deny["cloudfront_default_certificateは指定するべきではありません"] with input as cfg
}

のケースはOKでも、

test_failure_with_no_cloudfront_default_certificate_false if {
	cfg := parse_config("hcl2", `
		resource "aws_cloudfront_distribution" "main" {
			viewer_certificate {
				cloudfront_default_certificate = false
			}
		}
	`)
	deny["cloudfront_default_certificateは指定するべきではありません"] with input as cfg
}

のケースに失敗するため。
つまり、

has_field(obj, field) if {
	obj[field]
}

の定義だけではそのfieldが存在しないのか、fieldは存在するがその値が false なのかの区別ができない。
そのため、冒頭の3定義が必要になる。これがあれば2つ目のテストケースも成功するようになる。

株式会社エス・エム・エス

Discussion