Supply Chain

Scan a Whole Infrastructure as Code Repository with cnspec

Scan every Terraform, OpenTofu, CloudFormation, Bicep, Kubernetes, Helm, Kustomize, Ansible, and Dockerfile entry point in a repository with one cnspec command.

Most repositories hold more than one kind of infrastructure as code. A single service repo can carry Terraform for its cloud resources, a Helm chart for its deployment, and a Dockerfile for its image. Instead of adding one scan step per tool to your pipeline, point cnspec scan iac at the root of the repository. cnspec walks the tree, finds every entry point it recognizes, and scans each one with the same connector you would use on its own, such as cnspec scan helm or cnspec scan cloudformation.

This page is part of scanning your supply chain with cnspec. If you're new to cnspec, start with the Quickstart to install cnspec and run your first scan.

Experimental

The iac provider is experimental. Its resources, discovery names, and flags may change between releases. It requires cnspec 14.0 or later.

Prerequisites

To scan a repository with cnspec, you must have:

cnspec reads the files directly. None of the tools it scans for (Terraform, OpenTofu, Helm, Kustomize, Ansible, Bicep, or Docker) need to be installed on the machine that runs the scan.

Scan a repository

Scan the current directory:

cnspec scan iac .

cnspec reports one asset for the repository itself, plus one asset for every entry point it found in it. Each entry point asset is the same asset the tool's own connector produces, so a Helm chart found by cnspec scan iac is scanned by the same policies as one scanned with cnspec scan helm.

Terraform and OpenTofu are opt-in

A plain cnspec scan iac . does not scan Terraform or OpenTofu configurations. Add them to --discover. For a repository that uses Terraform:

cnspec scan iac . --discover auto,terraform

For a repository that uses OpenTofu, or is moving from Terraform to OpenTofu, name both:

cnspec scan iac . --discover auto,opentofu,terraform

To learn why both names are needed, read Scan Terraform and OpenTofu together.

You can also point cnspec at a single file. cnspec then scans only that file and ignores the rest of the directory.

What cnspec detects

cnspec decides which tool owns a file by its name, then asks that tool's provider to confirm. A file that a tool's pattern matches but that the tool doesn't recognize as its own (for example, a YAML file that is not a Kubernetes manifest) is skipped silently.

Tool--discover nameMatching filesIn autoScanned as
Ansibleansibleansible.cfg, inventory, roles/*/tasks/main.yml, *.yml, *.yamlYesThe directory
Azure Bicepbicep*.bicep, *.bicepparamYesThe directory
AWS CloudFormationcloudformation*.yaml, *.yml, *.json, *.templateYesEach template file
DockerfilesdockerfileDockerfile, Dockerfile.*, *.Dockerfile, *.dockerfileYesEach Dockerfile
Helm chartshelmChart.yamlYesThe chart directory
Kubernetes manifestsk8s*.yaml, *.ymlYesThe directory
Kustomize overlayskustomizekustomization.yaml, kustomization.yml, KustomizationYesThe directory
OpenTofuopentofu*.tofu, *.tofu.jsonNoThe directory
Terraformterraform*.tf, *.tf.jsonNoThe directory

The terraform and opentofu names find directories by file extension only, so an OpenTofu project written entirely in .tf files is found by terraform, not opentofu. To learn how the two names work together, read Scan Terraform and OpenTofu together.

To learn more about how each tool's files are read, see its guide: Ansible, Bicep, CloudFormation, Dockerfiles, Helm, Kubernetes manifests, Kustomize, OpenTofu, and Terraform.

Choose which tools to scan

--discover takes a comma-separated list of the names in the table above, plus auto:

ValueWhat it scans
(flag not set)Every tool marked In auto in the table above
autoThe same set as leaving the flag unset
auto,terraformThe default set plus Terraform
auto,opentofu,terraformThe default set plus OpenTofu and Terraform, reading directories that hold both as OpenTofu
auto,terraform,opentofuThe default set plus Terraform and OpenTofu, reading directories that hold both as Terraform
terraform,helmOnly Terraform configurations and Helm charts

An unknown name stops the scan with an error that lists every valid value.

--discover all also exists, but it first offers the root of the repository to every tool. A tool that claims the root then skips everything below it, so all can report fewer assets than naming the tools yourself. Use auto,opentofu,terraform or auto,terraform,opentofu to scan everything.

Scan Terraform and OpenTofu together

Terraform and OpenTofu share one configuration language, and OpenTofu reads .tf files exactly the way Terraform does. From the files alone, cnspec often can't tell which tool a directory is applied with, so it doesn't guess. You tell it by naming terraform, opentofu, or both in --discover.

Which directories each name finds

Each name finds a directory by the files in it:

The directory holdsFound by terraformFound by opentofu
Only .tf and .tf.json filesYesNo
Only .tofu and .tofu.json filesNoYes
Both .tf flavored and .tofu flavored filesYesYes

Most OpenTofu projects keep their .tf files, so most OpenTofu directories are found only by terraform. That is why a repository that uses OpenTofu needs both names: opentofu alone skips every directory that has no .tofu file. A directory found by terraform is read the way Terraform reads it, which for a directory with no .tofu files is also the way OpenTofu reads it. cnspec reports it as a Terraform HCL asset.

Directories that hold both

A directory that holds both .tf and .tofu files produces one asset, not two. The name you list first in --discover decides how cnspec reads it:

  • --discover auto,opentofu,terraform reads it the way OpenTofu does: a .tofu file replaces the .tf file with the same name (main.tofu replaces main.tf). cnspec reports an OpenTofu HCL asset.
  • --discover auto,terraform,opentofu reads it the way Terraform does: .tofu files are ignored. cnspec reports a Terraform HCL asset.

List first the tool that actually applies the directory, so cnspec checks the same configuration that tool deploys. The order doesn't matter for directories that only one of the names finds.

Example: migrate from Terraform to OpenTofu

A team is moving a repository from Terraform to OpenTofu one module at a time. Midway through, the repository looks like this:

infra/
├── network/
│   └── main.tf         # not migrated yet
├── storage/
│   ├── main.tf         # kept so Terraform can still apply the module
│   └── main.tofu       # adds OpenTofu state encryption
└── logging/
    └── main.tofu       # new module, written for OpenTofu

The team now applies all three modules with tofu. To scan every module the way OpenTofu applies it, list opentofu first and keep terraform so network is still found:

cnspec run iac infra --discover auto,opentofu,terraform -c "iac.detections { tool path }"
detections: [
  0: {
    tool: "opentofu"
    path: "logging"
  }
  1: {
    tool: "terraform"
    path: "network"
  }
  2: {
    tool: "opentofu"
    path: "storage"
  }
  3: {
    tool: "terraform"
    path: "storage"
  }
]

storage is detected by both names but becomes a single asset. Scanning the repository reports these assets:

cnspec scan iac infra --discover auto,opentofu,terraform
DirectoryAssetFiles cnspec reads
loggingOpenTofu HCL directory loggingmain.tofu
networkTerraform HCL directory networkmain.tf
storageOpenTofu HCL directory storagemain.tofu (replaces main.tf)

Compare the other choices for the same repository:

  • --discover auto,opentofu skips network, because it has no .tofu file.
  • --discover auto,terraform skips logging and reads storage from main.tf, so the state encryption settings in main.tofu are never checked.
  • --discover auto,terraform,opentofu finds every module but reads storage from main.tf, the way Terraform would.

Keep both names in --discover after the migration too: any module you leave in .tf files is still found only by terraform.

Mondoo's built-in Terraform policies select Terraform HCL assets, so they don't evaluate the OpenTofu HCL assets in this example. To learn how to write checks that cover both, read Policies for OpenTofu assets.

Skip directories

By default, cnspec skips these directories wherever they appear in the tree: .git, .terraform, .terragrunt-cache, .venv, node_modules, vendor, target, and dist.

--iac-ignore replaces that list. It does not add to it, so include the defaults you still want skipped:

cnspec scan iac . --iac-ignore .git,.terraform,node_modules,examples

To scan vendored code as well, pass an empty list:

cnspec scan iac . --iac-ignore ""

Preview what a scan will find

To see which entry points cnspec detects without running any policies, query the iac resource:

cnspec run iac . --discover auto,terraform -c "iac.detections { tool path error }"
detections: [
  0: {
    error: ""
    tool: "dockerfile"
    path: "Dockerfile"
  }
  1: {
    error: ""
    tool: "helm"
    path: "charts/api"
  }
  2: {
    error: "rpc error: code = Unknown desc = cannot load Chart.yaml: error converting YAML to JSON: yaml: line 2: did not find expected ',' or ']'"
    tool: "helm"
    path: "charts/broken"
  }
  3: {
    error: ""
    tool: "terraform"
    path: "terraform"
  }
]

path is relative to the directory you scanned. If a tool recognizes an entry point but can't read it, such as a Helm chart with a malformed Chart.yaml, cnspec records the error on that detection and keeps walking the tree. The rest of the repository is still scanned.

A detection that reports an error does not produce an asset and does not change the scan's exit code. If a malformed file must fail your pipeline, check iac.detections for errors in a separate step.

To learn about every field, read the iac resource reference.

Scan in CI/CD

cnspec scan iac is designed for CI/CD: one step scans every entry point in the checked-out repository, and new charts, templates, or modules are picked up without editing the pipeline.

cnspec scan iac . \
  --discover auto,terraform \
  --risk-threshold 90 \
  --output junit --output-target cnspec-junit.xml
  • --discover auto,terraform adds Terraform to the default set. If the repository uses OpenTofu, use --discover auto,opentofu,terraform instead.
  • --risk-threshold 90 fails the job with exit code 1 if any asset has a critical risk (90 or higher). To learn how to choose a threshold, read Exit code handling.
  • --output junit --output-target cnspec-junit.xml writes one JUnit test suite per asset, which most CI systems can display as test results. For tools that read SARIF, use --output sarif instead. To learn about every format, read Report Results.
  • Set MONDOO_CONFIG_BASE64 to your Mondoo service account credentials to report the results to Mondoo Platform, or add --incognito to keep them local.

The official mondoo/cnspec container image runs cnspec as its entrypoint, so you can use mondoo/cnspec:14 as the job image on any CI system that runs containers.

For complete pipeline examples, see the guide for your CI system:

Scan options

OptionDescription
--asset-nameOverride the asset name
--annotationAdd an annotation to the asset (key=value)
--incognitoRun in incognito mode (do not report results to Mondoo Platform)
-o, --outputSet the output format (compact, csv, full, hdf, json, junit, ocsf-json, ocsf-parquet, report, sarif, summary, yaml)
-f, --policy-bundlePath to a policy file (local path, s3:// URI, or http(s):// URL)
--policySpecify policies to execute (requires --policy-bundle)
--risk-thresholdExit with status 1 if any risk meets or exceeds this value (0-100)

To gate a CI/CD pipeline, combine --risk-threshold with a report file, for example --risk-threshold 90 --output junit --output-target cnspec-junit.xml. 90 fails the job only on critical risks, 70 also fails on high risks, and 40 also fails on medium risks. To scan every infrastructure as code entry point in a repository in one step, use cnspec scan iac. For complete pipeline examples, read Integrate Mondoo with CI/CD Platforms.

In addition to the options above, cnspec scan iac accepts --discover and --iac-ignore, described earlier on this page.

Learn more

On this page