App Service and Azure Functions

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

See the section bibliography for the reference material consulted while preparing these pages.

Examples on this page target Azure CLI 2.90.0 (checked 2026-09-26); re-verify current tier names, retirement dates and flags against az webapp --help, az functionapp --help, and the linked reference pages before relying on them, since App Service and Functions pricing tiers and hosting-plan names change more often than most CLI surfaces.

App Service and Azure Functions are two of the fully managed compute options covered in Choosing a Compute Service, which compares them against Container Apps and AKS; this page covers what is specific to running Web Apps and Function Apps once that choice has been made.

App Service plans and tiers

An App Service plan is the set of compute resources (VM size, region, OS) that one or more Web Apps run on; the plan’s tier determines its features and scaling limits, while its SKU (size) determines its VM size within that tier. The tiers a new reader is most likely to choose among:

Tier What it is for

Free / Shared

Multi-tenant compute shared with other customers' apps, no custom domain TLS, intended only for the smallest throwaway prototypes.

Basic

Dedicated compute, no autoscale, no deployment slots — suitable for dev/test workloads that still need a custom domain and TLS.

Standard

Adds autoscale and up to five deployment slots — the usual floor for a production workload.

Premium v3

More memory per core and more slots/instances than Standard, on newer VM hardware, at a comparable per-core price to Standard.

Premium v4

The newest Premium tier: faster processors, NVMe-backed local storage, and memory-optimized SKUs (P0V4 through P3V4, plus the memory-optimized P1mV4-P5mV4 sizes) with up to double the memory-to-core ratio of Premium v3, letting a workload often run on fewer/smaller instances for a lower total cost. Premium v4 supports both source-code and container-based Linux apps, and source-code Windows apps; regional availability is still expanding, so confirm the target region supports it before committing a production plan to it.

Isolated v2

Runs inside an App Service Environment (ASE) — a single-tenant deployment into a dedicated virtual network, for workloads that need network isolation beyond what Standard/Premium’s shared front-end provides.

In the Azure portal

  1. From the portal’s search bar, type App Service plans and select Create.

  2. Choose the resource group (rg-bookshelf), a name, the operating system (Linux or Windows), and the region.

  3. On the Pricing plan step, select Change size to browse tiers grouped as Dev / Test (Free, Shared, Basic) and Production (Standard, Premium v3, Premium v4, Isolated v2), then pick the SKU.

  4. Review and select Create.

With the Azure CLI

export RG=rg-bookshelf
export LOCATION=eastus2

az group create --name $RG --location $LOCATION

# Standard tier, Linux, for a small production workload
az appservice plan create \
  --name asp-bookshelf-admin \
  --resource-group $RG \
  --location $LOCATION \
  --sku S1 \
  --is-linux

# Premium v4, memory-optimized SKU, where the region supports it
az appservice plan create \
  --name asp-bookshelf-admin-p1mv4 \
  --resource-group $RG \
  --location $LOCATION \
  --sku P1mV4 \
  --is-linux

Web Apps: from code or from a container

A Web App is the application resource that runs on an App Service plan. It can run from source code that the platform builds for a supported language runtime, or from a container image — either way, az webapp up and az webapp create are the two entry points, aimed respectively at a quick source-code deploy and an explicit, scriptable create.

az webapp up inspects the current directory, infers a runtime stack (or an already-built container context), and creates whatever is missing (resource group, App Service plan, Web App) in one command — convenient for a first deploy or a demo, less suitable for a repeatable pipeline because it makes naming and sizing decisions on the reader’s behalf:

# Run from the application's source directory; infers runtime, plan and app name
az webapp up \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --location $LOCATION \
  --sku S1 \
  --runtime "JAVA:17-java17"

az webapp create is the explicit form, used once the plan already exists (as created above) and the image or runtime is known ahead of time. For a container image, --container-image-name names the image (optionally with a registry prefix and tag); the older --deployment-container-image-name and --docker-custom-image-name flags are deprecated in favor of it:

export ACR=acrbookshelf

# From a container image already pushed to acrbookshelf (see
# xref:cloud/azure/container-registry.adoc[])
az webapp create \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --plan asp-bookshelf-admin \
  --container-image-name "$ACR.azurecr.io/bookshelf-admin:1.0.0"

Pulling a private image from acrbookshelf needs credentials; the recommended path is a managed identity with AcrPull rather than the registry’s admin username/password, matching the identity-based pattern covered in Identity and Access and Azure Container Registry:

az webapp identity assign --name bookshelf-admin-web --resource-group $RG

az role assignment create \
  --assignee-object-id "$(az webapp identity show --name bookshelf-admin-web --resource-group $RG --query principalId -o tsv)" \
  --assignee-principal-type ServicePrincipal \
  --role "AcrPull" \
  --scope "$(az acr show --name $ACR --query id -o tsv)"

az webapp config set \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --generic-configurations '{"acrUseManagedIdentityCreds": true}'

In the Azure portal

  1. Open the App Service blade and select Create → Web App.

  2. On Basics, choose Code or Container as the publish model. Code asks for a runtime stack (Java, .NET, Node.js, Python, …​); Container asks for the image source (Azure Container Registry, Docker Hub, or another registry) on the Container step.

  3. For a container from acrbookshelf, choose Azure Container Registry, select the registry, image and tag, and set Authentication to Managed identity rather than Admin credentials.

  4. Review and select Create.

Deployment slots and swap

A deployment slot is a separate, fully live instance of a Web App — with its own hostname — running under the same App Service plan. The usual pattern is a staging slot that receives a new deployment and warms up while production keeps serving traffic, followed by a swap that exchanges the two slots' routing (and, depending on configuration, their app settings) with no cold start on the side that becomes production. Standard tier and above support up to five slots (Premium adds more); Basic and below support none.

az webapp deployment slot create \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --slot staging

# Deploy the new version to staging only (not shown: az webapp deploy / CI pipeline step)

# Validate the swap without completing it -- applies production's settings to staging first
az webapp deployment slot swap \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --slot staging \
  --target-slot production \
  --action preview

# Complete the swap
az webapp deployment slot swap \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --slot staging \
  --target-slot production

Autoscale

Autoscale (in Standard tier and above) adds or removes instances of a plan based on metric rules (CPU percentage, memory, HTTP queue length) or a schedule, within a configured minimum/maximum instance count. It is the App Service equivalent of the scaling rules covered for Container Apps in Container Apps: Scaling, Jobs and Dapr, applied at the plan level rather than per container revision.

In the Azure portal

  1. Open the App Service plan (not the Web App itself) and select Scale out (App Service plan).

  2. Choose Custom autoscale, set a default instance count, and add a rule (e.g. scale out when average CPU percentage is above 70% for 10 minutes, scale in below 25%).

  3. Set the Instance limits minimum and maximum, then Save.

With the Azure CLI

az monitor autoscale create \
  --resource-group $RG \
  --resource asp-bookshelf-admin \
  --resource-type Microsoft.Web/serverfarms \
  --name autoscale-bookshelf-admin \
  --min-count 1 \
  --max-count 5 \
  --count 1

az monitor autoscale rule create \
  --resource-group $RG \
  --autoscale-name autoscale-bookshelf-admin \
  --condition "CpuPercentage > 70 avg 10m" \
  --scale out 1

Sidecars on Linux

A Linux Web App can run one or more sidecar containers alongside the main application container — for a log shipper, a service-mesh proxy, or a local cache — sharing the same network namespace so the sidecar reaches the main container over localhost. This is the same shape of problem Container Apps solves natively (see Container Apps); App Service’s sidecar support exists for workloads already committed to App Service that need one auxiliary process without moving the whole application to Container Apps.

In the Azure portal

  1. Open the Web App (Linux, container-based) and select Deployment → Containers.

  2. Select Add to add a sidecar: give it a name, image source, and mark it as not the main container (only one container in the set is the main one, reachable from the app’s public hostname).

With the Azure CLI

Sidecars are managed as individual site containers with az webapp sitecontainers: one container is marked as the main container (--is-main true) and every other one is a sidecar (--is-main false) on its own port. This replaces the older Docker Compose multi-container mode (--multicontainer-config-type compose), which App Service is retiring on March 31, 2027 in favour of sidecars. bookshelf-admin-web was created above as a classic single custom container, so it is first converted to site containers — its existing image becomes the main container — and the sidecar is then added next to it (the same convert command also migrates an existing Compose app).

# Switch the app from a classic custom container to site containers
az webapp sitecontainers convert \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --mode sitecontainers

# An OpenTelemetry Collector sidecar, reachable from the main container on localhost:4317
az webapp sitecontainers create \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --container-name otel-collector \
  --image otel/opentelemetry-collector-contrib:latest \
  --target-port 4317 \
  --is-main false

az webapp sitecontainers list --name bookshelf-admin-web --resource-group $RG --output table

Authentication and authorization (Easy Auth)

App Service’s built-in authentication and authorization feature — commonly called Easy Auth — lets a Web App require sign-in and validate tokens at the platform level, before a request reaches the application code, using Microsoft Entra ID or another OpenID Connect/OAuth2 identity provider. In the current portal this is the Authentication blade (an older configuration surface remains reachable as Authentication (Classic) for apps that already used it); this page introduces it only as it applies to App Service and Functions, since Identity and Access already covers Microsoft Entra ID, app registrations and service principals in depth.

In the Azure portal

  1. Open the Web App (or Function App) and select Authentication from the left navigation.

  2. Select Add identity provider, choose Microsoft (or another supported provider), and either create a new app registration or pick an existing one.

  3. Choose whether unauthenticated requests are rejected (HTTP 401) or allowed through to the application (so the application itself decides), then Add.

With the Azure CLI

az webapp auth microsoft update \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --client-id "<app-registration-client-id>" \
  --client-secret "<client-secret-placeholder>" \
  --issuer "https://login.microsoftonline.com/<tenant-id>/v2.0"

az webapp auth update \
  --name bookshelf-admin-web \
  --resource-group $RG \
  --action LoginWithAzureActiveDirectory \
  --enabled true

Static Web Apps, briefly

Azure Static Web Apps hosts a static front end (built by a CI workflow it generates, typically GitHub Actions or Azure Pipelines) together with an optional serverless API backed by Azure Functions, fronted by a global CDN with free managed TLS certificates. Two tiers exist: Free, for personal projects and evaluation with no SLA, and Standard, billed per app with an SLA and higher bandwidth/custom-domain limits, intended for production workloads. This page does not develop Static Web Apps further — it is a distinct product from App Service Web Apps, aimed specifically at static/JAMstack front ends rather than general server-side applications.

In the Azure portal

  1. From the search bar, type Static Web Apps and select Create.

  2. Choose the resource group, a name, and the Free or Standard plan.

  3. Under Deployment details, connect a GitHub (or Azure DevOps) repository and branch; the wizard commits a build workflow to that repository on creation.

With the Azure CLI

az staticwebapp create \
  --name swa-bookshelf-storefront \
  --resource-group $RG \
  --location $LOCATION \
  --sku Free \
  --source "https://github.com/<org>/bookshelf-storefront" \
  --branch main \
  --app-location "/" \
  --output-location "dist"

Azure Functions: triggers and bindings

An Azure Functions app is a set of individually invoked functions, each started by exactly one trigger (HTTP request, a message landing on a Service Bus queue/topic, a timer schedule, a new blob, a Cosmos DB change feed entry, and others) and optionally reading or writing data through input and output bindings (a Cosmos DB document, a Storage queue message, a SignalR message) declared rather than hand-written against each service’s SDK. Bindings are what let a function connect to Service Bus, Cosmos DB or Storage with a few lines of declarative configuration instead of the client-construction code shown for those SDKs elsewhere in this section.

Hosting plans: Flex Consumption, Premium, and Dedicated

Three hosting plans back a Function App, trading cold-start behavior and always-on cost against scaling control:

Plan Scale-to-zero VNet integration Instance sizing

Flex Consumption

Yes, with a configurable always-ready instance count to avoid cold starts on a base load

Yes

Fixed per-plan memory size chosen at creation (e.g. 2048 MB), billed per execution plus always-ready instances

Premium

No (a minimum instance count always runs)

Yes

Selectable SKU (EP1/EP2/EP3), billed per instance-hour regardless of executions

Dedicated (App Service plan)

No

Yes

Same App Service plan tiers as Web Apps above — shares capacity with other Web Apps on the same plan

Flex Consumption is the current, generally available recommendation for new event-driven Function Apps that want scale-to-zero economics without the classic Consumption plan’s cold-start and concurrency limitations; the classic (non-Flex) Consumption plan on Linux is retiring on 30 September 2028 and Microsoft’s guidance is to create new apps on Flex Consumption rather than classic Consumption from this point on. Premium remains the right choice when every invocation must run inside a virtual network with no cold start at all and a steady baseline load justifies always-on billing; Dedicated fits a Function App that must share an existing App Service plan’s already-provisioned capacity with a Web App.

In the Azure portal

  1. From the search bar, type Function App and select Create.

  2. On Basics, choose the Hosting option: Flex Consumption, Functions Premium, or select an existing App Service plan for Dedicated (App Service Plan).

  3. For Flex Consumption, choose the Instance memory size and, on the Scale tab, set the Always ready instances count and Maximum instance count.

  4. Complete the remaining steps (storage account, runtime stack and version, region) and select Create.

With the Azure CLI

export STORAGE=stbookshelfadmin

az storage account create \
  --name $STORAGE \
  --resource-group $RG \
  --location $LOCATION \
  --sku Standard_LRS

# List the regions where Flex Consumption is currently available
az functionapp list-flexconsumption-locations --output table

# Flex Consumption, isolated worker model, Java
az functionapp create \
  --name func-bookshelf-admin \
  --resource-group $RG \
  --storage-account $STORAGE \
  --flexconsumption-location $LOCATION \
  --runtime java \
  --runtime-version 17

# Premium plan alternative, for a workload that needs no cold start
az functionapp plan create \
  --name plan-bookshelf-admin-premium \
  --resource-group $RG \
  --location $LOCATION \
  --sku EP1 \
  --is-linux

az functionapp create \
  --name func-bookshelf-admin-premium \
  --resource-group $RG \
  --storage-account $STORAGE \
  --plan plan-bookshelf-admin-premium \
  --runtime java \
  --runtime-version 17 \
  --functions-version 4

The isolated worker model

Azure Functions for .NET has historically offered two hosting models for function code: the in-process model (function code runs inside the same process as the Functions host) and the isolated worker model (function code runs in its own worker process, communicating with the host over gRPC). Support for the in-process model ends on 10 November 2026 — apps on it keep running afterward but stop receiving security and feature updates — so any assumption that in-process is the default or the simpler option no longer holds: the isolated worker model is Microsoft’s current recommendation for every new .NET Function App, and existing in-process apps should migrate before that date. Non-.NET language workers (Java, Node.js, Python, PowerShell) are unaffected by this distinction; the isolated worker model is specific to .NET’s two hosting modes.

The isolated worker model’s practical differences: full control over the dependency-injection container and middleware pipeline (rather than reusing the host’s own), support for .NET versions the in-process model cannot run (including .NET versions released after the .NET the Functions host itself targets), and a small amount of manual model binding that the in-process model previously handled implicitly through the host’s own types.

Durable Functions patterns (isolated worker model)

Durable Functions is an extension that lets an orchestrator function coordinate multiple other functions as a stateful workflow, checkpointing progress so the orchestration survives process restarts and scale events without the application managing that state itself. In the isolated worker model, an orchestrator takes a TaskOrchestrationContext parameter (from the Microsoft.DurableTask package) and calls activities through it (context.CallActivityAsync<T>(…​)) instead of the in-process model’s IDurableOrchestrationContext; the orchestrator function itself must remain deterministic (no direct I/O, no DateTime.Now, no unmanaged randomness) since the platform may replay it from its checkpointed history.

The recurring patterns, all expressed the same way in the isolated worker model:

Function chaining

Activities run in a fixed sequence, each one’s output feeding the next — a validate-then- charge-then-notify pipeline is a straightforward await chain inside the orchestrator.

Fan-out/fan-in

The orchestrator starts many activity calls in parallel and waits for all of them, then aggregates the results — for example, re-generating thumbnails for every cover image in a Bookshelf catalog import batch.

Async HTTP APIs

A client starts an orchestration through an HTTP-triggered starter function, which immediately returns a status-polling URL (and, in the isolated worker model, this is generated through DurableTaskClient.CreateCheckStatusResponse) instead of blocking the caller while the orchestration runs to completion.

Monitor

A long-running orchestrator polls an external condition on a timer (context.CreateTimer) and acts once it changes — for example, watching a batch import job’s external status until it reports done.

Human interaction

An orchestrator waits on an external event (context.WaitForExternalEvent) raised later by an API call, such as a manager’s approval before Bookshelf’s admin function finalizes a bulk price change, combined with a timer so the orchestration times out instead of waiting forever.

flowchart LR Start["HTTP trigger: start import"] --> O["Orchestrator function"] O -->|fan-out| A1["Activity: resize cover 1"] O -->|fan-out| A2["Activity: resize cover 2"] O -->|fan-out| A3["Activity: resize cover N"] A1 -->|fan-in| J["Orchestrator: aggregate results"] A2 -->|fan-in| J A3 -->|fan-in| J J --> Done["Activity: mark import complete"]

Identity-based connections

Both App Service and Functions can connect to Storage, Service Bus, Event Hubs, and several other bindings using a managed identity instead of a connection string, matching the credential-chain pattern already covered in Identity and Access. For Functions specifically, the trigger/binding configuration references an identity-based connection by a settings prefix instead of embedding a secret:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage__blobServiceUri": "https://stbookshelfadmin.blob.core.windows.net",
    "AzureWebJobsStorage__queueServiceUri": "https://stbookshelfadmin.queue.core.windows.net",
    "AzureWebJobsStorage__credential": "managedidentity",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
  }
}
az functionapp identity assign --name func-bookshelf-admin --resource-group $RG

az role assignment create \
  --assignee-object-id "$(az functionapp identity show --name func-bookshelf-admin --resource-group $RG --query principalId -o tsv)" \
  --assignee-principal-type ServicePrincipal \
  --role "Storage Blob Data Owner" \
  --scope "$(az storage account show --name $STORAGE --resource-group $RG --query id -o tsv)"

Not every trigger/binding type supports identity-based connections yet (a handful of third-party or legacy bindings still require a connection string), so check the specific binding’s reference page before assuming the pattern above applies unmodified.

Functions Core Tools: local development

Azure Functions Core Tools (func) runs the Functions host locally for development and testing, and drives deployment from a developer machine or CI runner:

# macOS
brew tap azure/functions
brew install azure-functions-core-tools@4

# Any platform with Node.js installed
npm install -g azure-functions-core-tools@4 --unsafe-perm true

# Scaffold a new isolated-worker Java function app locally
func init bookshelf-admin-func --worker-runtime java
cd bookshelf-admin-func
func new --template "HTTP trigger" --name AdminPing

# Run the host locally (uses local.settings.json, never committed)
func start

# Deploy the current directory's compiled output to an existing Function App
func azure functionapp publish func-bookshelf-admin

local.settings.json holds local-only configuration (connection strings, AzureWebJobsStorage for local development) and must never be committed — it is excluded by the scaffolded .gitignore by default.

Functions on Container Apps

A Function App can also run as a container hosted directly on Azure Container Apps (via the Microsoft.App resource provider, setting kind=functionapp on the Container App) rather than on one of the three Functions- specific plans above — the same triggers and bindings apply, but scaling, networking and observability follow Container Apps' own model (KEDA-driven scaling rules, Dapr, revisions) instead of the Functions host’s built-in scale controller. This is the right choice when a function genuinely needs to run alongside other Container Apps workloads in the same environment (sharing a virtual network, Dapr components, or a scaling rule vocabulary already used elsewhere) — Container Apps and Container Apps: Scaling, Jobs and Dapr cover that platform’s own concepts in depth, and Deploying Microservices on AKS covers the further step of running comparable event-driven workloads directly on AKS.

az containerapp create \
  --name ca-bookshelf-admin-func \
  --resource-group $RG \
  --environment cae-bookshelf \
  --image "$ACR.azurecr.io/bookshelf-admin-func:1.0.0" \
  --kind functionapp \
  --min-replicas 0 \
  --max-replicas 5

Beyond Azure Functions and App Service

Quarkus applications reach some of the same serverless/event-driven ground covered here (functions, cold-start optimization, deploying to a FaaS platform) from the JVM side; see Serverless and Cloud Functions. ASP.NET Core applications deployed as Web Apps rather than Functions follow the deployment concerns (publish profiles, containers, CI/CD) covered in Deployment, which this page does not repeat.

Clean up

az group delete --name "$RG" --yes --no-wait