Bicep ADF LinkedService - azure

I'm having a heck of a time trying to deploy a simple Azure BlobFS linked service into an ADF using Bicep (which I have only really started to learn).
The bicep I have thus far is:
//---Data Factory
resource datafactory 'Microsoft.DataFactory/factories#2018-06-01' = {
name: adf_name
location: loc_name
identity: {
type: 'SystemAssigned'
}
properties: {
globalParameters: {}
publicNetworkAccess: 'Enabled'
}
}
//--- Data Factory Linked Service
resource adls_linked_service 'Microsoft.DataFactory/factories/linkedservices#2018-06-01' = {
name: 'ls_adf_to_adls'
parent: datafactory
properties: {
annotations: []
connectVia: {
parameters: {}
referenceName: 'AutoResolveIntegrationRuntime'
type: 'IntegrationRuntimeReference'
}
description: 'linked_service_for_adls'
parameters: {}
type: 'AzureBlobFS'
typeProperties: {
accountKey: datafactory.identity.principalId
azureCloudType: 'AzurePublic'
credential: {
referenceName: 'string'
type: 'CredentialReference'
}
servicePrincipalCredentialType: 'SecureString'
servicePrincipalId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
servicePrincipalKey: {
type: 'SecureString'
value: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
tenant: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
url: bicepstorage.properties.primaryEndpoints.blob
}
}
}
The ADF resource deploys fine by itself as does the ADLS (symbolic name is: bicepstorage). The issue is when I added the linkedservice resource block. I get:
New-AzResourceGroupDeployment: /home/vsts/work/1/s/psh/deploy_main.ps1:12
Line |
12 | New-AzResourceGroupDeployment `
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| 22:46:27 - The deployment 'main' failed with error(s). Showing 1 out of
| 1 error(s). Status Message: Input is malformed. Reason: Could not get
| integration runtime details for AutoResolveIntegrationRuntime
| (Code:InputIsMalformedDetailed) CorrelationId:
| f77ef878-5314-46ea-9de6-65807845a104
The only integration runtime in the ADF is the 'AutoResolveIntegrationRuntime'. When I inspect it in the portal it's green, running and healthy.
I'm using task: AzurePowerShell#5 on ubuntu-latest in ADF, but I get the same error when I try to deploy the template directly from vscode.
I'm out of ideas and would really appreciate some assistance. I found the documentation for the 'connectVia' block (actually all the documentation on bicep linked services!) to be really confusing; if anyone could tell me exactly what is supposed to go in there, I'd really appreciate it.
Thanks.

As mentioned in this documentation, If you want to create a linked service to adls(blobfs) with default Azure IR (autoresolveintegrationruntime) then you can remove the ConnectionVia property in linked service block in your bicep template.
To test this I have created a bicep template which will deploy adlsgen2 storage account, data factory and a linked service to it using the service principal based authentication.
Here is the sample template for your reference:
param location string='westus'
//---Data Factory
resource storage 'Microsoft.Storage/storageAccounts#2022-09-01'={
name:'<storageAccountName>'
location:location
kind:'StorageV2'
sku:{
name:'Standard_GRS'
}
properties:{
accessTier:'Hot'
supportsHttpsTrafficOnly:true
isHnsEnabled:true
}
}
resource datafactory 'Microsoft.DataFactory/factories#2018-06-01' = {
name: '<AdfName>'
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
globalParameters: {}
publicNetworkAccess: 'Enabled'
}
}
//--- Data Factory Linked Service
resource adls_linked_service 'Microsoft.DataFactory/factories/linkedservices#2018-06-01' = {
name: '<linkedserviceName>'
parent: datafactory
properties: {
annotations: []
description: 'linked_service_for_adls'
parameters: {}
type: 'AzureBlobFS'
typeProperties: {
url: storage.properties.primaryEndpoints.dfs
//encryptedCredential:storage.listKeys(storage.id).keys[0].value
servicePrincipalCredential: {
type: 'SecureString'
value: '<serviceprincipalKey>'
}
servicePrincipalId:'<serviceprincipalappId>'
servicePrincipalCredentialType:'ServicePrincipalKey'
azureCloudType:'AzurePublic'
servicePrincipalKey: {
type: 'SecureString'
value: '<serviceprincipalKey>'
}
tenant: '<tenantId>'
}
}
}

Related

Azure B2C tenant deployment fails in Europe. Why?

I have this bicep file that deploys an Azure B2C tenant in location: 'Australia'
resource b2cDirectory 'Microsoft.AzureActiveDirectory/b2cDirectories#2021-04-01' = {
location: 'Australia'
name: 'tenantname1.onmicrosoft.com'
sku: {
name: 'PremiumP2'
tier: 'A0'
}
properties: {
createTenantProperties: {
countryCode: 'AU'
displayName: 'tenantname1 B1Cd223'
}
}
tags: {
Department: 'Dev'
}
}
This works fine. but it doesn't work for location: 'Europe' even though they say they support it.
This is the error I'm getting when I try to deploy this in location: 'Europe'.
Is this a temporary thing or am I missing something?
I tried in my environment and got below results:
Initially, I got same error in my environment.
{"status":"Failed","error":{"code":"Deployment Failed", "message":"Atleast one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-deployment-operations for usage details.","details":[{"code":"InternalSer verError", "message":"{\r\n"error"; {\r\n" "code":
"ResourceDeploymentFailure",\r\n"message": "The response for resou rce had empty or invalid content."\r\n }\r\n}"}]}}_
I tried with some changes in bicep code as below :
test.bicep:
resource b2cDirectory 'Microsoft.AzureActiveDirectory/b2cDirectories#2021-04-01' = {
location: 'europe'
name: 'demotenant326.onmicrosoft.com'
sku: {
name: 'Standard'
tier: 'A0'
}
properties: {
createTenantProperties: {
countryCode: 'DE'
displayName: 'demo1tenantv B1Cd223'
}
}
tags: {
Department: 'pro'
}
}
In the above bicep file I changed sku, country code = 'DE' indicates **Germany**and it deploys an Azure B2C tenant with location: 'europe' successfully.
Console:
New-AzResourceGroupDeployment -TemplateFile test.bicep -ResourceGroupName <your resource grp>
WARNING: /home/venkatesan/test.bicep(2,15) : Warning no-hardcoded-location: A resource location should not use a hard-coded string or variable value. Please use a parameter value, an expression, or the string 'global'. Found: 'europe' [https://aka.ms/bicep/linter/no-hardcoded-location]
DeploymentName : test
ResourceGroupName : <your resource grp>
ProvisioningState : Succeeded
Timestamp : 2/17/2023 8:08:56 AM
Mode : Incremental
TemplateLink :
Parameters :
Outputs :
DeploymentDebugLogLevel :
Reference:
Region availability and data residency - Azure AD B2C | Microsoft Learn

Deployment of queue, blob and ADLS2 private endpoints via Bicep goes wrong

I am trying to deploy a number of three azure storage resources under two storage accounts, and I want to implement three private endpoints as to only allow connection to these resources from VMs in the same VNET. Type of resources that need to be connected to in two separate storage accounts are (per storage account):
storageAccountTemp
Azure blob queue
Azure blob storage
storageAccountDatalake
ADLS 2 containers (datalake)
I have the following Azure Bicep code for deploying the permanent and temporary stores:
param location string
param environmentType string
param storageAccountSku string
param privateEndpointsSubnetId string
var privateEndpointNameTmpstBlob = 'pe-tmpst-blob-${environmentType}-001'
var privateEndpointNameTmpstQueue = 'pe-tmpst-queue-{environmentType}-001'
var privateEndpointNamePst = 'pe-pst-${environmentType}-001'
/// Temp storage ///
resource storageAccountTemp 'Microsoft.Storage/storageAccounts#2021-08-01' = {
name: 'tmpst${environmentType}'
location: location
sku: {
name: storageAccountSku
}
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
accessTier: 'Hot'
minimumTlsVersion: 'TLS1_0'
publicNetworkAccess: 'Disabled'
}
}
resource blobContainerForQueue 'Microsoft.Storage/storageAccounts/blobServices/containers#2021-08-01' = {
name: '${storageAccountTemp.name}/default/claimcheck-storage-${environmentType}'
properties: {
publicAccess: 'None'
}
}
resource storageQueueMain 'Microsoft.Storage/storageAccounts/queueServices/queues#2019-06-01' = {
name: '${storageAccountTemp.name}/default/queue-main-${environmentType}'
}
/// Persistant storage datalake ///
resource storageAccountDatalake 'Microsoft.Storage/storageAccounts#2021-08-01' = {
name: 'pstdatalake${environmentType}'
location: location
sku: {
name: storageAccountSku
}
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
accessTier: 'Hot'
minimumTlsVersion: 'TLS1_0'
isHnsEnabled: true
publicNetworkAccess: 'Disabled'
}
}
/// Data///
resource ContainerForData 'Microsoft.Storage/storageAccounts/blobServices/containers#2021-08-01' = {
name: '${storageAccountDatalake.name}/default/data-${environmentType}'
properties: {
publicAccess: 'None'
}
}
/// Private endpoints configuration for tempblob, queue and datalake ///
resource privateEndpointTmpstBlob 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNameTmpstBlob
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNameTmpstBlob
properties: {
privateLinkServiceId: storageAccountTemp.id
groupIds: ['blob']
}
}
]
}
}
resource privateEndpointTmpstQueue 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNameTmpstQueue
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNameTmpstQueue
properties: {
privateLinkServiceId: storageAccountTemp.id
groupIds: ['queue']
}
}
]
}
}
resource privateEndpointPst 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNamePst
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNamePst
properties: {
privateLinkServiceId: storageAccountDatalake.id
groupIds: ['blob']
}
}
]
}
}
As you can see, for the storage account, IsHnsEnabled is set to true, as to enable HierarchicalNamespace and thus ADLS2 functionality. The problem is, if I include the privateEndpointPst resource deployment in the Bicep deployment, and then try to view a datalake container in the portal from a VM that is in the same VNET as the private endpoint (which are in the subnet that makes the privateEndpointsSubnetId variable), I get the following message when trying to look at files in one of the datalake containers:
I believe it is not the problems in the picture. The reason for this is that when I deploy all three endpoints together, they all show this same problem when trying to look at blob/queue/datalake in storageAccountTemp and storageAccountDatalake when I deploy all three endpoints.
However, only deploying the two endpoints for the storageAccountTemp resources and not the one for Datalake, I can see the data in the portal when running from the VM in the VNET and code running from this VM can also reach the queue + blob. So not only does the deployment of the privateEndpointPst seem to mess up datalake reachability, it also in some way does the same to the reachability of my other queue and blob in the storageAccountTemp if I deploy them altogether. My mind is boggled as to why this is happening, and why I cannot seem to deploy the datalake endpoint in the right way. Also, sometimes, deploying the endpoints altogether WILL make the datalake endpoint work, and break the other two, which is even more mind-boggling. Clicking do you want to do some checks to detect common connectivity issues gives me the following information, which does not make me much wiser as to what is causing the issue (since I'm pretty sure it's not firewalls; sometimes I can access, sometimes not):
Does anyone see what could be wrong with my Bicep code for deploying the endpoint that might be causing this issue? I'm at quite a loss here. Also tried to replace groupIds: ['blob'] with groupIds: ['dfs'], but that does not seem to solve my problem.
I seem to have found the issue. For connecting to a datalake resource, one needs to have both a private endpoint with groupIds: ['blob'] and groupIds: ['dfs], since the blob API is still used for getting some meta-info about the containers (as far as I can understand).
So adding:
resource privateEndpointPstDfs 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNamePstDfs
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNamePstDfs
properties: {
privateLinkServiceId: storageAccountDatalake.id
groupIds: ['dfs']
}
}
]
}
}
Made the deployment work successfully.

Setting Azure App Service server stack on a Bicep template

I'm trying to deploy a .NET Core 3.1 Azure App Service on Linux using a Bicep template from Azure CLI. The app service and corresponding app service plan are deployed correctly but the app service stack settings are empty on the Azure portal and I have to set these manually. I tried setting the metadata property on the 'Microsoft.Web/sites' resource and also on the 'Microsoft.Web/sites/config' resource, but the result was the same.
This is my app service plan:
resource appServicePlan 'Microsoft.Web/serverfarms#2021-02-01' = {
name: 'MyAppService'
location: resourceGroup().location
properties: {
reserved: true
}
sku: {
name: 'P1v2'
}
kind: 'linux'
}
Here is my first attempt to set the stack using 'Microsoft.Web/sites' as suggested here:
https://github.com/Azure/bicep/issues/3314
resource appService 'Microsoft.Web/sites#2021-02-01' = {
name: 'MyApp'
location: resourceGroup().location
identity: {
type: 'SystemAssigned'
}
kind: 'app'
properties: {
enabled: true
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'dotnet|3.1'
appCommandLine: 'dotnet MyApp.dll'
metadata: [
{
name: 'CURRENT_STACK'
value: 'dotnetcore'
}
]
}
}
}
Here is my second attempt to set the stack using 'Microsoft.Web/sites/config' as suggested here:
Bicep - How to config Runtime Stack to Azure App Service (Bicep version 0.4)
resource appService 'Microsoft.Web/sites#2021-02-01' = {
name: 'MyApp'
location: resourceGroup().location
identity: {
type: 'SystemAssigned'
}
kind: 'app'
properties: {
enabled: true
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'dotnet|3.1'
appCommandLine: 'dotnet MyApp.dll'
}
}
resource webConfig 'config' = {
name: 'web'
properties: {
metadata: [
{
name: 'CURRENT_STACK'
value: 'dotnetcore'
}
]
}
}
}
The result is the same. The deployment is completed with the following warning:
Warning BCP037: The property "metadata" is not allowed on objects of
type "SiteConfig". Permissible properties include
"acrUseManagedIdentityCreds", "acrUserManagedIdentityID", "alwaysOn",
"apiDefinition", "apiManagementConfig", "autoHealEnabled",
"autoHealRules", "autoSwapSlotName", "azureStorageAccounts",
"connectionStrings", "cors", "defaultDocuments",
"detailedErrorLoggingEnabled", "documentRoot", "experiments",
"ftpsState", "functionAppScaleLimit",
"functionsRuntimeScaleMonitoringEnabled", "handlerMappings",
"healthCheckPath", "http20Enabled", "httpLoggingEnabled",
"ipSecurityRestrictions", "javaContainer", "javaContainerVersion",
"javaVersion", "keyVaultReferenceIdentity", "limits", "loadBalancing",
"localMySqlEnabled", "logsDirectorySizeLimit", "managedPipelineMode",
"managedServiceIdentityId", "minimumElasticInstanceCount",
"minTlsVersion", "netFrameworkVersion", "nodeVersion",
"numberOfWorkers", "phpVersion", "powerShellVersion",
"preWarmedInstanceCount", "publicNetworkAccess", "publishingUsername",
"push", "pythonVersion", "remoteDebuggingEnabled",
"remoteDebuggingVersion", "requestTracingEnabled",
"requestTracingExpirationTime", "scmIpSecurityRestrictions",
"scmIpSecurityRestrictionsUseMain", "scmMinTlsVersion", "scmType",
"tracingOptions", "use32BitWorkerProcess", "virtualApplications",
"vnetName", "vnetPrivatePortsCount", "vnetRouteAllEnabled",
"websiteTimeZone", "webSocketsEnabled", "windowsFxVersion",
"xManagedServiceIdentityId". If this is an inaccuracy in the
documentation, please report it to the Bicep Team.
[https://aka.ms/bicep-type-issues]
The resources are deployed, but the app service stack setting is blank and I have to set it manually to make it work.
I know that in the ARM template this is set on the CURRENT_STACK property of the Microsoft.Web/sites/config metadata (as suggested here https://cloudstep.io/2020/11/18/undocumented-arm-oddities-net-core-app-services/). However, this doesn't seem to be supported (yet) in Bicep. If anyone has found a working solution, please post it here.
Thanks.
The Metadata parameter is not available anymore in the SiteConfig. The stack setting can be mentioned LinuxFxVersion.
So, solution will be Instead of using dotnet|3.1 , You should use DOTNETCORE|3.1.The over all code will be as below:
resource appServicePlan 'Microsoft.Web/serverfarms#2021-02-01' = {
name: 'MyAppService'
location: resourceGroup().location
properties: {
reserved: true
}
sku: {
name: 'P1v2'
}
kind: 'linux'
}
resource appService 'Microsoft.Web/sites#2021-02-01' = {
name: 'anumantestapp'
location: resourceGroup().location
identity: {
type: 'SystemAssigned'
}
kind: 'app'
properties: {
enabled: true
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'DOTNETCORE|3.1'
appCommandLine: 'dotnet MyApp.dll'
}
}
}
Ouptut:

Azure Bicep script produces error "Changing property > 'agentPoolProfile.vnetSubnetID' is not allowed." on second execution

I'm using Azure Bicep to create a virtualNetwork with a single subnet and then use that as the input for creating an aks cluster with : vnetSubnetID: virtualNetwork.properties.subnets[0].id
The first time I run the command, it creates the virtual network and cluster just fine, but the second time I run the command it gives this error :
{"error":{"code":"InvalidTemplateDeployment","message":"The template
deployment 'cluster' is not valid according to the validation
procedure. The tracking id is '[REDACTED_JUST_IN_CASE]'. See inner errors for
details.","details":[{"code":"PropertyChangeNotAllowed","message":"Provisioning
of resource(s) for container service playground-cluster0 in resource
group showcase-kevinplayground2 failed. Message: {\n "code":
"PropertyChangeNotAllowed",\n "message": "Changing property
'agentPoolProfile.vnetSubnetID' is not allowed.",\n "target":
"agentPoolProfile.vnetSubnetID"\n }. Details: "}]}}
I double checked and there is just the one subnet inside the virtualNetwork created by the deployment (no other magically appeared or anything).
I repeated the experiment on a second resource group and the same thing happened, so it's reproducible.
Here is the full bicep file (just call az deployment group create --resource-group showcase-kevinplayground2 -f cluster.bicep in the resource group of your choice)
targetScope = 'resourceGroup'
resource virtualNetwork 'Microsoft.Network/virtualNetworks#2021-02-01' = {
name: 'aksVirtualNetwork'
location: resourceGroup().location
properties:{
addressSpace:{
addressPrefixes:[
'10.10.0.0/16'
]
}
subnets:[
{
name: 'aks'
properties:{
addressPrefix: '10.10.5.0/24'
}
}
]
}
}
resource aksManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities#2018-11-30' = {
name: 'playgroundIdentity'
location: resourceGroup().location
}
resource aks 'Microsoft.ContainerService/managedClusters#2021-02-01' = {
name: 'playground-cluster0'
location: resourceGroup().location
identity: {
type:'UserAssigned'
userAssignedIdentities: {
'${aksManagedIdentity.id}': {}
}
}
sku: {
name: 'Basic'
tier: 'Free'
}
properties: {
kubernetesVersion: '1.21.2'
dnsPrefix: 'playground'
enableRBAC: true
networkProfile: {
networkPlugin: 'azure'
networkPolicy: 'calico'
}
aadProfile: {
managed: true
enableAzureRBAC: true
}
autoUpgradeProfile: {}
apiServerAccessProfile: {
enablePrivateCluster: false
}
agentPoolProfiles: [
{
name: 'aksnodes'
count: 1
vmSize: 'Standard_B2s'
osDiskSizeGB: 30
osDiskType: 'Managed'
vnetSubnetID: virtualNetwork.properties.subnets[0].id
osType: 'Linux'
maxCount: 1
minCount: 1
enableAutoScaling: true
type: 'VirtualMachineScaleSets'
mode: 'System'
orchestratorVersion: null
}
]
}
}
Looking at this reported github issue, you need to use the resourceId function.
In your case, something like that should work:
vnetSubnetID: resourceId('Microsoft.Network/virtualNetworks/subnets', 'aksVirtualNetwork', 'aks')

App Service Managed Identity and Key Vault the right way

I am currently trying to deploy out a resource group using azure bicep, however, I am running into an issue using key vault for my azure app service. I would like to know if I am actually doing this the correct way. I have a main bicep file that is along the lines of:
// params removed for brevity...
targetScope = 'subscription'
resource rg 'Microsoft.Resources/resourceGroups#2021-04-01' = {
name: 'rg-${appName}-${region}'
location: 'centralus'
}
module appServicePlan 'appplan.bicep' = {
params: {
sku: appServicePlanSku
appName: appName
region: region
}
scope: rg
name: 'AppServicePlanDeploy'
}
module keyVault 'keyvault.bicep' = {
params: {
keyVaultName: keyVaultName
sqlPassword: sqlServerPassword
webSiteManagedId: webSite.outputs.webAppPrincipal
}
scope: rg
name: 'KeyVaultDeploy'
dependsOn: [
webSite
]
}
module ai 'ai.bicep' = {
scope: rg
name: 'ApplicationInsightsDeploy'
params: {
name: appName
region: region
keyVaultName: keyVault.outputs.keyVaultName
}
dependsOn: [
keyVault
]
}
resource kv 'Microsoft.KeyVault/vaults#2019-09-01' existing = {
name: keyVaultName
scope: rg
}
module sql 'sqlserver.bicep' = {
scope: rg
name: 'SQLServerDeploy'
params: {
appName: appName
region: region
sqlPassword: kv.getSecret('sqlPassword')
sqlCapacitity: sqlCapacitity
sqlSku: sqlSku
sqlTier: sqlTier
}
dependsOn: [
keyVault
]
}
module webSite 'site.bicep' = {
params: {
appName: appName
region: region
keyVaultName: keyVaultName
serverFarmId: appServicePlan.outputs.appServicePlanId
}
scope: rg
name: 'AppServiceDeploy'
dependsOn: [
appServicePlan
]
}
My question comes with the implementation of the site.bicep, I started off by passing the secret uri from exported variables and creating the web app last as app insights, sql, etc... all need to be setup and in keyvault before we use their exported secret uri to construct a config. I had something along the lines of:
site.bicep (before):
properties: {
serverFarmId: serverFarmId
keyVaultReferenceIdentity: userAssignedId
siteConfig: {
appSettings: [
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: '#Microsoft.KeyVault(SecretUri=${appInsightsConnectionString})'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: '#Microsoft.KeyVault(SecretUri=${appInsightsKey})'
}
]
netFrameworkVersion: 'v5.0'
}
}
The only problem with this implementation is that the key vault MUST be constructed before the website because sql, ai, and the other services will store their values inside of the key vault for the web app to consume by their respective uris. The issue with this is that the KeyVault rightfully so has no idea which azure service to let access it's keys.
My question is the solution of constructing the web app before the key vault the only way to beat this problem? I am using managed identities on the web app and would like to continue doing so if possible. My final solution ended up somewhat like this:
site.bicep (final)
// params removed for brevity...
resource webApplication 'Microsoft.Web/sites#2020-12-01' = {
name: 'app-${appName}-${region}'
location: resourceGroup().location
tags: {
'hidden-related:${resourceGroup().id}/providers/Microsoft.Web/serverfarms/appServicePlan': 'Resource'
}
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: serverFarmId
siteConfig: {
appSettings: [
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
}
{
name: 'AngularConfig:ApplicationInsightsKey'
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
}
]
netFrameworkVersion: 'v5.0'
}
}
}
output webAppPrincipal string = webApplication.identity.principalId
And the KeyVault which will take a dependsOn webSite
keyVault.bicep(final):
resource keyVault 'Microsoft.KeyVault/vaults#2019-09-01' = {
name: keyVaultName
location: resourceGroup().location
properties: {
enabledForDeployment: true
enabledForTemplateDeployment: true
enabledForDiskEncryption: true
enableRbacAuthorization: true
tenantId: subscription().tenantId
sku: {
name: 'standard'
family: 'A'
}
accessPolicies: [
{
tenantId: subscription().tenantId
objectId: webSiteManagedId
permissions: {
keys: [
'get'
]
secrets: [
'list'
'get'
]
}
}
]
}
}
Just treat your accessPolicies as separate resource and add them when both Key Vault and App Service are created. Same applies for Config section and Connection Strings. Check documentation here.
In ARM templates you can achieve same effect using nested templates. In Bicep it is kind the same, but you declare them as separate resource that usually contains parent name (e.g. name: '${kv.name}/add', name: '${webSite.name}/connectionstrings')
Sample
Step 1: Create an App Service without config section
resource webSite 'Microsoft.Web/sites#2020-12-01' = {
name: webSiteName
location: location
properties: {
serverFarmId: hostingPlan.id
siteConfig:{
netFrameworkVersion: 'v5.0'
}
}
identity: {
type:'SystemAssigned'
}
}
Step 2: Create Key Vault without access policies
resource kv 'Microsoft.KeyVault/vaults#2019-09-01' = {
name: keyVaultName
location: location
properties:{
sku:{
family: 'A'
name: 'standard'
}
tenantId: tenantId
enabledForTemplateDeployment: true
accessPolicies:[
]
}
}
Step 3: Create new access policy and reference Web Apps Managed Identity
resource keyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies#2021-06-01-preview' = {
name: '${kv.name}/add'
properties: {
accessPolicies: [
{
tenantId: tenantId
objectId: webSite.identity.principalId
permissions: {
keys: [
'get'
]
secrets: [
'list'
'get'
]
}
}
]
}
}
Step 4: Update Webb app config section
resource webSiteConnectionStrings 'Microsoft.Web/sites/config#2020-06-01' = {
name: '${webSite.name}/connectionstrings'
properties: {
DefaultConnection: {
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
type: 'SQLAzure'
}
}
}
One solution could be to use User Assigend Identity instead of System Assigned. Then you would deploy the following:
Deploy a user assigend identity
Key Vault and assign permissions for user assigned identity
Deploy web app with user assigned identity and read / write secrets
User assigned is independent of the resources and so you avoid your chicken and egg problem.
More:
https://learn.microsoft.com/en-us/azure/templates/microsoft.managedidentity/userassignedidentities?tabs=bicep
https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
You should split up three parts of your deployment into separate resources:
Deploy the Key Vault - without any access policies!
Deploy the App Service - with the SystemAssigned Identity, but without the app settings
Deploy the Key Vault Access Policy for the MSI
Deploy the App Settings

Resources