Dependency issue with Azure Bicep resource modules - azure

I am creating three resources: cosmos account, cosmos database, and cosmos containers. I have all of them within one folder except for the containers where I modularized it.
main.bicep
// Cosmos DB Account
resource cosmos 'Microsoft.DocumentDB/databaseAccounts#2020-06-01-preview' = if (deployDB) {
name: cosmosAccountName
location: location
tags: appTags
kind: 'GlobalDocumentDB'
identity: {
type: 'None'
}
properties: {
...
}
dependsOn: [
virtualNetwork
]
}
// Cosmos SQL Database
resource cosmosdb 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases#2020-04-01' = if (deployDB) {
name: '${cosmos.name}/${databaseName}'
properties: {
resource: {
id: databaseName
}
options: {
throughput: 400
}
}
}
// Cosmos DB Containers
module cosmosContainersUser '../../../cicd/bicep/modules/datastore/cosmos-db.bicep' = [for container in cosmosContainers: if (deployDB) {
name: container.containerName
params: {
cosmosContainerName: container.name
id: container.id
partitionKey: container.partitionKey
}
}]
cosmos-db.bicep
param cosmosContainerName string
param id string
param partitionKey string
// Cosmos Containers
resource cosmosContainerUser 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers#2021-06-15' = {
name: cosmosContainerName
properties: {
resource: {
id: id
indexingPolicy: {
indexingMode: 'consistent'
automatic: true
includedPaths:[
{
path: '/*'
}
]
excludedPaths: [
{
path: '/"_etag"/?'
}
]
}
partitionKey: {
kind: 'Hash'
paths: [
partitionKey
]
}
conflictResolutionPolicy: {
mode: 'LastWriterWins'
conflictResolutionPath: '/_ts'
}
}
}
}
This works no problem however main.bicep is still massive and I want to keep modularizing it and am having trouble moving the other resources into cosmos-db.bicep. If I was to add the cosmos database resource into the file:
cosmos-db.bicep
param databaseName string
param cosmosDbName string
param cosmosContainerName string
param id string
param partitionKey string
// Cosmos Database
resource cosmosdb 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases#2020-04-01' = {
name: cosmosDbName
properties: {
resource: {
id: databaseName
}
options: {
throughput: 400
}
}
}
// Cosmos Containers
resource cosmosContainerUser 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers#2021-06-15' = {
name: cosmosContainerName
properties: {
resource: {
id: id
indexingPolicy: {
indexingMode: 'consistent'
automatic: true
includedPaths:[
{
path: '/*'
}
]
excludedPaths: [
{
path: '/"_etag"/?'
}
]
}
partitionKey: {
kind: 'Hash'
paths: [
partitionKey
]
}
conflictResolutionPolicy: {
mode: 'LastWriterWins'
conflictResolutionPath: '/_ts'
}
}
}
}
main.bicep
module cosmosdb '../../../cicd/bicep/modules/datastore/cosmos-db.bicep' = {
name: 'cosmosDbName'
params: {
cosmosDbName: 'cosmosDbName'
databaseName: databaseName
}
}
I get a red line under the params keyword in both cosmosdb and cosmosContainersUser modules in main.bicep. For cosmosdb module it says: The specified "object" declaration is missing the following required properties: "cosmosContainerName", "id", "partitionKey".bicep(BCP035) and the params keyword in cosmosContainersUser module says The specified "object" declaration is missing the following required properties: "cosmosDbName", "databaseName".bicep(BCP035).
I'm guessing this is a dependency issue since these resources rely on one another. I can't use the parent key word in main.bicep because modules don't support it. I try to use dependsOn and I tried adding parent to the cosmosContainerUser in cosmos-db.bicep and still get the same error messages.

Related

How to check is a KeyVault secret already exists in bicep file

I have bicep files that contains a KeyVault module, and a SQL server module. In the SQL server bicep file, I run a deploymentScript which runs a PowerShell script to generator a password and adds it as a secret within the KeyVault. This password is used as the Admin password for the SQL server.
I would like to have the script only generates and saves the password if the secret doesn't already exist within the Key Vault.
main.bicep
// Resource Module
module resourceKeyVaultModule './modules/kv.bicep' = {
name: 'resourceKeyVaultModuleDeployment'
params: {
application: application
location: location
environment: environment
severity: severity
}
scope: resourceGroup
}
module resourceSqlServerModule './modules/sql.bicep' = {
name: 'resourceSqlServerModuleDeployment'
params: {
application: application
location: location
environment: environment
severity: severity
nameKeyVault: resourceKeyVaultModule.outputs.name
}
scope: resourceGroup
}
kv.bicep
// == Key Vault
resource keyVault 'Microsoft.KeyVault/vaults#2022-07-01' = {
name: nameKeyVault
location: location
tags: {
location: location
environment: environment
severity: severity
}
properties: {
accessPolicies: [
{
objectId: ''
permissions: {
certificates: [
'all'
]
keys: [
'all'
]
secrets: [
'all'
]
storage: [
'all'
]
}
tenantId: ''
}
]
sku: {
family: 'A'
name: 'standard'
}
tenantId: '
}
}
output name string = keyVault.name
sql.bicep
// == Generate Password
resource generatePassword 'Microsoft.Resources/deploymentScripts#2020-10-01' = {
name: 'generatePassword'
location: location
kind: 'AzurePowerShell'
properties: {
azPowerShellVersion: '3.0'
retentionInterval: 'PT1H'
arguments: '-lowercase 4 -uppercase 4 -numbers 4 -symbols 2'
scriptContent: loadTextContent('../../../Scripts/generatePassword.ps1')
}
}
// == Key Vault
resource keyVault 'Microsoft.KeyVault/vaults#2022-07-01' existing = {
name: nameKeyVault
}
// == SQL Server
resource sqlServer 'Microsoft.Sql/servers#2022-05-01-preview' = {
name: nameSqlServer
location: location
tags: {
location: location
environment: environment
severity: severity
}
properties: {
administratorLogin: nameSqlServer
administratorLoginPassword: generatePassword.properties.outputs.password
minimalTlsVersion: '1.2'
}
}
resource secretPasswordSqlAdmin 'Microsoft.KeyVault/vaults/secrets#2022-07-01' = {
name: 'password-sql-admin'
parent: keyVault
tags: {
location: location
environment: environment
severity: severity
}
properties: {
value: generatePassword.properties.outputs.password
}
}

Give access to Azure Logic App in a CICD pipeline to connect to a service bus topic with managed identity

I am trying to get a logic app to read messages from a service bus topic. I am getting the error with the connection in logic app designer as :
Could not retrieve values. Error code: 'Unauthorized', Message: 'The remote server returned an error: (401) Unauthorized. Manage,EntityRead claims required for this operation. TrackingId:, SystemTracker:, Timestamp: clientRequestId: '. More diagnostic information: x-ms-client-request-id is ''.
currently I have tried the following in my cicd pipeline:
Grant permission to the logic app standard to access the connection api
resource servicebusConnectorAccessPolicy 'Microsoft.Web/connections/accessPolicies#2018-07-01-preview' = {
name: '${servicebusConnector.name}/${logicAppName}'
location: location
properties: {
principal: {
type: 'ActiveDirectory'
identity: {
tenantId: subscription().tenantId
objectId: logicApp.identity.principalId
}
}
}
}
Get this error:
ERROR: {"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.","details":[{"code":"BadRequest","message":"{\r\n "error": {\r\n "code": "InvalidTemplate",\r\n "message": "Unable to process template language expressions for resource '/subscriptions//resourceGroups//providers/Microsoft.Web/connections/servicebus/accessPolicies/' at line '124' and column '5'. 'The language expression property 'principalId' doesn't exist, available properties are 'type, userAssignedIdentities'.'",\r\n "additionalInfo": [\r\n {\r\n "type": "TemplateViolation",\r\n "info": {\r\n "lineNumber": 124,\r\n "linePosition": 5,\r\n "path": ""\r\n }\r\n }\r\n ]\r\n }\r\n}"}]}}
also:
resource logicAppServiceBusRoleAssignment 'Microsoft.Authorization/roleAssignments#2020-10-01-preview' = {
scope: serviceBusNamespace
name: guid('${serviceBusNamespaceName}-${roleDefinitionId}')
properties: {
principalType: 'ServicePrincipal'
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
principalId: logicApp.identity.principalId
}
}
which yields this error:
ERROR: {"code": "InvalidTemplateDeployment", "message": "The template deployment failed with error: 'Authorization failed for template resource '' of type 'Microsoft.Authorization/roleAssignments'. The client '' with object id '' does not have permission to perform action 'Microsoft.Authorization/roleAssignments/write' at scope '/subscriptions//resourceGroups//providers/Microsoft.ServiceBus/namespaces//providers/Microsoft.Authorization/roleAssignments/'.'."}
for the role, it lacks permissions to add the role.
my logic app bicep file is as follows:
param logicAppName string // The name of the logic app to create.
param location string //Location for all resources.
param workflowDefinition object // the workflow schema in json
param connectorName string
param serviceBusNamespaceName string
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities#2022-01-31-preview' = {
name: <managedIDname>
location: location
}
resource servicebusConnector 'Microsoft.Web/connections#2016-06-01' = {
name: connectorName
location: location
kind: 'V2'
properties: {
api: {
id: concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/servicebus')
}
displayName: connectorName
parameterValueSet: {
name: 'managedIdentityAuth'
values: {
namespaceEndpoint: {
value: '<endpoint>'
}
}
}
}
}
resource logicApp 'Microsoft.Logic/workflows#2019-05-01' = {
name: logicAppName
location: location
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${managedIdentity.id}': {}
}
}
tags: {
displayName: logicAppName
}
properties: {
state: 'Enabled'
definition: workflowDefinition.definition
parameters: workflowDefinition.parameters
}
}
and for the service bus I have the following code:
param serviceBusNamespaceName string //Name of the Service Bus namespace
param serviceBusQueueName string // Name of the Queue
param location string // Location for all resources.
param topicApprovalName string // name of the topic for approval
param topicApprovalSubscriptionName string // name of the topic subscription
param connectorName string // name of the connector
param logicAppName string
param roleDefinitionId string = '8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
resource logicApp 'Microsoft.Logic/workflows#2019-05-01' existing = {
name: logicAppName
}
resource lamanagedidentity 'Microsoft.ManagedIdentity/userAssignedIdentities#2022-01-31-preview' existing = {
name: 'managedIdentity'
}
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces#2022-01-01-preview' = {
name: serviceBusNamespaceName
location: location
sku: {
name: 'Standard'
}
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${lamanagedidentity.id}': {}
}
}
properties: {}
}
resource serviceBusQueue 'Microsoft.ServiceBus/namespaces/queues#2022-01-01-preview' = {
parent: serviceBusNamespace
name: serviceBusQueueName
properties: {
lockDuration: 'PT5M'
maxSizeInMegabytes: 1024
requiresDuplicateDetection: false
requiresSession: false
defaultMessageTimeToLive: 'P10675199DT2H48M5.4775807S'
deadLetteringOnMessageExpiration: false
duplicateDetectionHistoryTimeWindow: 'PT10M'
maxDeliveryCount: 10
autoDeleteOnIdle: 'P10675199DT2H48M5.4775807S'
enablePartitioning: false
enableExpress: false
}
}
resource topic 'Microsoft.ServiceBus/namespaces/topics#2021-11-01' = {
name: topicApprovalName
parent: serviceBusNamespace
}
resource topicsubscription 'Microsoft.ServiceBus/namespaces/topics/subscriptions#2022-01-01-preview' = {
name: topicApprovalSubscriptionName
parent: topic
}
resource sbNamespaceAuthRules 'Microsoft.ServiceBus/namespaces/AuthorizationRules#2022-01-01-preview' = {
name: <auth name>
parent: serviceBusNamespace
properties: {
rights: [
'Listen'
'Manage'
'Send'
]
}
}
resource servicebusConnector 'Microsoft.Web/connections#2018-07-01-preview' = {
name: connectorName
location: location
kind: 'V2'
properties: {
api: {
id: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Web/locations/${location}/managedApis/servicebus'
}
displayName: connectorName
parameterValueSet: {
name: <name>
values: {
namespaceEndpoint: {
value: '<endpoint>'
}
}
}
}
}
// Grant permission to the logic app standard to access the connection api
resource servicebusConnectorAccessPolicy 'Microsoft.Web/connections/accessPolicies#2018-07-01-preview' = {
name: '${servicebusConnector.name}/${logicAppName}'
location: location
properties: {
principal: {
type: 'ActiveDirectory'
identity: {
tenantId: subscription().tenantId
objectId: logicApp.identity.principalId
}
}
}
}
//output connectionRuntimeUrl string = reference(servicebusConnector.id, servicebusConnector.apiVersion, 'full').properties.connectionRuntimeUrl
Not sure why this isn't working... the issue that I'm landing on is the object id of this:
resource servicebusConnectorAccessPolicy 'Microsoft.Web/connections/accessPolicies#2018-07-01-preview' = {
name: '${servicebusConnector.name}/${logicAppName}'
location: location
properties: {
principal: {
type: 'ActiveDirectory'
identity: {
tenantId: subscription().tenantId
objectId: logicApp.identity.principalId
}
}
}
}
any help or advice would be welcome, I've been bashing my head against this for three days, and it just won't work.
thank you very much
I tried using a keyvault and connecting with a primary connection string, this did not work.
I have tried managed identity and roles, but both are not working for me.
it can find the connection, but gives an access error as cited above.
I was expecting for the connection to be authorized and for there to not be an error with the access policy.

Bicep -Can not perform requested operation on nested resource

I encountered this error in the bicep file while creating function app with VNET integration. My Vnet is in another resource group named 'tst-vnet' separate from my function app. Is there a way to resolve this? Here is the code snippet:
resource functionApp 'Microsoft.Web/sites#2022-03-01' = {
name: functionAppName
location: location
tags: tags
kind: 'functionapp'
identity: {
type: 'SystemAssigned'
}
properties: {
httpsOnly: true
serverFarmId: functionAppHostingPlan.id
clientAffinityEnabled: true
publicNetworkAccess: 'Disabled'
siteConfig: {
appSettings: [
{
name: 'AzureWebJobsStorage'
value: 'DefaultEndpointsProtocol=https;AccountName=${functionAppStorageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(functionAppStorageAccount.id, functionAppStorageAccount.apiVersion).keys[0].value}'
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: runtimeStackVersion
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: runtimeStack
}
{
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
value: 'DefaultEndpointsProtocol=https;AccountName=${functionAppStorageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(functionAppStorageAccount.id, functionAppStorageAccount.apiVersion).keys[0].value}'
}
{
name: 'WEBSITE_CONTENTSHARE'
value: 'functionapp'
}
{
name: 'WEBSITE_VNET_ROUTE_ALL'
value: '1'
}
{
name: 'WEBSITE_DNS_SERVER'
value: '168.63.129.16'
}
{
name: 'WEBSITE_CONTENTOVERVNET'
value: '1'
}
]
minTlsVersion: '1.2'
linuxFxVersion: linuxFxVersion
ftpsState: 'FtpsOnly'
}
}
}
I created a module to in order to use a resource group scope in order to reference the 'tst-vnet'
module networkConfig 'modules/network-config.bicep' = {
name: '${deploymentPrefix}-fn-networkcfg'
dependsOn: [
functionApp
]
scope: resourceGroup(pvtResourceGroupName)
params: {
functionAppName: functionAppName
privateBackendSubnet: privateBackendSubnet
privateEndpointVNet: privateEndpointVNet
privateBackendSubnetCIDR: privateBackendSubnetCIDR
}
}
This is the content of network-config.bicep
resource networkConfig 'Microsoft.Web/sites/networkConfig#2022-03-01' = {
name: '${functionAppName}/virtualNetwork'
properties: {
subnetResourceId: resourceId('Microsoft.Network/virtualNetworks/subnets', privateEndpointVNet, privateBackendSubnet)
swiftSupported: true
}
}
Full error from resource group deployment
{
"status": "Failed",
"error": {
"code": "DeploymentFailed",
"message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.",
"details": [
{
"code": "NotFound",
"message": "{\r\n "error": {\r\n "code": "ParentResourceNotFound",\r\n "message": "Can not perform requested operation on nested resource. Parent resource 'ase-data-tst-edp-fn-001' not found."\r\n }\r\n}"
}
]
When declaring a module, you can set a scope for the module that is different than the scope for the containing Bicep file. In your scenario, as you have existing virtual network, which is different scope, the virtual network should be declared in separate module. For your reference, you can use below template to deploy the function app integrating with virtual network which is in another resource group.
main.bicep file
#description('Location for all resources except Application Insights.')
param location string = resourceGroup().location
#description('Location for Application Insights.')
param appInsightsLocation string
#description('The language worker runtime to load in the function app.')
#allowed([
'node'
'dotnet'
'java'
])
param runtime string = 'node'
#description('The name of the function app that you wish to create.')
param appName string = 'fnapp${uniqueString(resourceGroup().id)}'
#description('Storage Account type')
#allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_RAGRS'
])
param storageAccountType string = 'Standard_LRS'
#description('The name of the virtual network to be linked.')
param vnetName string
#description('The name of the subnet to be created within the virtual network.')
param subnetName string
#description('The name of the resource group where existing virtual network exists.')
param vnresourceGroup string
var functionAppName = appName
var hostingPlanName = appName
var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
var functionWorkerRuntime = runtime
resource virtualNetwork 'Microsoft.Network/virtualNetworks#2020-06-01' existing = {
name: vnetName
scope: resourceGroup(vnresourceGroup)
}
resource storageAccount 'Microsoft.Storage/storageAccounts#2021-04-01' = {
name: storageAccountName
location: location
sku: {
name: storageAccountType
}
kind: 'StorageV2'
}
resource appInsights 'Microsoft.Insights/components#2020-02-02' = {
name: functionAppName
location: appInsightsLocation
kind: 'web'
properties: {
Application_Type: 'web'
}
}
resource serverFarm 'Microsoft.Web/serverfarms#2020-06-01' = {
name: hostingPlanName
location: location
sku: {
name: 'EP1'
tier: 'ElasticPremium'
}
kind: 'elastic'
properties: {
maximumElasticWorkerCount: 20
}
}
resource function 'Microsoft.Web/sites#2020-06-01' = {
name: functionAppName
location: location
kind: 'functionapp'
properties: {
serverFarmId: serverFarm.id
siteConfig: {
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.properties.InstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: 'InstrumentationKey=${appInsights.properties.InstrumentationKey}'
}
{
name: 'AzureWebJobsStorage'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix= ${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
}
{
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value};'
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~3'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: functionWorkerRuntime
}
{
name: 'WEBSITE_NODE_DEFAULT_VERSION'
value: '~12'
}
]
}
}
dependsOn: [
virtualNetwork
]
}
resource networkConfig 'Microsoft.Web/sites/networkConfig#2020-06-01' = {
parent: function
name: 'virtualNetwork'
properties: {
subnetResourceId: subnet.outputs.subnetId
swiftSupported: true
}
dependsOn: [
subnet
]
}
module subnet 'modules/subnet.bicep' = {
name: subnetName
scope: resourceGroup(vnresourceGroup)
params: {
subnetName: subnetName
subnetPrefix: '10.2.1.0/24'
vnetName: vnetName
}
}
subnet.bicep
#description('The name of the virtual network to be linked.')
param vnetName string
#description('The name of the subnet to be created within the virtual network.')
param subnetName string
#description('The address Prefix of subnet to be created within the virtual network.')
param subnetPrefix string
param deploySubnet bool = true
resource VNET 'Microsoft.Network/virtualNetworks#2021-02-01' existing = {
name: vnetName
}
resource Subnets 'Microsoft.Network/virtualNetworks/subnets#2020-11-01'= if (deploySubnet) {
name: subnetName
parent: VNET
properties: {
addressPrefix: subnetPrefix
privateEndpointNetworkPolicies: 'Disabled'
privateLinkServiceNetworkPolicies: 'Enabled'
delegations: [
{
name: 'Microsoft.Web.serverFarms'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
output subnetId string = deploySubnet ? Subnets.id:''

Azure function app's signalr_extension is not populated to use in signalR upstreams when creating a function app by Bicep

I created an Azure Function App by Bicep and tried to get the signalr_extension's value to use in the "upstream" configuration section of a serverless Azure SignalR Service. This is how I try to obtain this value in Bicep:
var signalRKey = listKeys(resourceId('Microsoft.Web/sites/host', funcAppName, 'default'), '2022-03-01').systemkeys.signalr_extension
This is how I configure the signalR service's upstream:
urlTemplate: 'https://${funcAppName}.azurewebsites.net/runtime/webhooks/signalr?code=${signalRKey}'
Running the bicep templates leads to the failure below:
Encountered an error (ServiceUnavailable) from host runtime.
When I remove the {signalRKey} from urlTemplate and replace it with a fictitious hard-coded value, the signalR is provisioned successfully.
The other thing that I noticed was that the singalr_extension key value was not populated after the function app was provisioned.
What am I missing in this exercise?
This feature is not available and feasible in App Service Plans. The signalr_extension must be populated manually after deploying the function app and signalR.
The signalr_extension is only created once you've deployed your function app with a SignalRTrigger function.
You can generate this key upfront if you're deploying the Function App and signalR service at the same time:
param functionAppName string
// Create the function app key for signalR
resource signalRKey 'Microsoft.Web/sites/host/systemkeys#2021-03-01' = {
name: '${functionAppName}/default/signalr_extension'
properties: {
name: 'signalr_extension'
}
}
The ARM API to generate function keys is just pointing to the function app API so it could take some time before it become available (see issue on github).
I managed to get this working consistently by deploying the systemkey and signalr using module.
Also for function app running on linux, the AzureWebJobsStorage setting is mandatory.
functionapp-systemkey.bicep module:
param functionAppName string
param keyName string
resource signalRKey 'Microsoft.Web/sites/host/systemkeys#2021-03-01' = {
name: '${functionAppName}/default/${keyName}'
properties: {
name: keyName
}
}
signalr.bicep module:
param location string = resourceGroup().location
param signalRName string
param functionAppName string
resource signalR 'Microsoft.SignalRService/signalR#2022-02-01' = {
name: signalRName
location: location
sku: {
name: 'Free_F1'
tier: 'Free'
capacity: 1
}
properties: {
features: [
{
flag: 'ServiceMode'
value: 'Serverless'
}
{
flag: 'EnableConnectivityLogs'
value: 'true'
}
]
cors: {
allowedOrigins: [
'*'
]
}
tls: {
clientCertEnabled: false
}
upstream: {
templates: [
{
hubPattern: '*'
eventPattern: '*'
categoryPattern: '*'
auth: {
type: 'None'
}
urlTemplate: 'https://${signalRName}.azurewebsites.net/runtime/webhooks/signalr?code=${listKeys(resourceId('Microsoft.Web/sites/host', functionAppName, 'default'), '2022-03-01').systemkeys.signalr_extension}'
}
]
}
}
}
main.bicep:
param location string = resourceGroup().location
param storageName string
param appServicePlanName string
param functionAppName string
param signalRName string
resource storage 'Microsoft.Storage/storageAccounts#2021-09-01' = {
name: storageName
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
}
}
resource appServicePlan 'Microsoft.Web/serverfarms#2021-03-01' = {
name: appServicePlanName
location: location
sku: {
name: 'Y1'
tier: 'Dynamic'
size: 'Y1'
family: 'Y'
capacity: 0
}
kind: 'functionapp'
properties: {
perSiteScaling: false
elasticScaleEnabled: false
maximumElasticWorkerCount: 1
isSpot: false
reserved: true
isXenon: false
targetWorkerCount: 0
targetWorkerSizeId: 0
zoneRedundant: false
}
}
resource functionApp 'Microsoft.Web/sites#2021-03-01' = {
name: functionAppName
location: location
kind: 'functionapp,linux'
properties: {
serverFarmId: appServicePlan.id
clientAffinityEnabled: false
clientCertEnabled: false
httpsOnly: true
siteConfig:{
linuxFxVersion: 'DOTNET|6.0'
use32BitWorkerProcess: true
ftpsState: 'FtpsOnly'
cors: {
allowedOrigins: [
'https://portal.azure.com'
]
supportCredentials: false
}
minTlsVersion: '1.2'
appSettings: [
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'dotnet'
}
{
name: 'AzureWebJobsStorage'
value: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${listKeys(storage.id, '2019-06-01').keys[0].value};EndpointSuffix=core.windows.net;'
}
]
}
}
}
var signalrKeyName = 'signalr_extension'
module signalrKey 'modules/functionapp-systemkey.bicep' = {
name: '${functionAppName}-systemkey-${signalrKeyName}'
params: {
functionAppName: functionApp.name
keyName: signalrKeyName
}
}
module signalr 'modules/signalr.bicep' = {
name: signalRName
params: {
location: location
functionAppName: functionApp.name
signalRName: signalRName
}
dependsOn:[
signalrKey
]
}

How to populate Azure SignalR Service's upstream code in Bicep

I have a Bicep template to create an Azure SignalR Service per the following script. How can I obtain the upstream's code value within the bicep template and populate the urlTemplate's code value based on it? (the keyword TBD exhibits the exact spot in the following code.)
targetScope = 'resourceGroup'
param location string = resourceGroup().location
param signalRName string
resource signalR 'Microsoft.SignalRService/signalR#2022-02-01' = {
name: signalRName
location: location
kind: 'SignalR'
sku: {
name: 'Free_F1'
tier: 'Free'
capacity: 1
}
properties: {
features: [
{
flag: 'ServiceMode'
value: 'Serverless'
}
{
flag: 'EnableConnectivityLogs'
value: 'true'
}
{
flag: 'EnableMessagingLogs'
value: 'true'
}
{
flag: 'EnableLiveTrace'
value: 'true'
}
]
liveTraceConfiguration: {
enabled: 'true'
categories: [
{
name: 'ConnectivityLogs'
enabled: 'true'
}
{
name: 'MessagingLogs'
enabled: 'true'
}
{
name: 'HttpRequestLogs'
enabled: 'true'
}
]
}
cors: {
allowedOrigins: [
'*'
]
}
upstream: {
templates: [
{
hubPattern: '*'
eventPattern: '*'
categoryPattern: '*'
auth: {
type: 'None'
}
urlTemplate: 'https://${signalRName}.azurewebsites.net/runtime/webhooks/signalr?code=TBD'
}
]
}
}
}
I'm assuming that you are using a function app that has the same name as your signalR service.
Looking at the documentation:
The url always looks like that <Function_App_URL>/runtime/webhooks/signalr?code=<API_KEY>
the API_KEY is a function app systemkey called signalr_extension.
So you should be able to retrieve the key and use it like that:
var signalRKey = listKeys(resourceId('Microsoft.Web/sites/host', signalRName, 'default'), '2022-03-01').systemkeys.signalr_extension
resource signalR 'Microsoft.SignalRService/signalR#2022-02-01' = {
name: signalRName
...
properties: {
...
upstream: {
templates: [
{
...
urlTemplate: 'https://${signalRName}.azurewebsites.net/runtime/webhooks/signalr?code=${signalRKey}'
}
]
}
}
}

Resources