3 Ways To Check If Resource Exists In Azure Bicep

Azure Bicep and ARM templates are designed to be idempotent, this means that running a template multiple times should result in the same end state of the resources.

However, there are still cases when we might want to know during deployment if a particular resource exists, for example, when dealing with Key Vault access policies.

There are multiple ways to check whether a resource exists while deploying an Azure Bicep template. Three approaches described in this post are using resource tags, leveraging Azure PowerShell or CLI scripting, or running a deployment script.

As of May 2022, Azure Bicep does not provide a built-in way to check if a resource exists, however, the approaches described in this post provide a workaround which can be helpful in your use case.

Contents:

Overview

In this post, we discuss 3 different approaches how to check if a particular resource is already created, for example, it could be previously deployed through our Bicep template or created manually by an operator.

The first approach is to use tags to keep track if our resource exists. A specific tag is set during the first resource deployment, and later we check the tag to understand if resource was already deployed. It is very important to keep the tag value in sync with the resource state.

The second approach leverages Azure PowerShell or Azure CLI to fetch information about resource, in our case, whether it exists or not, and then the value is passed to the Bicep template. It has an advantage that the Bicep code is very simple, but the downside that we need some environment to run the script (usually CI/CD pipeline).

The third approach makes it possible to keep everything within Bicep template(s) by using deployment scripts resource in Azure Resource Manager.

There must be more ways to check if resource exists in Azure Bicep, and this post only contains a few ways which I’m aware of. Please let me know if you have better ways to achieve this.

Track If Resource Exist Using Tags

Any resource in Azure can have tags, a set of key-value pairs where values are arbitrary strings. Users can use tags for whatever purposes they want, but usually tags are used to keep some metadata related to the resources.

The idea here is to use tags of the resource’s container to store information about whether the resource was deployed. Here, the container means a resource group for an individual resource, or a subscription for a resource group.

Keep in mind that this is a workaround and might be not the best way to test whether the resource exists. We simply rely on tags to track state of the resource.

NOTE: Our tag’s value must be consistent with the state of the resource in question, and it is our responsibility to keep them in sync. If the tag is removed or the resource is deleted/created without updating the tag, then this approach will not work properly.

Our solution consists of two files: resource template and parent template, these are described in the following sections.

Resource Template: storage-account.bicep

This is a Bicep file that deploys our resource, in this case a storage account, and sets the tag on the resource group. Here are some notes for the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// =============== storage-account.bicep ===============

param resourceExists bool
param resourceExistsTagName string

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = if (!resourceExists) {
  name: 'stcontoso'
  location: resourceGroup().location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

resource tags 'Microsoft.Resources/tags@2021-04-01' = {
  name: 'default'
  properties: {
    tags: {
      '${resourceExistsTagName}': 'true'
    }
  }
  dependsOn: [
    storageAccount
  ]
}

Parent Template: main.bicep

This is a main template which retrieves the tag from the resource group and then deploys our storage-account.bicep as a module while passing the resourceExists flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// =============== main.bicep ===============

targetScope = 'subscription'

resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing = {
  name: 'rg-contoso'
}

var tagName = 'isResourceDeployed'
var resourceExists = contains(resourceGroup.tags, tagName) && resourceGroup.tags[tagName] == 'true'

module resourceDeployment 'resource.bicep' = {
  name: 'resourceDeployment'
  params: {
    resourceExists: resourceExists
    resourceExistsTagName: tagName
  }
  scope: resourceGroup
}

Use Azure PowerShell Or CLI To Check If Resource Exists

In many cases, we deploy our infrastructure using Azure Bicep template as part of CI/CD pipeline. In case of Azure, we have Azure Pipelines (part of Azure DevOps offering) which are commonly used to create and manage deployment pipelines, releases, etc.

Every CI/CD pipeline allows running scripts in some scripting language such as Bash, PowerShell, or Python. This is a fundamental capability of any deployment pipeline.

We will use Azure PowerShell or Azure CLI to check if the resource exists and pass this information to the Bicep template during deployment.

First, we define a Bicep template, it accepts a boolean parameter which indicates whether the resource already exists or not. Next, we provide examples of how to check resource existence using Azure PowerShell and Azure CLI.

Here are high-level steps how to do it in Azure Pipelines:

  1. Execute Azure PowerShell or Azure CLI script to find out if the resource exists.
  2. At the end of the script, set pipeline variable to hold the information if resource exists, see How To Set Azure Pipeline Variable In Scripts.
  3. Use the pipeline variable from the previous step to pass value for the parameter resourceExists in our Bicep template while deploying resources, see details in Deploy Azure Bicep In YAML and Classic Release Pipelines (CI/CD).

Bicep Template

The Bicep file accepts a boolean parameter resourceExists and acts based on this information. In our simple example, the storage account is deployed if it doesn’t exist yet.

// =============== storage-account.bicep ===============

param resourceExists bool

// Some business logic based on the information whether resource already exists
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = if (!resourceExists) {
  name: 'stcontoso'
  location: resourceGroup().location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

Azure PowerShell Script

There are multiple ways in Azure PowerShell to check if a particular resource exists. To begin with, each Azure resource has its own module with resource-specific commands.

For example, Az.Storage module contains Get-AzStorageAccount function which can be used to check if a particular storage account exists. Note that we specify -ErrorAction SilentlyContinue so that the function returns null if storage account cannot be found.

$StorageAccount = Get-AzStorageAccount -Name stcontoso -ResourceGroupName rg-contoso -ErrorAction SilentlyContinue
# ResourceExists variable holds boolean value indicating whether storage account exists
$ResourceExists = $null -eq $StorageAccount
Write-Host $ResourceExists # Outputs True or False

Alternatively, one can use the generic Get-AzResource function instead which allows retrieving any resource by name, type, etc. Here’s a simple example similar to the previous one.

$StorageAccount = Get-AzResource -ResourceName stcontoso -ResourceType 'Microsoft.Storage/storageAccounts' -ResourceGroupName rg-contoso -ErrorAction SilentlyContinue
$ResourceExists = $null -eq $StorageAccount
Write-Host $ResourceExists

Azure CLI Script

Azure CLI is another powerful way to interact with and manage Azure resources. Personally, I usually use Azure PowerShell when working with resources in command line, but Azure CLI is also a great option.

To check if resource exists, we can come up with something like the following in Bash for a storage account using az storage account show. Note that 2>nul suppresses stderr so that no error is thrown when resource cannot be not found.

NOTE: Azure CLI can be used within both PowerShell and Bash, here we chose Bash since the previous example already covers PowerShell case.

storage_account=$(az storage account show --name stcontoso --resource-group rg-contoso 2>nul)
resource_exists=$(if [ -z "$storage_account" ]; then echo 0; else echo 1; fi)
echo $resource_exists

Use Deployment Script To Check If Resource Exists

In the previous section, we discussed how to use Azure CLI or PowerShell as one of the steps in the CI/CD pipeline to check resource existence before deploying a Bicep template.

But what if we want to keep all logic within an Azure Bicep template? Without using any additional steps in the deployment pipeline. Here’s where deployment scripts might come handy.

Deployment scripts help extend Azure Bicep functionality by allowing users to perform custom steps through Azure PowerShell or Azure CLI commands.

Now, we can combine deployment scripts with the knowledge from the previous section about PowerShell and CLI. In this post, we will not dive into details but provide high-level steps to help you get started:

  1. Define a Microsoft.Resources/deploymentScripts resource in your Bicep template, for example, deploymentScriptResource.
    1. Set up necessary authentication and authorization so that script can access information about resources in Azure.
    2. Make forceUpdateTag unique for each deployment, for example, using utcNow, so that the script is run every time.
    3. Run Azure CLI or PowerShell commands to check whether resource exists and return this information from the deployment script. To return a value, in PowerShell use special $DeploymentScriptOutputs variable:
       $DeploymentScriptOutputs = @{}
       $DeploymentScriptOutputs['resourceExists'] = $ResourceExists
      
  2. In the Bicep template, retrieve the value from the outputs: deploymentScriptResource.properties.outputs.resourceExists
  3. Now, you can use the resourceExists value in your template’s logic. You might also want to leverage Modules In Azure Bicep to keep Bicep code clean and maintainable.

The setup with deployment scripts is usually more involved compared to other approaches we discussed in this post. However, deployment scripts is a powerful tool which might come in handy in many other use cases thanks to its flexibility. I’d recommend reading Use deployment scripts in Bicep to learn more about it.