Bicep Existing Resources

Not every Azure resource needs creation from scratch. Many real-world Bicep templates work alongside resources that already exist — a shared Key Vault managed by a security team, a virtual network set up by the networking team, or a Log Analytics workspace used by multiple applications. The existing keyword lets Bicep reference these pre-existing resources and use their properties without creating or modifying them.

Think of it like moving into a furnished apartment. The walls, plumbing, and furniture are already there. A move-in checklist references those existing items and assigns them new uses — the desk becomes a workspace, the shelves hold books — without rebuilding the apartment from scratch. Bicep's existing keyword does the same: it references what is already there and uses it in new deployments.

The existing Keyword – Syntax

resource <symbolicName> '<ResourceType>@<ApiVersion>' existing = {
  name: '<existingResourceName>'
}

The only difference from a normal resource declaration is the word existing before the opening brace. No properties block is needed — Bicep reads the resource from Azure rather than creating it. The symbolic name then provides access to that resource's properties throughout the rest of the file.

Simple Example – Reference an Existing Key Vault

param keyVaultName string = 'kv-shared-security'
param location string = 'eastus'

// Reference an existing Key Vault managed by the security team
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
  name: keyVaultName
}

// Create a new Web App that reads secrets from the existing Key Vault
resource webApp 'Microsoft.Web/sites@2023-01-01' = {
  name: 'app-mywebapp'
  location: location
  kind: 'app'
  properties: {
    serverFarmId: appServicePlan.id
    siteConfig: {
      appSettings: [
        {
          name: 'KEY_VAULT_URI'
          value: keyVault.properties.vaultUri    // ← read from existing resource
        }
      ]
    }
  }
}

The Key Vault is not modified in any way. Bicep simply reads its vaultUri property and passes it to the new Web App as an environment variable.

What Properties Are Available on Existing Resources

All properties of the existing resource become available through the symbolic name — the same properties available when Bicep creates the resource itself.

// Existing storage account
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
  name: 'myexistingstorage001'
}

// Access its properties
var blobEndpoint   = storageAccount.properties.primaryEndpoints.blob
var storageId      = storageAccount.id
var storageName    = storageAccount.name
var storageRgScope = storageAccount.id

Referencing an Existing Resource in a Different Resource Group

By default, Bicep looks for the existing resource in the same resource group as the deployment. To reference a resource in a different resource group, add the scope property.

Deployment Resource Group:  rg-myapp
Existing Resource Group:    rg-shared-infra

┌──────────────────────┐        ┌──────────────────────────┐
│   rg-myapp           │        │   rg-shared-infra        │
│                      │        │                          │
│   new webApp  ───────┼────────┼──► existing keyVault     │
│                      │  scope │                          │
└──────────────────────┘        └──────────────────────────┘
param sharedResourceGroup string = 'rg-shared-infra'
param keyVaultName string = 'kv-shared-prod'
param location string = 'eastus'

// Reference a Key Vault in a DIFFERENT resource group
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
  name: keyVaultName
  scope: resourceGroup(sharedResourceGroup)   // ← point to the other resource group
}

// Use the Key Vault URI in a new resource in the current resource group
resource webApp 'Microsoft.Web/sites@2023-01-01' = {
  name: 'app-mywebapp'
  location: location
  kind: 'app'
  properties: {
    serverFarmId: appServicePlan.id
    siteConfig: {
      appSettings: [
        {
          name: 'KEY_VAULT_URI'
          value: keyVault.properties.vaultUri
        }
      ]
    }
  }
}

Referencing an Existing Resource in a Different Subscription

param sharedSubscriptionId string = 'aaaa-bbbb-cccc-dddd'
param sharedResourceGroup string = 'rg-central-infra'
param logWorkspaceName string = 'log-central-analytics'

// Reference a Log Analytics workspace in a different subscription
resource logWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = {
  name: logWorkspaceName
  scope: resourceGroup(sharedSubscriptionId, sharedResourceGroup)
}

// Use its resource ID when setting up diagnostics
resource diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'send-to-central-log'
  scope: storageAccount
  properties: {
    workspaceId: logWorkspace.id    // ← cross-subscription workspace ID
    logs: []
    metrics: [
      {
        category: 'Transaction'
        enabled: true
      }
    ]
  }
}

Existing Child Resources

Referencing a child resource (like a subnet inside a VNet, or a secret inside a Key Vault) requires referencing the parent first, then the child.

Method 1 – Reference Parent Then Use Child Name

param vnetName string = 'vnet-shared-prod'
param subnetName string = 'app-subnet'

// Reference the existing parent virtual network
resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' existing = {
  name: vnetName
}

// Reference the specific subnet inside the existing VNet
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-09-01' existing = {
  name: subnetName
  parent: vnet    // ← use parent property to link to existing parent
}

// Use the subnet ID in a new network interface
resource networkInterface 'Microsoft.Network/networkInterfaces@2023-09-01' = {
  name: 'nic-myvm-001'
  location: 'eastus'
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: subnet.id    // ← subnet ID from existing resource
          }
          privateIPAllocationMethod: 'Dynamic'
        }
      }
    ]
  }
}

Method 2 – Nested existing Reference

resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' existing = {
  name: 'vnet-shared-prod'

  resource subnet 'subnets' existing = {
    name: 'app-subnet'
  }
}

// Access using parent::child notation
var subnetId = vnet::subnet.id

Assigning RBAC Roles to Existing Resources

One of the most common uses of existing resources is assigning Role-Based Access Control (RBAC) roles. The new resource (like a Managed Identity on a Web App) needs access to an existing resource (like a Storage Account or Key Vault).

param location string = 'eastus'
param existingStorageName string = 'sharedstorageaccount'

// Reference the existing storage account
resource existingStorage 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
  name: existingStorageName
}

// Create a new Web App with Managed Identity
resource webApp 'Microsoft.Web/sites@2023-01-01' = {
  name: 'app-mywebapp'
  location: location
  kind: 'app'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: appServicePlan.id
  }
}

// Assign the Storage Blob Data Reader role to the Web App
// so it can read blobs from the existing storage account
var storageBlobDataReaderRoleId = '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(existingStorage.id, webApp.id, storageBlobDataReaderRoleId)
  scope: existingStorage    // ← assign the role ON the existing resource
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataReaderRoleId)
    principalId: webApp.identity.principalId
    principalType: 'ServicePrincipal'
  }
}

When existing Is Not the Right Tool

ScenarioUse existing?Better Approach
Resource created elsewhere and used hereYesexisting keyword
Resource created by this Bicep fileNoUse symbolic name directly
Resource may or may not exist yetNoPass resource ID as a parameter
Modifying an existing resource's settingsNoDeclare it as a normal resource (Bicep manages idempotency)

Passing Resource IDs as Parameters – An Alternative Pattern

When the existing resource lives in a completely separate subscription and the cross-subscription scope syntax is too complex, passing the resource ID as a parameter is a simpler alternative.

// Alternative: pass the existing resource ID as a parameter
param existingSubnetId string   // Full resource ID passed in at deploy time
param location string = 'eastus'

resource networkInterface 'Microsoft.Network/networkInterfaces@2023-09-01' = {
  name: 'nic-myvm-001'
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          subnet: {
            id: existingSubnetId    // ← ID passed in as a parameter
          }
          privateIPAllocationMethod: 'Dynamic'
        }
      }
    ]
  }
}

Deploy with the subnet ID provided on the command line:

az deployment group create \
  --resource-group myRG \
  --template-file main.bicep \
  --parameters existingSubnetId='/subscriptions/aaaa/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-prod/subnets/app-subnet'

Complete Example – New App Using Four Existing Resources

param location string = 'eastus'
param environment string = 'prod'
param sharedRG string = 'rg-shared-infra'

// Reference four existing shared resources
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
  name: 'kv-shared-${environment}'
  scope: resourceGroup(sharedRG)
}

resource logWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = {
  name: 'log-shared-${environment}'
  scope: resourceGroup(sharedRG)
}

resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' existing = {
  name: 'vnet-shared-${environment}'
  scope: resourceGroup(sharedRG)
}

resource appSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-09-01' existing = {
  name: 'app-subnet'
  parent: vnet
}

// Create App Service Plan
resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
  name: 'asp-myapp-${environment}'
  location: location
  sku: {
    name: 'B1'
  }
}

// Create Web App that uses all four existing resources
resource webApp 'Microsoft.Web/sites@2023-01-01' = {
  name: 'app-myapp-${environment}'
  location: location
  kind: 'app'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: appServicePlan.id
    virtualNetworkSubnetId: appSubnet.id      // join existing subnet
    siteConfig: {
      appSettings: [
        {
          name: 'KEY_VAULT_URI'
          value: keyVault.properties.vaultUri  // use existing KV URI
        }
        {
          name: 'LOG_WORKSPACE_ID'
          value: logWorkspace.properties.customerId  // use existing workspace
        }
      ]
    }
  }
}

output webAppUrl string = 'https://${webApp.properties.defaultHostName}'

Summary

The existing keyword lets Bicep reference Azure resources that already exist without creating or modifying them. Existing resources expose all their properties through the symbolic name, just like freshly created resources. Cross-resource-group references use the scope property with resourceGroup(). Child resources reference their parent using the parent property. Common use cases include reading Key Vault URIs, obtaining subnet IDs for new VMs, and assigning RBAC roles to managed identities on existing resources.

Leave a Comment