Skip to content
Get Started for Free

Function Apps

Azure Function Apps are serverless compute resources that host and execute Azure Functions in response to events. They support multiple runtime environments including Python, Node.js, and .NET, and integrate with a wide range of trigger sources such as HTTP requests, queues, and timers. Function Apps are commonly used to build event-driven microservices, scheduled background jobs, and lightweight API backends. For more information, see Introduction to Azure Functions.

LocalStack for Azure provides a local environment for building and testing applications that make use of Azure Function Apps. The supported APIs are available on our API Coverage section, which provides information on the extent of Function Apps’ integration with LocalStack.

This guide walks you through creating a Function App on an App Service plan, backed by a Storage Account, deploying a Python HTTP-trigger function, invoking it, and managing its keys.

Launch LocalStack using your preferred method. For more information, see Introduction to LocalStack for Azure. Once the container is running, enable Azure CLI interception by running:

Terminal window
lstk az start-interception

This command points the az CLI away from the public Azure management REST API and toward the LocalStack for Azure emulator API. To revert this configuration, run:

Terminal window
lstk az stop-interception

This reconfigures the az CLI to send commands to the official Azure management REST API.

Create a resource group to hold all resources created in this guide:

Terminal window
az group create --name rg-func-demo --location westeurope
Output
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-func-demo",
"location": "westeurope",
"name": "rg-func-demo",
"properties": { "provisioningState": "Succeeded" },
"type": "Microsoft.Resources/resourceGroups"
}

Function Apps require a Storage Account for internal bookkeeping.

Terminal window
az storage account create \
--name safuncdoc89 \
--resource-group rg-func-demo \
--location westeurope \
--sku Standard_LRS
Output
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-func-demo/providers/Microsoft.Storage/storageAccounts/safuncdoc89",
"kind": "StorageV2",
"location": "westeurope",
"name": "safuncdoc89",
"provisioningState": "Succeeded",
"resourceGroup": "rg-func-demo",
"sku": { "name": "Standard_LRS", "tier": "Standard" },
"type": "Microsoft.Storage/storageAccounts",
...
}

Create a Linux App Service plan to host the function app:

Terminal window
az appservice plan create \
--name asp-func-doc89 \
--resource-group rg-func-demo \
--location westeurope \
--sku B1 \
--is-linux
Output
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-func-demo/providers/Microsoft.Web/serverfarms/asp-func-doc89",
"kind": "linux",
"name": "asp-func-doc89",
"provisioningState": "Succeeded",
"reserved": true,
"sku": {
"capacity": 1,
"family": "B",
"name": "B1",
"size": "B1",
"tier": "Basic"
},
...
}

Create a function app on the plan, backed by the storage account:

Terminal window
az functionapp create \
--name ls-func-doc89 \
--resource-group rg-func-demo \
--plan asp-func-doc89 \
--runtime python \
--runtime-version 3.13 \
--functions-version 4 \
--os-type linux \
--storage-account safuncdoc89
Output
{
"defaultHostName": "ls-func-doc89.azurewebsites.azure.localhost.localstack.cloud:4566",
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-func-demo/providers/Microsoft.Web/sites/ls-func-doc89",
"kind": "functionapp,linux",
"name": "ls-func-doc89",
"resourceGroup": "rg-func-demo",
"state": "Running",
"type": "Microsoft.Web/sites",
...
}

Add environment variables as application settings on the function app:

Terminal window
az functionapp config appsettings set \
--name ls-func-doc89 \
--resource-group rg-func-demo \
--settings \
FUNCTIONS_WORKER_RUNTIME=python \
MY_CUSTOM_SETTING=hello-world

The command hides the values of the settings:

Output
[
...
{ "name": "FUNCTIONS_WORKER_RUNTIME", "slotSetting": false, "value": null },
{ "name": "FUNCTIONS_EXTENSION_VERSION", "slotSetting": false, "value": null },
{ "name": "AzureWebJobsStorage", "slotSetting": false, "value": null },
...
{ "name": "MY_CUSTOM_SETTING", "slotSetting": false, "value": null }
]

List all application settings currently configured on the function app:

Terminal window
az functionapp config appsettings list \
--name ls-func-doc89 \
--resource-group rg-func-demo
Output
[
...
{ "name": "FUNCTIONS_WORKER_RUNTIME", "slotSetting": false, "value": "python" },
{ "name": "FUNCTIONS_EXTENSION_VERSION", "slotSetting": false, "value": "~4" },
{ "name": "AzureWebJobsStorage", "slotSetting": false, "value": "DefaultEndpointsProtocol=https;..." },
...
{ "name": "MY_CUSTOM_SETTING", "slotSetting": false, "value": "hello-world" }
]

Create a directory for the function source and add the required files:

Terminal window
mkdir my_func && cd my_func

Create function_app.py with an HTTP-triggered function:

function_app.py
import azure.functions as func
app = func.FunctionApp()
@app.function_name(name="HelloWorld")
@app.route(route="public", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
def public(req: func.HttpRequest) -> func.HttpResponse:
name = req.params.get("name", "World")
return func.HttpResponse(f"Hello, {name}!")

Create host.json:

host.json
{
"version": "2.0"
}

Create requirements.txt:

requirements.txt
azure-functions

Package everything into a zip archive and return to the parent directory:

Terminal window
zip my_func.zip function_app.py host.json requirements.txt
cd ..

Deploy the zip package to the function app. With --async true, the command uploads the package and then polls the deployment until it completes:

Terminal window
az functionapp deploy \
--resource-group rg-func-demo \
--name ls-func-doc89 \
--src-path ./my_func/my_func.zip \
--type zip \
--async true
Output
{
"active": true,
"complete": true,
"deployer": "OneDeploy",
"message": "OneDeploy",
"site_name": "ls-func-doc89",
"status": 4,
...
}

Read the function app’s host name and invoke the function:

Terminal window
export FUNCTION_APP_HOST=$(az functionapp show --name ls-func-doc89 --resource-group rg-func-demo --query defaultHostName --output tsv)
curl "http://$FUNCTION_APP_HOST/api/public?name=LocalStack"
Output
Hello, LocalStack!

On Azure, the function host can take a minute to start after a deployment, and requests answer with an empty body until it does.

List the functions the app defines:

Terminal window
az functionapp function list \
--name ls-func-doc89 \
--resource-group rg-func-demo
Output
[
{
"config": {
"bindings": [
{
"authLevel": "ANONYMOUS",
"direction": "IN",
"methods": ["GET"],
"name": "req",
"route": "public",
"type": "httpTrigger"
},
...
],
...
},
"invokeUrlTemplate": "https://ls-func-doc89.azurewebsites.azure.localhost.localstack.cloud:4566/api/public",
"language": "python",
"name": "ls-func-doc89/HelloWorld",
"type": "Microsoft.Web/sites/functions",
...
}
]

List the app’s host keys. The master key and the default function key are created on first use:

Terminal window
az functionapp keys list \
--name ls-func-doc89 \
--resource-group rg-func-demo
Output
{
"functionKeys": {
"default": "..."
},
"masterKey": "...",
"systemKeys": {}
}

List the keys of the HelloWorld function:

Terminal window
az functionapp function keys list \
--name ls-func-doc89 \
--resource-group rg-func-demo \
--function-name HelloWorld
Output
{
"default": "..."
}

Add a host key named ci, with a generated value:

Terminal window
az functionapp keys set \
--name ls-func-doc89 \
--resource-group rg-func-demo \
--key-type functionKeys \
--key-name ci
Output
{
"name": "ci",
"value": null
}

Delete it again:

Terminal window
az functionapp keys delete \
--name ls-func-doc89 \
--resource-group rg-func-demo \
--key-type functionKeys \
--key-name ci

Retrieve the full configuration of the function app:

Terminal window
az functionapp show --name ls-func-doc89 --resource-group rg-func-demo
Output
{
"defaultHostName": "ls-func-doc89.azurewebsites.azure.localhost.localstack.cloud:4566",
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-func-demo/providers/Microsoft.Web/sites/ls-func-doc89",
"kind": "functionapp,linux",
"name": "ls-func-doc89",
"resourceGroup": "rg-func-demo",
"state": "Running",
"type": "Microsoft.Web/sites",
...
}

Delete the function app:

Terminal window
az functionapp delete --name ls-func-doc89 --resource-group rg-func-demo

Then list the function apps in the resource group to confirm that none remains. The plan and the storage account stay until you delete the resource group:

Terminal window
az functionapp list --resource-group rg-func-demo
Output
[]
  • Full lifecycle: Create, read, update, and delete function apps with the Azure CLI, Bicep, Terraform, or the ARM API. A repeated PUT, such as a second terraform apply or a Bicep redeployment, updates the app in place and keeps its deployment.
  • Function host in a container: Each function app runs the Azure Functions host from Azure Functions Core Tools 4.15.1 (4.13.0 on arm64 hosts) in a Docker container of its own, started on deployment.
  • Python, Node.js, and .NET: Function apps on these runtimes are built and run.
  • Triggers: The Functions host runs the app’s triggers itself, so HTTP, timer, Blob Storage, Queue Storage, Service Bus, and Event Hubs triggers fire against the emulator’s services, as the samples show.
  • Identity-based connections: A trigger or binding can connect with the app’s managed identity (<CONNECTION>__credential, __clientId, __fullyQualifiedNamespace, __blobServiceUri), as the managed identity and Service Bus samples do.
  • Deployment: Code deploys through Kudu’s zip deploy (az functionapp deployment source config-zip), OneDeploy (az functionapp deploy), and Azure Functions Core Tools (func azure functionapp publish).
  • Functions and keys: Functions can be listed and shown, and triggers re-synced. Host keys, function keys, and the master key are created on first use and can be listed, set, and deleted.
  • App settings and connection strings: Both reach the function host as environment variables, and changing one restarts the host, as on Azure.
  • Stop, start, and restart: az functionapp stop, start, and restart change the app’s state and its container.
  • Deployment slots: A Consumption app can have one staging slot and an Elastic Premium app two, each with its own container. A swap exchanges the content and every setting that is not sticky.
  • Consumption plan auto-provisioning: az functionapp create --consumption-plan-location creates the region’s Linux Consumption plan, as on Azure.
  • Publishing: Publishing credentials and the publish profile are available, and the profile names the emulator’s hosts.
  • Log streaming: az webapp log tail streams the function host’s log.
  • Docker required for execution: Without Docker, a function app can be created and managed, but its code does not run.
  • Builds use an amd64 image: The function host runs natively on arm64, but the Oryx build image that builds each deployment is published for amd64 only (microsoft/Oryx#2133 asks for arm64). On an arm64 host without amd64 emulation, the build fails at once: a Python function app then gets its requirements.txt from a pip install in the multi-arch python image, a .NET function app starts from the published output in its package, and a Node.js function app starts with only the node_modules its package contains. On a Mac with Apple silicon, Docker Desktop runs the build under emulation, and under QEMU the build can crash (.NET does not support QEMU, and Oryx is a .NET app). In Docker Desktop’s general settings, select the Apple Virtualization framework as the Virtual Machine Manager, because Docker VMM has no Rosetta, and then turn on “Use Rosetta for x86_64/amd64 emulation on Apple Silicon”.
  • Durable Functions not supported: Stateful orchestration via Durable Functions is not emulated.
  • Event Grid and Cosmos DB triggers do not fire: Event Grid accepts an AzureFunction destination but drops its events, and the Cosmos DB emulator has no change feed. See the Event Grid and Cosmos DB limitations.
  • Keys are not enforced: An HTTP trigger with the function or admin authorization level answers without a key, because the host runs as Core Tools’ func start, which does not authorize HTTP triggers.
  • Consumption and Elastic Premium plans report the Basic tier: The Azure CLI and Azure Functions Core Tools therefore publish through Kudu’s zip deploy, which the emulator serves, instead of using run-from-package. The plan’s true tier is stored and sets the slot limits.
  • Run-from-package is not supported: WEBSITE_RUN_FROM_PACKAGE has no effect. The package is extracted and built.
  • Java and PowerShell function apps do not run: The app is created, and a deployment to it is refused with a 400 error that names the runtime.
  • Windows function apps run on Linux: A function app on a Windows plan is created and stored, and its code runs in the Linux runtime.
  • One instance per app: The emulator does not scale an app out.
  • OneDeploy on Linux Consumption: The emulator accepts az functionapp deploy for a Linux Consumption app, which Azure does not offer on that plan.
  • Swap options: Traffic routing, swap with preview, and auto swap are not implemented.

The App Service configuration applies to function apps. The following variables are specific to them:

Variable Default Type Description
LS_AZURE_FUNCTIONS_CORE_TOOLS_VERSION "4.15.1" ("4.13.0" on arm64) String Release of Azure Functions Core Tools that function app containers run.
LS_AZURE_FUNCTION_HOST_STARTUP_TIMEOUT "360" Integer (seconds) Time a deployment waits for the function host to report Running. The first start downloads the Functions extension bundle; later starts reuse a cached copy.

The following samples demonstrate how to use Azure Function Apps with LocalStack for Azure:

164 of 613 operations implemented

Operation ▲Implemented ▼
Page 1 of 0
Was this page helpful?