Edit

Share via


Migrate Consumption plan apps to the Flex Consumption plan

This article provides step-by-step instructions for migrating your existing function apps hosted in the Consumption plan in Azure Functions to instead use the Flex Consumption plan.

When you migrate your existing serverless apps, your functions can easily take advantage of these benefits of the Flex Consumption plan:

  • Enhanced performance: your apps benefit from improved scalability and always-ready instances to reduce cold start impacts.
  • Improved controls: fine-tune your functions with per-function scaling and concurrency settings.
  • Expanded networking: virtual network integration and private endpoints let you run your functions in both public and private networks.

The Flex Consumption plan is the recommended serverless hosting option for your functions going forward. For more information, see Flex Consumption plan benefits. For a detailed comparison between hosting plans, see Azure Functions hosting options.

Considerations

Before staring a migration, keep these considerations in mind:

  • Due to the significant configuration and behavior differences between the two plans, you aren't able to shift an existing Consumption plan app to the Flex Consumption plan. The migration process instead has you create a new Flex Consumption plan app that is equivalent to your current app. This new app runs in the same resource group and with the same dependencies as your current app.

  • You should prioritize the migration of your apps that run in a Consumption plan on Linux.

  • This article assumes that you have a general understanding of Functions concepts and architectures and are familiar with features of your apps being migrated. Such concepts include triggers and bindings, authentication, and networking customization.

  • Where possible, this article is targeted to a specific language runtime stack. Make sure to choose your app's language at the top of the article.

  • This article shows you how to both evaluate the current app and deploy your new Flex Consumption plan app using either the Azure portal or the Azure CLI. If your current app deployment is defined by using infrastructure-as-code (IaC), you can generally follow the same steps. You can perform the same actions directly in your ARM templates or Bicep files, with these resource-specific considerations:

Prerequisites

  • Access to the Azure subscription containing one or more function apps to migrate. The account used to run Azure CLI commands must be able to:

    • Create and manage function apps and App Service hosting plans.
    • Assign roles to managed identities.
    • Create and manage storage accounts.
    • Create and manage Application Insights resources.
    • Access all dependent resources of your app, such as Azure Key Vault, Azure Service Bus, or Azure Event Hubs.

    Being assigned to the Owner or Contributor roles in your resource group generally provides sufficient permissions.

  • Azure CLI, version v2.74.0, or a later version. Scripts are tested using Azure CLI in Azure Cloud Shell.

  • The resource-graph extension, which you can install by using the az extension add command:

    az extension add --name resource-graph
    
  • The jq tool, which is used to work with JSON output.

Assess your existing app

Before migrating to the Flex Consumption plan, you should perform these checks to make sure that your function app can be migrated successfully:

Identify potential apps to migrate

Use these steps to make a list of the function apps you need to migrate. In this list, make note of their names, resource groups, locations, and runtime stacks. You can then repeat the steps in this guide for each app you decide to migrate to the Flex Consumption plan.

The way that function app information is maintained depends on whether your app runs on Linux or Windows.

Use this az graph query command to list all function apps in your subscription that are running in a Consumption plan:

az graph query -q "resources | where subscriptionId == '$(az account show --query id -o tsv)' \
   | where type == 'microsoft.web/sites' | where ['kind'] == 'functionapp,linux' | where properties.sku == 'Dynamic' \
   | extend siteProperties=todynamic(properties.siteProperties.properties) | mv-expand siteProperties \
   | where siteProperties.name=='LinuxFxVersion' | project name, ___location, resourceGroup, stack=siteProperties.value" \
   --query data --output table

This command generates a table with the app name, ___location, resource group, and runtime stack for all Consumption apps running on Linux in the current subscription.

You're promoted to install the resource-graph extension, if it isn't already installed.

Confirm region compatibility

Confirm that the Flex Consumption plan is currently supported in the same region as the Consumption plan app you intend to migrate.

Use this az functionapp list-flexconsumption-locations command to list all regions where Flex Consumption plan is available:

az functionapp list-flexconsumption-locations --query "sort_by(@, &name)[].{Region:name}" -o table

This command generates a table of Azure regions where the Flex Consumption plan is currently supported.

Make sure that the region in which the Consumption plan app you want to migrate runs is included in the list.

Tip

If your region isn't currently supported and you still choose to migrate your function app, your app must run in a different region where the Flex Consumption plan is supported. However, running your app in a different region from other connected services can introduce extra latency. Make sure that the new region can meet your application's performance requirements before you complete the migration.

Verify language stack compatibility

Flex Consumption plans don't yet support all Functions language stacks. This table indicates which language stacks are currently supported:

Stack setting Stack name Supported
dotnet-isolated .NET (isolated worker model) ✅ Yes
node JavaScript/TypeScript ✅ Yes
java Java ✅ Yes
python Python ✅ Yes
powershell PowerShell ✅ Yes
dotnet .NET (in-process model) ❌ No
custom Custom handlers ❌ No

If your function app uses an unsupported runtime stack:

Verify stack version compatibility

Before migrating to the Flex Consumption plan, you must make sure that your app's runtime stack version is supported in your region when running in the new plan.

Use this az functionapp list-flexconsumption-runtimes command to verify Flex Consumption plan support for your language stack version in a specific region:

az functionapp list-flexconsumption-runtimes --___location <REGION> --runtime <LANGUAGE_STACK> --query '[].{version:version}' -o tsv

In this example, replace <REGION> with your current region and <LANGUAGE_STACK> with one of these values:

Language stack Value
C# (isolated worker model) dotnet-isolated
Java java
JavaScript node
PowerShell powershell
Python python
TypeScript node

This command displays all versions of the specified language stack supported by the Flex Consumption plan in your region.

If your function app uses an unsupported language stack version, you must first upgrade your app code to a supported version before migrating to the Flex Consumption plan.

Verify deployment slots usage

Consumption plan apps can have a deployment slot defined. For more information, see Azure Functions deployment slots. However, the Flex Consumption plan doesn't currently support deployment slots. Before you migrate, you must determine if your app has a deployment slot. If so, you need to define a strategy for how to manage your app without deployment slots when running in a Flex Consumption plan.

Use this az functionapp deployment slot list command to list any deployment slots defined in your function app:

az functionapp deployment slot list --name <APP_NAME> --resource-group <RESOURCE_GROUP> --output table

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively. If the command returns an entry, your app has deployment slots enabled.

If your function app is currently using deployment slots, you can't currently reproduce this functionality in the Flex Consumption plan. Before migrating, you should...

  • Migrate any new code or features from the deployment slot into the main (production) slot.
  • Consider rearchitecting your application to use separate function apps. In this way, you can develop, test, and deploy your function code to a second nonproduction app instead of using slots.

Verify the use of certificates

Transport Layer Security (TLS) certificates, previously known as Secure Sockets Layer (SSL) certificates, are used to help secure internet connections. TSL/SSL certificates, which include managed certificates, bring-your-own certificates (BYOC), or public-key certificates, aren't currently supported by the Flex Consumption plan.

Use the az webapp config ssl list command to list any TSL/SSL certificates available to your function app:

az webapp config ssl list --resource-group <RESOURCE_GROUP>  

In this example, replace <RESOURCE_GROUP> with your resource group name. If this command produces output, your app is likely using certificates.

If your app currently relies on TSL/SSL certificates, you shouldn't proceed with the migration until after support for certificates is added to the Flex Consumption plan.

Verify your Blob storage triggers

Currently, the Flex Consumption plan only supports event-based triggers for Azure Blob storage, which are defined with a Source setting of EventGrid. Blob storage triggers that use container polling and use a Source setting of LogsAndContainerScan aren't supported in Flex Consumption. Because container polling is the default, you must determine if any of your Blob storage triggers are using the default LogsAndContainerScan source setting. For more information, see Trigger on a blob container.

Use this [az functionapp function list] command to determine if your app has any Blob storage triggers that don't use Event Grid as the source:

az functionapp function list  --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
  --query "[?config.bindings[0].type=='blobTrigger' && config.bindings[0].source!='EventGrid'].{Function:name,TriggerType:config.bindings[0].type,Source:config.bindings[0].source}" \
  --output table

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively. If the command returns rows, there is at least one trigger using container polling in your function app.

If your app has any Blob storage triggers that don't have an Event Grid source, you must change to an Event Grid source before you migrate to the Flex Consumption plan.

The basic steps to change an existing Blob storage trigger to an Event Grid source are:

  1. Build the endpoint URL in your function app used to by the event subscription.

  2. Create an event subscription on your Blob storage container.

  3. Add or update the source property in your Blob storage trigger definition to EventGrid.

For more information, see Tutorial: Trigger Azure Functions on blob containers using an event subscription.

Consider dependent services

Because Azure Functions is a compute service, you must consider the effect of migration on data and services both upstream and downstream of your app.

Data protection strategies

Here are some strategies to protect both upstream and downstream data during the migration:

  • Idempotency: Ensure your functions can safely process the same message multiple times without negative side effects. For more information, see Designing Azure Functions for identical input.
  • Logging and monitoring: Enable detailed logging in both apps during migration to track message processing. For more information, see Monitor executions in Azure Functions.
  • Checkpointing: For streaming triggers, such as the Event Hubs trigger, implement correct checkpoint behaviors to track processing position. For more information, see Azure Functions reliable event processing.
  • Parallel processing: Consider temporarily running both apps in parallel during the cutover. Make sure to carefully monitor and validate how data is processed from the upstream service. For more information, see Active-active pattern for non-HTTPS trigger functions.
  • Gradual cutover: For high-volume systems, consider implementing a gradual cutover by redirecting portions of traffic to the new app. You can manage the routing of requests upstream from your apps by using services such as Azure API Management or Azure Application Gateway.

Mitigations by trigger type

You should plan mitigation strategies to protect data for the specific function triggers you might have in your app:

Trigger Risk to data Strategy
Azure Blob storage High Create a separate container for the event-based trigger in the new app.
With the new app running, switch clients to use the new container.
Allow the original container to be processed completely before stopping the old app.
Azure Cosmos DB High Create a dedicated lease container specifically for the new app.
Set this new lease container as the leaseCollectionName configuration in your new app.
Requires that your functions be idempotent or you must be able to handle the results of duplicate change feed processing.
Set the StartFromBeginning configuration to false in the new app to avoid reprocessing the entire feed.
Azure Event Grid Medium Recreate the same event subscription in the new app.
Requires that your functions be idempotent or you must be able to handle the results of duplicate event processing.
Azure Event Hubs Medium Create a new consumer group for use by the new app. For more information, see Migration strategies for Event Grid triggers.
Azure Service Bus High Create a new topic or queue for use by the new app.
Update senders and clients to use the new topic or queue.
After the original topic is empty, shut down the old app.
Azure Storage queue High Create a new queue for use by the new app.
Update senders and clients to use the new queue.
After the original queue is empty, shut down the old app.
HTTP Low Remember to switch clients and other apps or services to target the new HTTP endpoints after the migration.
Timer Low During cutover, make sure to offset the timer schedule between the two apps to avoid simultaneous executions from both apps.
Disable the timer trigger in the old app after the new app runs successfully.

Premigration tasks

Before proceeding with the migration, you must collect key information about and resources used by your Consumption plan app to help make a smooth transition to running in the Flex Consumption plan.

You should complete these tasks before you migrate your app to run in a Flex Consumption plan:

Collect app settings

If you plan to use the same trigger and bindings sources and other settings from app settings, you need to first take note of the current app settings in your existing Consumption plan app.

Use this az functionapp config appsettings list command to return an app_settings object that that contains the existing app setting as JSON:

app_settings=$(az functionapp config appsettings list --name `<APP_NAME>` --resource-group `<RESOURCE_GROUP>`)
echo $app_settings

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively.

Caution

App settings frequently contain keys and other shared secrets. Always store applications settings securely, ideally encrypted. For improved security, you should use Microsoft Entra ID authentication with managed identities in the new Flex Consumption plan app instead of shared secrets.

Collect application configurations

There are other app configurations not found in app settings. You should also capture these configurations from your existing app so that you can be sure to properly recreate them in the new app.

Review these settings. If any of them exist in the current app, you must decide whether they must also be recreated in the new Flex Consumption plan app:

Configuration Setting Comment
CORS settings cors Determines any existing cross-origin resource sharing (CORS) settings, which might be required by your clients.
Custom domains If your app is currently using a ___domain other than *.azurewebsites.net, you would need to replace this custom ___domain mapping with a mapping to your new app.
HTTP version http20Enabled Determines if HTTP 2.0 is required by your app.
HTTPS only httpsOnly Determines if TSL/SSL is required to access your app.
Incoming client certificates clientCertEnabled
clientCertMode
clientCertExclusionPaths
Sets requirements for client requests that use certificates for authentication.
Maximum scale-out limit WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT Sets the limit on scaled-out instances. The default maximum value is 200. This value is found in your app settings, but in a Flex Consumption plan app it instead gets added as a site setting (maximumInstanceCount).
Minimum inbound TLS version minTlsVersion Sets a minimum version of TLS required by your app.
Minimum inbound TLS Cipher minTlsCipherSuite Sets a minimum TLS cipher requirement for your app.
Mounted Azure Files shares azureStorageAccounts Determines if any explicitly mounted file shares exist in your app (Linux-only).
SCM basic auth publishing credentials scm.allow Determines if the scm publishing site is enabled. While not recommended for security, it's required by some publishing methods.

Use this script to obtain the relevant application configurations of your existing app:

# Set the app and resource group names
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 $rgName 

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively. If any of the site settings or checks return non-null values, make a note of them.

Identify managed identities and role-based access

Before migrating, you should document whether your app relies on the system-assigned managed identity or any user-assigned managed identities. You must also determine the role-based access control (RBAC) permissions granted to these identities. You must recreate the system-assigned managed identity and any role assignments in your new app. You should be able to reuse your user-assigned managed identities in your new app.

This script checks for both the system-assigned managed identity and any user-assigned managed identities associated with your app:

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 name: $(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."
fi

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively. Make a note of all identities and their role assignments.

Identify built-in authentication settings

Before migrating to Flex Consumption, you should collect information about any built-in authentication configurations. If you want to have your app use the same client authentication behaviors, you must recreate them in the new app. For more information, see Authentication and authorization in Azure Functions.

Pay special attention to redirect URIs, allowed external redirects, and token settings to ensure a smooth transition for authenticated users.

Use this az webapp auth show command to determine if built-in authentication is configured in your function app:

az webapp auth show --name <APP_NAME> --resource-group <RESOURCE_GROUP>

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively. Review the output to determine if authentication is enabled and which identity providers are configured.

You should recreate these setting in your new app post-migration so that your clients can maintain access using their preferred provider.

Review inbound access restrictions

It's possible to set inbound access restrictions on apps in a Consumption plan. You might want to maintain these restrictions in your new app. For each restriction defined, make sure to capture these properties:

  • IP addresses or CIDR ranges
  • Priority values
  • Action type (Allow/Deny)
  • Names of the rules

This az functionapp config access-restriction show command returns a list of any existing IP-based access restrictions:

az functionapp config access-restriction show --name <APP_NAME> --resource-group <RESOURCE_GROUP>

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively.

When running in the Flex Consumption plan, you can recreate these inbound IP-based restrictions. You can further secure your app by implementing other networking restrictions, such as virtual network integration and inbound private endpoints. For more information, see Virtual network integration.

Get the code deployment package

To be able to redeploy your app, you must have either your project's source files or the deployment package. Ideally, your project files are maintained in source control so that you can easily redeploy function code to your new app. If you have your source code files, you can skip this section.

If you no longer have access to your project source files, you can download the current deployment package from the existing Consumption plan app in Azure. The ___location of the deployment package depends on whether you run on Linux or Windows.

Consumption plan apps on Linux maintain the deployment zip package file in one of these locations:

  • An Azure Blob storage container named scm-releases in the default host storage account (AzureWebJobsStorage). This container is the default deployment source for a Consumption plan app on Linux.

  • If your app has a WEBSITE_RUN_FROM_PACKAGE setting that is a URL, the package is in an externally accessible ___location that is maintained by you. An external package should be hosted in a blob storage container with restricted access. For more information, see External package URL.

Tip

If your storage account is restricted to managed identity access only, you might need to grant your Azure account read access to the storage container by adding it to the Storage Blob Data Reader role.

Use these steps to download the deployment package from your current app:

  1. Use this az functionapp config appsettings list command to get the WEBSITE_RUN_FROM_PACKAGE app setting, if present:

    az functionapp config appsettings list --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
        --query "[?name=='WEBSITE_RUN_FROM_PACKAGE'].value" -o tsv
    

    In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name, respectively. If this command returns a URL, then you can download the deployment package file from that remote ___location and skip to the next section.

  2. If the WEBSITE_RUN_FROM_PACKAGE value is 1 or nothing, use this script to get the deployment package for the existing app:

    appName=<APP_NAME>
    rgName=<RESOURCE_GROUP>
    
    echo "Getting the storage account connection string from app settings..."
    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 "Download the package? $packageName? (Y to proceed, any other key to exit)"
    read -r answer
    if [[ "$answer" == "Y" || "$answer" == "y" ]]; then
       echo "Proceeding with download..."
       az storage blob download --connection-string $storageConnection --container-name scm-releases \
    --name $packageName --file $packageName
    else
       echo "Exiting script."
       exit 0
    fi
    

    Again, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group name and app name. The package .zip file is downloaded to the directory from which you executed the command.

The deployment package is compressed using the squashfs format. To see what's inside the package, you must use tools that can decompress this format.

Capture performance benchmarks (optional)

If you plan to validate performance improvement in your app based on the migration to the Flex Consumption plan, you should (optionally) capture the performance benchmarks of your current plan. Then, you can compare them to the same benchmarks for your app running in a Flex Consumption plan for comparison.

Tip

Always compare performance under similar conditions, such as time-of-day, day-of-week, and client load. Try to run the two benchmarks as close together as possible.

Here are some benchmarks to consider for your structured performance testing:

Suggested benchmark Comment
Cold-start Measure the time from first request to the first response after an idle period.
Throughput Measure the maximum requests-per-second using load testing tools to determine how the app handles concurrent requests.
Latency Track the P50, P95, and P99 response times under various load conditions. You can monitor these metrics in Application Insights.

You can use this Kusto query to review the suggested latency response times in Application Insights:

requests
| where timestamp > ago(1d)
| summarize percentiles(duration, 50, 95, 99) by bin(timestamp, 1h)
| render timechart

Migration Steps

The actual migration of your functions from a Consumption plan app to a Flex Consumption plan app follows these main steps:

Step 1: Final review of the plan

Before proceeding with the migration process, take a moment to perform these last preparatory steps:

  • Review all the collected information: Go through all the notes, configuration details, and application settings you documented in the previous assessment and premigration sections. If anything is unclear, rerun the Azure CLI commands again or get the information from the portal.

  • Define your migration plan: Based on your findings, create a checklist for your migration that highlights:

    • Any settings that need special attention
    • Triggers and bindings or other dependencies that might be affected during migration
    • Testing strategy for post-migration validation
    • Rollback plan if there are unexpected issues
  • Downtime planning: Consider when to stop the original function app to avoid both data loss and duplicate processing of events, and how this might affect your users or downstream systems. In some cases, you might need to disable specific functions before stopping the entire app.

A careful final review helps ensure a smoother migration process and minimizes the risk of overlooking important configurations.

Step 2: Create an app in the Flex Consumption plan

There are various ways to create a function app in the Flex Consumption plan along with other required Azure resources:

Create option Reference articles
Azure CLI Create a Flex Consumption app
Azure portal Create a function app in the Azure portal
Infrastructure-as-code ARM template
azd
Bicep
Terraform
Visual Studio Code Visual Studio Code deployment
Visual Studio Visual Studio deployment

Tip

When possible, you should use Microsoft Entra ID for authentication instead of connection strings, which contain shared keys. Using managed identities is a best practice that improves security by eliminating the need to store shared secrets directly in application settings. If your original app used connection strings, the Flex Consumption plan is designed to support managed identities. Most of these links show you how to enable managed identities in your function app.

Step 3: Apply migrated app settings in the new app

Before deploying your code, you must configure the new app with the relevant Flex Consumption plan app settings from your original function app.

Important

Not all Consumption plan app settings are supported when running in a Flex Consumption plan. For more information, see Flex Consumption plan deprecations.

Run this script that performs these tasks:

  1. Gets app settings from the old app, ignoring settings that don't apply in a Flex Consumption plan or that already exist in the new app.
  2. Writes the collected settings locally to a temporary file.
  3. Applies settings from the file to your new app.
  4. Deletes the temporary file.
sourceAppName=<SOURCE_APP_NAME>
destAppName=<DESTINATION_APP_NAME>
rgName=<RESOURCE_GROUP>

echo "Getting app settings from the old app..."
app_settings=$(az functionapp config appsettings list --name $sourceAppName --resource-group $rgName)

# Filter out settings that don't apply to Flex Consumption apps or that will already have been created
filtered_settings=$(echo "$app_settings" | jq 'map(select(
  (.name | ascii_downcase) != "website_use_placeholder_dotnetisolated" and
  (.name | ascii_downcase | startswith("azurewebjobsstorage") | not) and
  (.name | ascii_downcase) != "website_mount_enabled" and
  (.name | ascii_downcase) != "enable_oryx_build" and
  (.name | ascii_downcase) != "functions_extension_version" and
  (.name | ascii_downcase) != "functions_worker_runtime" and
  (.name | ascii_downcase) != "functions_worker_runtime_version" and
  (.name | ascii_downcase) != "functions_max_http_concurrency" and
  (.name | ascii_downcase) != "functions_worker_process_count" and
  (.name | ascii_downcase) != "functions_worker_dynamic_concurrency_enabled" and
  (.name | ascii_downcase) != "scm_do_build_during_deployment" and
  (.name | ascii_downcase) != "website_contentazurefileconnectionstring" and
  (.name | ascii_downcase) != "website_contentovervnet" and
  (.name | ascii_downcase) != "website_contentshare" and
  (.name | ascii_downcase) != "website_dns_server" and
  (.name | ascii_downcase) != "website_max_dynamic_application_scale_out" and
  (.name | ascii_downcase) != "website_node_default_version" and
  (.name | ascii_downcase) != "website_run_from_package" and
  (.name | ascii_downcase) != "website_skip_contentshare_validation" and
  (.name | ascii_downcase) != "website_vnet_route_all" and
  (.name | ascii_downcase) != "applicationinsights_connection_string"
))')

echo "Settings to migrate..."
echo "$filtered_settings"

echo "Writing settings to a local a local file (app_settings_filtered.json)..."
echo "$filtered_settings" > app_settings_filtered.json

echo "Applying settings to the new app..."
output=$(az functionapp config appsettings set --name $destAppName --resource-group $rgName --settings @app_settings_filtered.json)

echo "Deleting the temporary settings file..."
rm -rf app_settings_filtered.json  

echo "Current app settings in the new app..."
az functionapp config appsettings list --name $destAppName --resource-group $rgName 

In this example, replace <RESOURCE_GROUP>, <SOURCE_APP_NAME>, and <DEST_APP_NAME> with your resource group name and the old a new app names, respectively. This script assumes that both apps are in the same resource group.

Step 4: Apply other app configurations

Find the list of other app configurations from your old app that you collected during premigration and also set them in the new app.

In this script, set the value for any configuration set in the original app and comment-out any commands for any configuration not set (null):

appName=<APP_NAME>
rgName=<RESOURCE_GROUP>
http20Setting=<YOUR_HTTP_20_SETTING>
minTlsVersion=<YOUR_TLS_VERSION>
minTlsCipher=<YOUR_TLS_CIPHER>
httpsOnly=<YOUR_HTTPS_ONLY_SETTING>
certEnabled=<CLIENT_CERT_ENABLED>
certMode=<YOUR_CLIENT_CERT_MODE>
certExPaths=<CERT_EXCLUSION_PATHS>
scmAllowBasicAuth=<ALLOW_SCM_BASIC_AUTH>

# Apply HTTP version and minimum TLS settings
az functionapp config set --name $appName --resource-group $rgName --http20-enabled $http20Setting  
az functionapp config set --name $appName --resource-group $rgName --min-tls-version $minTlsVersion

# Apply the HTTPS-only setting
az functionapp update --name $appName --resource-group $rgName --set HttpsOnly=$httpsOnly

# Apply incoming client cert settings
az functionapp update --name $appName --resource-group $rgName --set clientCertEnabled=$certEnabled
az functionapp update --name $appName --resource-group $rgName --set clientCertMode=$certMode
az functionapp update --name $appName --resource-group $rgName --set clientCertExclusionPaths=$certExPaths

# Apply the TLS cipher suite setting
az functionapp update --name $appName --resource-group $rgName --set minTlsCipherSuite=$minTlsCipher

# Apply the allow scm basic auth configuration
az resource update --resource-group $rgName --name scm --namespace Microsoft.Web --resource-type basicPublishingCredentialsPolicies \
	--parent sites/$appName --set properties.allow=$scmAllowBasicAuth

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively. Also, replace the placeholders of any variable definitions for existing settings you want to recreate in the new app, and comment-out any null settings.

Step 5: Configure scale and concurrency settings

The Flex Consumption plan implements per-function scaling, where each function within your app can scale independently based on its workload. Scaling is also more strictly related to concurrency settings, which are used to make scaling decisions based on the current concurrent executions. For more information, see both Per-function scaling and Concurrency in the Flex Consumption plan article.

Consider concurrency settings first if you want your new app to scale similarly to your original app. Setting higher concurrency values can result in fewer instances being created to handle the same load.

If you had a custom scale-out limit set in your original app, you can also apply it to your new app. Otherwise, you can skip to the next section.

The default maximum instance count is 100, and it must be set to a value of 40 or higher.

Use this az functionapp scale config set command to set the maximum scale-out.

az functionapp scale config set --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
    --maximum-instance-count <MAX_SCALE_SETTING>

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively. Replace <MAX_SCALE_SETTING> with the maximum scale value you're setting.

Step 6: Configure storage mounts

If your original app ran on Linux and had one or more explicitly connected storage shares, you might want to reconnect the same storage shares in your new app.

Use this az webapp config storage-account add command to reconnect each storage share in your new app.

az webapp config storage-account add --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
  --custom-id <MOUNT_NAME> --storage-type AzureFiles --account-name <STORAGE_ACCOUNT> \
  --share-name <STORAGE_SHARE_NAME> --access-key <ACCESS_KEY> --mount-path <MOUNT_PATH>

In this example, make these replacements based on the details you documented during premigration:

Placeholder Description
<APP_NAME> The name of your function app.
<RESOURCE_GROUP> The name of your resource group.
<STORAGE_ACCOUNT> The name of your storage account to connect.
<ACCESS_KEY> The key used access the storage account, which is only needed when using key-based access.
<STORAGE_SHARE_NAME> Name of the Azure Files share in your storage account.
<MOUNT_NAME> The name used for the connected share in your app.
<MOUNT_PATH> The path to your connected share in your app.

Repeat this step for each file share being reconnected.

Step 7: Configure any custom domains and CORS access

If your original app had any bound custom domains or any CORS settings defined, recreate them in your new app. For more information about custom domains, see Set up an existing custom ___domain in Azure App Service.

  1. Use this az functionapp config hostname add command to rebind any custom ___domain mappings to your app:

    az functionapp config hostname add --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
        --hostname <CUSTOM_DOMAIN>
    

    In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively. Replace <CUSTOM_DOMAIN> with your custom ___domain name.

  2. Use this az functionapp cors add command to replace any CORS settings:

    az functionapp cors add --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
        --allowed-origins <ALLOWED_ORIGIN_1> <ALLOWED_ORIGIN_2> <ALLOWED_ORIGIN_N>
    

    In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively. Replace <ALLOWED_ORIGIN_*> with your allowed origins.

Step 8: Configure managed identities and assign roles

The way that you configure managed identities in your new app depends on the kind of managed identity:

Managed identity type Create identity Role assignments
User-assigned Optional You can continue to use the same user-assigned managed identities with the new app. You must reassign these identities to your Flex Consumption app and verify that they still have the correct role assignments in remote services. If you choose to create new identities for the new app, you must assign the same roles as the existing identities.
System-assigned Yes Because each function app has its own system-assigned managed identity, you must enable the system-assigned managed identity in the new app and reassign the same roles as in the original app.

Recreating the role assignments correctly is key to ensuring your function app has the same access to Azure resources after the migration.

Tip

If your original app used connection strings or other shared secrets for authentication, this is a great opportunity to improve your app's security by switching to using Microsoft Entra ID authentication with managed identities. For more information, see Tutorial: Create a function app that connects to Azure services using identities instead of secrets.

  1. Use this az functionapp identity assign command to enable the system-assigned managed identity in your new app:

    az functionapp identity assign --name <APP_NAME> --resource-group <RESOURCE_GROUP>
    

    In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively.

  2. Use this script to get the principal ID of the system assigned identity and add it to the required roles:

    # Get the principal ID of the system identity
    principalId=$(az functionapp identity show --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
        --query principalId -o tsv)
    
    # Assign a role in a specific resource (scope) to the system identity
    az role assignment create --assignee $principalId --role "<ROLE_NAME>" --scope "<RESOURCE_ID>"
    

    In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively. Replace <ROLE_NAME> and <RESOURCE_ID> with the role name and specific resource you captured from the original app.

  3. Repeat the previous commands for each role required by the new app.

Step 9: Configure built-in authentication

If your original app used built-in client authentication, you should recreate it in your new app. If you're planning to reuse the same client registration, make sure to set the new app's authenticated endpoints in the authentication provider.

Based on the information you collected earlier, use the az webapp auth update command to recreate each built-in authentication registration required by your app.

Step 10: Configure Network Access Restrictions

If your original app had any IP-based inbound access restrictions, you can recreate any of the same inbound access rules you want to keep in your new app.

Tip

The Flex Consumption plan fully supports virtual network integration. Because of this, you also have the option to use inbound private endpoints after migration. For more information, see Private endpoints.

Use this az functionapp config access-restriction add command for each IP access restriction you want to replicate in the new app:

az functionapp config access-restriction add --name <APP_NAME> --resource-group <RESOURCE_GROUP> \
  --rule-name <RULE_NAME> --action Deny --ip-address <IP_ADDRESS> --priority <PRIORITY>

In this example, replace these placeholders with the values from your original app:

Placeholder Value
<APP_NAME> Your function app name.
<RESOURCE_GROUP> Your resource group.
<RULE_NAME> Friendly name for the IP rule.
<Priority> Priority for the exclusion.
<IP_Address> The IP address to exclude.

Run this command for each documented IP restriction from the original app.

Step 11: Enable monitoring

Before you start your new app in the Flex Consumption plan, make sure that Application Insights is enabled. Having Application Insights configured helps you to troubleshoot any issues that might occur during code deployment and start-up.

Implement a comprehensive monitoring strategy that covers app metrics, logs, and costs. By using such a strategy, you can validate the success of your migration, identify any issues promptly, and optimize the performance and cost of your new app.

If you plan to compare this new app with your current app, make sure your scheme also collects the required benchmarks for comparison. For more information, see Configure monitoring.

Step 12: Deploy Your App Code to the New Flex Consumption App

With your new Flex Consumption plan app fully configured based on the settings from the original app, it's time to deploy your code to the new app resources in Azure.

Caution

After successful deployment, triggers in your new app immediately start processing data from connected services. To minimize duplicated data and prevent data loss while starting the new app and shutting-down the original app, you should review the strategies that you defined in mitigations by trigger type.

Functions provides several ways to deploy your code, either from the code project or as a ready-to-run deployment package.

Tip

If your project code is maintained in a source code repository, now is the perfect time to configure a continuous deployment pipeline. Continuous deployment lets you automatically deploy application updates based on changes in a connected repository.

You should update your existing deployment workflows to deploy your source code to your new app:

You can also create a new continuous deployment workflow for your new app. For more information, see Continuous deployment for Azure Functions

Post-migration tasks

After a successful migration, you should perform these follow-up tasks:

Verify basic functionality

  1. Verify the new app is running in a Flex Consumption plan:

    Use this az functionapp show command two view the details about the hosting plan:

    az functionapp show --name <APP_NAME> --resource-group <RESOURCE_GROUP> --query "serverFarmId"
    

    In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively.

  2. Use an HTTP client to call at least one HTTP trigger endpoint on your new app to make sure it responds as expected.

Capture performance benchmarks

With your new app running, you can run the same performance benchmarks that you collected from your original app, such as:

Suggested benchmark Comment
Cold-start Measure the time from first request to the first response after an idle period.
Throughput Measure the maximum requests-per-second using load testing tools to determine how the app handles concurrent requests.
Latency Track the P50, P95, and P99 response times under various load conditions. You can monitor these metrics in Application Insights.

You can use this Kusto query to review the suggested latency response times in Application Insights:

requests
| where timestamp > ago(1d)
| summarize percentiles(duration, 50, 95, 99) by bin(timestamp, 1h)
| render timechart

Note

Flex Consumption plan metrics differ from Consumption plan metrics. When comparing performance before and after migration, keep in mind that you must use different metrics to track similar performance characteristics. For more information, see Configure monitoring.

Create custom dashboards

Azure Monitor metrics and Application Insights enable you to create dashboards in the Azure portal that display charts from both platform metrics and runtime logs and analytics.

Consider setting-up dashboards and alerts on your key metrics in the Azure portal. For more information, see Monitor your app in Azure.

Refine plan settings

Actual performance improvements and cost implications of the migration can vary based on your app-specific workloads and configuration. The Flex Consumption plan provides several settings that you can adjust to refine the performance of your app. You might want to make adjustments to more closely match the behavior of the original app or to balance cost versus performance. For more information, see Fine-tune your app in the Flex Consumption article.

Remove the original app (optional)

After thoroughly testing your new Flex Consumption function app and validating that everything is working as expected, you might want to clean up resources to avoid unnecessary costs. Even though triggers in the original app are likely already disabled, you might wait a few days or even weeks before removing the original app entirely. This delay, which depends on your application's usage patterns, makes sure that all scenarios, including infrequent ones, are properly tested. Only after you're satisfied with the migration results, should you proceed to remove your original function app.

Important

This action deletes your original function app. The Consumption plan remains intact if other apps are using it. Before you proceed, make sure you've successfully migrated all functionality to the new Flex Consumption app, verified no traffic is being directed to the original app, and backed up any relevant logs, configuration, or data that might be needed for reference.

Use the az functionapp delete command to delete the original function app:

az functionapp delete --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>

In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively.

Troubleshooting and Recovery Strategies

Despite careful planning, migration issues can occur. Here's how to handle potential issues during migration:

Issue Solution
Cold start performance issues • Review concurrency settings
• Check for missing dependencies
Missing bindings • Verify extension bundles
• Update binding configurations
Permission errors • Check identity assignments and role permissions
Network connectivity issues • Validate access restrictions and networking settings
Missing application insights • Recreate the Application Insights connection
App fails to start See General troubleshooting steps
Triggers aren't processing events See General troubleshooting steps

If you experience issues migrating a production app, you might want to rollback the migration to the original app while you troubleshoot.

General troubleshooting steps

Use these steps for cases where the new app fails to start or function triggers aren't processing events:

  1. In your new app page in the Azure portal, select Diagnose and solve problems in the left pane of the app page. Select Availability and Performance and review the Function App Down or Reporting Errors detector. For more information, see Azure Functions diagnostics overview.

  2. In the app page, select Monitoring > Application Insights > View Application Insights data then select Investigate > Failures and check for any failure events.

  3. Select Monitoring > Logs and run this Kusto query to check these tables for errors:

    traces
        | where severityLevel == 3
        | where cloud_RoleName == "<APP_NAME>"
        | where timestamp > ago(1d)
        | project timestamp, message, operation_Name, customDimensions
        | order by timestamp desc
    

    In these queries, replace <APP_NAME> with the name of your new app. These queries check for errors in the past day (where timestamp > ago(1d)).

  4. Back in the app page, select Settings > Environment variables and verify that all critical application settings were correctly transferred. Look for any deprecated settings that might have been incorrectly migrated or any typos or incorrect connection strings. Verify the default host storage connection.

  5. Select Settings > Identity and double-check that the expected identities exist and that they have been assigned to the correct roles.

  6. In your code, verify that all binding configurations are correct, paying particular attention to connection string names, storage queue and container names, and consumer group settings in Event Hubs triggers.

Rollback steps for critical production apps

If you aren't able to troubleshoot successfully, you might want to revert to using your original app while you continue to troubleshoot.

  1. If the original app was stopped, restart it:

    Use this az functionapp start command to restart the original function app:

    az functionapp delete --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>
    
  2. If you created new queues/topics/containers, ensure clients are redirected back to the original resources.

  3. If you modified DNS or custom domains, revert these changes to point to the original app.

Providing feedback

If you encounter issues with your migration using this article or want to provide other feedback on this guidance, use one of these methods to get help or provide your feedback: