带有上游设置的 ARM 脚本 ServerLess SignalR 到 Function App

ARM scripting ServerLess SignalR with Upstream Setting to Function App

我有一个 'ServerLess' SignalR 资源,其上游设置可与支持功能应用程序对话。一切正常。问题是如何自动创建这些资源,因为两者之间似乎存在循环引用。

SignalR 上游设置需要功能应用中的 'signalr_extension' 应用密钥以包含在上游 URL 模板中。相反,功能应用程序需要 'AzureSignalRConnectionString' 应用程序设置。因此循环引用。

使事情复杂化的是,'signalr_extension' App Key 似乎仅在您将 SignalR 触发的函数部署到 Function App 时创建。是否可以在 ARM 模板创建时在 Function App 中设置随机应用密钥 'manually',然后用于构建 SignalR 资源上游设置?

我已经尝试 'dump' 使用 listkeys/listsecrets 将一个有效的函数应用程序作为 ARM 模板中的输出,但是 Host/App 键似乎没有暴露。

是否可以编写脚本(使用 ARM 或 Azure CLI)创建这些资源?

经过一些试验,我想我已经解决了这个问题。以下是步骤:

  1. 在ARM模板中,创建Function App和SignalR。使 Function App 依赖于 (dependsOn) Signal。将 SignalR AzureSignalRConnectionString appsetting 注入 Function App。

  2. 将函数代码部署到函数应用

  3. 使用 Azure CLI 提取 'signalr_extension':

    az functionapp keys list --name --resource-group

  4. 将生成的 systemKeys/signalr_extension 值放入另一个 Azire CLI 命令中:

    az signalr 上游更新 --name --resource-group --template url-template="https://.azurewebsites.net/runtime/webhooks/signalr?code=<步骤 3 中的代码"

至少这一切都可以编写到 DevOps 管道中。

使用 Azure Bicep,我设法一步完成:
仅在 windows.

上测试了函数应用程序 v4 dotnet6
  • 创建存储帐户
  • 创建应用服务计划(windows)
  • 创建没有应用设置的函数应用
  • 创建 signalr_extension 系统密钥
  • 使用 signalr_extension 系统密钥创建 signalR 服务
  • 使用 AzureSignalRConnectionString 连接字符串部署函数应用程序设置。

main.bicep:

param location string = resourceGroup().location

param storageName string
param appServicePlanName string
param functionAppName string
param signalRName string

// Create storage
resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' = {
  name: storageName
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
  }
}

// Create serverless app service plan
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: 'Y1'
    tier: 'Dynamic'
    size: 'Y1'
    family: 'Y'
    capacity: 0
  }
  properties: {
    reserved: false
  }
}

// Create the function app without the app settings
resource functionApp 'Microsoft.Web/sites@2021-03-01' = {
  name: functionAppName
  location: location
  kind: 'functionapp'
  properties: {
    serverFarmId: appServicePlan.id
    clientAffinityEnabled: false
    clientCertEnabled: false
    httpsOnly: true
    siteConfig: {
      ftpsState: 'Disabled'
      minTlsVersion: '1.2'
      cors: {
        allowedOrigins: [
          'https://portal.azure.com'
        ]
        supportCredentials: false
      }
    }
  }
}

// Create the signalr key
var signalrKeyName = 'signalr_extension'
resource signalRKey 'Microsoft.Web/sites/host/systemkeys@2021-03-01' = {
  name: '${functionApp.name}/default/${signalrKeyName}'
  properties: {
    name: signalrKeyName
  }
}

// Create the signalR service and inject the signalr_extension key
resource signalR 'Microsoft.SignalRService/signalR@2022-02-01' = {
  name: signalRName
  location: location
  dependsOn: [
    signalRKey
  ]
  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://${functionApp.name}.azurewebsites.net/runtime/webhooks/signalr?code=${listKeys(resourceId('Microsoft.Web/sites/host', functionApp.name, 'default'), '2022-03-01').systemkeys.signalr_extension}'
        }
      ]
    }
  }
}

// Deploy the app settings at the end with the storage and signalR connectionstring
resource functionAppAppSettings 'Microsoft.Web/sites/config@2020-09-01' = {
  name: '${functionApp.name}/appsettings'
  properties: {
    FUNCTIONS_EXTENSION_VERSION: '~4'
    FUNCTIONS_WORKER_RUNTIME: 'dotnet'
    WEBSITE_RUN_FROM_PACKAGE: '1'
    AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${listKeys(storage.id, '2019-06-01').keys[0].value};EndpointSuffix=core.windows.net;'
    WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${listKeys(storage.id, '2019-06-01').keys[0].value};EndpointSuffix=core.windows.net;'
    WEBSITE_CONTENTSHARE: functionApp.name
    AzureSignalRConnectionString: listkeys(signalR.id, signalR.apiVersion).primaryConnectionString
  }
}