In den Lektionen 3.13.1 bis 3.13.3 lief die Function App lokal. Jetzt kommt sie nach Azure: als Bicep im Repository, ausgeliefert mit azd, mit verwalteter Identität für den Speicher des Hosts und für Application Insights, ohne Verbindungszeichenfolge mit Schlüssel. Danach messen Sie, was Serverless verspricht und kostet: den Kaltstart und den Preis je Ausführung. Am Ende steht die Entscheidung, ob der Import der Kladde als Worker in Container Apps bleibt oder als Functions läuft, und danach wird alles wieder abgebaut.
Kosten dieser Lektion: Ein Flex-Consumption-Plan kostet ohne immer bereite Instanzen nur, wenn Funktionen laufen, und hat ein kostenloses
Kontingent im Monat; dazu kommen ein Speicherkonto, ein Log-Analytics-Arbeitsbereich und Application Insights nach Datenmenge. Sehen Sie die
aktuellen Preise im Preisrechner nach, bevor Sie beginnen. Eine immer bereite Instanz kostet auch,
wenn sie wartet; schalten Sie sie nur für die Messung ein. Die Lektion endet mit azd down --purge.
Die Function App als Bicep
Die Datei infra/functions.bicep legt alles an, was die App braucht. Learn beschreibt dieselbe Form in seinem Beispiel für Flex Consumption mit
verwalteten Identitäten; hier ist sie für die Kladde zugeschnitten:
// functions.bicep: the Kladde's Function App in a Flex Consumption plan, without a single key
@description('Region of all resources.')
param location string = resourceGroup().location
@description('Short name of the environment, part of every resource name.')
@allowed(['dev', 'test', 'prod'])
param environmentName string = 'dev'
@description('Memory of one instance in MB.')
@allowed([512, 2048, 4096])
param instanceMemoryMB int = 2048
@description('Always ready instances of the HTTP group; every one is paid while it waits.')
@minValue(0)
param alwaysReadyHttp int = 0
var token = toLower(uniqueString(resourceGroup().id, environmentName))
var appName = 'func-kladde-${environmentName}-${token}'
var deploymentContainer = 'app-package'
// Role definitions from the Azure built-in roles.
var storageBlobDataOwner = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
var storageQueueDataContributor = '974c5e8b-45b9-4653-ba55-5f855dd0fb88'
var storageTableDataContributor = '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'
var monitoringMetricsPublisher = '3913510d-42f4-4e42-8a64-420c390055eb'
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: 'id-kladde-func-${environmentName}'
location: location
}
resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: 'stkladdefn${token}'
location: location
kind: 'StorageV2'
sku: { name: 'Standard_LRS' }
properties: {
allowSharedKeyAccess: false
allowBlobPublicAccess: false
minimumTlsVersion: 'TLS1_2'
}
}
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = {
parent: storage
name: 'default'
}
resource packages 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = {
parent: blobService
name: deploymentContainer
}
resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
name: 'log-kladde-func-${environmentName}'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: 30
}
}
resource insights 'Microsoft.Insights/components@2020-02-02' = {
name: 'appi-kladde-func-${environmentName}'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: workspace.id
DisableLocalAuth: true
}
}
resource plan 'Microsoft.Web/serverfarms@2024-04-01' = {
name: 'plan-kladde-func-${environmentName}'
location: location
kind: 'functionapp'
sku: {
tier: 'FlexConsumption'
name: 'FC1'
}
properties: {
reserved: true
}
}
resource app 'Microsoft.Web/sites@2024-04-01' = {
name: appName
location: location
kind: 'functionapp,linux'
identity: {
type: 'UserAssigned'
userAssignedIdentities: { '${identity.id}': {} }
}
properties: {
serverFarmId: plan.id
httpsOnly: true
siteConfig: { minTlsVersion: '1.2' }
functionAppConfig: {
deployment: {
storage: {
type: 'blobContainer'
value: '${storage.properties.primaryEndpoints.blob}${deploymentContainer}'
authentication: {
type: 'UserAssignedIdentity'
userAssignedIdentityResourceId: identity.id
}
}
}
scaleAndConcurrency: {
maximumInstanceCount: 40
instanceMemoryMB: instanceMemoryMB
alwaysReady: alwaysReadyHttp > 0 ? [ { name: 'http', instanceCount: alwaysReadyHttp } ] : []
}
runtime: {
name: 'dotnet-isolated'
version: '10'
}
}
}
resource settings 'config' = {
name: 'appsettings'
properties: {
AzureWebJobsStorage__accountName: storage.name
AzureWebJobsStorage__credential: 'managedidentity'
AzureWebJobsStorage__clientId: identity.properties.clientId
APPLICATIONINSIGHTS_CONNECTION_STRING: insights.properties.ConnectionString
APPLICATIONINSIGHTS_AUTHENTICATION_STRING: 'ClientId=${identity.properties.clientId};Authorization=AAD'
}
}
}
var storageRoles = [storageBlobDataOwner, storageQueueDataContributor, storageTableDataContributor]
resource storageRoleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for role in storageRoles: {
name: guid(storage.id, identity.id, role)
scope: storage
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', role)
principalId: identity.properties.principalId
principalType: 'ServicePrincipal'
}
}]
resource metricsRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(insights.id, identity.id, monitoringMetricsPublisher)
scope: insights
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', monitoringMetricsPublisher)
principalId: identity.properties.principalId
principalType: 'ServicePrincipal'
}
}
output appName string = app.name
output defaultHostName string = app.properties.defaultHostName
Was hier zählt:
- Der Plan ist
FlexConsumptionmit dem NamenFC1, immer unter Linux (reserved: true,kind: 'functionapp,linux'). functionAppConfigist der Teil, den es nur bei Flex Consumption gibt: wohin das Paket der App ausgeliefert wird (ein Container im Speicherkonto, gelesen mit der Identität), wie die App skaliert (höchstens 40 Instanzen, je 2.048 MB) und welche Laufzeit läuft (dotnet-isolated, Version10).- Kein Schlüssel. Das Speicherkonto erlaubt keinen Zugriff mit Kontoschlüssel (
allowSharedKeyAccess: false); der Host meldet sich mit der benutzerzugewiesenen Identität an (AzureWebJobsStorage__credentialund__clientId), Application Insights nimmt Telemetrie nur mit Entra-Anmeldung an (DisableLocalAuth,APPLICATIONINSIGHTS_AUTHENTICATION_STRING). Die Verbindungszeichenfolge von Application Insights enthält keinen Schlüssel, der Zugriff gewährt; sie sagt nur, wohin. - Die Rollen vergibt die Vorlage selbst, mit Namen aus
guid()wie in Lektion 3.3.1: Blob Data Owner, Queue und Table Data Contributor auf dem Speicher (Durable Functions mit dem Anbieter Azure Storage braucht Warteschlangen und Tabellen), Monitoring Metrics Publisher auf Application Insights. Für Service Bus aus Lektion 3.13.2 kommt eine Zuweisung von Azure Service Bus Data Receiver auf dem Namespace der Kladde dazu.
az bicep build --file infra/functions.bicep übersetzt die Datei ohne Warnung. Prüfen Sie vor dem Ausliefern mit What-if, wie in Lektion 3.2.4, und
denken Sie daran, dass What-if nicht beweist, dass die Bereitstellung gelingt.