skip to Main Content

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?

2

Answers


  1. Chosen as BEST ANSWER

    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.


  2. 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
      ]
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search