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 |
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.
Deploy to staging; warm up; swap with production. Zero downtime; instant rollback.
Managed identity
System-assigned identity; auto-rotated; tied to app. RBAC on resources (Key Vault, Storage).
App Service authentication ("Easy Auth")
No code change for basic auth; injects headers like X-MS-CLIENT-PRINCIPAL-NAME.
Application settings + Connection strings
Become environment variables in your app. Use appsettings.json defaults; override per-env.
Auto-scale
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)
✅ Correct: isolated worker
❌ Wrong: secrets in app settings
✅ 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.