Checkov, Trivy and tflint
Checkov, Trivy and tflint read code, not clouds: they flag insecure or invalid configuration before anything is planned. The Azure Enterprise Baseline and the Azure Web App and Data and ETL Blueprints run them in pre-commit and again in CI, and the code repositories scan every image they build.
What it does
tflint lints OpenTofu code with rule sets, here the Terraform recommended preset and the azurerm ruleset, which knows valid SKUs, sizes and arguments. Checkov evaluates resources against hundreds of security policies (encryption, network exposure, logging) and fails on a violation unless it is suppressed. Trivy scans configuration for misconfigurations and container images for known vulnerabilities in OS packages and language libraries.
How BuiltForProd uses it
| Scanner | Version | Configuration | Runs in |
|---|---|---|---|
| tflint | 0.64.0 with tflint-ruleset-azurerm 0.32.0 | .tflint.hcl: own modules only (call_module_type = "none"); terraform_required_version and terraform_required_providers off, because root.hcl generates versions.tf | Pre-commit and the lint-and-scan job of every infrastructure plan.yml |
| Checkov | 3.3.19 | .checkov.yaml: framework terraform, skips the generated .terragrunt-cache and .terragrunt-stack folders | Pre-commit and plan.yml over modules/, as a hard gate |
| Trivy | trivy-action 0.36.0 | Configuration scan; image scan of CRITICAL and HIGH with a fix available | Pre-commit in the infrastructure repositories; the Baseline's plan.yml (reported, Checkov is the gate); the image job of each code repository's ci.yml (fails the build) |
The code repositories also run Checkov 3.3.22 in pre-commit over the whole repository, Dockerfiles and workflows included. The lint-and-scan job runs on a GitHub-hosted runner with no Azure access, before any plan, so a finding stops the pull request without touching state; see GitHub Actions.
Suppressing a finding. A check that does not apply is skipped in-line, on the resource, with the reason beside it, so the exception is reviewed with the code it excuses. The Azure Container Registry shows the pattern: each Premium-only control is skipped with the setting that turns it on.
resource "azurerm_container_registry" "this" {
#checkov:skip=CKV_AZURE_139: public network access is the default because the code pipelines push from GitHub-hosted runners (Entra authentication only); public_network_access_enabled = false with Premium and private_endpoints is the switch
#checkov:skip=CKV_AZURE_165: geo-replication is a Premium option for a second region, not a baseline default
#checkov:skip=CKV_AZURE_164: Docker Content Trust is being retired by Azure; image signing with Notation is the supply-chain option
#checkov:skip=CKV_AZURE_166: quarantine needs Premium and a scanner gate; Defender for Containers scans the registry (security.hcl)
#checkov:skip=CKV_AZURE_167: the retention policy is Premium only; on Standard the purge-untagged task enforces the same untagged_retention_days
#checkov:skip=CKV_AZURE_233: zone redundancy is Premium only; zone_redundancy_enabled applies with sku = "Premium"
#checkov:skip=CKV_AZURE_237: dedicated data endpoints are Premium only and needed only behind a firewall allow-list
name = var.registry_name
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
sku = var.sku
admin_enabled = false
anonymous_pull_enabled = false
public_network_access_enabled = var.public_network_access_enabled
network_rule_bypass_option = "AzureServices"
zone_redundancy_enabled = local.premium ? var.zone_redundancy_enabled : false
retention_policy_in_days = local.premium ? var.untagged_retention_days : null
quarantine_policy_enabled = local.premium ? var.quarantine_policy_enabled : null
# AcrPull and AcrPush (the blueprints grant AcrPull) are honored only in the legacy registry permission mode;
# the ABAC repository mode would need the Container Registry Repository Reader/Writer roles everywhere.
role_assignment_mode = "LegacyRegistryPermissions"
tags = var.tags
Repository-wide skips are rare: the Baseline's .checkov.yaml skips only CKV_TF_1, because every module is local.
Terms you will see
| Term | Meaning |
|---|---|
| Ruleset | A tflint plugin of provider-specific rules, here azurerm. |
| Check ID | A Checkov policy identifier such as CKV_AZURE_139. |
| In-line skip | #checkov:skip=<id>: <reason> on the resource it applies to. |
ignore-unfixed | Trivy setting that leaves out vulnerabilities with no fix yet. |
| Lint and scan | The first job of every infrastructure plan.yml. |
Where to read more
- Azure Enterprise Baseline overview, Azure Web App Blueprint overview and Azure Data and ETL Blueprint overview.
- pre-commit for the local copy of these checks.
- Policy as code for the idea behind them.