1

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?

Thomas
  • 24,234
  • 6
  • 81
  • 125
Arash
  • 3,628
  • 5
  • 46
  • 70

2 Answers2

1

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
  ]
}
Thomas
  • 24,234
  • 6
  • 81
  • 125
  • Thanks for the update. I am going to try your updated solution soon. However, I am getting this warning: Resource type "Microsoft.Web/sites/host/systemkeys@2021-03-01" does not have types available.bicep(BCP081) Should I ignore it? – Arash May 27 '22 at 18:20
  • I updated my bicep template per your input but the issue still persists! Error message: Encountered an error (ServiceUnavailable) from host runtime. – Arash May 27 '22 at 19:16
  • It happens in functionapp-systemkey.bicep module. – Arash May 27 '22 at 19:58
  • By the way, my function is dotnet-isolated, but yours is dotnet. I changed the function to dotnet from dotnet-isolated and got a difference error message: "The language expression property 'signalr_extension' doesn't exist, available properties are ..." – Arash May 27 '22 at 21:14
  • are you using the same api version ? – Thomas May 27 '22 at 21:59
  • yes I am using the same. – Arash May 28 '22 at 10:48
  • Just wondering, any idea why this script does not work for me? – Arash May 30 '22 at 11:59
  • Have you tried running the exact same template ? Which azure region are you deploying to ? – Thomas May 30 '22 at 12:03
  • I am using East US. I am not running the same template but modified mine to be alike. – Arash May 30 '22 at 12:09
  • 1
    could you try running mine please ? Just trying to isolate the issue. Let me try dotnet-isolated as well – Thomas May 30 '22 at 13:08
  • I've tried with dotnet-isolated and it works as well. Also if you can share your bicep file, that would be great. – Thomas May 30 '22 at 13:14
  • I tried your code as is and it works. I'm trying to find out what is going wrong with mine. – Arash May 30 '22 at 13:20
  • Using your template for "dotnet" works fine. Switching it to "dotnet-isolated" consistently fails with the error message of "Encountered an error (ServiceUnavailable) from host runtime." – Arash May 30 '22 at 13:30
  • Please share your file and I can have a look. – Thomas May 30 '22 at 13:38
  • UPDATE: your code with dotnet-isolated works fine if I do not use "Basic" and "B1" tier. In terms of sharing the files, I need to have an internal discussion first, and I will let you know. Thanks. – Arash May 30 '22 at 13:45
  • I tried B1, B2 and S1 and none of them was conclusive. I'm wondering if your solution only works with Y1 and Dynamic sku. – Arash May 30 '22 at 14:15
  • I only tried with the serverless model not with the dedicated app service plan model. – Thomas May 30 '22 at 19:57
  • Apparently this solution does not work with dedicated app service plans. You can feel free to test it by B2 or S1 plans and you'll see the failure. – Arash May 31 '22 at 11:09
  • just wondering, did you have a chance to try your template against B1, B2, and S1 plans? – Arash Jun 01 '22 at 12:33
  • So i tried with `b1/linux/dotnet-isolated` and not working. for some reason the keys are not created. but if you use `b1/linux/dotnet` it is working. its really the combination you re testing that is not working. – Thomas Jun 01 '22 at 23:57
  • you should probably open an issue on github – Thomas Jun 02 '22 at 00:08
  • did u make any progress on that ? – Thomas Jun 26 '22 at 11:31
  • 1
    Microsoft's Azure support team has been investigating this issue in App Service Plans. They have not gotten back to me yet. – Arash Jun 27 '22 at 12:02
0

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.

Arash
  • 3,628
  • 5
  • 46
  • 70