Automation: Consumption to Flex Consumption Upgrade
These are the Azure CLI scripts to automate the upgrade from Linux Consumption plan to Flex Consumption plan. All scripts use
bashsyntax compatible with Azure Cloud Shell. For PowerShell, adapt accordingly.
>
Source docs: Linux migration guide
Prerequisites
# Ensure Azure CLI v2.77.0+
az --version
# Install resource-graph extension
az extension add --name resource-graph
# Login and set subscription
az login
az account set --subscription <SUBSCRIPTION_ID>Step 1: Identify Candidate Apps
# List all Linux Consumption apps with eligibility status
az functionapp flex-migration listThis returns two arrays:
eligible_apps— apps that can be migrated to Flex Consumptionineligible_apps— apps with specific reasons why not
The output includes app name, resource group, location, and runtime stack for each app.
Step 2: Assessment Checks
Set variables for your app:
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>2a. Confirm Region Compatibility
# List all regions where Flex Consumption is available
az functionapp list-flexconsumption-locations --query "sort_by(@, &name)[].{Region:name}" -o table2b. Verify Language Stack Compatibility
Supported stacks: dotnet-isolated, node, java, python, powershell, custom. Not supported: dotnet (in-process) — must migrate to isolated first.
2c. Verify Stack Version Compatibility
# Check supported versions for a specific runtime in a specific region
az functionapp list-flexconsumption-runtimes --location <REGION> --runtime <LANGUAGE_STACK> \
--query '[].{version:version}' -o tsvReplace <REGION> with the app's region and <LANGUAGE_STACK> with one of: dotnet-isolated, java, node, powershell, python.
2d. Check Deployment Slots
# List any deployment slots
az functionapp deployment slot list --name $appName --resource-group $rgName --output tableIf this returns entries, the app has slots. Flex Consumption does NOT support slots — plan accordingly.
2e. Check TLS/SSL Certificates
# List certificates available to the app
az webapp config ssl list --resource-group $rgNameIf this returns output, the app likely uses certificates. Flex Consumption does NOT support certs yet.
2f. Check Blob Storage Triggers
# Find blob triggers NOT using EventGrid source
az functionapp function list --name $appName --resource-group $rgName \
--query "[?config.bindings[0].type=='blobTrigger' && config.bindings[0].source!='EventGrid'].{Function:name,TriggerType:config.bindings[0].type,Source:config.bindings[0].source}" \
--output tableIf this returns rows, convert those blob triggers from LogsAndContainerScan to EventGrid before upgrading.
Step 3: Pre-Migration — Collect Settings
3a. Collect App Settings
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>
# Get all app settings as JSON
app_settings=$(az functionapp config appsettings list --name $appName --resource-group $rgName)
echo "$app_settings"3b. Collect Application Configurations
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>
echo "Getting commonly used site settings..."
az functionapp config show --name $appName --resource-group $rgName \
--query "{http20Enabled:http20Enabled, httpsOnly:httpsOnly, minTlsVersion:minTlsVersion, \
minTlsCipherSuite:minTlsCipherSuite, clientCertEnabled:clientCertEnabled, \
clientCertMode:clientCertMode, clientCertExclusionPaths:clientCertExclusionPaths}"
echo "Checking for SCM basic publishing credentials policies..."
az resource show --resource-group $rgName --name scm --namespace Microsoft.Web \
--resource-type basicPublishingCredentialsPolicies --parent sites/$appName --query properties
echo "Checking for the maximum scale-out limit configuration..."
az functionapp config appsettings list --name $appName --resource-group $rgName \
--query "[?name=='WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT'].value" -o tsv
echo "Checking for any file share mount configurations..."
az webapp config storage-account list --name $appName --resource-group $rgName
echo "Checking for any custom domains..."
az functionapp config hostname list --webapp-name $appName --resource-group $rgName \
--query "[?contains(name, 'azurewebsites.net')==\`false\`]" --output table
echo "Checking for any CORS settings..."
az functionapp cors show --name $appName --resource-group $rgName3c. Identify Managed Identities and Role Assignments
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>
echo "Checking for a system-assigned managed identity..."
systemUserId=$(az functionapp identity show --name $appName --resource-group $rgName \
--query "principalId" -o tsv)
if [[ -n "$systemUserId" ]]; then
echo "System-assigned identity principal ID: $systemUserId"
echo "Checking for role assignments..."
az role assignment list --assignee $systemUserId --all
else
echo "No system-assigned identity found."
fi
echo "Checking for user-assigned managed identities..."
userIdentities=$(az functionapp identity show --name $appName --resource-group $rgName \
--query 'userAssignedIdentities' -o json)
if [[ "$userIdentities" != "{}" && "$userIdentities" != "null" ]]; then
echo "$userIdentities" | jq -c 'to_entries[]' | while read -r identity; do
echo "User-assigned identity: $(echo "$identity" | jq -r '.key' | sed 's|.*/userAssignedIdentities/||')"
echo "Checking for role assignments..."
az role assignment list --assignee $(echo "$identity" | jq -r '.value.principalId') --all --output json
echo
done
else
echo "No user-assigned identities found."
fi3d. Check Built-in Authentication
az webapp auth show --name $appName --resource-group $rgName3e. Review Inbound Access Restrictions
az functionapp config access-restriction show --name $appName --resource-group $rgName3f. Get Deployment Package (if needed)
Ideally your project files are in source control and you can redeploy from there. If not:
#### Check WEBSITERUNFROM_PACKAGE
az functionapp config appsettings list --name $appName --resource-group $rgName \
--query "[?name=='WEBSITE_RUN_FROM_PACKAGE'].value" -o tsvIf this returns a URL, download the package from that remote location.
#### Download from scm-releases blob container
Linux Consumption apps store deployment packages in the scm-releases blob container (in squashfs format).
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>
echo "Getting the storage account connection string..."
storageConnection=$(az functionapp config appsettings list --name $appName --resource-group $rgName \
--query "[?name=='AzureWebJobsStorage'].value" -o tsv)
echo "Getting the package name..."
packageName=$(az storage blob list --connection-string $storageConnection --container-name scm-releases \
--query "[0].name" -o tsv)
echo "Downloading package: $packageName"
az storage blob download --connection-string $storageConnection --container-name scm-releases \
--name $packageName --file $packageName💡 If your storage account is restricted to managed identity access only, you may need to grant your Azure account the
Storage Blob Data Readerrole.
Step 4: Create the Flex Consumption App
# Automated migration — creates new app and migrates most configurations
az functionapp flex-migration start \
--source-name <SOURCE_APP_NAME> \
--source-resource-group <SOURCE_RESOURCE_GROUP> \
--name <NEW_APP_NAME> \
--resource-group <RESOURCE_GROUP>Optional flags:
--storage-account <ACCOUNT>— use a different storage account--maximum-instance-count <COUNT>— set max scale-out instances--skip-access-restrictions— skip migrating IP access restrictions--skip-cors— skip migrating CORS settings--skip-hostnames— skip migrating custom domains--skip-managed-identities— skip migrating managed identity configurations--skip-storage-mount— skip migrating storage mount configurations
The command automatically:
- Assesses your source app for Flex Consumption compatibility
- Creates a new function app in the Flex Consumption plan
- Migrates app settings, identity assignments, storage mounts, CORS, custom domains, and access restrictions
Verify Migration Results
# Verify new app exists and is configured
az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
--query "{name:name, kind:kind, sku:properties.sku}" --output table
# Review migrated app settings
az functionapp config appsettings list --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
--output table
# Check managed identity
az functionapp identity show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>
# Check custom domains
az functionapp config hostname list --webapp-name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
--output tableConfigure Items Not Auto-Migrated
The flex-migration start command handles most settings, but these may need manual configuration:
#### Built-in Authentication
# Recreate auth settings if your original app used Easy Auth
az webapp auth update --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
--enabled true --action <AUTH_ACTION>#### Scale and Concurrency (if custom values needed)
# Set maximum scale-out (default: 100, range: 1-1000)
az functionapp scale config set --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
--maximum-instance-count <MAX_SCALE_SETTING>⚠️ Reducing below 40 for HTTP apps can cause frequent request failures.
Step 5: Deploy Code
⚠️ Code is NOT automatically migrated. The new app is created with config only — you must deploy code separately.
ask_user: Choose Deployment Method
Present these options to the user:
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?
>
- Update CI/CD pipeline — I'll help you update your Azure Pipelines or GitHub Actions workflow to target the new app
- Deploy from local project — I'll run
func azure functionapp publish <NEW_APP_NAME>from your project directory- Deploy existing package — I'll deploy the package we downloaded earlier from the original app
Option A: Update CI/CD Pipeline (if user selects option 1)
Update your existing pipeline (Azure Pipelines or GitHub Actions) to target the new app name.
Option B: Deploy from Local Project (if user selects option 2)
# From your project directory
func azure functionapp publish <NEW_APP_NAME>Option C: Deploy Existing Package (if user selects option 3)
# Deploy the zip package downloaded in Step 3
az functionapp deployment source config-zip --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
--src <PACKAGE_PATH.zip>After Successful Deployment
Inform the user:
Code deployed! Next steps to consider:
>
- The original app is still running — keep it as rollback for a few days
- Update any clients/pipelines to point to the new URL
- Enable HTTPS-only and managed identity on the new app for better security
- When confident, you can delete the original app
Step 6: Post-Upgrade Validation
Smoke Test (run this first)
# Minimum viability check — confirm the app is reachable at all
DEFAULT_HOST=$(az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
--query "defaultHostName" -o tsv)
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://$DEFAULT_HOST")
echo "App responded with HTTP $HTTP_STATUS"
# Expected: 2xx or 401/404 (means the host is up).
# If 503 or connection refused → the app failed to start. Check logs:
# az functionapp log tail --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>Verify Plan
az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> --query "serverFarmId"Test HTTP Endpoints
# Test an HTTP trigger function
curl -s -o /dev/null -w "%{http_code}" "https://$DEFAULT_HOST/api/<FUNCTION_NAME>"Performance Benchmarks (Application Insights KQL)
requests
| where timestamp > ago(1d)
| summarize percentiles(duration, 50, 95, 99) by bin(timestamp, 1h)
| render timechartCheck for Errors
traces
| where severityLevel == 3
| where cloud_RoleName == "<NEW_APP_NAME>"
| where timestamp > ago(1d)
| project timestamp, message, operation_Name, customDimensions
| order by timestamp descStep 7: Cleanup (Optional)
# ⛔ REQUIRES ask_user confirmation before executing
# Delete the original function app
az functionapp delete --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>💡 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.
Rollback
# Restart the original app if it was stopped
az functionapp start --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>
# Optionally delete the new Flex Consumption app
az functionapp delete --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>