<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Cloudeval AI Blog]]></title><description><![CDATA[Cloudeval AI Blog]]></description><link>https://cloudeval.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Cloudeval AI Blog</title><link>https://cloudeval.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 12 Oct 2026 06:37:05 GMT</lastBuildDate><atom:link href="https://cloudeval.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Review Bicep before deployment: what-if, linter and cost]]></title><description><![CDATA[TL;DR: To review a Bicep change before deployment, compile it with az bicep build, run the Bicep linter, az deployment group validate and what-if, then review the compiled ARM JSON for design, cost an]]></description><link>https://cloudeval.hashnode.dev/review-bicep-before-deployment</link><guid isPermaLink="true">https://cloudeval.hashnode.dev/review-bicep-before-deployment</guid><category><![CDATA[Azure]]></category><category><![CDATA[Bicep]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Infrastructure as code]]></category><category><![CDATA[GitHub Actions]]></category><dc:creator><![CDATA[Prateek Singh]]></dc:creator><pubDate>Tue, 06 Oct 2026 09:32:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abece53ffeae644324fef94/2f4a9c44-20bd-4465-800c-247f6aeb8190.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR:</strong> To review a Bicep change before deployment, compile it with <code>az bicep build</code>, run the Bicep linter, <code>az deployment group validate</code> and 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.</p>
</blockquote>
<p><em>By Prateek Singh, founder of Ganak AI Labs. Updated 6 October 2026.</em></p>
<p>Your pull request touches <code>main.bicep</code>. CI is green, and what-if prints a tidy list of <code>+ Create</code> and <code>~ Modify</code> lines. Someone approves it.</p>
<p>Two weeks later you learn the "small" change opened a storage account to the internet, or doubled the bill with a new SKU.</p>
<p>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.</p>
<p><em>Disclosure: I'm the founder of Cloudeval.</em></p>
<p><strong>Key terms used below</strong></p>
<ul>
<li><p><strong>Bicep:</strong> Microsoft's language for Azure infrastructure. It compiles to an ARM template.</p>
</li>
<li><p><strong>ARM template:</strong> the Azure Resource Manager JSON file that Azure actually deploys.</p>
</li>
<li><p><strong>what-if:</strong> an Azure CLI command that previews changes against what's already deployed.</p>
</li>
<li><p><strong>Bicep linter:</strong> the built-in checker, configured in <code>bicepconfig.json</code>.</p>
</li>
</ul>
<h2>What does each Bicep pre-deployment check catch?</h2>
<table>
<thead>
<tr>
<th>Check</th>
<th>Catches</th>
<th>Needs Azure access?</th>
<th>Misses</th>
</tr>
</thead>
<tbody><tr>
<td><code>az bicep build</code></td>
<td>Syntax and type errors</td>
<td>No</td>
<td>Intent and design</td>
</tr>
<tr>
<td>Bicep linter</td>
<td>Hard-coded URLs, secrets in outputs, insecure defaults, old API versions</td>
<td>No</td>
<td>Topology, cost, cross-resource risk</td>
</tr>
<tr>
<td><code>az deployment group validate</code></td>
<td>Whether Resource Manager accepts the template at a real scope</td>
<td>Yes</td>
<td>Whether you should deploy it</td>
</tr>
<tr>
<td>what-if</td>
<td>The resource-level diff against current Azure state</td>
<td>Yes, deploy-level</td>
<td>Whether that diff is safe or affordable</td>
</tr>
<tr>
<td>Design, cost and security review</td>
<td>Public exposure, dependency shape, cost drivers</td>
<td>Not for template review</td>
<td>Runtime behaviour, live drift</td>
</tr>
</tbody></table>
<p>Keep the first four. The gap is the last row.</p>
<h2>How do I review a Bicep change before deployment?</h2>
<ol>
<li><p>Compile the Bicep file to ARM JSON with <code>az bicep build</code>.</p>
</li>
<li><p>Run the native checks: the linter, <code>validate</code> and what-if.</p>
</li>
<li><p>Review design, cost and security on the compiled ARM JSON, locally or on every pull request.</p>
</li>
<li><p>Read the findings in a fixed order, then re-run what-if right before you deploy.</p>
</li>
</ol>
<h2>Step 1: Compile with <code>az bicep build</code></h2>
<p>Bicep compiles to ARM JSON, and that's what Azure actually deploys.</p>
<pre><code class="language-bash">az bicep build --file ./infra/main.bicep --outfile ./infra/main.json
</code></pre>
<p>Compile a <code>.bicepparam</code> file the same way:</p>
<pre><code class="language-bash">az bicep build-params --file ./infra/main.bicepparam --outfile ./infra/main.parameters.json
</code></pre>
<p>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.</p>
<h2>Step 2: Run the native checks (linter, validate, what-if)</h2>
<h3>The linter</h3>
<p>Set the rules that matter for review to <code>error</code> in <code>bicepconfig.json</code>:</p>
<pre><code class="language-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" }
      }
    }
  }
}
</code></pre>
<p>Then run it in CI:</p>
<pre><code class="language-bash">az bicep lint --file ./infra/main.bicep
</code></pre>
<h3>validate and what-if</h3>
<p>These two need a real Azure scope:</p>
<pre><code class="language-bash">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
</code></pre>
<p><code>sub</code>, <code>mg</code> and <code>tenant</code> variants cover other scopes. Keep in mind:</p>
<ul>
<li><p>what-if needs deploy permissions, including <code>Microsoft.Resources/deployments/*</code>. Reviewers often lack them.</p>
</li>
<li><p>Azure-set defaults can show up as changes that won't happen.</p>
</li>
<li><p>Resources deployed through <code>templateLink</code>, including template specs, aren't expanded.</p>
</li>
</ul>
<p>For refactors, <code>bicep snapshot</code> (Bicep CLI 0.41.2+) diffs what a <code>.bicepparam</code> would deploy, without contacting Azure:</p>
<pre><code class="language-bash">bicep snapshot --mode overwrite ./infra/main.bicepparam   # on main
bicep snapshot --mode validate  ./infra/main.bicepparam   # on the PR branch
</code></pre>
<p>You now know what will change. You still don't know if it's a good idea.</p>
<h2>Step 3: Review design, cost and security</h2>
<p>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.</p>
<p>I'll use <a href="https://cloudeval.ai/?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=step3">Cloudeval</a> here. It reviews the compiled ARM JSON and returns architecture and dependency diagrams with cost and security findings. It doesn't read <code>.bicep</code> files directly, which is why Step 1 matters.</p>
<p>Here's a two-minute demo:</p>
<p><a class="embed-card" href="https://www.youtube.com/watch?v=Xzt4cNKUHxU">https://www.youtube.com/watch?v=Xzt4cNKUHxU</a></p>

<h3>Option A: from your terminal</h3>
<p>Install and sign in:</p>
<pre><code class="language-bash">npm install -g @ganakailabs/cloudeval-cli   # Node.js 20+
cloudeval login            # or: cloudeval login --headless  (SSH/containers)
cloudeval auth status
</code></pre>
<p>Create a project from the compiled template and run every report:</p>
<pre><code class="language-bash">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
</code></pre>
<p>This runs against the template, not your subscription, so you need a Cloudeval account but no Azure credentials.</p>
<p>Read the results:</p>
<pre><code class="language-bash">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
</code></pre>
<img src="https://mintcdn.com/ganakailabs-db727e50/431cYuJMCQHimeRQ/assets/images/reports/issues-list.png" alt="Cloudeval Top Actions list with findings such as Azure.Storage.BlobPublicAccess and Azure.Storage.Firewall, each tagged Security, Cost or Operational" style="display:block;margin:0 auto" />

<p><em>Each finding names the rule, the resource type and the pillar.</em></p>
<p>Or open the diagram:</p>
<pre><code class="language-bash">cloudeval open project "$PROJECT_ID" \
  --view preview --layout dependency --print-url --no-open
</code></pre>
<img src="https://mintcdn.com/ganakailabs-db727e50/431cYuJMCQHimeRQ/assets/images/diagrams/architecture-code-split-view.png" alt="Cloudeval split view with the generated architecture diagram on the left and the project file explorer and ARM JSON outline on the right" style="display:block;margin:0 auto" />

<p><em>The generated diagram sits next to your files and ARM JSON outline.</em></p>
<p>Prefer clicking? In the web app, choose Projects, then Create project, then Upload Template File, and drop in <code>main.json</code> (up to 2 MB). The <a href="https://docs.cloudeval.ai/guides/connect/arm-bicep?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=import-docs">import guide</a> covers URLs, parameter files and linked templates.</p>
<img src="https://mintcdn.com/ganakailabs-db727e50/431cYuJMCQHimeRQ/assets/images/workspace/code-editor-arm-template.png" alt="Imported ARM template beside project files and the generated ARM JSON outline in Cloudeval" style="display:block;margin:0 auto" />

<p><em>Your template, ARM JSON outline and report history live in one workspace.</em></p>
<h3>Option B: on every pull request (GitHub Action)</h3>
<p><code>ganakailabs/cloudeval-action</code> posts one result comment per PR run. It can also write SARIF output and enforce merge gates.</p>
<p>Setup takes three steps (see the <a href="https://docs.cloudeval.ai/guides/review/github-actions?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=action-docs">GitHub Actions guide</a>):</p>
<ol>
<li><p>Link the repo to a Cloudeval project with the Cloudeval GitHub App.</p>
</li>
<li><p>Create a CI access key scoped to that project.</p>
</li>
<li><p>Add the <code>CLOUDEVAL_ACCESS_KEY</code> and <code>CLOUDEVAL_PROJECT_ID</code> repository secrets.</p>
</li>
</ol>
<p>One Bicep detail: the action reviews the synced files at the pushed commit, and it reads ARM JSON. So commit the compiled <code>infra/main.json</code> and <code>main.parameters.json</code>, and fail CI if they're stale.</p>
<pre><code class="language-yaml">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
</code></pre>
<p>Keep the jobs separate. The review job stops on a dirty working tree, and building inside it would dirty it.</p>
<p>Set the entry point and gates in <code>.cloudeval/config.yaml</code> at the root of the synced source. With a source root of <code>infra</code>, that's <code>infra/.cloudeval/config.yaml</code>:</p>
<pre><code class="language-yaml">version: 1
stacks:
  - id: main
    entry: main.json                   # relative to the source root: infra/main.json -&gt; 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
</code></pre>
<p>Keys you leave out of <code>ci.gates</code> take defaults, so set the score floor on purpose. Start with <code>comment_only</code>. Once you trust the thresholds, switch to <code>block_pull_request</code> and make the job a required status check.</p>
<h2>Step 4: Read the findings</h2>
<p>The PR comment keeps your gate separate from what the review saw:</p>
<pre><code class="language-text">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)
</code></pre>
<p><code>Overall</code> reflects your thresholds. <code>critical</code> is the posture Cloudeval observed. A PR can pass a loose gate and still deserve a hard look.</p>
<img src="https://mintcdn.com/ganakailabs-db727e50/BrRYz9nGRkigfTsV/assets/images/cli/github-action-validation-architecture.png" alt="Expanded Cloudeval PR comment showing validation details (unit tests and policy checks) and architecture signals such as resource count, relationships and high-risk findings" style="display:block;margin:0 auto" />

<p><em>The expanded comment shows validation results and architecture signals.</em></p>
<p>Read the findings in this order:</p>
<ol>
<li><p>Dependency view. Does the topology match the PR description? Look for surprise public endpoints, identity or subnet changes.</p>
</li>
<li><p>High-severity findings. Did this PR change the affected resource?</p>
</li>
<li><p>Cost drivers. Should a SKU change before rollout? Numbers are estimates.</p>
</li>
<li><p>Gaps. "Not assessed" doesn't mean safe.</p>
</li>
</ol>
<img src="https://mintcdn.com/ganakailabs-db727e50/431cYuJMCQHimeRQ/assets/images/diagrams/dependency-view.png" alt="Cloudeval dependency view of an Azure template with one resource selected and its upstream and downstream paths highlighted" style="display:block;margin:0 auto" />

<p><em>Select a resource to see its upstream and downstream paths.</em></p>
<p>Then run what-if at deploy time anyway.</p>
<h2>What belongs in a Bicep pre-deploy checklist?</h2>
<pre><code class="language-markdown">## 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
</code></pre>
<h2>What doesn't a template review catch?</h2>
<p>Cloudeval adds diagrams, cost and security findings from 1,650+ checks, right in the PR. It covers Azure, plus AWS CloudFormation (beta).</p>
<p>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.</p>
<h2>Frequently asked questions</h2>
<h3>How do I preview Azure deployment changes before deploying?</h3>
<p>Run <code>az deployment group what-if</code> with the template and parameters you'll deploy. It lists each resource that would be created, modified or deleted, and needs deploy permissions.</p>
<h3>Why does Bicep what-if show changes that won't happen?</h3>
<p>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.</p>
<h3>Can I review a Bicep change without access to the Azure subscription?</h3>
<p>Partly. <code>build</code>, the linter and <code>bicep snapshot</code> run locally, but <code>validate</code> and what-if need deploy-level access. Cloudeval works from the compiled ARM JSON, so you need a Cloudeval account, not subscription credentials.</p>
<h3>Does Cloudeval read <code>.bicep</code> files directly?</h3>
<p>No. Compile with <code>az bicep build</code> and review the ARM JSON. That's what Azure actually deploys anyway.</p>
<h3>How do I block a merge on high-risk findings or cost?</h3>
<p>Add <code>ci.gates</code> to <code>.cloudeval/config.yaml</code>, for example <code>fail_when_high_risk_findings_exist</code> or <code>max_monthly_cost_usd</code>. Set <code>enforcement: block_pull_request</code> and make the job a required check in branch protection.</p>
<hr />
<p>For the longer version, read <a href="https://cloudeval.ai/answers/review-bicep-before-deployment?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=cta">How do I review a Bicep or ARM template before deployment?</a> Or browse a <a href="https://github.com/ganakailabs/cloudeval-azure-arm-review-example/pull/8">public example PR</a> with real caveats, including failed validation tests.</p>
<p><strong>Related on cloudeval.ai</strong></p>
<ul>
<li><p><a href="https://cloudeval.ai/answers/bicep-to-cloud-diagram?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=related-diagram">How to convert Bicep to a cloud architecture diagram</a></p>
</li>
<li><p><a href="https://cloudeval.ai/solutions/github-cloud-review?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=related-github">Cloud infrastructure review in GitHub pull requests</a></p>
</li>
<li><p><a href="https://cloudeval.ai/glossary/iac-review?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=related-glossary">What is IaC review?</a></p>
</li>
<li><p><a href="https://cloudeval.ai/compare?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=related-compare">Compare Cloudeval with other cloud review and diagram tools</a></p>
</li>
</ul>
<blockquote>
<p><strong>Try it</strong></p>
<p>Compile your Bicep, then review the ARM JSON before it ships. Open the <a href="https://cloudeval.ai/app?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=try-app">Cloudeval app</a> and follow the <a href="https://docs.cloudeval.ai/guides/review/iac-before-deployment?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=try-docs">IaC pre-deployment review guide</a>.</p>
</blockquote>
<blockquote>
<p><strong>About the author</strong></p>
<p><strong>Prateek Singh</strong> is the founder of Ganak AI Labs and builds Cloudeval AI.</p>
<p><a href="https://cloudeval.ai/?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=author">cloudeval.ai</a> | <a href="https://docs.cloudeval.ai/?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=review-bicep-tutorial&amp;utm_content=author">Docs</a> | <a href="https://www.youtube.com/@GanakAILabs">YouTube</a> | <a href="https://github.com/ganakailabs">GitHub</a> | <a href="https://www.linkedin.com/in/prateekkumarsingh/">LinkedIn</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Give Cursor, Claude Code or Codex your Azure architecture over MCP]]></title><description><![CDATA[TL;DR: To give Cursor, Claude Code or Codex your Azure architecture, run the Cloudeval CLI's local MCP server (npx -y @ganakailabs/cloudeval-cli mcp serve --toolset readonly) with a project-scoped, re]]></description><link>https://cloudeval.hashnode.dev/give-cursor-claude-code-or-codex-your-azure-architecture-over-mcp</link><guid isPermaLink="true">https://cloudeval.hashnode.dev/give-cursor-claude-code-or-codex-your-azure-architecture-over-mcp</guid><dc:creator><![CDATA[Prateek Singh]]></dc:creator><pubDate>Fri, 02 Oct 2026 08:02:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abece53ffeae644324fef94/4b412edf-b0ad-4303-9e82-58bd17ffe8c7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR:</strong> To give Cursor, Claude Code or Codex your Azure architecture, run the Cloudeval CLI's local MCP server (<code>npx -y @ganakailabs/cloudeval-cli mcp serve --toolset readonly</code>) with a project-scoped, read-only access key. The agent can then read your architecture graph and saved cost and Well-Architected reports. It can't deploy or change Azure resources.</p>
</blockquote>
<p><em>By Prateek Singh, founder of Ganak AI Labs, the team behind Cloudeval AI. Published 2 October 2026; commands checked against @ganakailabs/cloudeval-cli 0.38.5 and each client's MCP docs that day.</em></p>
<p>Ask a coding agent "is this storage account reachable from the internet?" and it will happily read <code>main.bicep</code> and give you an answer. What it can't see is everything outside the file: the other resource groups, the subnet the private endpoint lands in, the cost report from last week, the Well-Architected findings nobody has triaged yet. So it guesses, confidently.</p>
<p>The Model Context Protocol (MCP) fixes the plumbing side of this. Instead of you pasting screenshots and JSON into chat, the agent calls a tool server and gets structured answers back. This post shows how to connect Cursor, Claude Code or Codex to an MCP server that exposes your Azure architecture graph and saved reports, with a read-only toolset and a scoped key. It finishes with prompts that actually produce useful answers.</p>
<p>This isn't Microsoft's Azure MCP Server, which queries live Azure resources. The two can run side by side.</p>
<p><strong>Key terms used below</strong></p>
<ul>
<li><strong>MCP (Model Context Protocol):</strong> an open protocol that lets AI clients such as Cursor, Claude Code and Codex call external tool servers and get structured results.</li>
<li><strong>stdio server:</strong> an MCP server the client starts as a local process. It has no network endpoint.</li>
<li><strong>ARM / Bicep:</strong> Azure Resource Manager JSON templates, and Microsoft's Bicep language that compiles to them.</li>
</ul>
<p>The server I'm using is the one in the <a href="https://cloudeval.ai/?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=intro">Cloudeval</a> CLI, because it's the one I know best.</p>
<p><em>Disclosure: I'm the founder of Cloudeval (Ganak AI Labs).</em></p>
<p>If you prefer to watch first, here is the 52-second version of the idea: the same review context, reachable from the terminal, the IDE, CI and your own agents.</p>
<p><a class="embed-card" href="https://www.youtube.com/watch?v=wA6poXNBlVs">https://www.youtube.com/watch?v=wA6poXNBlVs</a></p>

<h2>What can a coding agent read over MCP, and what can't it do?</h2>
<p>Before wiring anything up, it's worth being precise about the boundary.</p>
<table>
<thead>
<tr>
<th>The agent <strong>can</strong> read (default <code>readonly</code> toolset)</th>
<th>The agent <strong>can't</strong> do</th>
</tr>
</thead>
<tbody><tr>
<td>Projects your key can see, plus details and overview</td>
<td>Deploy, change or remediate Azure resources</td>
</tr>
<tr>
<td>Architecture graph nodes and relationships</td>
<td>See anything outside the projects the key is scoped to</td>
</tr>
<tr>
<td>Saved cost and Well-Architected reports and rule results</td>
<td>Run reports or AI answers (those need an explicit, wider toolset)</td>
</tr>
<tr>
<td>The validation check catalogue</td>
<td>Reach a hosted endpoint: the server runs locally over <strong>stdio</strong></td>
</tr>
<tr>
<td>Agent Profile definitions (<code>architecture</code>, <code>cost</code>, <code>security-reviewer</code>, ...)</td>
<td>Read live AWS or GCP. Inputs are live Azure (Cloud sync), ARM JSON, Bicep compiled to ARM, and AWS CloudFormation as a static beta</td>
</tr>
</tbody></table>
<p>The data comes from Cloudeval projects, so you need at least one: either a live Azure subscription connected through Cloud sync, or an imported template. If you write Bicep, compile it first (<code>az bicep build</code>) and import the ARM JSON. Cloudeval reviews the compiled output rather than parsing <code>.bicep</code> directly.</p>
<h2>How do I connect Cursor, Claude Code or Codex to Azure architecture over MCP?</h2>
<ol>
<li>Create a scoped, read-only access key.</li>
<li>Check the server locally with <code>mcp status</code> and <code>doctor --mcp</code>.</li>
<li>Add the stdio server to your client's MCP config.</li>
<li>Verify the tool list and the project scope.</li>
<li>Keep the <code>readonly</code> toolset and widen access only on purpose.</li>
<li>Use Agent Profiles as reviewer lenses.</li>
<li>Use prompts that ask for cited evidence.</li>
</ol>
<p>Each step is covered below.</p>
<h2>Step 1: Create a scoped, read-only access key</h2>
<p>Don't hand an agent your interactive login. Create a dedicated key:</p>
<ol>
<li>In the Cloudeval app, open <strong>Developer -&gt; API &amp; CLI access keys</strong>.</li>
<li>Create a key from the <strong>MCP Read-only</strong> template.</li>
<li>Scope it to the one project you want the agent to see.</li>
<li>Check the expiry and capabilities. The template defaults to 30 days. It includes project, connection, report, download, diagram-export and MCP access, but <strong>not</strong> billing reads or evaluation runs.</li>
</ol>
<p><img src="https://mintcdn.com/ganakailabs-db727e50/UudZHLgJXMxYURyC/assets/images/mcp/glama-create-scoped-key.png" alt="Cloudeval create auth key form with the MCP Read-only template selected, a project scope picker and a 30d expiry" />
<em>The create-key form with the MCP Read-only template, a project scope and the 30-day default expiry (the docs example names the key for another client; name yours after the agent that uses it). (Source: Cloudeval docs)</em></p>
<p>Store it in your shell profile or secret manager as <code>CLOUDEVAL_ACCESS_KEY</code>. Each client config below reads it from the environment, so the secret never lands in a file you might commit.</p>
<pre><code class="language-bash">export CLOUDEVAL_ACCESS_KEY="&lt;your scoped key&gt;"   # keep out of dotfiles you commit
</code></pre>
<h2>Step 2: Check the server locally</h2>
<p>You need Node.js 20+. The no-install form uses <code>npx</code>. If you'd rather install globally, run <code>npm install -g @ganakailabs/cloudeval-cli</code> and use <code>cloudeval</code> directly.</p>
<pre><code class="language-bash"># Inspect the MCP surface and diagnose local setup before touching any client
npx -y @ganakailabs/cloudeval-cli mcp status --format json
npx -y @ganakailabs/cloudeval-cli doctor --mcp --format json
</code></pre>
<p><img src="https://mintcdn.com/ganakailabs-db727e50/431cYuJMCQHimeRQ/assets/images/cli/cli-json-output.png" alt="Terminal output of cloudeval doctor --mcp --format json listing the MCP server info, toolsets, tools, resources and prompts" />
<em><code>doctor --mcp --format json</code> shows the server, toolsets, tools, resources and prompts before any client is involved (docs capture from an earlier CLI version, so your lists may be longer). (Source: Cloudeval docs)</em></p>
<p>The command every client will launch is:</p>
<pre><code class="language-bash">npx -y @ganakailabs/cloudeval-cli mcp serve --toolset readonly
</code></pre>
<p>Two things that trip people up:</p>
<ul>
<li><strong>Don't run <code>login</code> through the MCP process.</strong> <code>mcp serve</code> reserves stdin for protocol messages. Authenticate beforehand (stored <code>cloudeval login</code>, or <code>cloudeval login --headless</code> over SSH), or rely on the env var.</li>
<li><strong>Containers don't inherit your host login.</strong> Pass the scoped key at runtime.</li>
</ul>
<p>The CLI can also generate the config for you. Always start with <code>--dry-run</code> so you can read what it would write:</p>
<pre><code class="language-bash">cloudeval mcp setup cursor --dry-run --toolset readonly
cloudeval mcp setup codex  --dry-run --toolset readonly
cloudeval mcp setup claude --dry-run
cloudeval mcp setup generic --dry-run --toolset readonly --format json
</code></pre>
<p>The supported targets are <code>codex</code>, <code>claude</code>, <code>cursor</code>, <code>vscode</code> and <code>generic</code>. <code>--toolset</code> defaults to <code>readonly</code>. Without <code>--dry-run</code>, setup can write to the client's config file. Pass <code>--config-path</code> if you want to control where it goes.</p>
<h2>Step 3: Wire up your client</h2>
<p>Every client below runs the same stdio command. Only the config syntax differs.</p>
<p><img src="https://mintcdn.com/ganakailabs-db727e50/431cYuJMCQHimeRQ/assets/images/mcp/mcp-client-configs.png" alt="Cloudeval MCP client configs for Codex, Cursor, Claude Code, VS Code and generic clients" />
<em>Codex uses a registration command; Cursor, Claude Code, VS Code and generic clients share one stdio config shape. (Source: Cloudeval docs)</em></p>
<h3>Cursor</h3>
<p>Add this to <code>.cursor/mcp.json</code> (project) or <code>~/.cursor/mcp.json</code> (global). Cursor's <code>${env:NAME}</code> interpolation keeps the key out of the file:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "cloudeval": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@ganakailabs/cloudeval-cli", "mcp", "serve", "--toolset", "readonly"],
      "env": { "CLOUDEVAL_ACCESS_KEY": "${env:CLOUDEVAL_ACCESS_KEY}" }
    }
  }
}
</code></pre>
<p>If Cursor is launched from the dock and doesn't see your shell variables, Cursor also supports an <code>envFile</code> for stdio servers. Point it at a git-ignored file.</p>
<h3>Claude Code</h3>
<p>Claude Code wants its own options (like <code>--env</code>) before <code>--</code>, and the server command after it:</p>
<pre><code class="language-bash">claude mcp add cloudeval --env CLOUDEVAL_ACCESS_KEY="$CLOUDEVAL_ACCESS_KEY" \
  -- npx -y @ganakailabs/cloudeval-cli mcp serve --toolset readonly

claude mcp get cloudeval     # confirm it registered and connected
</code></pre>
<p>This uses the default local scope, which is stored in <code>~/.claude.json</code>. Avoid <code>--scope project</code> here: that writes <code>.mcp.json</code> into the repo, and you'd be one <code>git add .</code> away from committing the key.</p>
<h3>Codex</h3>
<p>You can register it with the documented one-liner:</p>
<pre><code class="language-bash">codex mcp add cloudeval -- cloudeval mcp serve --toolset readonly
</code></pre>
<p>To forward the key from your environment instead of storing its value, edit <code>~/.codex/config.toml</code>:</p>
<pre><code class="language-toml">[mcp_servers.cloudeval]
command = "npx"
args = ["-y", "@ganakailabs/cloudeval-cli", "mcp", "serve", "--toolset", "readonly"]
env_vars = ["CLOUDEVAL_ACCESS_KEY"]
</code></pre>
<h3>Anything else that speaks stdio</h3>
<p>Use the generic <code>mcpServers</code> shape, which is the same entry published in the official MCP Registry under <code>io.github.ganakailabs/cloudeval</code>:</p>
<pre><code class="language-json">{"mcpServers":{"cloudeval":{"command":"npx","args":["-y","@ganakailabs/cloudeval-cli","mcp","serve","--toolset","readonly"],"env":{"CLOUDEVAL_ACCESS_KEY":"&lt;your scoped key&gt;"}}}}
</code></pre>
<h2>Step 4: Verify before you trust it</h2>
<p>Restart the client if it needs a restart, then:</p>
<ol>
<li>Ask it to <strong>list the available Cloudeval tools</strong>.</li>
<li>Run something low-risk: <code>capabilities_get</code> or <code>projects_list</code>.</li>
<li>Check that the result shows the profile, base URL and auth mode you expect, and <strong>only</strong> the project you scoped the key to.</li>
</ol>


<p>If billing tools return permission errors, that's expected. The MCP Read-only key doesn't grant billing read access.</p>
<h2>Step 5: Keep it read-only and widen on purpose</h2>
<p><code>readonly</code> is the default because review agents rarely need anything else. If a job calls for more, widen deliberately with one of the focused toolsets:</p>
<table>
<thead>
<tr>
<th>Toolset</th>
<th>Use it for</th>
</tr>
</thead>
<tbody><tr>
<td><code>readonly</code></td>
<td>Project, graph, report and capability reads (the default)</td>
</tr>
<tr>
<td><code>graph</code></td>
<td>Graph, timeline, diff, sync-run and insight reads</td>
</tr>
<tr>
<td><code>validation</code></td>
<td><code>template_validate</code>, <code>template_test</code>, <code>template_parse</code> and rule-catalogue reads</td>
</tr>
<tr>
<td><code>reports</code></td>
<td>Report runs, downloads and deeplinks</td>
</tr>
<tr>
<td><code>billing</code></td>
<td>Billing inspection, invoices and explicit checkout tools</td>
</tr>
<tr>
<td><code>all</code></td>
<td>The full surface</td>
</tr>
</tbody></table>
<p>Note what's <strong>not</strong> in <code>readonly</code>: <code>ask</code>, <code>agent_profiles_run</code>, <code>reports_run</code> and the template-validation calls. Those start work on the Cloudeval side and can use credits. Your key also has to allow them.</p>
<p>After changing toolsets, restart the client so it refreshes its tool list.</p>
<h2>Step 6: Use Agent Profiles as reviewer lenses</h2>
<p>Agent Profiles change how an answer is organized, not what evidence is available. The public ids are <code>architecture</code>, <code>cost</code>, <code>triage</code>, <code>remediation</code>, <code>visual-explainer</code>, <code>scripter</code>, <code>change-reviewer</code>, <code>evidence-auditor</code> and <code>security-reviewer</code>. There's no separate Well-Architected profile, because <code>architecture</code> already includes that lens.</p>
<p>On <code>readonly</code>, the agent can discover them with <code>agent_profiles_list</code> and <code>agent_profiles_get</code>. Running one is excluded from <code>readonly</code>, so either use a wider toolset or run it from your terminal:</p>
<pre><code class="language-bash">cloudeval agents run change-reviewer "Review this pull request" --project &lt;project-id&gt;
cloudeval agents run security-reviewer "Review exposure and identity risk" --project &lt;project-id&gt;
</code></pre>
<p>The <code>change-reviewer</code> profile is built to return PASS/WARN/BLOCK with current, proposed and live evidence kept separate. If evidence is stale or missing, it should report an evidence gap rather than a pass.</p>
<h2>Step 7: Prompts that work</h2>
<p>Vague prompts get vague answers. These work better because each one names the evidence it wants:</p>
<pre><code class="language-text">Using the cloudeval tools, list my projects, then get the architecture graph for "&lt;project name&gt;".
Which resources have public network access, and which relationships connect them to data stores?
Cite the resource ID for each claim.
</code></pre>
<pre><code class="language-text">Read the latest Well-Architected report for project &lt;id&gt;. Give me the five highest-severity findings
as a table: resource, finding, pillar, and the report reference. If any pillar is "Not assessed",
list it separately. Don't fill gaps with general advice.
</code></pre>
<pre><code class="language-text">I'm about to change infra/main.json in this repo. Using the project graph, tell me which resources
depend on the App Service plan defined there, so I know what this PR could affect.
</code></pre>
<pre><code class="language-text">Get the agent profile "cost" and summarize what it emphasizes. Then, using only the latest cost
report for project &lt;id&gt;, name the top three cost drivers and what evidence supports each one.
</code></pre>
<p>The server also publishes MCP prompts (<code>cloudeval-architecture-review</code>, <code>cloudeval-template-preflight</code>, <code>cloudeval-impact-analysis</code> and others) and resources such as <code>cloudeval://projects</code> and <code>cloudeval://reports/latest</code>. Clients that support prompts and resources show them in their prompt or <code>@</code> menus.</p>
<p><img src="https://mintcdn.com/ganakailabs-db727e50/WhoaERvnDvS8ic-R/assets/images/workspace/workspace-chat-insights.png" alt="Cloudeval agent answer grounded in selected resources and the diagram" />
<em>A useful agent answer stays tied to the project, the selected resources and the visible diagram. (Source: Cloudeval docs)</em></p>
<p>A habit worth keeping: <strong>ask the agent to name the project, resource or report behind every important claim.</strong> If it can't, treat the answer as a hypothesis.</p>
<h2>What are the common MCP setup mistakes?</h2>
<ul>
<li><strong>Committing the key.</strong> Use local or user scope and <code>${env:...}</code> or <code>env_vars</code>. Don't put it in Dockerfiles or project-scoped config you check in.</li>
<li><strong>Expecting a URL.</strong> This is a local stdio server. There's no Cloudeval-hosted remote MCP endpoint to paste into a web chat.</li>
<li><strong>Assuming it sees live state.</strong> A template-backed project describes the template you imported, not what's running. Check the sync time on live projects.</li>
<li><strong>Letting the agent apply changes.</strong> The agent can prepare a fix. A human reviews it, and the normal deployment pipeline ships it.</li>
</ul>
<h2>Frequently asked questions</h2>
<h3>Is this the same as Microsoft's Azure MCP Server?</h3>
<p>No. Microsoft's Azure MCP Server queries live Azure resources. The Cloudeval MCP server gives the agent review context: project architecture graphs, saved cost and Well-Architected reports and the validation check catalogue. They can run side by side in the same client.</p>
<h3>How do I add an MCP server to Claude Code?</h3>
<p>Run <code>claude mcp add &lt;name&gt;</code>, put Claude Code's own options such as <code>--env</code> before <code>--</code>, and the server command after it. Then run <code>claude mcp get &lt;name&gt;</code> to confirm it connected. The default local scope keeps the entry out of your repo.</p>
<h3>Does it work in VS Code?</h3>
<p>Yes. <code>cloudeval mcp setup vscode --dry-run</code> prints the config for VS Code. The server is the same stdio command used by Cursor, Claude Code and Codex.</p>
<h3>Can the agent change my Azure resources over MCP?</h3>
<p>No. The default <code>readonly</code> toolset reads projects, graphs, reports and the check catalogue. It can't deploy, change or remediate Azure resources. Tools that start work, such as report runs, are only in wider toolsets that you enable on purpose.</p>
<h3>Is there a hosted MCP URL I can paste into a web chat?</h3>
<p>No. It's a local stdio server that your client launches as a process. There's no Cloudeval-hosted remote MCP endpoint.</p>
<h2>Takeaway</h2>
<p>MCP doesn't make an agent smarter about your architecture by itself. What helps is giving it scoped access to real evidence: a graph, saved reports and a check catalogue, with read-only as the default. Start with one project, one key and the <code>readonly</code> toolset, then make the agent cite its sources. Widen access only when a specific job needs it.</p>
<p>If you're choosing between MCP servers for Azure work, I wrote up <a href="https://cloudeval.ai/answers/mcp-tools-for-azure-infrastructure-review?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=cta">which MCP tool fits which Azure review job</a>. It covers Microsoft's <a href="https://learn.microsoft.com/en-us/azure/developer/azure-mcp-server/overview">Azure MCP Server</a> for live resource queries, Bicep tooling while authoring, and project-level review context. The full client reference is in the <a href="https://docs.cloudeval.ai/agents/mcp-client-setup?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=docs">MCP client setup docs</a>.</p>
<p><strong>Related on cloudeval.ai</strong></p>
<ul>
<li><a href="https://cloudeval.ai/answers/mcp-tools-for-azure-infrastructure-review?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=related-answer">Which MCP tools help review Azure infrastructure?</a></li>
<li><a href="https://cloudeval.ai/answers/review-bicep-before-deployment?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=related-bicep">How to review a Bicep or ARM template before deployment</a></li>
<li><a href="https://docs.cloudeval.ai/agents/profiles?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=related-profiles-docs">Agent Profiles reference</a></li>
<li><a href="https://cloudeval.ai/compare?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=related-compare">Compare Cloudeval with other cloud review and diagram tools</a></li>
</ul>
<blockquote>
<p><strong>Try it</strong></p>
<p>Want your own agent to answer from your architecture? Open the <a href="https://cloudeval.ai/app?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=try-app">Cloudeval app</a> and follow the <a href="https://docs.cloudeval.ai/agents/mcp-client-setup?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=try-docs">MCP client setup guide</a>.</p>
</blockquote>
<blockquote>
<p><strong>About the author</strong></p>
<p><strong>Prateek Singh</strong> is the founder of Ganak AI Labs and builds Cloudeval AI.</p>
<p><a href="https://cloudeval.ai/?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=author">cloudeval.ai</a> · <a href="https://docs.cloudeval.ai/?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=mcp-azure-review&amp;utm_content=author">Docs</a> · <a href="https://www.youtube.com/@GanakAILabs">YouTube</a> · <a href="https://github.com/ganakailabs">GitHub</a> · <a href="https://www.linkedin.com/in/prateekkumarsingh/">LinkedIn</a></p>
</blockquote>
]]></content:encoded></item></channel></rss>