Supply Chain

Secure Azure Bicep and ARM Templates with cnspec

Scan Azure Bicep files and ARM templates for security misconfigurations with cnspec.

Catch insecure Azure resource configurations before they reach a deployment. cnspec parses .bicep source files, .bicepparam parameter files, and ARM template JSON, exposing resources, parameters, variables, modules, outputs, user-defined types and functions, and import statements as queryable MQL resources. Value expressions such as resource names, locations, and deployment conditions are also parsed into queryable trees, so policies can inspect how a value is built (a hardcoded literal, an interpolated parameter, a function call) without deploying it. The Mondoo Azure Security policy ships Bicep variants of every check, so the same controls that evaluate your live Azure subscription also evaluate your Bicep files and ARM templates in pull requests and CI pipelines. You can also use the Bicep provider inside your own policies to enforce additional standards.

Prerequisites

To scan Bicep files with cnspec, you must have:

Scan Bicep files

Scan a single Bicep file:

cnspec scan bicep main.bicep

Scan a directory of Bicep files:

cnspec scan bicep ./infra/

Scan an ARM template JSON file directly:

cnspec scan bicep azuredeploy.json

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, full, json, junit, 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)

Example checks

Run cnspec shell bicep main.bicep to open the interactive shell. From there you can make checks like the examples below.

Inspect a file's scope and metadata

bicep.files { path targetScope metadata }

List all resource declarations

bicep.files { resources { type symbolicName apiVersion } }

Check that all storage accounts enforce HTTPS

bicep.files {
  resources.where(type == "Microsoft.Storage/storageAccounts").all(
    properties["properties"]["supportsHttpsTrafficOnly"] == true
  )
}

Find resources without an explicit location

bicep.files { resources.where(location == "") { symbolicName type } }

Require a baseline tag on every resource

bicep.files { resources.all(tags.keys.contains("owner")) }

Flag hardcoded resource locations

Use the parsed location expression tree to tell a hardcoded literal apart from a value derived from a function such as resourceGroup().location:

bicep.files {
  resources.where(locationTree.kind == "literal") { symbolicName location }
}

Find existing-resource references and loops

bicep.files { resources.where(existing) { symbolicName type } }
bicep.files { resources.where(isLoop) { symbolicName loopIterator loopExpression } }

List all parameters and whether they are secure

bicep.files { parameters { name type secure } }

Require secure credential parameters

Make sure password-shaped parameters carry @secure and a minimum length:

bicep.files {
  parameters.where(name.downcase.contains("password")).all(secure && minLength != null)
}

Inspect parameter constraints

bicep.files { parameters { name type minLength maxLength allowed } }

Inspect module references

bicep.files { modules { name source isRegistry } }

Resolve and traverse into local modules

target resolves a local module path to the bicep.file it references, so you can query the module's own resources:

bicep.files {
  modules.where(target != null) {
    name
    target { path resources { type } }
  }
}

Inspect user-defined types, functions, and imports

bicep.files { types { name kind definition exported } }
bicep.files { functions { name returnType parameters } }
bicep.files { imports { source symbols namespace wildcard } }

Inspect outputs

bicep.files { outputs { name type expression } }

Query parameter files

.bicepparam files carry the actual parameter values used for a deployment:

bicep.paramFiles { path using params }

Query a compiled ARM template

bicep.template.resources { type name apiVersion }

Learn more

On this page