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

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}'
}
]
}
}
}

Related

I am trying to deploy a Logic-App with a Service-Bus Deploy to azure app using Bicep template

I am trying to deploy a bicep template to create a logic app and service bus using VS Code. But When I am trying to deploy the Bicep to azure portal
I am getting bad request error
enter image description here
Here is my Bicep look like
param prefix string
param location string = resourceGroup().location
param sendGridApiKey string
resource serviceBus 'Microsoft.ServiceBus/namespaces#2021-11-01' existing = {
name: '${prefix}sb'
}
resource serviceBusConnection 'Microsoft.Web/connections#2016-06-01' = {
name: '${prefix}sbconn'
location: location
properties: {
displayName: '${prefix}sb'
api: {
id: '${subscription().id}/providers/Microsoft.Web/locations/${location}/managedApis/servicebus'
}
parameterValueSet: {
name: 'managedIdentityAuth'
values: {
namespaceEndpoint: {
value: 'sb://${serviceBus.name}.servicebus.windows.net'
}
}
}
}
}
resource sendGridConnection 'Microsoft.Web/connections#2016-06-01' = {
name: '${prefix}sndgrdconn'
location: location
properties: {
displayName: '${prefix}sndgrdconn'
api: {
id: '${subscription().id}/providers/Microsoft.Web/locations/${location}/managedApis/sendgrid'
}
parameterValues: {
apiKey: sendGridApiKey
}
}
}
resource logicAppEmailSend 'Microsoft.Logic/workflows#2019-05-01' = {
name: '${prefix}logic-EmailSend'
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
state: 'Enabled'
definition: {
'$schema': 'https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#'
contentVersion: '1.0.0.0'
parameters: {
'$connections': {
defaultValue: {}
type: 'Object'
}
}
}
}
}
I have tried a lot but didn't find the error
thanks in advance.
I have modified a bit of code in the given code so it's working, and I have deployed the bicep file using VS Code to Azure
{subscription().id}/providers/Microsoft.Web/locations/{location}/servicebus
I have changed the above path to
{subscription().id}/providers/Microsoft.Web/locations/{location}/managedApis/servicebus
Thanks to #mattruma for Bicep template
param prefix string
param location string = resourceGroup().location
param sendGridApiKey string
resource serviceBus 'Microsoft.ServiceBus/namespaces#2021-11-01' existing = {
name: '${prefix}sb'
}
resource serviceBusConnection 'Microsoft.Web/connections#2016-06-01' = {
name: '${prefix}sbconn'
location: location
properties: {
displayName: '${prefix}sb'
api:
{
id: '${subscription().id}/providers/Microsoft.Web/locations/${location}/managedApis/servicebus'
}
parameterValueSet:
{
name: 'managedIdentityAuth'
values:
{
namespaceEndpoint:
{
value: 'sb://${serviceBus.name}.servicebus.windows.net'
}
}
}
}
}
resource logicAppEmailSend 'Microsoft.Logic/workflows#2019-05-01' = {
name: '${prefix}logic-EmailSend'
location: location
identity: {
type: 'SystemAssigned'
}
properties:
{
state: 'Enabled'
definition:
{
'$schema': 'https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#'
contentVersion: '1.0.0.0'
parameters:
{
'$connections': {
defaultValue: { }
type: 'Object'
}
}
triggers:
{
'When_a_message_is_received_in_a_queue_(auto-complete)': {
recurrence:
{
frequency: 'Minute'
interval: 3
}
evaluatedRecurrence:
{
frequency: 'Minute'
interval: 3
}
type: 'ApiConnection'
inputs:
{
host:
{
connection:
{
name: '#parameters(\'$connections\')[\'servicebus\'][\'connectionId\']'
}
}
method: 'get'
path: '/#{encodeURIComponent(encodeURIComponent(\'emailsend\'))}/messages/head'
queries:
{
queueType: 'Main'
}
}
}
}
actions:
{
'Dead-letter_the_message_in_a_queue': {
runAfter:
{
Send_email: [
'Failed'
'TimedOut'
]
}
type: 'ApiConnection'
inputs:
{
host:
{
connection:
{
name: '#parameters(\'$connections\')[\'servicebus\'][\'connectionId\']'
}
}
method: 'post'
path: '/#{encodeURIComponent(encodeURIComponent(\'emailsend\'))}/messages/deadletter'
queries:
{
deadLetterErrorDescription: ''
deadLetterReason: ''
lockToken: '#triggerBody()?[\'LockToken\']'
sessionId: '#triggerBody()?[\'SessionId\']'
}
}
}
Parse_message:
{
runAfter: { }
type: 'ParseJson'
inputs:
{
content: '#base64ToString(triggerBody()?[\'ContentData\'])'
schema:
{
properties:
{
body:
{
type: 'string'
}
subject:
{
type: 'string'
}
to:
{
type: 'string'
}
}
type: 'object'
}
}
}
Send_email:
{
runAfter:
{
Parse_message: [
'Succeeded'
]
}
type: 'ApiConnection'
inputs:
{
body:
{
from: 'someone#somewhere.com'
fromname: 'Someone'
ishtml: true
subject: '#body(\'Parse_message\')?[\'subject\']'
text: '<p>#{body(\'Parse_message\')?[\'body\']}</p>'
to: '#body(\'Parse_message\')?[\'to\']'
}
host:
{
connection:
{
name: '#parameters(\'$connections\')[\'sendgrid\'][\'connectionId\']'
}
}
method: 'post'
path: '/v4/mail/send'
}
}
}
outputs: { }
}
parameters:
{
'$connections': {
value:
{
servicebus:
{
connectionId: serviceBusConnection.id
connectionName: serviceBusConnection.name
connectionProperties: {
authentication:
{
type: 'ManagedServiceIdentity'
}
}
id: '${subscription().id}/providers/Microsoft.Web/locations/${location}/managedApis/servicebus'
}
}
}
}
}
}
After successfully deploying to azure portal
Click on Goto resource group it will redirect to below page

Azure Dashboard template for App Service Plan

I woudl like to create a Azure Dashboard template to monitor CPU percentage for my App Service Plan. Currently It look like this:
resource dashboardName_resource 'Microsoft.Portal/dashboards#2020-09-01-preview' = {
name: dashboardName
location: location
properties: {
lenses: [
{
order: 0
parts: [
{
position: {
x: 0
y: 4
rowSpan: 3
colSpan: 11
}
metadata: {
inputs: [
{
name: 'queryInputs'
value: {
timespan: {
duration: 'PT1H'
}
id: resourceId(appServicePlanResourceGroup, 'Microsoft.Web/serverfarms', appServicePlanName)
chartType: 0
metrics: [
{
name: 'CPU Percentage'
resourceId: resourceId(appServicePlanResourceGroup, 'Microsoft.Web/serverfarms', appServicePlanName)
}
]
}
}
]
type: 'Extension/Microsoft_Azure_Monitoring/PartType/MetricsChartPart'
}
}
]
}
]
}
}
The validation & deployment was succesfull, but when I get to this dashboard i got this:
Does anyon knows why?
By referring a document from MS sample replica of your bicep code is created and was able to deploy it successfully.
While I am doing a repro, I have changed type extension in .bicep file fromtype:'Extension/Microsoft_Azure_Monitoring/PartType/MetricsChartPart to type:'Extension/HubsExtension/PartType/MonitorChartPart' which got succeeded and able to view the CPU metrics.
Attaching a sample code for your reference:
param location string = resourceGroup().location
param appServicePlanResourceGroup string = 'xxxxx'
param appServicePlanName string = 'xxxxxxx'
resource symbolicname 'Microsoft.Portal/dashboards#2020-09-01-preview' = {
name: 'xxxxmydemo'
location: location
tags: {
demo: 'repro01'
}
properties: {
lenses: [
{
metadata: {}
order: 2
parts: [
{
metadata: {
type: 'Extension/HubsExtension/PartType/MonitorChartPart'
inputs: [
{
name: 'queryInputs'
value: {
timespan: {
duration: 'PT1H'
}
id: resourceId(appServicePlanResourceGroup, 'Microsoft.Web/serverfarms', appServicePlanName)
chartType: 0
metrics: [
{
name: 'CPU Percentage'
resourceId: resourceId(appServicePlanResourceGroup, 'Microsoft.Web/serverfarms', appServicePlanName)
}
]
}
}
]
}
position: {
colSpan: 11
metadata: {}
rowSpan: 5
x: 3
y: 4
}
}
]
}
]
metadata: {}
}
}
Output for your reference:
Note: Validate access control(IAM) permissions for the resource group ideally and it should be part of it.

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
]
}

Azure Application Gateway (Standard V1) returns 502 while deleting instances in VM Scale Set

While deleting instance in VM Scale Set, Application Gateway returns 502 for a brief few seconds.
I have made sure that:
Connection draining on the HTTP setting used is enabled, timeout is set to 60s
Cookie affinity is disabled
No NSG blocking requests from Application Gateway
From my understanding, since connection draining is enabled, deregistering backend instances (in my case, VM instances in a VM scale set) should stop receiving new requests and allow currently in-process requests to finish within the draining timeout.
Also, cookie affinity is disabled so that Application Gateway won't route to the same instances every request, thus making sure the requests won't be routed to the deregistering instances.
Is my understanding or something about my configuration here is wrong?
This is the full Bicep template for my Application Gateway:
resource agw 'Microsoft.Network/applicationGateways#2020-11-01' = {
name: 'agw-1'
location: 'southeastasia'
tags: {
env: 'prod'
}
properties: {
sku: {
name: 'Standard_Medium'
tier: 'Standard'
capacity: 1
}
gatewayIPConfigurations: [
{
name: 'appGatewayIpConfig'
properties: {
subnet: {
id: '${vnetId}/subnets/publicApplicationGateways'
}
}
}
]
sslCertificates: []
authenticationCertificates: []
frontendIPConfigurations: [
{
name: 'appGwPublicFrontendIp'
properties: {
privateIPAllocationMethod: 'Dynamic'
publicIPAddress: {
id: pipId
}
}
}
{
name: 'appGwPrivateFrontendIp'
properties: {
privateIPAllocationMethod: 'Dynamic'
subnet: {
id: '${vnetId}/subnets/publicApplicationGateways'
}
}
}
]
frontendPorts: [
{
name: 'port_80'
properties: {
port: 80
}
}
{
name: 'port_443'
properties: {
port: 443
}
}
]
backendAddressPools: [
{
name: 'webchat'
properties: {
backendAddresses: []
}
}
]
backendHttpSettingsCollection: [
{
name: 'http'
properties: {
port: 80
protocol: 'Http'
cookieBasedAffinity: 'Disabled'
connectionDraining: {
enabled: true
drainTimeoutInSec: 60
}
pickHostNameFromBackendAddress: false
affinityCookieName: 'ApplicationGatewayAffinity'
requestTimeout: 60
}
}
]
httpListeners: [
{
name: 'http-webchat'
properties: {
frontendIPConfiguration: {
id: '${agwId}/frontendIPConfigurations/appGwPublicFrontendIp'
}
frontendPort: {
id: '${agwId}/frontendPorts/port_80'
}
protocol: 'Http'
hostName: 'bagaswh.com'
hostNames: []
requireServerNameIndication: false
}
}
{
name: 'https-webchat'
properties: {
frontendIPConfiguration: {
id: '${agwId}/frontendIPConfigurations/appGwPublicFrontendIp'
}
frontendPort: {
id: '${agwId}/frontendPorts/port_443'
}
protocol: 'Https'
sslCertificate: {
id: '${agwId}/sslCertificates/bagaswh.com'
}
hostName: 'bagaswh.com'
hostNames: []
requireServerNameIndication: true
}
}
]
urlPathMaps: []
requestRoutingRules: [
{
name: 'http-public-webchat'
properties: {
ruleType: 'Basic'
httpListener: {
id: '${agwId}/httpListeners/http-webchat'
}
backendAddressPool: {
id: '${agwId}/backendAddressPools/webchat'
}
backendHttpSettings: {
id: '${agwId}/backendHttpSettingsCollection/http'
}
}
}
{
name: 'https-public-webchat'
properties: {
ruleType: 'Basic'
httpListener: {
id: '${agwId}/httpListeners/https-webchat'
}
backendAddressPool: {
id: '${agwId}/backendAddressPools/webchat'
}
backendHttpSettings: {
id: '${agwId}/backendHttpSettingsCollection/http'
}
}
}
]
probes: [
{
name: 'http-webchat-healthProbe1'
properties: {
protocol: 'Http'
host: 'bagaswh.com'
path: '/'
pickHostNameFromBackendHttpSettings: false
interval: 10
timeout: 60
unhealthyThreshold: 3
}
}
]
rewriteRuleSets: []
redirectConfigurations: []
enableHttp2: false
}
}

Dependency issue with Azure Bicep resource modules

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.

Resources