diff --git a/plugin/skills/azure-prepare/references/services/app-service/deployment-slots.md b/plugin/skills/azure-prepare/references/services/app-service/deployment-slots.md index b1352cbf3..e34fff5d7 100644 --- a/plugin/skills/azure-prepare/references/services/app-service/deployment-slots.md +++ b/plugin/skills/azure-prepare/references/services/app-service/deployment-slots.md @@ -15,7 +15,7 @@ resource stagingSlot 'Microsoft.Web/sites/slots@2022-09-01' = { } ``` -## Slot Requirements +## Slot Requirements — App Service | SKU Tier | Slots Supported | |----------|-----------------| @@ -24,6 +24,84 @@ resource stagingSlot 'Microsoft.Web/sites/slots@2022-09-01' = { | Standard | 5 | | Premium | 20 | +## Slot Requirements — Azure Functions + +> ⚠️ Slot support for Azure Functions varies by OS and hosting plan. + +| Hosting Plan | OS | Slots Supported | +|---|---|---| +| Flex Consumption (FC1) | Linux | ❌ 0 | +| Consumption (Y1) | **Windows** | ✅ 1 staging slot | +| Consumption (Y1) | Linux | ❌ 0 | +| Elastic Premium (EP1-EP3) | Windows or Linux | ✅ 20 slots | +| Dedicated (Standard+) | Windows or Linux | ✅ 5–20 slots | + +> 💡 **For Azure Functions requiring deployment slots:** +> - **Windows Consumption (Y1) supports 1 staging slot** — this is a supported platform capability. +> If you need it, use it. See the Bicep example below. +> - Recommendation for new projects: prefer **Elastic Premium (EP1+)** (no cold starts, VNet integration) +> or a **Dedicated plan (Standard+)**. Y1 cold starts can affect slot swap warm-up reliability. +> - **Linux Consumption and Flex Consumption do not support deployment slots.** + +### Windows Consumption Function App with Staging Slot (Bicep) + +```bicep +resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = { + name: '${resourcePrefix}-funcplan-${uniqueHash}' + location: location + sku: { name: 'Y1', tier: 'Dynamic' } + // No 'reserved: true' — Windows Consumption +} + +resource functionApp 'Microsoft.Web/sites@2022-09-01' = { + name: '${resourcePrefix}-${serviceName}-${uniqueHash}' + location: location + kind: 'functionapp' // Windows (no 'linux' suffix) + identity: { type: 'SystemAssigned' } + properties: { + serverFarmId: functionAppPlan.id + httpsOnly: true + siteConfig: { + appSettings: [ + { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' } + { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' } + { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' } + { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } + { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-prod' } + { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } + { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString } + ] + } + } +} + +// Staging slot — only 1 staging slot supported on Consumption +resource stagingSlot 'Microsoft.Web/sites/slots@2022-09-01' = { + parent: functionApp + name: 'staging' + location: location + kind: 'functionapp' + properties: { + serverFarmId: functionAppPlan.id + siteConfig: { + appSettings: [ + { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' } + { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' } + { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' } + { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } + { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-staging' } // MUST differ from production + { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } + { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString } + ] + } + } +} +``` + +> ⚠️ `WEBSITE_CONTENTSHARE` **must be unique per slot** on Windows Consumption — each slot needs its own file share. +> Use slot-sticky settings (via `slotConfigNames`) for `WEBSITE_CONTENTSHARE` and `WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` +> so these values do not swap with production. + ## Deployment Flow 1. Deploy to staging slot diff --git a/plugin/skills/azure-prepare/references/services/functions/README.md b/plugin/skills/azure-prepare/references/services/functions/README.md index b75b766d0..a9e6fc420 100644 --- a/plugin/skills/azure-prepare/references/services/functions/README.md +++ b/plugin/skills/azure-prepare/references/services/functions/README.md @@ -44,12 +44,19 @@ services: **Use Flex Consumption for new deployments** (all AZD templates default to Flex). -| Plan | Use Case | Scaling | VNET | -|------|----------|---------|------| -| **Flex Consumption** ⭐ | Default for new projects | Auto, pay-per-execution | ✅ | -| Consumption (Y1) | Variable workloads, cost optimization | Auto, scale to zero | ❌ | -| Premium (EP1-EP3) | No cold starts, longer execution | Auto, min instances | ✅ | -| Dedicated | Predictable load, existing App Service | Manual or auto | ✅ | +| Plan | Use Case | Scaling | VNET | Slots | +|------|----------|---------|------|-------| +| **Flex Consumption** ⭐ | Default for new projects | Auto, pay-per-execution | ✅ | ❌ | +| Consumption Windows (Y1) | Legacy/maintenance, Windows-only features | Auto, scale to zero | ❌ | ✅ 1 staging slot | +| Consumption Linux (Y1) | Legacy/maintenance | Auto, scale to zero | ❌ | ❌ | +| Premium (EP1-EP3) | No cold starts, longer execution, slots | Auto, min instances | ✅ | ✅ 20 slots | +| Dedicated | Predictable load, existing App Service | Manual or auto | ✅ | ✅ varies by SKU | + +> ⚠️ **Deployment Slots Guidance:** +> - **Windows Consumption (Y1)** supports 1 staging slot — valid for existing apps or specific Windows requirements. +> Prefer **Elastic Premium (EP1)** or **Dedicated** for new apps requiring slots, as Consumption cold starts affect swap reliability. +> - **Linux Consumption and Flex Consumption** do **not** support deployment slots. +> - For new projects needing slots: use **Elastic Premium** or an **App Service Plan (Standard+)**. ## Runtime Stacks diff --git a/plugin/skills/azure-prepare/references/services/functions/bicep.md b/plugin/skills/azure-prepare/references/services/functions/bicep.md index adbf0d06f..1de308b64 100644 --- a/plugin/skills/azure-prepare/references/services/functions/bicep.md +++ b/plugin/skills/azure-prepare/references/services/functions/bicep.md @@ -137,6 +137,14 @@ resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04- **⚠️ Not recommended for new deployments. Use Flex Consumption instead.** +> 💡 **OS and Slots Matter for Consumption:** +> - **Linux Consumption** (`kind: 'functionapp,linux'`, `reserved: true`): Does **not** support deployment slots. +> - **Windows Consumption** (`kind: 'functionapp'`, no `reserved`): Supports **1 staging slot** (2 total including production). +> If a user specifically needs Windows Consumption with a slot, that is supported — use the Windows pattern below. +> For new apps needing slots, prefer **Elastic Premium (EP1)** for better performance and no cold-start issues. + +### Linux Consumption (no slot support) + ```bicep resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: '${resourcePrefix}func${uniqueHash}' @@ -161,16 +169,86 @@ resource functionApp 'Microsoft.Web/sites@2022-09-01' = { serverFarmId: functionAppPlan.id httpsOnly: true siteConfig: { - linuxFxVersion: 'Node|' // Query latest GA: https://learn.microsoft.com/en-us/azure/azure-functions/supported-languages + linuxFxVersion: 'Node|20' appSettings: [ { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' } { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' } + { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: appInsights.properties.ConnectionString } + ] + } + } +} +``` + +### Windows Consumption (supports 1 staging slot) + +> ⚠️ **Windows Consumption is not recommended for new projects** — consider Flex Consumption or Elastic Premium. +> Use this pattern only for existing Windows apps or when Windows-specific features are required. + +```bicep +resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = { + name: '${resourcePrefix}-funcplan-${uniqueHash}' + location: location + sku: { name: 'Y1', tier: 'Dynamic' } + // No 'reserved: true' for Windows +} + +resource functionApp 'Microsoft.Web/sites@2022-09-01' = { + name: '${resourcePrefix}-${serviceName}-${uniqueHash}' + location: location + kind: 'functionapp' // Windows (no 'linux' suffix) + identity: { type: 'SystemAssigned' } + properties: { + serverFarmId: functionAppPlan.id + httpsOnly: true + siteConfig: { + appSettings: [ + { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' } + { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' } + { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' } + { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } + { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-prod' } + { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString } ] } } } + +// 1 staging slot is supported on Windows Consumption +resource stagingSlot 'Microsoft.Web/sites/slots@2022-09-01' = { + parent: functionApp + name: 'staging' + location: location + kind: 'functionapp' + properties: { + serverFarmId: functionAppPlan.id + siteConfig: { + appSettings: [ + { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' } + { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' } + { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' } + { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } + { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-staging' } // MUST differ from production + { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' } + { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString } + ] + } + } +} + +// Sticky settings — do not swap WEBSITE_CONTENTSHARE between slots +resource slotConfigNames 'Microsoft.Web/sites/config@2022-09-01' = { + parent: functionApp + name: 'slotConfigNames' + properties: { + appSettingNames: [ + 'WEBSITE_CONTENTSHARE' + 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' + ] + } +} ``` ## Service Bus Integration (Managed Identity) diff --git a/plugin/skills/azure-prepare/references/services/functions/terraform.md b/plugin/skills/azure-prepare/references/services/functions/terraform.md index acdf85b7c..11701a947 100644 --- a/plugin/skills/azure-prepare/references/services/functions/terraform.md +++ b/plugin/skills/azure-prepare/references/services/functions/terraform.md @@ -180,6 +180,14 @@ module "function_app" { **⚠️ Not recommended for new deployments. Use Flex Consumption instead.** +> 💡 **OS and Slots Matter for Consumption:** +> - **Linux Consumption** (`os_type = "Linux"`): Does **not** support deployment slots. +> - **Windows Consumption** (`os_type = "Windows"`): Supports **1 staging slot** (2 total including production). +> If a user specifically needs Windows Consumption with a slot, that is supported — use the Windows pattern below. +> For new apps needing slots, prefer **Elastic Premium (EP1)** for better performance and no cold-start issues. + +### Linux Consumption (no slot support) + ```hcl resource "azurerm_storage_account" "function_storage" { name = "${var.resource_prefix}func${var.unique_hash}" @@ -226,6 +234,77 @@ resource "azurerm_linux_function_app" "function_app" { } ``` +### Windows Consumption (supports 1 staging slot) + +> ⚠️ **Windows Consumption is not recommended for new projects** — consider Flex Consumption or Elastic Premium. +> Use this pattern only for existing Windows apps or when Windows-specific features are required. + +```hcl +resource "azurerm_service_plan" "function_plan" { + name = "${var.resource_prefix}-funcplan-${var.unique_hash}" + location = var.location + resource_group_name = azurerm_resource_group.main.name + os_type = "Windows" + sku_name = "Y1" +} + +resource "azurerm_windows_function_app" "function_app" { + name = "${var.resource_prefix}-${var.service_name}-${var.unique_hash}" + location = var.location + resource_group_name = azurerm_resource_group.main.name + service_plan_id = azurerm_service_plan.function_plan.id + https_only = true + + storage_account_name = azurerm_storage_account.function_storage.name + storage_account_access_key = azurerm_storage_account.function_storage.primary_access_key + + site_config { + application_insights_connection_string = azurerm_application_insights.function_insights.connection_string + application_stack { + node_version = "~20" + } + } + + app_settings = { + "FUNCTIONS_EXTENSION_VERSION" = "~4" + "FUNCTIONS_WORKER_RUNTIME" = "node" + "WEBSITE_CONTENTSHARE" = "${lower(var.service_name)}-prod" # must differ per slot; Azure Files share names are lowercase + "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.function_storage.primary_connection_string + } + + sticky_settings { + app_setting_names = ["WEBSITE_CONTENTSHARE", "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] + } + + identity { + type = "SystemAssigned" + } +} + +# 1 staging slot is supported on Windows Consumption +resource "azurerm_windows_function_app_slot" "staging" { + name = "staging" + function_app_id = azurerm_windows_function_app.function_app.id + + storage_account_name = azurerm_storage_account.function_storage.name + storage_account_access_key = azurerm_storage_account.function_storage.primary_access_key + + site_config { + application_insights_connection_string = azurerm_application_insights.function_insights.connection_string + application_stack { + node_version = "~20" + } + } + + app_settings = { + "FUNCTIONS_EXTENSION_VERSION" = "~4" + "FUNCTIONS_WORKER_RUNTIME" = "node" + "WEBSITE_CONTENTSHARE" = "${var.service_name}-staging" # MUST differ from production + "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.function_storage.primary_connection_string + } +} +``` + ## Service Bus Integration (Managed Identity) ```hcl