Tom Troughton
Tom Troughton

Reputation: 4325

Bicep template causing "Object reference not set to an instance of an object" exception on deployment

Below I've added the contents of 3 Bicep files. If you add these to the same directory and run a subscription-based deployment to Azure as follows...

az deployment sub create --name "VerifyBug" --location "northeurope" --template-file .\main.bicep

...you'll get a "Object reference not set to an instance of an object". I've just spent the best part of 24 hours trying to work out what exactly was causing this exception because it was coming from a much larger deployment that this replicates. There is no indication of where the exception is being thrown.

What I've found is that the problem is with the serverfarm.outputs.serverfarmId value. See how this is being used in an object which is later unioned with another object. It seems the problem is a combination of using an ARM output inside a union operator, but I'd love a more technical explanation. I'm mainly posting this here to help anyone else avoid similar pain in the future.

The solution I've found is to break everything after the serverfarm module out of function.bicep into a separate module which takes the server farm ID as a parameter. Everything then works, but I'd love to hear an explanation about why this is.

main.bicep

targetScope = 'subscription'

var location = 'northeurope'

var resourceNamePrefix = 'test-resource'

resource resourceGroup 'Microsoft.Resources/resourceGroups@2020-10-01' = {
  name: '${resourceNamePrefix}-rg'
  location: location
}

module func 'function.bicep' = {
  name: '${deployment().name}-Func'
  scope: resourceGroup
  params: {
    resourceNamePrefix: resourceNamePrefix
    location: location
  }
}

function.bicep

param location string
param resourceNamePrefix string
param networking object = {}

module serverfarm 'appServicePlan.bicep' = {
  name: '${deployment().name}-AppSvcPlan'
  params: {
    resourceNamePrefix: resourceNamePrefix
    location: location
  }
}

var siteConfig = {
  linuxFxVersion: 'DOTNET-ISOLATED|6.0'
  http20Enabled: true
  alwaysOn: true
  ftpsState: 'Disabled'
  functionAppScaleLimit: 1
  minimumElasticInstanceCount: 1
  vnetRouteAllEnabled: !empty(networking)
}

var basicProperties = {
  serverFarmId: serverfarm.outputs.serverfarmId
  httpsOnly: true
  redundancyMode: 'None'
  reserved: true
  siteConfig: siteConfig
}

var networkingProperties = empty(networking) ? {} : {
  virtualNetworkSubnetId: networking.subnetResourceId
}
var functionProperties = union(basicProperties, networkingProperties)

resource function 'Microsoft.Web/sites@2021-02-01' = {
  name: '${resourceNamePrefix}-fn'
  location: location
  kind: 'functionapp,linux'
  properties: functionProperties
}

appServicePlan.bicep

param resourceNamePrefix string
param location string

resource serverfarm 'Microsoft.Web/serverfarms@2021-02-01' = {
  kind: 'linux'
  name: '${resourceNamePrefix}-appserviceplan'
  location: location
  sku: {
    name: 'S1'
    tier: 'Standard'
  }
  properties: {
    reserved: true 
    maximumElasticWorkerCount: 1
  }
}

output serverfarmId string = serverfarm.id

Upvotes: 1

Views: 2408

Answers (2)

ArtiomLK
ArtiomLK

Reputation: 2260

Solution

Switching the deployment region to another location solved the issue in my case.

Explanation

The bellow error message could randomly happen in some Azure location:

Object reference not set to an instance of an object

For instance, the above error message randomly occurred at validation time in the brazilsouth Azure location, sometimes it worked and sometimes it didn't. Thus, switching to another region solved the issue.

The important part is how to track down the issue:

  1. Since the issue happens randomly at validation time, I suggest to validate the deployment multiple times (even if it works the first few times). You could use the following script to easy the process:

    for i in {1..10}
    do
    az deployment sub validate \
    --name mydeployment \
    --location westus3 \
    --template-file main.bicep \
    --parameters @my-parameters.json
    done
    
  2. Once you validated multiple times against a region where the mentioned issue, Object reference not set to an instance of an object, does not happen, update the deployment to that region.

Upvotes: 0

Stringfellow
Stringfellow

Reputation: 2908

It is because "Resource Manager resolves variables before starting the deployment operations." Reference.

In the following part, the Resource Manager is trying to resolve serverFarmId: serverfarm.outputs.serverfarmId but it doesn't exist yet.

var basicProperties = {
  serverFarmId: serverfarm.outputs.serverfarmId
  httpsOnly: true
  redundancyMode: 'None'
  reserved: true
  siteConfig: siteConfig
}

Revised function.bicep

Here I removed the variables and reconstructed the resource deployment, then I changed the parameter from a virtual network object to a string with the subnet ID. I've had better results working with empty strings than objects. I ran the deployment and it worked without error.

param location string
param resourceNamePrefix string
param subnetResourceId string = ''

module serverfarm 'appServicePlan.bicep' = {
  name: '${deployment().name}-AppSvcPlan'
  params: {
    resourceNamePrefix: resourceNamePrefix
    location: location
  }
}

resource function 'Microsoft.Web/sites@2021-02-01' = {
  name: '${resourceNamePrefix}-fn'
  location: location
  kind: 'functionapp,linux'
  properties: {
    serverFarmId: serverfarm.outputs.serverfarmId
    httpsOnly: true
    redundancyMode: 'None'
    reserved: true
    siteConfig: {
      linuxFxVersion: 'DOTNET-ISOLATED|6.0'
      http20Enabled: true
      alwaysOn: true
      ftpsState: 'Disabled'
      functionAppScaleLimit: 1
      minimumElasticInstanceCount: 1
      vnetRouteAllEnabled: !empty(subnetResourceId)
    }
    virtualNetworkSubnetId: !empty(subnetResourceId) ? subnetResourceId : null
  }
}

Upvotes: 1

Related Questions