skip to Main Content

For a project I want to deploy three related resources to Azure through Bicep templates: 1) App Service with System Assigned Managed Identity, 2) Key Vault and 3) Access policy for the App Service (step 1) to the Key Vault (step 2).

The AppService deployment outputs the principalId of the System Assigned Identity which is then later on used when deploying the KeyVaultAccessPolicy.

However, when I run the AZ CLI (az deployment sub create --location WestEurope --template-file ./main.bicep --parameters ./parameters/parameters-dev.json)
to deploy this to Azure I get the following error:

'The language expression property 'outputs' doesn't exist, available properties are 'templateHash, parameters, mode, provisioningState, timestamp, duration, correlationId, providers, dependencies, outputResources'.

Does anyone have an idea why referencing the principalId of the App Service does not work here? Many thanks for any help.

Modules and main.bicep:

main.bicep

module appService 'modules/appService.bicep' = {
  name:  'deployAppService'
  scope: resourceGroup(appServiceResourceGroup)
  params: {
    name: appServiceName
    location: appServiceLocation
    alwaysOn: appServiceAlwaysOn
    apimIpAddress: appServiceApimIpAddress
    appServicePlanResourceGroup: appServicePlanResourceGroup
    appServicePlanName: appServicePlanName
  }
}

module keyVault 'modules/keyVault.bicep' = {
  name: 'deployKeyVault'
  scope: resourceGroup(appServiceResourceGroup)
  params: {
    name: keyVaultName
    dependsOn: [ appService ]
    location: appServiceLocation
  }
}

module keyVaultAccessPolicy 'modules/keyVaultAccessPolicy.bicep' = {
  name: 'deployKeyVaultAccessPolicy'
  scope: resourceGroup(appServiceResourceGroup)
  params: {
    name: '${appServiceName}-ap'
    dependsOn: [ keyVault ]
    objectId: appService.outputs.appServiceManagedIdentity
  }
}

appService.bicep

resource appService 'Microsoft.Web/sites@2020-12-01' = {
  name: name
  location: location
  kind: 'app'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: '${subscription().id}/resourceGroups/${appServicePlanResourceGroup}/providers/Microsoft.Web/serverfarms/${appServicePlanName}'
    enabled: true
  }
}

output appServiceManagedIdentity string = appService.identity.principalId

keyVault.bicep

resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
  name: name
  location: location
  dependsOn: dependsOn
  properties: {
    enabledForDeployment: true
    enabledForTemplateDeployment: true
    enabledForDiskEncryption: true
    tenantId: subscription().tenantId
    accessPolicies: []
    sku: {
      name: 'standard'
      family: 'A'
    }
  }
}

keyVaultAccessPolicy.bicep

resource keyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = {
  name: name
  dependsOn: dependsOn
  properties: {
    accessPolicies: [
      {
        tenantId: subscription().tenantId
        objectId: objectId
        permissions: {
          secrets: [
            'get'
          ]
        }
      }
    ]
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    Already found the answer:

    1. I missed that modules already contain an dependsOn property and so there is no need to pass the dependencies as param.
    2. The KeyVaultAccessPolicy's name did not contain a reference to the parent resource (KeyVault), see name: '${keyVaultName}/add' below

    See working modules and main.bicep below:

    main.bicep

    module appService 'modules/appService.bicep' = {
      name:  'deployAppService'
      scope: resourceGroup(appServiceResourceGroup)
      params: {
        name: appServiceName
        location: appServiceLocation
        alwaysOn: appServiceAlwaysOn
        apimIpAddress: appServiceApimIpAddress
        appServicePlanResourceGroup: appServicePlanResourceGroup
        appServicePlanName: appServicePlanName
      }
    }
    
    module keyVault 'modules/keyVault.bicep' = {
      scope: resourceGroup(appServiceResourceGroup)
      name: 'keyVaultDeploy'
      params: {
        location: appServiceLocation
        name: keyVaultName
      }
    }
    
    module keyVaultAccessPolicy 'modules/keyVaultAccessPolicy.bicep' = {
      scope: resourceGroup(appServiceResourceGroup)
      name: 'keyVaultAccessPolicyDeploy'
      dependsOn: [
        keyVault
      ]
      params: {
        keyVaultName: keyVaultName
        objectId: appService.outputs.appServiceManagedIdentity
      }
    }
    

    appService.bicep

    resource appService 'Microsoft.Web/sites@2020-12-01' = {
      name: name
      location: location
      kind: 'app'
      identity: {
        type: 'SystemAssigned'
      }
      properties: {
        // left out 
      }
    }
    
    output appServiceManagedIdentity string = appService.identity.principalId
    

    keyVault.bicep

    resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
      name: name
      location: location
      properties: {
        enabledForDeployment: true
        enabledForTemplateDeployment: true
        enabledForDiskEncryption: true
        tenantId: subscription().tenantId
        accessPolicies: []
        sku: {
          name: 'standard'
          family: 'A'
        }
      }
    }
    

    keyVaultAccessPolicy.bicep

    resource keyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = {
      name: '${keyVaultName}/add'
      properties: {
        accessPolicies: [
          {
            tenantId: subscription().tenantId
            objectId: objectId
            permissions: {
              secrets: [
                'get'
              ]
            }
          }
        ]
      }
    }
    

  2. Just for a heads up you don’t need to pass these stuffs with parameters.

    Just use the existing tag with the scope it will be easier to get datafrom any resource.

    https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/existing-resource#different-scope

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search