我有一个带有上游设置的"ServerLess"SignalR资源,可以与后台功能应用程序对话。一切都在起作用。问题是如何自动创建这些资源,因为两者之间似乎存在循环引用。
SignalR上游设置需要功能应用程序中的"signal_extension"应用程序密钥才能包含在上游URL模板中。相反,函数应用程序需要"AzureSignalRConnectionString"应用程序设置。因此循环引用。
更复杂的是,"signal_extension"应用程序密钥似乎只有在您将signalr触发的函数部署到Function App时才会创建。是否可以在ARM模板创建时在功能应用程序中"手动"设置随机化的应用程序密钥,然后用于构建SignalR资源上游设置?
我曾尝试使用listkeys/listsecrets在ARM模板中"转储"一个正在工作的函数应用程序作为输出,但主机/应用程序密钥似乎没有暴露。
是否可以为这些资源的创建编写脚本(使用ARM或Azure CLI)?
经过一些实验,我想我已经解决了这个问题。以下是步骤:
-
在ARM模板中,创建Function App和SignalR。使功能应用程序依赖于Signal。将SignalR-AzureSignalRConnectionString应用程序设置注入函数应用程序。
-
将功能代码部署到功能应用程序。
-
使用Azure CLI提取"signal_extension":
az函数应用程序密钥列表--名称<函数app_name>--资源组<;resource_group>
-
获取生成的systemKeys/signer_extension值,并将其放入另一个Azire CLI命令中:
az信号器上游更新--name<signal_name>--资源组<;resource_group>--template url template=";https://<函数app_name>。azurewebsites.net/runtime/webhooks/signer?code=<来自步骤3的代码";
至少这些都可以在DevOps管道中编写脚本。
使用Azure Bicep,我成功地在一个步骤中使其工作:
仅在windows上测试了功能应用v4 dotnet6
- 创建存储帐户
- 创建应用程序服务计划(windows)
- 创建没有应用程序设置的功能应用程序
- 创建
signalr_extension
系统密钥 - 使用
signalr_extension
系统密钥创建signalR服务 - 使用
AzureSignalRConnectionString
连接字符串部署功能应用程序appsettings
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
}
}