TL;DR: To review a Bicep change before deployment, compile it with
az bicep build, run the Bicep linter,az deployment group validateand what-if, then review the compiled ARM JSON for design, cost and security. what-if shows what will change in Azure. It doesn't tell you whether the change is safe, affordable or exposed.
By Prateek Singh, founder of Ganak AI Labs. Updated 6 October 2026.
Your pull request touches main.bicep. CI is green, and what-if prints a tidy list of + Create and ~ Modify lines. Someone approves it.
Two weeks later you learn the "small" change opened a storage account to the internet, or doubled the bill with a new SKU.
None of the tools lied. what-if tells you what will change, not whether it's safe. Here's how to answer that second question in the same PR, plus a checklist to copy.
Disclosure: I'm the founder of Cloudeval.
Key terms used below
- Bicep: Microsoft's language for Azure infrastructure. It compiles to an ARM template.
- ARM template: the Azure Resource Manager JSON file that Azure actually deploys.
- what-if: an Azure CLI command that previews changes against what's already deployed.
-
Bicep linter: the built-in checker, configured in
bicepconfig.json.
What does each Bicep pre-deployment check catch?
| Check | Catches | Needs Azure access? | Misses |
|---|---|---|---|
az bicep build |
Syntax and type errors | No | Intent and design |
| Bicep linter | Hard-coded URLs, secrets in outputs, insecure defaults, old API versions | No | Topology, cost, cross-resource risk |
az deployment group validate |
Whether Resource Manager accepts the template at a real scope | Yes | Whether you should deploy it |
| what-if | The resource-level diff against current Azure state | Yes, deploy-level | Whether that diff is safe or affordable |
| Design, cost and security review | Public exposure, dependency shape, cost drivers | Not for template review | Runtime behaviour, live drift |
Keep the first four. The gap is the last row.
How do I review a Bicep change before deployment?
- Compile the Bicep file to ARM JSON with
az bicep build. - Run the native checks: the linter,
validateand what-if. - Review design, cost and security on the compiled ARM JSON, locally or on every pull request.
- Read the findings in a fixed order, then re-run what-if right before you deploy.
Step 1: Compile with az bicep build
Bicep compiles to ARM JSON, and that's what Azure actually deploys.
az bicep build --file ./infra/main.bicep --outfile ./infra/main.json
Compile a .bicepparam file the same way:
az bicep build-params --file ./infra/main.bicepparam --outfile ./infra/main.parameters.json
Make this your first CI step. Skim the JSON on module changes too. A one-line version bump can change many resources, and only the compiled output shows it.
Step 2: Run the native checks (linter, validate, what-if)
The linter
Set the rules that matter for review to error in bicepconfig.json:
{
"analyzers": {
"core": {
"enabled": true,
"rules": {
"outputs-should-not-contain-secrets": { "level": "error" },
"secure-parameter-default": { "level": "error" },
"use-secure-value-for-secure-inputs": { "level": "error" },
"no-hardcoded-environment-urls": { "level": "error" },
"use-recent-api-versions": { "level": "warning" }
}
}
}
}
Then run it in CI:
az bicep lint --file ./infra/main.bicep
validate and what-if
These two need a real Azure scope:
az deployment group validate \
--resource-group rg-app-dev \
--template-file ./infra/main.bicep \
--parameters ./infra/main.bicepparam
az deployment group what-if \
--resource-group rg-app-dev \
--template-file ./infra/main.bicep \
--parameters ./infra/main.bicepparam
sub, mg and tenant variants cover other scopes. Keep in mind:
- what-if needs deploy permissions, including
Microsoft.Resources/deployments/*. Reviewers often lack them. - Azure-set defaults can show up as changes that won't happen.
- Resources deployed through
templateLink, including template specs, aren't expanded.
For refactors, bicep snapshot (Bicep CLI 0.41.2+) diffs what a .bicepparam would deploy, without contacting Azure:
bicep snapshot --mode overwrite ./infra/main.bicepparam # on main
bicep snapshot --mode validate ./infra/main.bicepparam # on the PR branch
You now know what will change. You still don't know if it's a good idea.
Step 3: Review design, cost and security
Most pipelines skip this layer because it meant reading raw JSON by hand. Use any tool your team trusts. PSRule for Azure is a solid open-source option.
I'll use Cloudeval here. It reviews the compiled ARM JSON and returns architecture and dependency diagrams with cost and security findings. It doesn't read .bicep files directly, which is why Step 1 matters.
Here's a two-minute demo:
Option A: from your terminal
Install and sign in:
npm install -g @ganakailabs/cloudeval-cli # Node.js 20+
cloudeval login # or: cloudeval login --headless (SSH/containers)
cloudeval auth status
Create a project from the compiled template and run every report:
cloudeval projects create \
--template-file ./infra/main.json \
--parameters-file ./infra/main.parameters.json \
--name "PR 1234 - main.bicep" \
--provider azure \
--format json \
--output ./cloudeval-project.json
PROJECT_ID=$(jq -r '.data.project.id' ./cloudeval-project.json)
cloudeval reports run --project "$PROJECT_ID" --type all --wait --format json
This runs against the template, not your subscription, so you need a Cloudeval account but no Azure credentials.
Read the results:
cloudeval reports waf --project "$PROJECT_ID" --format markdown # architecture findings
cloudeval reports cost --project "$PROJECT_ID" --format markdown # cost estimate
cloudeval issues list \
--project "$PROJECT_ID" \
--type architecture,cost \
--severity critical,high \
--format json

Each finding names the rule, the resource type and the pillar.
Or open the diagram:
cloudeval open project "$PROJECT_ID" \
--view preview --layout dependency --print-url --no-open

The generated diagram sits next to your files and ARM JSON outline.
Prefer clicking? In the web app, choose Projects, then Create project, then Upload Template File, and drop in main.json (up to 2 MB). The import guide covers URLs, parameter files and linked templates.

Your template, ARM JSON outline and report history live in one workspace.
Option B: on every pull request (GitHub Action)
ganakailabs/cloudeval-action posts one result comment per PR run. It can also write SARIF output and enforce merge gates.
Setup takes three steps (see the GitHub Actions guide):
- Link the repo to a Cloudeval project with the Cloudeval GitHub App.
- Create a CI access key scoped to that project.
- Add the
CLOUDEVAL_ACCESS_KEYandCLOUDEVAL_PROJECT_IDrepository secrets.
One Bicep detail: the action reviews the synced files at the pushed commit, and it reads ARM JSON. So commit the compiled infra/main.json and main.parameters.json, and fail CI if they're stale.
name: Bicep review
on:
pull_request:
permissions:
contents: read
pull-requests: write
issues: write
jobs:
native-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: az bicep lint --file ./infra/main.bicep
- name: Compiled ARM JSON must be up to date
run: |
az bicep build --file ./infra/main.bicep --outfile ./infra/main.json
git diff --exit-code -- ./infra/main.json
cloudeval-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ganakailabs/cloudeval-action@v1
with:
access_key: ${{ secrets.CLOUDEVAL_ACCESS_KEY }}
project_id: ${{ secrets.CLOUDEVAL_PROJECT_ID }}
mode: review
post_pr_comment: true
upload_artifacts: true
Keep the jobs separate. The review job stops on a dirty working tree, and building inside it would dirty it.
Set the entry point and gates in .cloudeval/config.yaml at the root of the synced source. With a source root of infra, that's infra/.cloudeval/config.yaml:
version: 1
stacks:
- id: main
entry: main.json # relative to the source root: infra/main.json -> main.json
parameters: main.parameters.json
resolve:
linked_templates: true
ci:
gates:
enforcement: comment_only # switch to block_pull_request once thresholds are tuned
minimum_well_architected_score: 80 # the default once ci.gates exists; set your own floor
fail_when_high_risk_findings_exist: true
fail_when_validation_fails: true
max_monthly_cost_usd: 500
Keys you leave out of ci.gates take defaults, so set the score floor on purpose. Start with comment_only. Once you trust the thresholds, switch to block_pull_request and make the job a required status check.
Step 4: Read the findings
The PR comment keeps your gate separate from what the review saw:
Overall: PASS
Well-Architected posture: 23.1/100 (critical)
Validation: 3 unit tests failed
Policy checks: good
Cost: 143.81 USD/mo (under 100K budget)
Overall reflects your thresholds. critical is the posture Cloudeval observed. A PR can pass a loose gate and still deserve a hard look.

The expanded comment shows validation results and architecture signals.
Read the findings in this order:
- Dependency view. Does the topology match the PR description? Look for surprise public endpoints, identity or subnet changes.
- High-severity findings. Did this PR change the affected resource?
- Cost drivers. Should a SKU change before rollout? Numbers are estimates.
- Gaps. "Not assessed" doesn't mean safe.

Select a resource to see its upstream and downstream paths.
Then run what-if at deploy time anyway.
What belongs in a Bicep pre-deploy checklist?
## Bicep pre-deploy review
- [ ] `az bicep build` passes, compiled ARM JSON is committed and up to date
- [ ] `az bicep lint` clean (security-relevant rules set to `error`)
- [ ] `az deployment group validate` passes at the target scope
- [ ] `what-if` diff matches the PR description (no surprise Delete/Modify)
- [ ] Linked/nested templates and template specs reviewed separately (what-if doesn't expand templateLink)
- [ ] Dependency diagram reviewed: no unintended public exposure, identity or subnet changes
- [ ] High/critical findings triaged: fixed, waived with reason, or ticketed
- [ ] Monthly cost estimate checked against budget, SKU choices justified
- [ ] "Not assessed" areas noted, not assumed safe
- [ ] what-if re-run immediately before the real deployment
What doesn't a template review catch?
Cloudeval adds diagrams, cost and security findings from 1,650+ checks, right in the PR. It covers Azure, plus AWS CloudFormation (beta).
It can't prove runtime behaviour, and it doesn't replace what-if, the only real answer to "what changes in this subscription right now". It doesn't catch live drift yet either. Drift comparison is planned, and live Azure state comes in through a separate Cloud sync.
Frequently asked questions
How do I preview Azure deployment changes before deploying?
Run az deployment group what-if with the template and parameters you'll deploy. It lists each resource that would be created, modified or deleted, and needs deploy permissions.
Why does Bicep what-if show changes that won't happen?
Properties Azure sets as defaults can show up as changes even though nothing will change. Check unexpected Delete or Modify lines against the PR description first.
Can I review a Bicep change without access to the Azure subscription?
Partly. build, the linter and bicep snapshot run locally, but validate and what-if need deploy-level access. Cloudeval works from the compiled ARM JSON, so you need a Cloudeval account, not subscription credentials.
Does Cloudeval read .bicep files directly?
No. Compile with az bicep build and review the ARM JSON. That's what Azure actually deploys anyway.
How do I block a merge on high-risk findings or cost?
Add ci.gates to .cloudeval/config.yaml, for example fail_when_high_risk_findings_exist or max_monthly_cost_usd. Set enforcement: block_pull_request and make the job a required check in branch protection.
For the longer version, read How do I review a Bicep or ARM template before deployment? Or browse a public example PR with real caveats, including failed validation tests.
Related on cloudeval.ai
- How to convert Bicep to a cloud architecture diagram
- Cloud infrastructure review in GitHub pull requests
- What is IaC review?
- Compare Cloudeval with other cloud review and diagram tools
Try it
Compile your Bicep, then review the ARM JSON before it ships. Open the Cloudeval app and follow the IaC pre-deployment review guide.
About the author
Prateek Singh is the founder of Ganak AI Labs and builds Cloudeval AI.
cloudeval.ai | Docs | YouTube | GitHub | LinkedIn
Top comments (0)