27
loading...
This website collects cookies to deliver better user experience
tests against structured configuration data using the Open Policy Agent Rego query language
. In the case of Terraform, this means we're actually running unit tests against sample JSON and actual tests against the Terraform plan JSON. >= 0.12
. In order to test our Terraform, we will be generating the output of a plan in our current working directory.main.tf
file:terraform {
required_providers {}
}
resource "null_resource" "fake_instance" {
count = 2
}
implements the standard resource lifecycle but takes no further action.
Meaning, we're not really doing anything with this Terraform other than generating some resources for our Terraform file. Specifically, we're creating 2 resources because count = 2
. terraform init
terraform plan -out=tfplan_2_resources_planned
terraform show -json ./tfplan_2_resources_planned | jq > tfplan_2_resources_planned.json
jq
to view a few of these, but I'd definitely suggest just scrolling through the file yourself!cat tfplan_2_resources_planned.json | jq .resource_changes
cat tfplan_2_resources_planned.json | jq '.resource_changes[].type'
cat tfplan_2_resources_planned.json | jq '.resource_changes[].change.actions[]'
terraform_version
key in the state file or whether the AWS provider is using the official release. It's really worth digging into this file's structure.mkdir policy
. It's idiomatic to have the directory called policy
for Conftest. We'll write some simple Rego that checks how many null_resource
objects we are creating. Name this file main.rego
.package main
planned_resources = [res |
res := input.planned_values.root_module.resources[_]
res.type == "null_resource"
]
num_planned_resources := count(planned_resources)
deny[msg] {
not num_planned_resources == 2
msg := "there should be 2 total null_resources"
}
planned_resources
is using a list comprehension, which is common in Python. In this case, it is parsing the input, which in our case is the Terraform plan file. Then, it narrows down the JSON to planned_values.root_module.resources[_]
. The [_]
indicates that you are searching all objects. Finally, it narrows down the objects to only have a type of null_resource
. In our case, that's all we're creating, but typically your Terraform will have many, many more resources.count()
is a built-in function. There are many such functions, which can be seen here.deny[msg]
is the common block that Conftest is checking and will provide a status on whether the test fails. Inside the block, if anything evaluates to true, then the deny block will fail and return the msg variable. Thus, that is why we declare the the test as not == 2. conftest test tfplan_2_resources_planned.json
should generate something like: 1 test, 1 passed, 0 warnings, 0 failures, 0 exceptions
. As mentioned above, it's idiomatic that Conftest will look in the directory ./policy
for Rego policies. If you do not have it in that directory, this also works: conftest test --policy [location_here] tfplan_2_resources_planned.json
main_test.rego
with the following content:test_num_planned_resources {
num_planned_resources == 1 with input as {
"planned_values": {
"root_module": {
"resources": [
{
"address": "null_resource.fake_instance[0]",
"type": "null_resource",
}
]
}
},
}
}
opa test -v policy/*.rego