Work with existing resources

Completed

Bicep files often need to refer to resources that were created elsewhere. These resources might be created manually, maybe by a colleague using the Azure portal. Or they might be created in another Bicep file. There are many reasons why you need to refer to these resources, such as:

  • You're adding an SQL database into an Azure SQL logical server instance that someone already created.
  • You're configuring diagnostics settings for resources that are defined in another Bicep module.
  • You have to securely access the keys for a storage account that was manually deployed into your subscription.

Bicep provides the existing keyword for you to use in these situations.

Note

The commands in this unit are shown to illustrate concepts. Don't run the commands yet. You'll practice what you learn here soon.

Refer to existing resources

Within a Bicep file, you can define a resource that already exists. The declaration looks similar to a normal resource definition, but there are a few key differences. In the following example of an existing resource definition, the definition refers to a storage account named toydesigndocs. The storage account is in the same resource group that your Bicep template is deploying resources to.

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = {
  name: 'toydesigndocs'
}

Let's look closely at what makes up this definition:

  • As you would with a normal resource, you include the resource keyword, a symbolic name, and the resource type and API version.

    Note

    Remember, the symbolic name is used only within this Bicep file. If you create this resource by using one Bicep file and refer to it by using the existing resource in a different Bicep file, the symbolic names don't have to match.

  • The existing keyword indicates to Bicep that this resource definition is a reference to an already-created resource, and that Bicep shouldn't try to deploy it.

  • The name property is the Azure resource name of the storage account that was previously deployed.

  • You don't need to specify the location, sku, or properties, because the template doesn't deploy the resource. It merely references an existing resource. Think of it as a placeholder resource.

Refer to child resources

You can refer to an existing child resource, too. Use the same kind of syntax that you used when you deployed a child resource. The following example shows how you can refer to an existing subnet, which is a child resource of a virtual network. The example uses a nested child resource, as shown here:

resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' existing = {
  name: 'toy-design-vnet'

  resource managementSubnet 'subnets' existing = {
    name: 'management'
  }
}

Notice that both the parent and child resource have the existing keyword applied.

You can then refer to the subnet by using the same :: operator that you use for other nested child resources:

output managementSubnetResourceId string = vnet::managementSubnet.id

Refer to resources outside the resource group

Often, you need to refer to resources in a different resource group. For example, if you have a virtual network in a centralized resource group, you might want to deploy a virtual machine into that virtual network in its own resource group. You can use the scope keyword to refer to existing resources in a different resource group. The following example shows how you could refer to a virtual network named toy-design-vnet within the networking-rg resource group:

resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' existing = {
  scope: resourceGroup('networking-rg')
  name: 'toy-design-vnet'
}

Notice that the scope uses the resourceGroup() keyword to refer to the resource group that contains the virtual network.

You can even refer to resources within a different Azure subscription, as long as the subscription is within your Microsoft Entra tenant. If your networking team provisions the virtual network in a different subscription, the template could refer to it, as in this example:

resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' existing = {
  scope: resourceGroup('A123b4567c-1234-1a2b-2b1a-1234abc12345', 'networking-rg')
  name: 'toy-design-vnet'
}

Notice that the scope uses the resourceGroup() keyword to refer to the Azure subscription ID (A123b4567c-1234-1a2b-2b1a-1234abc12345) and resource group name that contains the virtual network.

Now that you understand how to refer to existing resources, let's look at how you can use this capability in your templates.

Add child and extension resources to an existing resource

You can add a child resource to an already-created parent resource by using a combination of the existing keyword and the parent keyword. The following example template creates an Azure SQL database within a server that already exists:

resource server 'Microsoft.Sql/servers@2023-08-01-preview' existing = {
  name: serverName
}

resource database 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
  parent: server
  name: databaseName
  location: location
  sku: {
    name: 'Standard'
    tier: 'Standard'
  }
}

If you need to deploy an extension resource to an existing resource, you can use the scope keyword. Here's a template that uses the existing keyword and the scope keyword to add a resource lock to a storage account that already exists:

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = {
  name: 'toydesigndocs'
}

resource lockResource 'Microsoft.Authorization/locks@2020-05-01' = {
  scope: storageAccount
  name: 'DontDelete'
  properties: {
    level: 'CanNotDelete'
    notes: 'Prevents deletion of the toy design documents storage account.'
  }
}

Refer to an existing resource's properties

Resources often need to refer to the properties of other resources. For example, if you deploy an application, it might need to know the keys or connection information for another resource. By using the existing keyword, you get access to the properties of the resource that you're referring to.

Tip

It's a best practice to look up keys from other resources in this way instead of passing them around through outputs. You'll always get the most up-to-date data. Also, importantly, outputs are not designed to handle secure data such as keys.

The way that you access the information about a resource depends on the type of information you're getting. If it's a property that isn't secure, you ordinarily use only the properties of the resource. The following example template deploys an Azure Functions application, and uses the access details (instrumentation key) for an Application Insights instance that was already created:

resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
  name: applicationInsightsName
}

resource functionApp 'Microsoft.Web/sites@2023-12-01' = {
  name: functionAppName
  location: location
  kind: 'functionapp'
  properties: {
    siteConfig: {
      appSettings: [
        // ...
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: applicationInsights.properties.InstrumentationKey
        }
      ]
    }
  }
}

In this example, because the instrumentation key isn't considered sensitive data, it's available in the properties of the resource. When you need to access secure data, such as the credentials to use to access a resource, use the listKeys() function, as shown in the following code:

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = {
  name: storageAccountName
}

resource functionApp 'Microsoft.Web/sites@2023-12-01' = {
  name: functionAppName
  location: location
  kind: 'functionapp'
  properties: {
    siteConfig: {
      appSettings: [
        // ...
        {
          name: 'StorageAccountKey'
          value: storageAccount.listKeys().keys[0].value
        }
      ]
    }
  }
}

Notice that the listKeys function returns a keys array. The Bicep code retrieves the value property from the first item in the keys array. Each resource type has different information available from the listKeys() function. The Bicep extension for Visual Studio Code gives you hints to help you to understand the data that each resource's listKeys() function returns. The following screenshot shows the listKeys() function's output for a storage account:

Screenshot of the Bicep extension for Visual Studio Code. IntelliSense displays several the information returned by the listKeys function for a storage account.

Some resources support other functions, too. Visual Studio Code's IntelliSense lists the functions available for each resource. In the following screenshot, you can see that storage accounts provide functions named listAccountSas() and listServiceSas() in addition to listKeys():

Screenshot of the Bicep extension for Visual Studio Code. IntelliSense displays several functions available for the storage account.

Important

The listKeys() function provides access to sensitive data about the resource. This means that the user or service principal that runs the deployment needs to have the appropriate level of permission on the resource. This is usually the Contributor built-in role, or a custom role that assigns the appropriate permission.