Skip to content

Azure App Service & Functions

Key Points

  • App Service: managed PaaS for web apps. Plans (Basic/Standard/Premium/Isolated). Slots for blue-green. Auto-scale; deployment slots; managed identity; integrated AAD auth.
  • Azure Functions: serverless. Two hosting models: Consumption (cheapest, cold starts) and Premium / App Service Plan (no cold start, more $).
  • Functions in .NET: Isolated worker model is now standard (Functions v4 isolated). Out-of-process; supports newest .NET (8/9/10).
  • Triggers: HTTP, Timer, Service Bus, Event Hub, Queue, Blob, Cosmos change feed, Durable. Bindings simplify input/output.
  • Cold start: 1-3s on Consumption. AOT-compiled isolated functions reduce dramatically.

Concepts (deep dive)

What App Service is

App Service is the original Azure PaaS for web apps: hand Azure a published .NET app (zip, container, or dotnet publish output), and Azure runs it on a managed VM with TLS, load balancing, autoscale, deployment slots, and identity already wired in. You don't see the VM; you see "the app." It's older than Container Apps and pre-dates the container-first cloud, which is exactly why it still wins for a lot of .NET workloads — dotnet publish → push → running, no Dockerfile required, no Kubernetes to learn.

   ┌──────────────────────────────────────────────────────────┐
   │                 App Service Plan (VM SKU)                │
   │   ┌──────────────┐   ┌──────────────┐   ┌────────────┐  │
   │   │  Web App A   │   │  Web App B   │   │ Func App C │  │
   │   │ (slots: prod │   │              │   │            │  │
   │   │  + staging)  │   │              │   │            │  │
   │   └──────────────┘   └──────────────┘   └────────────┘  │
   │       sharing VMs, autoscale, identity, certs           │
   └──────────────────────────────────────────────────────────┘

The plan is the billable thing (a set of VMs at a given SKU); the apps are the workloads that run on the plan. Multiple apps share one plan unless you size them apart for isolation.

What Functions is

Azure Functions is event-driven compute: write a method that runs in response to a trigger (HTTP, timer, queue message, blob upload, Cosmos change), let the platform handle the rest. The framework owns the process lifecycle, scaling, and the trigger plumbing; you own the method body. The killer feature is consumption-plan billing — pay per execution, idle = $0 — which makes Functions the right answer for sporadic and bursty workloads where keeping a web app warm 24/7 is wasteful.

   ┌──────────────────────────────────────────────────────────┐
   │                       Functions host                      │
   │   triggers ──► [your code]                                │
   │     ┌──HTTP──►   [Function: HandleApiRequest]             │
   │     ┌──Timer─►   [Function: NightlyRollup]                │
   │     ┌──SBus──►   [Function: ProcessOrder]                 │
   │     ┌──Blob──►   [Function: ResizeImage]                  │
   │     ┌──Cosmos►   [Function: ReactToChange]                │
   │   bindings <──► input/output (auto serialize)             │
   │   scaling: per-trigger, automatic                         │
   └──────────────────────────────────────────────────────────┘

In-process vs isolated worker (the .NET runtime story)

Functions has had two execution models over the years and the distinction trips up everyone:

  • In-process (legacy) ran your function code inside the Functions host process. That was convenient (no IPC, fast cold start) but it chained your app's .NET version to the host's .NET version — you could only use the .NET that the Functions host had been compiled against.
  • Isolated worker (current) runs your function code in a separate process that the host talks to over gRPC. You ship a full .NET console app; you pick any supported .NET version; you control DI, middleware, and Program.cs. AOT-compatible.

For new code in 2026: always isolated worker. In-process is in maintenance.

Cold start (where it comes from, when to care)

A function on the Consumption plan starts at $0/idle because the platform isn't keeping a worker warm for you. When the first request arrives, the platform has to (1) allocate a worker, (2) pull and unzip your code, (3) start the .NET process, (4) load the JIT'd assemblies, (5) run startup. That sequence is the cold start — typically 1–3 seconds for a standard .NET function, dropping to ~100ms with AOT publishing. Premium and App Service plans keep workers pre-warmed in exchange for non-zero idle cost. Pick by traffic shape: bursty/sporadic → Consumption + accept the cold start (or AOT it away); steady or latency-sensitive → Premium.

App Service plans

The plan tier controls the hardware (CPU, memory, # cores), feature availability (slots, VNet integration, custom domain SSL, AAD auth), and the per-VM cost. Premium v3 is the modern production default; Standard still appears in older estates; Isolated v2 is for App Service Environment (dedicated tenant, regulated workloads).

Plan Use
Free / Shared Demos
Basic Dev/test
Standard Production small-medium; staging slots
Premium v3 Most production; better hardware; vnet integration
Isolated v2 App Service Environment; isolation
az webapp create --plan myplan --name myapp --runtime "DOTNET|9.0"

Deployment slots

A slot is a parallel copy of the app inside the same plan, with its own URL and its own app settings (some slot-sticky, some not). The blue-green flow: deploy to staging, let it warm up (JIT runs, caches fill, health probes pass), then swap — the platform routes prod traffic to the warmed slot and the previous prod becomes staging. Zero downtime because the swap is a routing flip on already-warm instances; rollback is one more swap.

[production] ←→ [staging]   (swap)

Deploy to staging; warm up; swap with production. Zero downtime; instant rollback.

Managed identity

// .NET app on App Service:
var token = await new DefaultAzureCredential().GetTokenAsync(...);

System-assigned identity; auto-rotated; tied to app. RBAC on resources (Key Vault, Storage).

App Service authentication ("Easy Auth")

Client → App Service AuthN middleware → Backend
        (cookie/JWT validation; AAD/OIDC sign-in handled)

No code change for basic auth; injects headers like X-MS-CLIENT-PRINCIPAL-NAME.

Application settings + Connection strings

az webapp config appsettings set --name myapp --settings Foo=Bar

Become environment variables in your app. Use appsettings.json defaults; override per-env.

Auto-scale

CPU > 70% for 5 min → +1 instance (max 10)
CPU < 30% for 10 min → -1 instance (min 2)

Or schedule-based.

Functions: isolated worker

Concretely, an isolated-worker Functions project is a regular .NET console app (<OutputType>Exe) with a few worker NuGet packages and [Function("...")]-decorated methods. The Functions host on the platform side launches your process, talks to it over gRPC, and routes trigger events to your methods.

<!-- MyFunc.csproj -->
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<PackageReference Include="Microsoft.Azure.Functions.Worker" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" />
public class HttpFn(ILogger<HttpFn> log)
{
    [Function("Hello")]
    public HttpResponseData Run(
        [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequestData req)
    {
        var resp = req.CreateResponse(HttpStatusCode.OK);
        resp.WriteString("hello");
        return resp;
    }
}

Isolated worker = your function process; communicates with host via gRPC. Supports any .NET version.

In-process model (legacy)

Tied to the runtime version of the host. Limited to specific .NET versions. Use isolated for new functions.

Triggers and bindings

A trigger is what starts a function invocation (a message arrives, a timer fires, a request hits an HTTP endpoint). Bindings are typed input/output to other Azure resources — declare a [CosmosDBOutput] parameter, return a value into it, and the framework writes to Cosmos for you with no SDK code. The model is "configuration over code" for I/O at the function's boundary, leaving the method body focused on business logic.

[Function("OrderProcessor")]
public async Task Run(
    [ServiceBusTrigger("orders")] string msg,
    [CosmosDBOutput("db", "orders")] IAsyncCollector<Order> output)
{
    var order = JsonSerializer.Deserialize<Order>(msg);
    await output.AddAsync(order);
}

Bindings auto-handle input deserialize / output serialize.

Trigger Use
HttpTrigger API, webhooks
TimerTrigger Scheduled
ServiceBusTrigger Queue worker
EventHubTrigger High-throughput stream
BlobTrigger File arrival
CosmosDBTrigger Change feed
QueueTrigger Storage queue

Hosting plans

Plan Cold start Pricing
Consumption Yes (1-3s) Pay per execution + GB-s
Premium No Always-on; pre-warmed
App Service No Reuse App Service plan
Container Apps + Dapr No Container-based

Cold start mitigation

  • Premium plan: pre-warmed instances.
  • AOT functions: ~100ms cold start vs 1-3s.
  • Consumption: keep warm via timer trigger every 5 min (anti-pattern but works).

Durable Functions

Stateful workflows on Functions. See Workflow Engines.

Function app vs multiple functions

A function app is a deployment unit; can contain many functions. Share infra (config, deps, identity). Don't put unrelated functions in one app — coupling.

Functions OpenTelemetry

.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s => s.AddOpenTelemetry()
    .UseFunctionsWorkerDefaults()
    .WithTracing(t => t.AddOtlpExporter()));

Built-in OTel for isolated worker.

App Service vs Container Apps vs AKS

Need Pick
.NET app, no containers App Service
Containers + simple Container Apps
Complex K8s AKS
Functions / event-driven Functions

Cost notes

  • App Service Premium: ~$80-150/instance/mo.
  • Functions Consumption: pay per use; near-zero idle.
  • Functions Premium: always-on; ~$150/mo+.
  • Container Apps: per-vCPU-second; scale-to-zero.

Deployment

# App Service
dotnet publish -c Release
az webapp deploy --src-path bin/Release/publish.zip

# Or via azd / GitHub Actions / Azure DevOps

# Functions
func azure functionapp publish MyFunc

Slots for safe deploys

az webapp deployment slot create -n myapp --slot staging
# deploy to staging
az webapp deployment slot swap -n myapp --slot staging --target-slot production

Health-checked swap; rollback by swapping back.


Code: correct vs wrong

❌ Wrong: in-process Functions (legacy)

public static class Func1 { [FunctionName("...")] public static IActionResult Run(...) {} }

✅ Correct: isolated worker

public class Func1 { [Function("...")] public HttpResponseData Run(...) {} }

❌ Wrong: secrets in app settings

az webapp config appsettings set --settings ConnectionString="Server=...;Pwd=secret"

✅ Correct: managed identity + Key Vault reference

az webapp config appsettings set --settings ConnectionString="@Microsoft.KeyVault(SecretUri=https://kv/...)"

Design patterns for this topic

Pattern 1 — "Slots for blue-green"

  • Intent: zero-downtime deploys.

Pattern 2 — "Managed identity + Key Vault"

  • Intent: no secrets in config.

Pattern 3 — "Isolated worker Functions"

  • Intent: newest .NET; no version coupling.

Pattern 4 — "Premium plan to skip cold start"

  • Intent: consistent latency.

Pattern 5 — "AOT Functions for serverless"

  • Intent: fast cold start.

Pros & cons / trade-offs

Service Pros Cons
App Service Easiest .NET hosting Azure-only
Functions Pay-per-use Cold starts
Premium Functions No cold Cost
Container Apps Containers + scale More to manage

When to use / when to avoid

  • Use App Service for .NET web apps.
  • Use Functions for events / scheduled / sporadic.
  • Avoid in-process Functions for new code.
  • Avoid Functions for sustained high-RPS — App Service / Container Apps better.

Interview Q&A

Q1. App Service vs Functions? App Service: long-running web. Functions: short-lived event-driven.

Q2. Slots benefit? Blue-green deploys. Warm up staging; swap; rollback if bad.

Q3. Isolated vs in-process Functions? Isolated: separate worker process; any .NET version. In-process: tied to host runtime; legacy.

Q4. Cold start mitigation? Premium plan; AOT; or warmup hack.

Q5. Managed identity? App Service / Functions get an Azure AD identity. Used for Key Vault, Storage, etc.

Q6. Easy Auth? App Service authentication middleware. AAD, social logins without code.

Q7. App Service plans? Free/Basic/Standard/Premium v3/Isolated. Premium v3 most production.

Q8. Why Functions Premium? Always-on; no cold start; vnet integration; longer runtime.

Q9. Durable Functions? Stateful orchestrations on Functions. Long-running workflows.

Q10. Functions OpenTelemetry? Built-in for isolated worker.

Q11. Container Apps vs App Service vs Functions? Container Apps: containers + Dapr + KEDA scale. App Service: managed for web apps. Functions: serverless event-driven.

Q12. Cost optimization? Reserved instances; consumption Functions for sporadic; right-size plans.


Gotchas / common mistakes

  • ⚠️ Secrets in app settings — use Key Vault references.
  • ⚠️ In-process Functions locked to runtime version.
  • ⚠️ No staging slot — risky direct deploys.
  • ⚠️ No managed identity — secret rot.
  • ⚠️ Cold start ignored in latency-sensitive apps.

Further reading