Mondoo Policy Checks for Terraform (HCL, Plan, and State) with MQL
Learn how to write Mondoo MQL checks for Terraform HCL, Plan, and State. Use the cnspec shell, Mondoo Terraform provider, filters, and variants to build production-grade Policy as Code across your IaC and runtime.
- Terraform HCL (your source code).
- Terraform Plan (JSON) (the proposed changes).
- Terraform State (JSON) (what exists according to Terraform).
In this guide you'll:
- Install and use the Mondoo Terraform provider in the
cnspec shell. - Explore Terraform resources with MQL, Write checks using selectors, filters, and aggregators like
.where()and.all(). - Package checks into Mondoo policies with filters and variants so a single check can run consistently against HCL, Plan, and State.
Mondoo Terraform Provider Overview
What are Mondoo Providers?
Mondoo providers are plugins for cnspec/cnquery that let you connect Mondoo to different platforms and data sources. Each provider understands how to talk to a specific technology (like AWS, GCP, Azure, Kubernetes, or Terraform) and exposes that environment in a way you can query with MQL.
Think of providers as connectors: they translate an external system's data (cloud APIs, config files, Terraform plans, etc.) into Mondoo's queryable objects.
The cnspec Terraform Provider
Mondoo's Terraform provider for cnspec is not the same thing as a HashiCorp Terraform provider. Instead, it's a cnspec plugin that knows how to parse Terraform data and make it available in MQL.
With the Terraform provider you can:
- Scan Terraform code and outputs: HCL files, Plan JSON, and State JSON.
- Query resources consistently: Exposes Terraform resources as
terraform.resources(HCL),terraform.plan.resourceChanges(Plan) orterraform.state.resources(State), so you can write the same style of MQL across all stages of a Terraform development lifecycle. - Use policy variants: Define a check once and run it against Terraform HCL, Plan, State, and even runtime cloud APIs.
Install the Terraform provider
cnspec providers install terraformUse the interactive shell
Use the cnspec shell to prototype MQL quickly before you codify checks in YAML.
HCL (directory with .tf files):
cnspec shell terraform ./path/to/terraform/dirPlan (JSON produced by terraform show -json):
Generate a JSON plan file:
terraform show -json tfplan.binary > tfplan.jsonOpen a JSON plan file in the shell:
cnspec shell terraform ./path/to/tfplan.jsonState (local terraform.tfstate or exported JSON state):
Generate a JSON Terraform state:
terraform show -json > tfstate.jsonOpen a JSON Terraform state file in shell:
cnspec shell terraform ./path/to/terraform.tfstateBelow is a practical, step-by-step flow you can use in the shell to discover resources, drill into arguments, and transform an ad-hoc query into a production check.
1) List resources and identify targets
Start broad to see what Terraform resources Mondoo parsed:
terraform.resources { nameLabel }This returns an array like:
cnspec> terraform.resources
terraform.resources: [
0: terraform.block type="resource" labels=[
0: "random_string"
1: "random"
]
1: terraform.block type="resource" labels=[
0: "google_cloud_run_v2_service"
1: "hello_service"
]
2: terraform.block type="resource" labels=[
0: "google_cloud_run_v2_service_iam_member"
1: "public_invoker"
]
3: terraform.block type="resource" labels=[
0: "google_redis_instance"
1: "dev_memstore"
]
4: terraform.block type="resource" labels=[
0: "google_storage_bucket"
1: "example"
]
]2) Filter to a specific resource type
Use .where() to narrow down:
terraform.resources.where( nameLabel == "google_storage_bucket" ) { * }Target exactly what you care about:
terraform.resources.where( nameLabel == "google_storage_bucket" ) { arguments }This returns all of the Terraform arguments found on in the HCL code:
cnspec> terraform.resources.where( nameLabel == "google_storage_bucket" ) { arguments }
terraform.resources.where: [
0: {
arguments: {
labels: {
environment: "dev"
}
location: "us-central1"
name: [
0: "lunalectric-bucket-"
1: "random_string.random.id"
]
uniform_bucket_level_access: true
}
}
]4) Evaluate every matching resource with .all(...)
When writing pass/fail checks, .all() lets you assert a condition across every matching resource:
terraform.resources.where( nameLabel == "google_storage_bucket" ).all( arguments.uniform_bucket_level_access == true )Using Filters to Control When Checks Run
Mondoo checks are only evaluated when their filters match. This prevents noise and ensures each check runs on the right content and platform.
Example: Only run on Terraform HCL and only when S3 buckets are present:
filters: asset.platform == "terraform-hcl" && terraform.resources.contains(nameLabel == "aws_s3_bucket")Terraform Plan (JSON)
Terraform Plan checks catch misconfigurations before they land in State or production. Mondoo's Terraform provider normalizes Plan data so your MQL patterns look the same as HCL/State—ideal for PR gating in CI.
Open a cnspec shell against a Plan file
First, produce a JSON plan:
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.jsonThen open the shell on the JSON:
cnspec shell terraform ./path/to/tfplan.json1) List resources in the Plan
Start broad to see what resource changes are in the plan:
terraform.plan.resourceChanges { * }2) Filter to a specific resource type
Use .where() to narrow down to a specific resource type:
terraform.plan.resourceChanges.where( type == "google_storage_bucket" ) { * }3) Inspect specific arguments proposed by the Plan
Target exactly what you care about to see what the uniform_bucket_level_access configuration will be after running terraform apply:
terraform.plan.resourceChanges.where( type == "google_storage_bucket" ) { change.after.uniform_bucket_level_access }4) Evaluate every matching resource with .all(...)
When writing pass/fail checks, .all() lets you assert a condition across every matching resource. This returns a single boolean true only if all planned google_storage_bucket resources enable uniform_bucket_level_access:
terraform.plan.resourceChanges.where( type == "google_storage_bucket" ).all( change.after.uniform_bucket_level_access == true )5) Add a filter so the check runs only on Plans (and only when relevant)
Use filters to ensure the check only runs on Plan files and only when the relevant resource type is present:
filters: asset.platform == "terraform-plan" && terraform.plan.resourceChanges.contains( type == "google_storage_bucket" )Terraform State (JSON)
Terraform State checks verify what actually exists, closing the loop after HCL and Plan. Mondoo exposes state via terraform.state.resources, so you can assert real-world configuration with the same MQL style.
Open a cnspec shell against a State file
First, export the current state to JSON:
terraform show -json > tfstate.jsonThen open the shell on the exported state:
cnspec shell terraform ./tfstate.json1) List resource types in Terraform State
Start broad to see what resources exist in state:
terraform.state.resources { type }2) Filter to a specific resource type and expand fields
Use .where() to narrow down to a specific resource type:
terraform.state.resources.where(type == "google_storage_bucket") { values }3) Inspect specific values from State
Target exactly what you care about to see the current uniform_bucket_level_access configuration for each GCS bucket in State:
terraform.state.resources.where( type == "google_storage_bucket" ) { values.uniform_bucket_level_access }4) Evaluate every matching resource with .all(...)
When writing pass/fail checks, .all() lets you assert a condition across every matching resource. This returns a single boolean true only if every GCS bucket in State has UBLA enabled:
terraform.state.resources.where( type == "google_storage_bucket" ).all( values.uniform_bucket_level_access == true )5) Add a filter so the check runs only on State (and only when relevant)
Use filters to ensure the check only runs on State files and only when the relevant resource type is present:
filters: asset.platform == "terraform-state" && terraform.state.resources.contains(type == "google_storage_bucket")Putting it all together
The real power of Mondoo comes from authoring one logical check that spans Runtime, Terraform HCL, Terraform Plan, and Terraform State using policy variants. A single policy definition can include:
- A clear title and numeric impact
- Rich docs (description, rationale)
- Multiple remediation paths (Terraform, Console, CLI)
- Variants that adapt the same intent to different platforms and contexts
The Mondoo Policy team publishes open source examples you can study and reuse. One check that illustrates this is the “Ensure that Cloud Storage buckets have uniform bucket-level access enabled” check, which defines variants for:
- Runtime (gcp-storage-bucket)
- Terraform HCL
- Terraform Plan
- Terraform State
👉 Full Example (open source): Ensure that Cloud Storage buckets have uniform bucket-level access enabled
Why this pattern works
One intent, many surfaces: The top-level check captures the security rule once; variants adapt it to each stage (Runtime, HCL, Plan, State).
Noise-free filtering: Each variant runs only when the target platform and resource type are present.
Clear ownership: title, impact, and rich docs (with multiple remediation paths) make the check understandable and actionable for SRE, SecOps, and platform teams.
CI → Prod continuity: HCL/Plan variants prevent bad changes; State/Runtime variants ensure reality stays compliant.