Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Infrastructure as Code By James Joyner IV · · 10 min read

Bicep Scopes and the existing Keyword: The Two Things That Bite Everyone

Most Bicep deployment failures trace to scope confusion or a missing existing keyword that silently recreates a live resource. Here's how to get both right.

  • #iac
  • #ai
  • #bicep
  • #azure
  • #scopes

If you’ve shipped Bicep at any scale, you’ve hit both of these. The first is the cryptic “the scope of this deployment doesn’t match” error that appears when a module targets the wrong level and gives you nothing to go on. The second is quieter and worse: a reference without the existing keyword that Bicep interprets as a create, so a deployment you thought would only read an output from a shared resource instead tries to recreate or overwrite it. Both are structural mistakes that the editor won’t catch, and both surface only at deploy time — sometimes destructively.

These two concepts, scopes and existing, account for a disproportionate share of real-world Bicep pain. This guide explains how each works and how to verify you’ve got them right before you apply anything.

The existing keyword: reference, don’t recreate

When you want to point at a resource that already lives in Azure — to read a property, grab a connection string, or attach a role assignment — you declare it with existing:

// Reference an existing storage account; do NOT create one.
resource sharedStorage 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
  name: 'sharedplatformsa'
}

// Read a property from it.
output primaryEndpoint string = sharedStorage.properties.primaryEndpoints.blob

Leave out existing and the meaning flips entirely:

// DANGER: without 'existing', this is a CREATE/UPDATE of the resource.
resource sharedStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'sharedplatformsa'
  location: 'eastus'
  sku: { name: 'Standard_LRS' }
  kind: 'StorageV2'
}

That second block doesn’t error in your editor. It deploys, and depending on the properties you supplied, it can overwrite the SKU, replication, or network rules of a storage account that other teams depend on. The missing keyword is a one-character difference between “read this” and “redefine this,” which is exactly why it’s the most common destructive Bicep mistake. Whenever you reference something you didn’t mean to manage, ask: did I write existing?

Scopes: where does this thing deploy?

Bicep deployments target a scope — tenant, management group, subscription, or resource group — declared with targetScope:

targetScope = 'subscription'

// A subscription-scoped deployment can create resource groups...
resource rg 'Microsoft.Resources/resourceGroups@2023-07-01' = {
  name: 'rg-payments-prod'
  location: 'eastus'
}

// ...and deploy a module INTO that resource group by setting its scope.
module network 'modules/network.bicep' = {
  name: 'network'
  scope: rg
  params: { addressSpace: '10.20.0.0/16' }
}

The targetScope of the file must match how you invoke it (az deployment sub create for a subscription-scoped file), and each module’s scope: must point at the right level. Get the module scope wrong and you get the “scope doesn’t match” error; get the invocation wrong and the deployment is rejected before it starts. The subtle case is passing outputs between scopes — a subscription-scoped parent consuming an output from a resource-group-scoped module — which works only when the dependency and scope wiring are correct.

Cross-subscription references need permissions too

A reference can reach across subscriptions, but the scope you give it changes which identity needs access:

resource remoteKv 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
  name: 'platform-kv'
  scope: resourceGroup(otherSubId, 'rg-platform-shared')
}

This resolves the Key Vault in a different subscription — but only if the identity running the deployment has read access there. A “correct” template can still fail on RBAC, which is why a scope change often surfaces a new permission requirement. That’s a feature, not a bug: it forces you to grant explicit, least-privilege access rather than silently assuming it.

Verify with what-if before every apply

Here’s the discipline that catches both failure modes: never apply a Bicep deployment without running what-if first. It’s Azure’s equivalent of terraform plan, and it shows you exactly what will be created, modified, or deleted at each scope:

# Preview a subscription-scoped deployment.
az deployment sub what-if \
  --location eastus \
  --template-file main.bicep \
  --parameters @prod.bicepparam

If you forgot an existing keyword, what-if shows a + Create (or worse, a ~ Modify) on a resource you only meant to reference — caught before any damage. If a module is scoped wrong, the preview either errors or shows resources landing in the wrong place. The what-if output is the single best safety check in the Bicep workflow, and it costs seconds.

Drafting and reviewing with AI, deciding with what-if

This is a good review task for an LLM, with a firm human checkpoint. A prompt:

You are an Azure infrastructure engineer. Review this Bicep file for scope correctness and misuse of the existing keyword. Flag any reference that would create a resource it should only read, and any module targeting the wrong scope. End with the exact az deployment what-if commands to run.

The model found a real issue and ended where it should:

I found one high-severity issue: the appInsights resource on line 34 has no existing keyword but its name matches a shared Application Insights instance referenced elsewhere. As written, this deployment would attempt to redefine it, potentially resetting its retention settings. I’ve added existing. Before applying, run: az deployment group what-if --resource-group rg-payments-prod --template-file main.bicep --parameters @prod.bicepparam and confirm the plan shows no unexpected creates or modifies on appInsights.

That’s the AI-drafts, human-verifies loop working exactly as intended. The model caught the missing keyword and the destructive consequence — and then handed the decision back to a human with the precise what-if command, because confirming “no unexpected creates” against real plan output is a judgment a person makes, not one to delegate to a model’s confidence.

Get these two right and most of Bicep gets easier

Scopes and existing are where Bicep deployments most often go sideways, and both are learnable in an afternoon. Reference, don’t recreate: write existing whenever you touch a resource you didn’t mean to manage. Deploy at the right level: match targetScope, invocation, and module scope:. And confirm with what-if every single time, because the cost of a wrong guess is sometimes a recreated production resource.

For reviewing your templates, see our Bicep existing resources and scope review prompt, and pair it with the Bicep module authoring prompt and the deployment stacks and what-if prompt. The Infrastructure as Code category covers the rest of the Azure IaC toolchain. Run what-if before you apply — it’s the cheapest insurance against the two mistakes that bite everyone.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.