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:
- cnspec installed on your workstation
- Bicep files (
.bicep) or ARM template JSON files to scan
Scan Bicep files
Scan a single Bicep file:
cnspec scan bicep main.bicepScan a directory of Bicep files:
cnspec scan bicep ./infra/Scan an ARM template JSON file directly:
cnspec scan bicep azuredeploy.jsonScan options
| Option | Description |
|---|---|
--asset-name | Override the asset name |
--annotation | Add an annotation to the asset (key=value) |
--incognito | Run in incognito mode (do not report results to Mondoo Platform) |
-o, --output | Set the output format (compact, full, json, junit, summary, yaml) |
-f, --policy-bundle | Path to a policy file (local path, s3:// URI, or http(s):// URL) |
--policy | Specify policies to execute (requires --policy-bundle) |
--risk-threshold | Exit 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
-
For the full MQL resource reference, see the MQL Bicep provider documentation.
-
To learn more about how the MQL query language works, read Write Effective MQL.