4.2.2. Configuring and Deploying Function Apps
💡 First Principle: Functions bill and scale by execution, but they deploy and configure by function app — the unit that owns the hosting plan, runtime version, and settings for every function inside it. Most configuration questions dissolve once you ask: is this about one function (trigger/bindings) or about the app (plan, settings, deployment)?
The hosting plan decision is this subsection's centerpiece, because it prices the serverless promise:
| Plan | Scaling | Cold start | Distinguishers |
|---|---|---|---|
| Consumption | Event-driven, to zero | Yes | Cheapest; timeout limits (~5–10 min) |
| Flex Consumption | Event-driven, to zero, faster scale | Reduced (always-ready instances) | VNet support + per-instance concurrency |
| Premium | Event-driven, min instances > 0 | Eliminated (pre-warmed) | VNet, unbounded duration, bigger SKUs |
| Dedicated (App Service plan) | Plan's instances, no scale-to-zero | No | Reuse existing plan capacity; predictable cost |
The selection keywords mirror 1.2.1's table discipline: "minimize cost, tolerate latency" → Consumption; "no cold starts" or "VNet-integrated with pre-warmed capacity" → Premium (or Flex Consumption when scale-to-zero must be kept); "we already pay for an App Service plan" → Dedicated. Cold start — the seconds-long init when scaling from zero — matters doubly for AI glue code that fronts interactive chat.
Configuration lives in application settings (environment variables at runtime, same mechanism as 3.1.3), with a few reserved ones the exam checks: AzureWebJobsStorage (the storage account the runtime itself requires), FUNCTIONS_WORKER_RUNTIME (python), and your binding connections (SB_CONN, COSMOS_CONN) — ideally as Key Vault references over a managed identity.
Deployment is zip deploy (func azure functionapp publish, or CI/CD pushing the build artifact) with "run from package" mounting the zip read-only — atomic deploys, trivial rollback. Python apps can alternatively ship as containers to Premium/Dedicated plans or into Container Apps — at which point Phase 3's whole ACR pipeline applies to your functions too.
⚠️ Exam Trap: "Users report the first request after idle periods takes several seconds" is the cold-start signature; the fix is Premium's pre-warmed instances (or Flex Consumption's always-ready) — not more Consumption instances, which all still start cold. Conversely, don't buy Premium when the scenario only says "minimize cost for sporadic overnight batch triggers" — that's Consumption tolerating its cold starts by design.
Configuration splits across two surfaces worth distinguishing: application settings hold environment-specific values (connections, feature switches), while host.json holds runtime behavior shared by every environment — trigger batch sizes, retry defaults, logging levels. A setting in the wrong layer is a classic review flag. On Premium and Dedicated plans, deployment slots bring the swap-based zero-downtime promotion familiar from App Service; Consumption's slot support is limited, which nudges teams toward run-from-package plus quick redeploys instead. Each plan also carries scale-out ceilings (Consumption bursts widest; Dedicated is bounded by the plan's instance count), and every function app integrates with Application Insights through one connection-string setting — which is how the Phase 5 observability story attaches to your glue code with no extra plumbing.
Two reserved settings complete the inventory: FUNCTIONS_EXTENSION_VERSION pins the runtime major version (upgrades are deliberate, not ambient), and per-plan default timeouts differ — Consumption starts at five minutes (extensible to ten), while Premium and Dedicated default to thirty and can run unbounded. Scenario math about 'a 12-minute job on Consumption' resolves on these numbers.
Reflection Question: Why does the Consumption plan require an execution-time limit while Premium doesn't — what resource reality does the timeout encode?