| title | Migrate Consumption plan apps to Flex Consumption in Azure Functions | |
|---|---|---|
| description | Learn how to migrate an existing function app in Azure running in an Azure Functions Consumption hosting plan to instead run in the Flex Consumption hosting plan. | |
| ms.service | azure-functions | |
| ms.collection |
|
|
| ms.date | 09/23/2025 | |
| ms.topic | concept-article | |
| zone_pivot_groups | app-service-platform-windows-linux |
This article walks you through migrating your existing function apps from the Consumption plan to the Flex Consumption plan. The good news: for most apps, this migration is straightforward and your code doesn't need to change.
::: zone pivot="platform-linux"
Important
The Linux Consumption hosting plan for Azure Functions is being retired on September 30, 2028. You have plenty of time to migrate, and we provide tools to help make it easy. Key dates:
| Date | What happens |
|---|---|
| September 30, 2025 | No new features for Linux Consumption. The option is removed from Azure portal, Visual Studio, and VS Code (but you can still manage existing apps via CLI and IaC). The last supported language versions for Linux Consumption are: .NET 9, Python 3.12, Node.js 22, PowerShell 7.4, and Java 21. Newer language versions aren't supported for Linux Consumption. |
| September 30, 2028 | Retirement date. No technical support and no new Linux Consumption apps can be created. |
::: zone-end
Select your operating system at the top of the article to see the right instructions for your app.
::: zone pivot="platform-linux"
Tip
We've made this easier for you. Azure Functions provides CLI commands (az functionapp flex-migration) that automate most of the migration steps. You can also use the Azure portal if you prefer a visual approach—just select the Azure portal tab in each section below.
::: zone-end
When you migrate, your functions get these benefits without changing your code:
- Faster cold starts: always-ready instances mean your functions respond more quickly.
- Better scaling: per-function scaling and concurrency controls give you more control.
- Virtual network support: connect your functions to private networks and use private endpoints.
- Active investment: Flex Consumption is where new features and improvements land first.
For more details, see Flex Consumption plan benefits and hosting plan comparison.
Here's what the migration process looks like:
- Your code stays the same. You don't need to rewrite your functions if you are on a Flex Consumption supported language version, and this guide will help you check.
- You'll create a new app. The migration creates a new Flex Consumption app alongside your existing one, so you can test before switching over.
- Same resource group. Your new app runs in the same resource group with access to the same dependencies.
- You control the timing. Test your new app thoroughly before redirecting traffic and retiring the old one.
Note
If you're using Azure Government, Flex Consumption isn't available there yet. Review this guidance now so you're ready when it becomes available.
This guide provides instructions for both the Azure portal and the Azure CLI—use whichever you're more comfortable with. If you deploy using infrastructure-as-code (IaC), you can follow the same steps in your ARM templates, Bicep files, or Terraform configurations:
- The Flex Consumption plan uses a new
functionAppConfigsection in theMicrosoft.Web/sitesresource. See Flex Consumption plan deprecations. - See Automate resource deployment for resource configuration details.
- Check out ready-to-use examples for ARM templates, Bicep, and Terraform.
-
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.77.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 addcommand:az extension add --name resource-graph -
The
jqtool, which is used to work with JSON output.
-
Access to the Azure portal
-
Access to the Azure subscription containing one or more function apps to migrate. The account used to access the portal must be able to:
- Create and manage function apps and App Service hosting plans.
- Assign roles to managed identities.
- Create and manage storage accounts.
- 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.
-
A modern web browser that's up-to-date.
Tip
Already know which app to migrate? You can skip this section and go straight to Assess your existing app.
If you have multiple function apps and aren't sure which ones need to migrate, this section helps you find them. You'll get a list of app names, resource groups, locations, and runtime stacks.
::: zone pivot="platform-linux"
Run this command to see which of your Linux Consumption apps are ready to migrate:
az functionapp flex-migration list
This command automatically scans your subscription and returns two arrays:
- eligible_apps: Linux Consumption apps that can be migrated to Flex Consumption. These apps are compatible with Flex Consumption.
- ineligible_apps: Apps that can't be migrated, along with the specific reasons why. The reasons for incompatibility need to be reviewed and addressed before continuing.
Note
This command only evaluates function apps running on the Linux Consumption plan. Apps running on other hosting plans (Windows Consumption, Premium, Dedicated, or Flex Consumption) won't appear in either the eligible_apps or ineligible_apps arrays. If you have many function apps and aren't sure which hosting plan each one uses, you can run az functionapp list --query "[].{name:name, sku:sku}" -o table to see all apps and their SKUs, where Dynamic indicates a Consumption plan app.
The output includes the app name, resource group, location, and runtime stack for each app, along with eligibility status and migration readiness information.
-
Navigate to the Azure Resource Graph Explorer in the Azure portal.
-
Copy this Kusto query, paste it in the query window, and select Run query:
resources | 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=tostring(siteProperties.value)
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.
::: zone-end
::: zone pivot="platform-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' | where properties.sku == 'Dynamic' \
| project name, location, resourceGroup" \
--query data --output table
This command generates a table with the app name, location, and resource group for all Consumption apps running on Windows in the current subscription.
You're prompted to install the resource-graph extension, if it isn't already installed.
-
Navigate to the Azure Resource Graph Explorer in the Azure portal.
-
Copy this Kusto query, paste it in the query window, and select Run query:
resources | where type == 'microsoft.web/sites' | where ['kind'] == 'functionapp' | where properties.sku == 'Dynamic' | project name, location, resourceGroup
This command generates a table with the app name, location, and resource group for all Consumption apps running on Windows in the current subscription.
::: zone-end
Before migrating, run through this quick checklist to make sure your app is ready. Most apps pass these checks without issues:
[!div class="checklist"]
Confirm that the Flex Consumption plan is currently supported in the same region as the Consumption plan app you intend to migrate.
:::zone pivot="platform-linux"
Confirmed: When the
az functionapp flex-migration listcommand output has your app in theeligible_appslist, the Flex Consumption plan is supported in the same region used by your current Linux Consumption app. In this case, you can continue to Verify language stack compatibility.
Action required: When the
az functionapp flex-migration listcommand output has your app in theineligible_appslist, you see an error message statingThe site '<name>' is not in a region supported in Flex Consumption. Please see the list regions supported in Flex Consumption by running az functionapp list-flexconsumption-locations. In this case, the Flex Consumption plan isn't yet supported in the region used by your current Linux Consumption app.
:::zone-end
:::zone pivot="platform-windows"
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.
The create process for a function app in the Azure portal filters out regions that the Flex Consumption plan doesn't currently support.
-
In the Azure portal, select Create a resource in the left-hand menu and select Function app > Create.
-
Select Flex Consumption > Select and in the Basics tab expand Region.
-
Review the supported Flex Consumption plan regions.
Make sure that the region in which the Consumption plan app you want to migrate runs is included in the list.
:::zone-end
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.
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 | ✅ Yes |
:::zone pivot="platform-linux"
Confirmed: If the
az functionapp flex-migration listcommand included your app in theeligible_appslist, your Linux Consumption app is already using a supported language stack by Flex Consumption and you can continue to Verify stack version compatibility.
Action required: If the
az functionapp flex-migration listcommand included your app in theineligible_appslist with an error message statingRuntime '<name>' not supported for function apps on the Flex Consumption plan., your Linux Consumption app isn't running a supported runtime by Flex Consumption yet.
:::zone-end
If your function app uses an unsupported runtime stack:
- For C# apps that run in-process with the runtime (
dotnet), you must first migrate your app to .NET isolated. For more information, see Migrate C# apps from the in-process model to the isolated worker model.
Before migrating, you must make sure that your app's runtime stack version is supported when running in a Flex Consumption plan in the current region.
:::zone pivot="platform-linux"
Confirmed: If the
az functionapp flex-migration listcommand included your app in theeligible_appslist, your Linux Consumption app is already using a supported language stack version by Flex Consumption and you can continue to Verify deployment slots usage.
Action required: If the
az functionapp flex-migration listcommand included your app in theineligible_appslist with an error message statingInvalid version {0} for runtime {1} for function apps on the Flex Consumption plan. Supported versions for runtime {1} are {2}., your Linux Consumption app isn't running a supported runtime by Flex Consumption yet.
:::zone-end
:::zone pivot="platform-windows"
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.
The create process for a function app in the Azure portal filters out language stack versions that the Flex Consumption plan doesn't currently support.
-
In the Azure portal, select Create a resource in the left-hand menu and select Function app > Create.
-
Select Flex Consumption > Select and in the Basics tab select your language Runtime stack and Region.
-
Expand Version and review the supported versions of your language stack in your chosen region.
:::zone-end
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.
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.
:::zone pivot="platform-linux"
Confirmed: When your current app has deployment slots enabled, the
az functionapp flex-migration listcommand shows your function app in theeligible_appslist without a warning, continue to Verify the use of certificates.
Action required: Your current app has deployment slots enabled, the
az functionapp flex-migration listcommand shows your function app in theeligible_appslist but adds a warning that states:The site '<name>' has slots configured. This will not block migration, but please note that slots are not supported in Flex Consumption.
:::zone-end
:::zone pivot="platform-windows"
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.
To determine whether your function app has deployment slots enabled:
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, select Deployment > Deployment slots.
-
If you see any slots listed in the Deployment slots page, your function app is currently using deployment slots.
:::zone-end
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:
- 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, or
- Migrate any new code or features from the deployment slot into the main (production) slot.
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.
:::zone pivot="platform-linux"
Confirmed: If the
az functionapp flex-migration listcommand included your app in theeligible_appslist, your Linux Consumption app is already not using certificates, and you can continue to Verify your Blob storage triggers.
Action required: If the
az functionapp flex-migration listcommand included your app in theineligible_appslist with an error message statingThe site '<name>' is using TSL/SSL certificates. TSL/SSL certificates are not supported in Flex Consumption.orThe site '<name>' has the WEBSITE_LOAD_CERTIFICATES app setting configured. Certificate loading is not supported in Flex Consumption., your Linux Consumption app isn't yet compatible with Flex Consumption.
:::zone-end
:::zone pivot="platform-windows"
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.
To determine whether your function app is using TSL/SSL certificates:
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, select Settings > Certificates.
-
Check the Managed certificates, Bring your own certificates (.pfx), Public key certificates (.cer) tabs for any installed certificates.
:::zone-end
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.
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.
:::zone pivot="platform-linux"
Confirmed: If the
az functionapp flex-migration listcommand included your app in theeligible_appslist, your Linux Consumption app is already not using a blob storage triggers withEventGridas the source. You can continue to Consider dependent services.
Action required: If the
az functionapp flex-migration listcommand included your app in theineligible_appslist with an error message statingThe site '<name>' has blob storage trigger(s) that don't use Event Grid as the source: <list> Flex Consumption only supports Event Grid-based blob triggers. Please convert these triggers to use Event Grid or replace them with Event Grid triggers before migration., your Linux Consumption app isn't yet compatible with Flex Consumption.
:::zone-end :::zone pivot="platform-windows"
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.
To determine whether your function app has any Blob storage triggers that don't use Event Grid as the source:
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the Overview on the Functions tab, look for any functions with a Trigger type of
Blob. -
Select a blob trigger function and in the Code + Test select Resource JSON.
-
Locate the
properties.config.bindingssection of the function definition. This section should have abindings.typeofblobTrigger. If thebindingsobject has nosourceproperty orsourcehas a value ofLogsAndContainerScan, then the trigger is using container polling. An Event Grid source trigger instead has asourcevalue ofEventGrid. -
Repeat steps 3-4 for any remaining Blob storage trigger functions in your app.
:::zone-end
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:
-
Add or update the
sourceproperty in your Blob storage trigger definition toEventGridand redeploy the app. -
Build the endpoint URL in your function app used to be used by the event subscription.
-
Create an event subscription on your Blob storage container.
For more information, see Tutorial: Trigger Azure Functions on blob containers using an event subscription.
Tip
Simple HTTP-only app? If your functions only use HTTP triggers and don't connect to other Azure services, you can likely skip most of this section—just remember to update any clients to point to your new app's URL after migration.
Because Azure Functions is a compute service, you should consider the effect of migration on data and services both upstream and downstream of your app.
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 Custom multi-region solutions for resiliency.
- 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.
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. |
::: zone pivot="platform-linux"
Here's the easy part! The az functionapp flex-migration start command does most of the work for you—it collects your app's configuration and creates a new Flex Consumption app with the same settings.
az functionapp flex-migration start \
--source-name <SOURCE_APP_NAME> \
--source-resource-group <SOURCE_RESOURCE_GROUP> \
--name <NEW_APP_NAME> \
--resource-group <RESOURCE_GROUP>
In this example, replace these placeholders with the indicated values:
| Placeholder | Value |
|---|---|
<SOURCE_APP_NAME> |
The name of your original app. |
<SOURCE_RESOURCE_GROUP> |
The resource group of the original app. |
<NEW_APP_NAME> |
The name of the new app. |
<RESOURCE_GROUP> |
The resource group of the new app. |
The az functionapp flex-migration start command performs these basic tasks:
- Assesses your source app for compatibility with the Flex Consumption hosting plan.
- Creates a function app in the Flex Consumption plan.
- Migrates most configurations including app settings, identity assignments, storage mounts, CORS settings, custom domains, and access restrictions.
The migration command supports several options to customize the migration:
| Option | Description |
|---|---|
--storage-account |
Specify a different storage account for the new app |
--maximum-instance-count |
Set the maximum number of instances for scaling |
--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 |
For complete command options, use az functionapp flex-migration start --help.
After you've completed running az functionapp flex-migration start successfully, continue to Get the code deployment package.
::: zone-end
::: zone pivot="platform-windows"
Before creating your new Flex Consumption app, you'll gather some information about your current app. This ensures nothing gets lost in the transition.
Tip
This is mostly copy-paste work. You're just collecting settings from your existing app so you can apply them to the new one.
Complete these tasks before migrating:
[!div class="checklist"]
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.
To get your current function app settings:
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, expand Settings and select Environment variables.
-
In the App settings tab, select Advanced edit then copy and save the JSON app settings content.
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.
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 your clients might require. |
| 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 | clientCertEnabledclientCertModeclientCertExclusionPaths |
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, some publishing methods require it. |
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.
To review the relevant application configurations of your existing app:
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, expand Settings and select Configuration.
-
In the General settings tab, make a note of these settings:
- SCM Basic Auth Publishing Credentials
- HTTP version
- HTTPS Only
- Minimum Inbound TLS Version
- Minimum Inbound TLS Cipher
- Incoming client certificates
-
In the Path mappings tab, verify if there are any existing file share mounts required by your app. If there are, note the storage Account name, Share name, Mount path, and access Type for each mounted share.
-
Under Settings > Scale out, if Enforce Scale Out Limit is set to Yes, note the Maximum Scale Out Limit value.
-
Under Settings > Custom domains, note any domain names other than
*.azurewebsites.net, the binding type, and SSL certificate information. -
Under API > CORS, note any explicitly allowed CORS origins and other CORS settings.
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.
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, expand Settings and select Identity.
-
Check the System assigned tab to see if the system-assigned managed identity is enabled. If enabled, select Azure role assignments to view the roles assigned to this identity.
-
Check the User assigned tab to see if there any user-assigned managed identities are assigned. Note the names of any user-assigned identities.
-
For each user-assigned managed identity, select the identity and in the identity page select Azure role assignments.
-
Make a note of each role assignment granted to the identity and determine whether your app requires it.
Document all identities and their role assignments so that you can recreate the same permissions structure for your new Flex Consumption app.
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.
To determine if built-in client authentication is configured:
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, expand Settings and select Authentication.
-
If built-in authentication is enabled, make a note of which client identity providers are configured. Also note any advanced settings such as token store, allowed external redirects, and allowed token audiences.
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.
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, expand Settings and select Networking.
-
If you see Enabled with no access restrictions for Public network access then there's no inbound access restrictions. Otherwise Select Access restrictions.
-
Document all configured IP-based access restrictions currently configured.
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.
:::zone-end
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 and continue to Capture performance benchmarks (optional).
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.
::: zone pivot="platform-linux"
Consumption plan apps on Linux maintain the deployment zip package file in one of these locations:
-
An Azure Blob storage container named
scm-releasesin 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_PACKAGEsetting that is a URL, the package is in an externally accessible location that you maintain. 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.
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.
Use these steps to download the deployment package from your current app:
-
Use this
az functionapp config appsettings listcommand to get theWEBSITE_RUN_FROM_PACKAGEapp setting, if present:az functionapp config appsettings list --name <APP_NAME> --resource-group <RESOURCE_GROUP> \ --query "[?name=='WEBSITE_RUN_FROM_PACKAGE'].value" -o tsvIn 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. -
If the
WEBSITE_RUN_FROM_PACKAGEvalue is1or 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 fiAgain, 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.
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, expand Settings > Environment variables and see if a setting named
WEBSITE_RUN_FROM_PACKAGEexists. -
If
WEBSITE_RUN_FROM_PACKAGEexists, check if it's set to a value of1or a URL. If set to a URL, that URL is the location of the package file for your app content. Download the .zip file from that URL location that you own. -
If the
WEBSITE_RUN_FROM_PACKAGEsetting doesn't exist or is set to1, you must download the package from the specific storage account, which depends on whether you're running on Linux or Windows. -
Get the storage account name from the
AzureWebJobsStorageorAzureWebJobsStorage__accountNameapplication setting. For a connection string, theAccountNameis the name your storage account. -
In the portal, search for your storage account name.
-
In the storage account page, locate the deployment package and download it.
-
Expand Data storage > Containers and select
scm_releases. Choose the file namedscm-latest-<APP_NAME>.zipand select Download.
::: zone-end ::: zone pivot="platform-windows"
The location of your project source files depends on the WEBSITE_RUN_FROM_PACKAGE app setting as follows:
WEBSITE_RUN_FROM_PACKAGE value |
Source file location |
|---|---|
1 |
The files are in a zip package that is stored in the Azure Files share of the storage account defined by the WEBSITE_CONTENTAZUREFILECONNECTIONSTRING setting. The name of the files share is defined by the WEBSITE_CONTENTSHARE setting. |
| An endpoint URL | The files are in a zip package in an externally accessible location that you maintain. An external package should be hosted in a blob storage container with restricted access. For more information, see External package URL. |
-
Use this
az functionapp config appsettings listcommand to get theWEBSITE_RUN_FROM_PACKAGEapp setting, if present:az functionapp config appsettings list --name <APP_NAME> --resource-group <RESOURCE_GROUP> \ --query "[?name=='WEBSITE_RUN_FROM_PACKAGE'].value" -o tsvIn 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. -
If the
WEBSITE_RUN_FROM_PACKAGEvalue is1or 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 and file share from app settings..." json=$(az functionapp config appsettings list --name $appName --resource-group $rgName \ --query "[?name=='WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' || name=='WEBSITE_CONTENTSHARE'].value" -o json) storageConnection=$(echo "$json" | jq -r '.[0]') fileShare=$(echo "$json" | jq -r '.[1]') echo "Getting the package name..." packageName=$(az storage file list --share-name $fileShare --connection-string $storageConnection \ --path "data/SitePackages" --query "[?ends_with(name, '.zip')] | sort_by(@, &properties.lastModified)[-1].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 file download --connection-string $storageConnection --share-name $fileShare \ --path "data/SitePackages/$packageName" else echo "Exiting script." exit 0 fiAgain, 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.
-
In the Azure portal, search for or otherwise navigate to your function app page.
-
In the left menu, expand Settings > Environment variables and see if a setting named
WEBSITE_RUN_FROM_PACKAGEexists. -
If
WEBSITE_RUN_FROM_PACKAGEexists, check if it's set to a value of1or a URL. If set to a URL, that URL is the location of the package file for your app content. Download the .zip file from that URL location that you own. -
If the
WEBSITE_RUN_FROM_PACKAGEsetting doesn't exist or is set to1, you must download the package from the specific storage account, which depends on whether you're running on Linux or Windows. -
Get the storage account name from the
WEBSITE_CONTENTAZUREFILECONNECTIONSTRINGsetting, where theAccountNameis the name your storage account. Also, make a note of theWEBSITE_CONTENTSHAREvalue, which is the name of the file share. -
In the portal, search for your storage account name.
-
In the storage account page, locate the deployment package and download it.
-
Expand Data storage > File shares, select the share name from
WEBSITE_CONTENTSHARE, and browse to thedata\SitePackagessubfolder. Choose the most recent .zip file and select Download.
::: zone-end
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 timechartThe migration of your functions from a Consumption plan app to a Flex Consumption plan app follows these main steps:
::: zone pivot="platform-linux"
[!div class="checklist"]
After running the az functionapp flex-migration start command, you should verify that your new Flex Consumption app was created successfully and properly configured. Here are some steps to validate the migration results:
-
Verify the new app exists and is running:
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 tableCompare these settings with your source app to ensure critical configurations were transferred.
-
Check managed identity configuration:
az functionapp identity show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> -
Verify any custom domains were migrated:
az functionapp config hostname list --webapp-name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \ --output table
-
In the Azure portal, search for your new function app by name.
-
In the app's Overview page, verify:
- Status shows as
Running - Plan type shows as
Flex Consumption - The Resource group and Location match your expectations
- Status shows as
-
In the left menu, expand Settings > Environment variables and review the App settings tab to ensure your application settings were migrated correctly.
-
Check Settings > Identity to verify that managed identities were configured as expected.
-
If applicable, check Settings > Custom domains to confirm any custom domain mappings were migrated.
-
Review Settings > Networking to verify access restrictions were applied if they existed in the source app.
The automated migration command should have transferred most configurations. However, you should manually verify that these items weren't migrated and they might need to be configured manually:
- Certificates: TSL/SSL certificates aren't supported in Flex Consumption yet
- Deployment slots: Not supported in Flex Consumption
- Built-in authentication settings: These need to be reconfigured manually
- CORS settings: May need manual verification depending on your configuration
If any critical settings are missing or incorrect, you can manually configure them using the steps outlined in the Windows migration process sections of this article.
:::zone-end
::: zone pivot="platform-windows"
[!div class="checklist"]
- Final review of the plan
- Create an app in the Flex Consumption plan
- Apply migrated app settings in the new app
- Apply other app configurations
- Configure scale and concurrency settings
- Configure any custom domains and CORS access
- Configure managed identities and assign roles
- Configure Network Access Restrictions
- Enable monitoring
- Configure built-in authentication
- Deploy Your App Code to the New Flex Consumption App
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.
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.
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:
- 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.
- Writes the collected settings locally to a temporary file.
- Applies settings from the file to your new app.
- 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.
To transfer settings:
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Environment variables and in the App settings tab select + Add.
-
Type or paste-in both the setting Name and Value and then select Apply.
-
Repeat the previous step for each setting in the old app that you need to recreate in the new app. If a setting already exists in the new app, skip it. You should also skip any setting that's deprecated in the Flex Consumption plan.
-
After adding all relevant settings, select Apply > Save.
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.
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Configuration and on the General settings tab update these settings to match what you documented from your original Consumption plan app:
- SCM Basic Auth Publishing Credentials
- HTTP version
- HTTPS Only
- Minimum Inbound TLS Version and cypher
- Client Certificate settings
-
Select Save to apply the configuration changes.
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 between 1 and 1,000.
Note
Reducing maximum instance count below 40 for HTTP function apps can cause frequent request failures and prolonged throttling windows when traffic exceeds capacity. This setting is intended only for advanced scenarios where limited scale-out is acceptable and has been fully tested.
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.
To configure scale and concurrency in your new app:
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Scale and concurrency and for Maximum instance count set a maximum value out to which your app is allowed to scale.
-
Select
Saveto apply the changes.
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.
-
Use this
az functionapp config hostname addcommand 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. -
Use this
az functionapp cors addcommand 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.
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Custom domains, select + Add custom domain, configure the custom domain, select Validate, and then select Add.
-
Repeat the previous step to add any other custom domains.
-
In the left menu, expand API > CORS, add one or more Allowed origins, and select Save.
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.
-
Use this
az functionapp identity assigncommand 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. -
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. -
Repeat the previous commands for each role required by the new app.
Use this script to get the ID of an existing user-assigned managed identity and associate it with your new app:
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>
userName=<USER_IDENTITY_NAME>
userId=$(az identity show --name $userName --resource-group $rgName --query 'id' -o tsv)
az functionapp identity assign --name $appName --resource-group $rgName --identities $userId
Repeat this script for each role required by the new app.
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Identity and on the System assigned tab set Status to On.
-
Select Azure role assignments in the left pane.
-
Select + Add role assignment the set these assignment properties for a role you documented in the original app:
Property Description Scope The resource type being accessed. Subscription The subscription of the resource. Resource The specific resource within the selected scope. Role Search for and select the role being assigned. -
Select Save to add the scope, and repeat the previous step for each documented role required by the new app.
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Identity and on the User assigned tab select + Add.
-
Select an existing identity and then Add.
-
Select the identity you just added and select Azure role assignments in the left pane.
-
Select + Add role assignment the set these assignment properties for a role you documented in the original app:
Property Description Scope The resource type being accessed. Subscription The subscription of the resource. Resource The specific resource within the selected scope. Role Search for and select the role being assigned. -
Select Save to add the scope, and repeat the previous step for each documented role required by the new app.
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.
To add IP-based networking restrictions:
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Networking and select the link next to Public network access.
-
In the Access restrictions page under Site access and rules, select + Add and enter these settings for a specific IP restriction:
Setting Value Name Friendly name for the IP rule. Action Deny Priority Priority for the exclusion. Type Select either IPv4 or IPv6. IP Address Block The IP addresses to exclude. -
Select Add rule and repeat the previous step for each access restriction you documented.
-
After you enter all of the IP restrictions from the original app, select Save.
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.
:::zone-end
If your original app used built-in client authentication (sometimes called Easy Auth), 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.
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In the left menu, expand Settings > Authentication and select Add identity provider.
-
Select your desired Identity provider and set the configurations and permissions required by the authenticator.
For more information, see these provider-specific articles:
- Configure your Azure Functions app to use Microsoft Entra sign-in
- Configure your Azure Functions app to use GitHub login
- Configure your Azure Functions app to use Google authentication
- Configure your Azure Functions app to use Facebook login
- Configure your Azure Functions app to use X login
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
You can use these tools to achieve a one-off deployment of your code project to your new plan:
You can use this az functionapp deployment source config-zip command to redeploy a downloaded package or a newly created deployment package:
az functionapp deployment source config-zip --resource-group <RESOURCE_GROUP> --name <APP_NAME> --src <PACKAGE_PATH>
In this example, replace <RESOURCE_GROUP> and <APP_NAME> with your resource group and function app names, respectively. Replace <PACKAGE_PATH> with the path and file name of your deployment package, such as /path/to/function/package.zip.
🎉 Congratulations! Your app is now running on Flex Consumption. Here are some optional follow-up tasks to get the most out of your new plan:
[!div class="checklist"]
-
Verify the new app is running in a Flex Consumption plan:
Use this
az functionapp showcommand 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.-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
In Overview > Essentials, verify that the Status of your app is
Runningand that the Plan type isFlex Consumption.
-
-
Use an HTTP client to call at least one HTTP trigger endpoint on your new app to make sure it responds as expected.
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 timechartNote
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.
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.
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.
If you manage your function app infrastructure using Bicep or Terraform, you need to update your IaC files to target the Flex Consumption plan. This section shows the key differences between Consumption and Flex Consumption plan resource definitions.
Important
You can't convert an existing Consumption plan app to Flex Consumption in place. You need to create new resources with a new name or delete the existing resources before deploying the Flex Consumption equivalents.
When migrating your IaC from Consumption to Flex Consumption, consider these important changes:
| Aspect | Consumption plan | Flex Consumption plan |
|---|---|---|
| Hosting plan SKU | Y1 (Dynamic) |
FC1 (FlexConsumption) |
| Plan required | Optional (auto-created) | Required (must be explicit) |
| Operating system | Windows or Linux | Linux only |
| Configuration | App settings | functionAppConfig section |
| Storage content share | WEBSITE_CONTENTSHARE setting |
deployment.storage in functionAppConfig |
The examples below demonstrate the key differences between Consumption and Flex Consumption plan resource definitions, and use system assigned managed identity, but they are not complete. They don't include all required resources such as storage accounts, Application Insights, or all necessary role assignments. For complete, production-ready examples, review the Flex Consumption IaC samples.
Consumption plan (before):
// Consumption plan (optional - auto-created if omitted)
resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: hostingPlanName
location: location
sku: {
name: 'Y1'
tier: 'Dynamic'
}
properties: {
reserved: true // Linux
}
}
resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
name: functionAppName
location: location
kind: 'functionapp,linux'
properties: {
serverFarmId: hostingPlan.id
siteConfig: {
linuxFxVersion: 'DOTNET-ISOLATED|8.0'
appSettings: [
{ name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
{ name: 'FUNCTIONS_WORKER_RUNTIME', value: 'dotnet-isolated' }
{ name: 'AzureWebJobsStorage__accountName', value: storageAccount.name }
{ name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING__accountName', value: storageAccount.name }
{ name: 'WEBSITE_CONTENTSHARE', value: functionAppName }
{ name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: appInsights.properties.ConnectionString }
{ name: 'APPLICATIONINSIGHTS_AUTHENTICATION_STRING', value: 'Authorization=AAD' }
]
}
}
identity: {
type: 'SystemAssigned'
}
}Flex Consumption plan (after):
// Flex Consumption plan (required)
resource hostingPlan 'Microsoft.Web/serverfarms@2023-12-01' = {
name: hostingPlanName
location: location
sku: {
name: 'FC1'
tier: 'FlexConsumption'
}
kind: 'functionapp'
properties: {
reserved: true
}
}
// Deployment storage container (required)
resource deploymentContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = {
name: '${storageAccount.name}/default/deployments'
}
resource functionApp 'Microsoft.Web/sites@2023-12-01' = {
name: functionAppName
location: location
kind: 'functionapp,linux'
properties: {
serverFarmId: hostingPlan.id
functionAppConfig: {
deployment: {
storage: {
type: 'blobContainer'
value: '${storageAccount.properties.primaryEndpoints.blob}deployments'
authentication: {
type: 'SystemAssignedIdentity'
}
}
}
scaleAndConcurrency: {
maximumInstanceCount: 100
instanceMemoryMB: 2048
}
runtime: {
name: 'dotnet-isolated'
version: '8.0'
}
}
siteConfig: {
appSettings: [
{ name: 'AzureWebJobsStorage__accountName', value: storageAccount.name }
{ name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: appInsights.properties.ConnectionString }
{ name: 'APPLICATIONINSIGHTS_AUTHENTICATION_STRING', value: 'Authorization=AAD' }
]
}
}
identity: {
type: 'SystemAssigned'
}
}Note
When using APPLICATIONINSIGHTS_AUTHENTICATION_STRING with Authorization=AAD, you must also assign the Monitoring Metrics Publisher role to the function app's managed identity on the Application Insights resource.
For complete Bicep examples, see the Flex Consumption Bicep samples.
Consumption plan (before):
resource "azurerm_service_plan" "consumption" {
name = var.hosting_plan_name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
os_type = "Linux"
sku_name = "Y1"
}
resource "azurerm_linux_function_app" "consumption" {
name = var.function_app_name
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
service_plan_id = azurerm_service_plan.consumption.id
storage_account_name = azurerm_storage_account.sa.name
storage_uses_managed_identity = true
identity {
type = "SystemAssigned"
}
site_config {
application_insights_connection_string = azurerm_application_insights.appInsights.connection_string
application_stack {
dotnet_version = "8.0"
use_dotnet_isolated_runtime = true
}
}
app_settings = {
"FUNCTIONS_WORKER_RUNTIME" = "dotnet-isolated"
"APPLICATIONINSIGHTS_AUTHENTICATION_STRING" = "Authorization=AAD"
}
}Flex Consumption plan (after):
resource "azurerm_service_plan" "flex" {
name = var.functionPlanName
resource_group_name = azurerm_resource_group.rg.name
location = var.location
sku_name = "FC1"
os_type = "Linux"
}
resource "azurerm_storage_container" "deploymentpackage" {
name = "deploymentpackage"
storage_account_id = azurerm_storage_account.sa.id
container_access_type = "private"
}
locals {
blobStorageAndContainer = "${azurerm_storage_account.sa.primary_blob_endpoint}deploymentpackage"
}
resource "azurerm_function_app_flex_consumption" "flex" {
name = var.functionAppName
resource_group_name = azurerm_resource_group.rg.name
location = var.location
service_plan_id = azurerm_service_plan.flex.id
storage_container_type = "blobContainer"
storage_container_endpoint = local.blobStorageAndContainer
storage_authentication_type = "SystemAssignedIdentity"
runtime_name = var.functionAppRuntime
runtime_version = var.functionAppRuntimeVersion
maximum_instance_count = var.maximumInstanceCount
instance_memory_in_mb = var.instanceMemoryMB
identity {
type = "SystemAssigned"
}
site_config {
application_insights_connection_string = azurerm_application_insights.appInsights.connection_string
}
app_settings = {
"AzureWebJobsStorage" = "" # Required: see note below
"AzureWebJobsStorage__accountName" = azurerm_storage_account.sa.name
"APPLICATIONINSIGHTS_AUTHENTICATION_STRING" = "Authorization=AAD"
}
}
resource "azurerm_role_assignment" "storage_roleassignment" {
scope = azurerm_storage_account.sa.id
role_definition_name = "Storage Blob Data Owner"
principal_id = azurerm_function_app_flex_consumption.flex.identity.0.principal_id
principal_type = "ServicePrincipal"
}Note
When using the azurerm provider with Flex Consumption, you must set AzureWebJobsStorage to an empty string ("") as a workaround until this fix is released. Use AzureWebJobsStorage__accountName with managed identity authentication for the actual storage connection.
For complete Terraform examples, see the Flex Consumption Terraform samples.
If you use Infrastructure as Code (IaC) to manage your Azure resources, you need to update your IaC files after migrating to Flex Consumption to prevent configuration drift. Here's a recommended approach:
-
Don't mix manual and IaC deployments: If you used the Azure CLI or portal to create your Flex Consumption app during migration, update your IaC files before the next deployment. Otherwise, your IaC might attempt to recreate the old Consumption plan resources.
-
Update resource names or use lifecycle management: Since you can't convert a Consumption app to Flex Consumption in place, you have two options:
- New resource names: Update your IaC to use new names for the hosting plan and function app. This approach keeps your old resources intact until you're confident the migration succeeded.
- Import existing resources: If you want to keep the same names, delete the old resources first, then let your IaC create the new Flex Consumption resources. Alternatively, import the manually created resources into your Terraform state using
terraform importor reference existing resources in Bicep.
-
Verify state alignment: After updating your IaC files, run a plan/preview operation (
terraform planoraz deployment group what-if) to confirm no unexpected changes will occur. -
Update CI/CD pipelines: If your deployment pipelines reference the old Consumption plan configuration, update them to use the new Flex Consumption resource definitions and deployment methods.
Tip
To minimize disruption, consider running both the old Consumption app and new Flex Consumption app in parallel during a transition period. Update your IaC to manage the new Flex Consumption app, verify it works correctly, then remove the old Consumption app resources from both Azure and your IaC files.
Tip
No rush here. We recommend keeping your original app around for a few days or weeks while you verify everything works. The Consumption plan only charges for actual usage, so keeping the old app (with triggers disabled) costs very little.
Once you're confident the new app is working correctly, you can clean up the original. This is entirely optional—some teams keep the old app around as a reference or rollback option.
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.
-
In the Azure portal, search for or otherwise navigate to the page for your new app.
-
Select Delete from the top menu.
-
Confirm the deletion by typing the app name and selecting Delete.
Most migrations complete without issues, but if something doesn't work as expected, here's how to fix common problems:
| 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.
Use these steps for cases where the new app fails to start or function triggers aren't processing events:
-
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.
-
In the app page, select Monitoring > Application Insights > View Application Insights data then select Investigate > Failures and check for any failure events.
-
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 descrequests | where success == false | where cloud_RoleName == "<APP_NAME>" | where timestamp > ago(1d) | project timestamp, name, resultCode, url, 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)). -
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.
-
Select Settings > Identity and double-check that the expected identities exist and that they have been assigned to the correct roles.
-
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.
If you aren't able to troubleshoot successfully, you might want to revert to using your original app while you continue to troubleshoot.
-
If the original app was stopped, restart it:
Use this
az functionapp startcommand to restart the original function app:az functionapp delete --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>In the Azure portal, search for or otherwise navigate to the page for your new app and select Start from the top menu.
-
If you created new queues/topics/containers, ensure clients are redirected back to the original resources.
-
If you modified DNS or custom domains, revert these changes to point to the original app.
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:
-Get help at Microsoft Q&A
-Create an issue in the Azure Functions repo
-Provide product feedback
-Create a support ticket