Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Assess and upgrade Azure workloads between plans, tiers, or SKUs with automated migration steps
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/services/functions/automation.md
1# Automation: Consumption to Flex Consumption Upgrade23> These are the Azure CLI scripts to automate the upgrade from Linux Consumption plan to Flex Consumption plan.4> All scripts use `bash` syntax compatible with Azure Cloud Shell. For PowerShell, adapt accordingly.5>6> **Source docs**: [Linux migration guide](https://learn.microsoft.com/en-us/azure/azure-functions/migration/migrate-plan-consumption-to-flex?pivots=platform-linux)78## Prerequisites910```bash11# Ensure Azure CLI v2.77.0+12az --version1314# Install resource-graph extension15az extension add --name resource-graph1617# Login and set subscription18az login19az account set --subscription <SUBSCRIPTION_ID>20```2122---2324## Step 1: Identify Candidate Apps2526```bash27# List all Linux Consumption apps with eligibility status28az functionapp flex-migration list29```3031This returns two arrays:32- `eligible_apps` — apps that can be migrated to Flex Consumption33- `ineligible_apps` — apps with specific reasons why not3435The output includes app name, resource group, location, and runtime stack for each app.3637---3839## Step 2: Assessment Checks4041Set variables for your app:4243```bash44appName=<APP_NAME>45rgName=<RESOURCE_GROUP>46```4748### 2a. Confirm Region Compatibility4950```bash51# List all regions where Flex Consumption is available52az functionapp list-flexconsumption-locations --query "sort_by(@, &name)[].{Region:name}" -o table53```5455### 2b. Verify Language Stack Compatibility5657Supported stacks: `dotnet-isolated`, `node`, `java`, `python`, `powershell`, `custom`.58**Not supported**: `dotnet` (in-process) — must migrate to isolated first.5960### 2c. Verify Stack Version Compatibility6162```bash63# Check supported versions for a specific runtime in a specific region64az functionapp list-flexconsumption-runtimes --location <REGION> --runtime <LANGUAGE_STACK> \65--query '[].{version:version}' -o tsv66```6768Replace `<REGION>` with the app's region and `<LANGUAGE_STACK>` with one of: `dotnet-isolated`, `java`, `node`, `powershell`, `python`.6970### 2d. Check Deployment Slots7172```bash73# List any deployment slots74az functionapp deployment slot list --name $appName --resource-group $rgName --output table75```7677If this returns entries, the app has slots. Flex Consumption does NOT support slots — plan accordingly.7879### 2e. Check TLS/SSL Certificates8081```bash82# List certificates available to the app83az webapp config ssl list --resource-group $rgName84```8586If this returns output, the app likely uses certificates. Flex Consumption does NOT support certs yet.8788### 2f. Check Blob Storage Triggers8990```bash91# Find blob triggers NOT using EventGrid source92az functionapp function list --name $appName --resource-group $rgName \93--query "[?config.bindings[0].type=='blobTrigger' && config.bindings[0].source!='EventGrid'].{Function:name,TriggerType:config.bindings[0].type,Source:config.bindings[0].source}" \94--output table95```9697If this returns rows, convert those blob triggers from `LogsAndContainerScan` to `EventGrid` before upgrading.9899---100101## Step 3: Pre-Migration — Collect Settings102103### 3a. Collect App Settings104105```bash106appName=<APP_NAME>107rgName=<RESOURCE_GROUP>108109# Get all app settings as JSON110app_settings=$(az functionapp config appsettings list --name $appName --resource-group $rgName)111echo "$app_settings"112```113114### 3b. Collect Application Configurations115116```bash117appName=<APP_NAME>118rgName=<RESOURCE_GROUP>119120echo "Getting commonly used site settings..."121az functionapp config show --name $appName --resource-group $rgName \122--query "{http20Enabled:http20Enabled, httpsOnly:httpsOnly, minTlsVersion:minTlsVersion, \123minTlsCipherSuite:minTlsCipherSuite, clientCertEnabled:clientCertEnabled, \124clientCertMode:clientCertMode, clientCertExclusionPaths:clientCertExclusionPaths}"125126echo "Checking for SCM basic publishing credentials policies..."127az resource show --resource-group $rgName --name scm --namespace Microsoft.Web \128--resource-type basicPublishingCredentialsPolicies --parent sites/$appName --query properties129130echo "Checking for the maximum scale-out limit configuration..."131az functionapp config appsettings list --name $appName --resource-group $rgName \132--query "[?name=='WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT'].value" -o tsv133134echo "Checking for any file share mount configurations..."135az webapp config storage-account list --name $appName --resource-group $rgName136137echo "Checking for any custom domains..."138az functionapp config hostname list --webapp-name $appName --resource-group $rgName \139--query "[?contains(name, 'azurewebsites.net')==\`false\`]" --output table140141echo "Checking for any CORS settings..."142az functionapp cors show --name $appName --resource-group $rgName143```144145### 3c. Identify Managed Identities and Role Assignments146147```bash148appName=<APP_NAME>149rgName=<RESOURCE_GROUP>150151echo "Checking for a system-assigned managed identity..."152systemUserId=$(az functionapp identity show --name $appName --resource-group $rgName \153--query "principalId" -o tsv)154155if [[ -n "$systemUserId" ]]; then156echo "System-assigned identity principal ID: $systemUserId"157echo "Checking for role assignments..."158az role assignment list --assignee $systemUserId --all159else160echo "No system-assigned identity found."161fi162163echo "Checking for user-assigned managed identities..."164userIdentities=$(az functionapp identity show --name $appName --resource-group $rgName \165--query 'userAssignedIdentities' -o json)166167if [[ "$userIdentities" != "{}" && "$userIdentities" != "null" ]]; then168echo "$userIdentities" | jq -c 'to_entries[]' | while read -r identity; do169echo "User-assigned identity: $(echo "$identity" | jq -r '.key' | sed 's|.*/userAssignedIdentities/||')"170echo "Checking for role assignments..."171az role assignment list --assignee $(echo "$identity" | jq -r '.value.principalId') --all --output json172echo173done174else175echo "No user-assigned identities found."176fi177```178179### 3d. Check Built-in Authentication180181```bash182az webapp auth show --name $appName --resource-group $rgName183```184185### 3e. Review Inbound Access Restrictions186187```bash188az functionapp config access-restriction show --name $appName --resource-group $rgName189```190191### 3f. Get Deployment Package (if needed)192193Ideally your project files are in source control and you can redeploy from there. If not:194195#### Check WEBSITE_RUN_FROM_PACKAGE196197```bash198az functionapp config appsettings list --name $appName --resource-group $rgName \199--query "[?name=='WEBSITE_RUN_FROM_PACKAGE'].value" -o tsv200```201202If this returns a URL, download the package from that remote location.203204#### Download from scm-releases blob container205206Linux Consumption apps store deployment packages in the `scm-releases` blob container (in `squashfs` format).207208```bash209appName=<APP_NAME>210rgName=<RESOURCE_GROUP>211212echo "Getting the storage account connection string..."213storageConnection=$(az functionapp config appsettings list --name $appName --resource-group $rgName \214--query "[?name=='AzureWebJobsStorage'].value" -o tsv)215216echo "Getting the package name..."217packageName=$(az storage blob list --connection-string $storageConnection --container-name scm-releases \218--query "[0].name" -o tsv)219220echo "Downloading package: $packageName"221az storage blob download --connection-string $storageConnection --container-name scm-releases \222--name $packageName --file $packageName223```224225> 💡 If your storage account is restricted to managed identity access only, you may need to grant your Azure account the `Storage Blob Data Reader` role.226227---228229## Step 4: Create the Flex Consumption App230231```bash232# Automated migration — creates new app and migrates most configurations233az functionapp flex-migration start \234--source-name <SOURCE_APP_NAME> \235--source-resource-group <SOURCE_RESOURCE_GROUP> \236--name <NEW_APP_NAME> \237--resource-group <RESOURCE_GROUP>238```239240**Optional flags**:241- `--storage-account <ACCOUNT>` — use a different storage account242- `--maximum-instance-count <COUNT>` — set max scale-out instances243- `--skip-access-restrictions` — skip migrating IP access restrictions244- `--skip-cors` — skip migrating CORS settings245- `--skip-hostnames` — skip migrating custom domains246- `--skip-managed-identities` — skip migrating managed identity configurations247- `--skip-storage-mount` — skip migrating storage mount configurations248249The command automatically:250- Assesses your source app for Flex Consumption compatibility251- Creates a new function app in the Flex Consumption plan252- Migrates app settings, identity assignments, storage mounts, CORS, custom domains, and access restrictions253254### Verify Migration Results255256```bash257# Verify new app exists and is configured258az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \259--query "{name:name, kind:kind, sku:properties.sku}" --output table260261# Review migrated app settings262az functionapp config appsettings list --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \263--output table264265# Check managed identity266az functionapp identity show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>267268# Check custom domains269az functionapp config hostname list --webapp-name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \270--output table271```272273### Configure Items Not Auto-Migrated274275The `flex-migration start` command handles most settings, but these may need manual configuration:276277#### Built-in Authentication278279```bash280# Recreate auth settings if your original app used Easy Auth281az webapp auth update --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \282--enabled true --action <AUTH_ACTION>283```284285#### Scale and Concurrency (if custom values needed)286287```bash288# Set maximum scale-out (default: 100, range: 1-1000)289az functionapp scale config set --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \290--maximum-instance-count <MAX_SCALE_SETTING>291```292293> ⚠️ Reducing below 40 for HTTP apps can cause frequent request failures.294295---296297## Step 5: Deploy Code298299> ⚠️ **Code is NOT automatically migrated.** The new app is created with config only — you must deploy code separately.300301### ask_user: Choose Deployment Method302303Present these options to the user:304305> Your new Flex Consumption app `<NEW_APP_NAME>` has been created and configured. Now we need to deploy your function code. How would you like to proceed?306>307> 1. **Update CI/CD pipeline** — I'll help you update your Azure Pipelines or GitHub Actions workflow to target the new app308> 2. **Deploy from local project** — I'll run `func azure functionapp publish <NEW_APP_NAME>` from your project directory309> 3. **Deploy existing package** — I'll deploy the package we downloaded earlier from the original app310311---312313### Option A: Update CI/CD Pipeline (if user selects option 1)314315Update your existing pipeline (Azure Pipelines or GitHub Actions) to target the new app name.316317- [Build and deploy with Azure Pipelines](https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-azure-devops)318- [Build and deploy with GitHub Actions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-github-actions)319320### Option B: Deploy from Local Project (if user selects option 2)321322```bash323# From your project directory324func azure functionapp publish <NEW_APP_NAME>325```326327### Option C: Deploy Existing Package (if user selects option 3)328329```bash330# Deploy the zip package downloaded in Step 3331az functionapp deployment source config-zip --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \332--src <PACKAGE_PATH.zip>333```334335### After Successful Deployment336337Inform the user:338339> Code deployed! Next steps to consider:340>341> - The original app is still running — keep it as rollback for a few days342> - Update any clients/pipelines to point to the new URL343> - Enable HTTPS-only and managed identity on the new app for better security344> - When confident, you can delete the original app345346---347348## Step 6: Post-Upgrade Validation349350### Smoke Test (run this first)351352```bash353# Minimum viability check — confirm the app is reachable at all354DEFAULT_HOST=$(az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \355--query "defaultHostName" -o tsv)356357HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://$DEFAULT_HOST")358echo "App responded with HTTP $HTTP_STATUS"359360# Expected: 2xx or 401/404 (means the host is up).361# If 503 or connection refused → the app failed to start. Check logs:362# az functionapp log tail --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>363```364365### Verify Plan366367```bash368az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> --query "serverFarmId"369```370371### Test HTTP Endpoints372373```bash374# Test an HTTP trigger function375curl -s -o /dev/null -w "%{http_code}" "https://$DEFAULT_HOST/api/<FUNCTION_NAME>"376```377378### Performance Benchmarks (Application Insights KQL)379380```kql381requests382| where timestamp > ago(1d)383| summarize percentiles(duration, 50, 95, 99) by bin(timestamp, 1h)384| render timechart385```386387### Check for Errors388389```kql390traces391| where severityLevel == 3392| where cloud_RoleName == "<NEW_APP_NAME>"393| where timestamp > ago(1d)394| project timestamp, message, operation_Name, customDimensions395| order by timestamp desc396```397398---399400## Step 7: Cleanup (Optional)401402```bash403# ⛔ REQUIRES ask_user confirmation before executing404405# Delete the original function app406az functionapp delete --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>407```408409> 💡 No rush. The Consumption plan only charges for actual usage, so keeping the old app (with triggers disabled) costs very little. We recommend keeping it for a few days/weeks.410411---412413## Rollback414415```bash416# Restart the original app if it was stopped417az functionapp start --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>418419# Optionally delete the new Flex Consumption app420az functionapp delete --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>421```422