Skip to content
CAMPUX Cloud Bootcamp Phase Four · Class Forty-One
Phase Four — Operate, Secure & AI
4 drills · 2 situations · 1 Lab
Developer skills · AZ-204 retired 2026
Class Forty-One

Azure Functions

The worker Class Forty kept promising: code that runs when a message arrives, without asking you to manage a server that waits all day for work. The hosting plan controls whether compute can scale to zero, stays ready, and how each choice affects latency and cost.

Phase Four · Operate, Secure, AI · Class 41 of 42 · 31 min · Assumes Class 11 and Class 40

After this you can write a function with a trigger and a binding, and say when a function is the wrong shape.

New to this? Read Class 11 and Class 40 first. Already done them, or know the material? Carry on.

The hands-on part also needs Python, Azure Functions Core Tools and Azurite (version checks are on the page).

§1

Code that isn't running most of the time

Class Forty ended with a promise: a worker would drain the order-events queue. One way to build that worker is a program that runs continuously — a virtual machine or an App Service that waits for work. That can mean paying for allocated compute while the queue is empty. Azure Functions lets you respond to events without managing the server that runs your code. The hosting plan decides whether compute can scale to zero between events or stay ready to respond faster. You still choose timeouts, retries, connections, and monitoring; the platform manages the host.

The shift is in what you manage. With a VM you look after the machine, operating system, runtime, scaling, and the polling loop. With a Function you write the handler and configure the app; Azure manages the host and, on event-driven plans, adjusts compute as events arrive. You remain responsible for the function's code, settings, identity, and behavior. This is what "serverless" means here: Azure runs the servers, but you do not manage them directly.

Two workloads from earlier in the bootcamp could use Functions. The nightly price import from Class Eleven can run from a timer trigger; with a scale-to-zero plan and no always-ready instances, compute can scale down between runs. The order-events consumer from Class Forty can run from a queue trigger; its plan determines how the app scales and whether any capacity stays ready. In both cases, Functions runs your event-handling code while Azure manages the host.

You bring the function; Azure brings everything around it.

§2

Triggers and bindings — the whole programming model

A Function has three main parts: a trigger, the code, and optional bindings. Each function has one trigger; it tells Azure what event starts that function. Bindings are optional connections to data the function reads or writes. They can reduce the connection code you write, though you can also call a service directly when you need more control.

Trigger & binding
A trigger is the single event that starts a function — a queue or Service Bus message, an HTTP request, a timer/cron schedule, a new blob, an Event Grid event. A binding is a declarative input or output connection — read this blob, write to that queue — that hands the function its data without connection code.

The triggers show what Functions can respond to. A queue or Service Bus trigger makes a function a consumer for Class Forty's queue. Azure's extension receives the message and applies the configured settlement and retry behavior when your code succeeds or fails. A timer trigger runs code on a schedule, such as the price import. An HTTP trigger responds to a web request, such as a webhook or lightweight API endpoint, often behind the Class Thirty-Eight gateway. A blob or Event Grid trigger responds to an upload or another event. In each case, an event starts the code. Bindings can reduce the plumbing; for example, an output binding can send a result to a queue. You can use a service's SDK directly when you need behavior the binding does not expose.

Input, output, and the direction of a binding

The word that trips people is direction. A binding is either in or out, and the two do opposite work. An input binding reads something and hands it to your code before the function body runs — for example, a customer record looked up from the order ID on a message. An output binding sends data your function returns to another service, such as placing a result on a queue. The Functions runtime handles that connection, but you still need to understand how failures, retries, and duplicate messages are handled for the specific trigger and service. The trigger is a special input that also wakes the function; other bindings move data in or out. Say it simply: one trigger starts the function, input bindings provide data, and output bindings send results.

HTTP
Wakes on a request; the function is a small API or webhook receiver, usually behind the Class Thirty-Eight gateway. The caller waits for a response, so startup delay is especially visible here.
Timer
Wakes on a six-field CRON schedule — cron in the cloud, without you managing a server to hold it. The Class Eleven price import is a good fit for a plan that can scale compute to zero between runs.
Queue / Service Bus
Starts when a message is available and is the consumer at the end of Class Forty. Delivery and retry rules vary by queue service, extension, and configuration. Design for possible redelivery by making important work idempotent; dead-letter handling also depends on the service and its settings.
Blob
Wakes when an object lands in a container — resize the image, parse the upload. The classic source polled with a lag; the Event Grid-backed blob source fires promptly instead of on a scan.
Event Grid
Wakes on an event from across the estate — a resource created, a blob written, a custom event you publish. The push-based spine for reacting to things that happen, rather than messages that queue.
§3

The hosting plan is the decision that bites

The code is the easy part; the plan you run it on affects the bill, scaling, and response time. Compare these four options and their trade-offs.

Table 1 — the Functions hosting plans, decided on the trade you can accept
QuestionConsumption (classic)Flex ConsumptionPremiumApp Service plan
Scales to zero?Yes — compute charges follow executionsYes — compute charges follow executionsNo — has a minimum allocated-instance costNo — always on
Cold starts?Expected after idleCan happen; always-ready instances reduce themAlways-ready and prewarmed instances reduce themAlways-on capacity reduces them
VNet / private endpoints?NoYesYesYes
Max run per execution5 min default, 10 max30 min default, extendableLong / unboundedUnbounded
Best forExisting apps; the legacy incumbentNew serverless work — the recommended defaultLatency-critical, heavy, VNet-boundYou already run an App Service plan

Read the table as one question — what can you tolerate? The serverless idea comes in two generations. Classic Consumption scales from zero and bills compute based on executions, duration, and memory. After idle, an HTTP request may wait while an instance starts; a background queue job may tolerate that delay more easily. This plan also has a five-minute default execution timeout, configurable up to ten minutes. Flex Consumption is Microsoft's recommended starting point for new serverless work: it can scale to zero, starts faster on demand, supports VNet integration, and has a thirty-minute default timeout that can be extended. Paid always-ready instances can reduce startup delays further. Premium keeps a minimum amount of capacity allocated and uses always-ready and prewarmed instances to reduce cold starts, at a standing cost. An App Service plan can host functions on existing always-on capacity when spare capacity and the workload fit. These plans do not remove every possible delay or other service cost. Start with Flex Consumption for new serverless work, then confirm current limits, measured latency, reliability needs, and the complete cost before choosing.1

What actually decides to scale — the controller you never see

On event-driven plans, Azure manages how many instances run; you can still set limits and concurrency controls. The scale controller monitors incoming events and uses rules for each trigger to decide when to add or remove instances. The details depend on the hosting plan: Flex Consumption scales functions independently in most cases, while other plans typically scale the app as a unit. A growing queue can lead to more workers, but scaling is bounded by plan limits, regional capacity, and the systems those workers call. This is event-driven scaling: Azure responds to work arriving instead of waiting for you to set a CPU threshold.

Two consequences follow, and both are interview-shaped. First, scale-out is bounded — a per-app maximum instance count exists, and on Consumption a burst is rate-limited so a thousand instances do not appear in a single second — which matters the moment your function fans out onto a database that cannot take a thousand connections. The senior move is to cap the concurrency on purpose (the host's batch and concurrency settings, or the plan's maximum) rather than let the controller stampede a fixed backend, which is the failure the Think-in-Systems box below names. Second, the newer plans and triggers use target-based scaling: instead of adding one instance at a time, the controller reads the backlog and the per-instance throughput and jumps straight to roughly the right instance count, draining a spike in far fewer steps. You do not configure the controller so much as shape what it reacts to — and knowing it is there is the difference between "Functions just scales" and being able to say how, and where that scaling stops.

Play

Play it through

Three minutes, two workloads. This exercise compares Flex Consumption, which can scale compute to zero, with Premium, which keeps capacity allocated to reduce startup delay. It pauses when it needs your hands and can be restarted at any time.

§4

The three rules that keep a function honest

Functions are unforgiving of assumptions carried over from long-running code. Treat each run as stateless: the same warm instance may reuse an in-memory value on a later run, but another instance will not share it, and a restart can erase it. That makes memory unsuitable for anything that must persist or stay correct — a running total, a cursor, or a half-finished workflow belongs in storage, a queue, or a database. A function that seems to "remember" can work in a small test and fail as soon as work moves between instances.

Second, a function must be idempotent, for the exact reason Class Forty drilled: queue and Service Bus triggers deliver at-least-once, so your function will occasionally run twice on the same message — a redelivery after a host recycle, a lock that lapsed mid-run. Keying on the order id and checking before acting is not optional hygiene here; it is the difference between a redelivery being invisible and a customer being charged twice. The trigger's at-least-once contract and the function's idempotency are two halves of one design.

Third, keep a function short and single-purpose. Execution timeouts vary by hosting plan and configuration; a long timeout does not make a function a good place to hold a multi-step process. Chaining work — charge, then wait for the warehouse, then wait for the courier, then email — makes failures harder to resume safely. Work that spans steps, waits, or fan-out is usually easier to recover with an orchestrator, which is §5.2

One function, one job. Save important state outside its memory.

§5

Durable Functions — the phrase for workflows

The moment a job stops being "handle this one message" and becomes "run these five steps, some of which wait," keeping the whole process inside one function becomes harder to recover and monitor. The answer may be Durable Functions, an extension that adds a stateful orchestrator: a function that describes a workflow in ordinary code — call this, await the result, call that, wait for a human to approve, fan out to parallel tasks and fan back in — while the runtime checkpoints its progress to storage and replays it after a restart. Each activity still needs safe retry behavior, and checkpoint storage can add cost.

Durable Functions
An extension to Azure Functions for stateful workflows: an orchestrator function coordinates multiple activity functions — chaining, fan-out/fan-in, waiting for external events or human approval, long-running timers — with the runtime persisting and replaying state so a multi-step process survives restarts and outlives any single execution.

You do not need it for the order-events consumer, which is one message and one job. You reach for it when fulfilment becomes a real workflow — charge the card, and only if that succeeds reserve the stock, then wait for the warehouse to confirm, then email the customer — because that sequence has ordering, failure branches, and a wait no single stateless run should hold open. Naming Durable Functions when the interviewer describes a multi-step process is the signal that you know where a plain function stops; the pattern also outlives Azure, because "orchestrator plus activities plus checkpointed state" is how every serverless workflow engine, from Step Functions to Temporal, is shaped.3

The four patterns worth naming out loud

Durable Functions is not one trick; it is a small set of shapes an interviewer will describe without naming, waiting for you to name them. Four are worth having ready, and each is a workflow a single stateless function could not hold.

Function chaining
Run steps in a fixed order, each starting only if the last succeeded: charge → reserve → confirm → email. The orchestrator awaits each activity in turn and checkpoints between them, so a mid-run restart resumes at the step it reached rather than re-charging the card.
Fan-out / fan-in
Start many activities in parallel, then wait for all of them and aggregate: transcode one video into a dozen resolutions at once, then write a single "done" record when the last returns. Tracking which of the parallel tasks have finished is the runtime's job, not a table you maintain.
Async HTTP / monitor
Kick off long work, hand the caller a status URL to poll, and let a durable timer watch an external job — checking a report every five minutes for up to an hour without holding a request or an instance open the whole time.
Human interaction
Pause and wait for an external event — a manager's approval, a customer's reply — with a timeout that takes a default action if no one answers in seventy-two hours. The orchestration sleeps, costing nothing, and wakes when the event arrives or the timer fires first.

Underneath these sits a third function type beyond orchestrators and activities: the entity function, a small piece of addressable, durable state — a counter, a cart, a per-customer tally — that many callers can update in order without a race. It is the honest home for the "running count" that Drill 04 forbids in a module variable: state that must persist and be shared, made explicit and durable rather than smuggled into memory the next instance will not have.

Functions, Container Apps, or Logic Apps

Three Azure services answer "run my code when something happens," and the interview question is which, and why. They are less rivals than three points on one line — how much you write versus how much the platform already did.

Table 2 — three ways to run event-driven work, from most code to least
ServiceYou writeReach for it when
Azure FunctionsA short handler per event, in codeThe unit of work is small, event-driven and bursty — the default for glue and queue consumers
Container AppsA whole container image — your runtime, your dependenciesThe work needs a full container, a long-running service, or a runtime Functions does not host (Class Twenty-Seven)
Logic AppsAlmost no code — a visual workflow of prebuilt connectorsThe job is wiring SaaS systems together — "a form is submitted, add a row and send mail" — more integration than computation

The rule the answer should carry: Logic Apps when the value is the connectors and you would rather not write code; Functions when it is a small piece of your own logic per event; Container Apps when the thing that runs is larger than a function or has to be a container. Durable Functions and Logic Apps overlap on orchestration — both run multi-step workflows — and the honest split is code versus canvas: Durable Functions for a developer who wants the workflow in source control and under test, Logic Apps for a workflow that is mostly connecting other people's systems. Naming all three, and the axis between them, is a stronger answer than defending Functions as the tool for every job.

Case File · Campux Retail

The worker Class Forty promised, finally built

a Function App consumes the queue, scales with the work, and can hand a longer process to an orchestrator

The consumer at the end of Campux's orders topic is a Function App on Flex Consumption, with no always-ready instances configured: one function, triggered by the fulfilment subscription. Its handler checks the order ID before changing business state, so a redelivered message cannot charge or ship twice. It completes the message only after the work succeeds; repeated failures eventually reach the configured delivery limit and dead-letter queue, where the team can inspect and replay them. During a Black Friday spike, the Functions scale controller can add instances subject to plan limits, available capacity, and trigger scaling behavior, then scale down as the backlog falls. On-demand compute costs can fall during idle periods, while storage, monitoring, and other connected services may still cost money. A timer-triggered price import could use the same app if its runtime and scaling needs fit.

Then fulfilment grows into a multi-step workflow. Finance wants the charge before the stock reservation, the warehouse confirmation awaited, and the customer email sent only after success—with a record of which step each order reached. A single stateless invocation should not stay open while waiting hours for a warehouse response. Durable Functions can coordinate activity functions for charge → reserve → await-warehouse → email, persisting orchestration state so the workflow can resume after a restart. You still need idempotent activities and failure handling, and the backing storage has its own cost. The checkout from Class Forty still records accepted work promptly; fulfilment can proceed in the background and report failures for a human to resolve.

Fig. 1 · Campux's Function App — events start the code; the hosting plan controls the ready capacity
Service Bus orders → fulfilment timer · 02:00 price import HTTP request webhook / API triggers Function App Flex Consumption idempotent handler scales 0 → many → 0 storage / state database binding output bindings can scale to zero · on-demand compute billed while active
One function per job, each started by a trigger — a Service Bus message, a timer, an HTTP call — with bindings carrying data in and out. On Flex Consumption without always-ready instances, on-demand compute can scale to zero. Connected services may still cost money, and an at-least-once trigger means the handler must be idempotent. Multi-step fulfilment can move to a Durable Functions orchestration.
Watch · Microsoft Learn

Official documentation

Read the overview and hosting-plan comparison before the lab
Microsoft Learn · Docs

Azure Functions — overview (triggers, bindings, the model)
learn.microsoft.com/azure/azure-functions/functions-overview

Azure Functions hosting options — Consumption, Flex, Premium, Dedicated
learn.microsoft.com/azure/azure-functions/functions-scale

Create and deploy with Flex Consumption
learn.microsoft.com/azure/azure-functions/functions-create-first-function-vs-code

Lab · A function that wakes on a schedule

Create a timer-triggered function and watch it run on Flex Consumption

~25 minutes · Visual Studio Code + Azure · Flex Consumption with on-demand instances

This lab uses the recommended serverless plan for new Azure Functions apps. Flex Consumption does not support the older portal-based “+ Create function” editor. Create the function in Visual Studio Code, deploy it to Azure, then use the portal to monitor it. If you need an in-portal code editor, choose a supported plan and language instead.1

  1. Install Node.js 22, Visual Studio Code, the Azure Functions extension, and Azure Functions Core Tools 4. In VS Code, press F1 and choose Azure Functions: Create New Project. Choose JavaScript and the Timer trigger template; if asked for a programming model, choose the current Node.js model (v4). Set the schedule to 0 */1 * * * *.

    What to notice: this is the function project — the code and its trigger. The six-field CRON expression begins with seconds; 0 */1 * * * * means “at second zero of every minute.”
  2. Press F1 again and choose Azure Functions: Create Function App in Azure. Select your subscription, then create a dedicated resource group named rg-func-lab. Enter a globally unique app name, choose a supported region and Node.js 22, and select Flex Consumption. The wizard creates the app and its required Azure resources in that group.

    What to notice: Flex Consumption is the hosting plan. With no always-ready instances configured, it can scale on demand, including down to zero. It still uses a storage account and monitoring, which can have their own charges.
  3. Press F1 and choose Azure Functions: Deploy to Function App. Select the app you just created and confirm deployment. In the Azure portal, open the Function App's Monitor view and check for timer invocations.

    What to notice: each invocation shows a run and its duration; it is not the entire Azure bill. The app can scale its on-demand compute down between runs, while storage, monitoring, and other services can still accrue charges.
  4. Tear it down so the timer stops and nothing lingers:

    az group delete --name rg-func-lab --yes --no-wait
    The lesson: this removes the resources in this lab's resource group. Before running the deletion command, confirm the group name and make sure it contains only these lab resources. A queue trigger or HTTP trigger uses the same Functions model with a different event and response-time trade-off.
You need an Azure subscription; Azure resource charges may apply. Node.js 22, Visual Studio Code, the Azure Functions extension, and Core Tools are required for this path. The portal's older “+ Create function” experience is not supported on Flex Consumption; create the project in VS Code, then deploy it to the Flex app. Names in the VS Code command palette can vary slightly between extension versions.
Think in systems

Zoom out: cheap-per-run is not free, and scale-to-zero has a first customer

Compute that can scale to zero may reduce idle compute charges, but it does not make a Function App free at rest. Reason about what event-driven compute does to the system around it before the exam.

Feedback loops

A queue backs up, so Functions scales out to dozens of instances to drain it — and all of them hit the same database at once, which slows, which makes each function run longer, which makes Functions scale out further. Elastic compute in front of a fixed backend is a stampede waiting for a trigger. What limits the concurrency, and where does the backpressure live?

Dependencies & coupling

On a plan with no always-ready instances, the first request after idle may wait for an instance to start. A background drain may tolerate this; a customer-facing HTTP function may not. You traded some standing compute cost for variable startup latency — who feels that variance, and can your response-time target tolerate it?

The constraint

Functions scales the compute effortlessly, so the ceiling moves to whatever it calls — the database connections, the downstream API's rate limit, the Service Bus throughput. The wall is no longer your worker; it is the least elastic thing behind it. Adding function instances past that point just queues the contention somewhere less visible.

Second-order effects

Per-execution billing makes cost scale with traffic, which is wonderful until a retry storm or a recursive trigger — a function that writes to the queue that triggers it — turns a bug into a runaway invoice. Cheap-per-run removed the standing cost and added a new failure mode: the loop that bills. What caps it?

What breaks when it scales

Execution limits vary by plan, and in-memory state does not survive an instance restart. What breaks first as volume climbs — the execution limit, the downstream, or the assumption that one function should hold the whole workflow? Which needs a plan change, and which needs Durable Functions?

The engineer who ships is asked "does it run?" The engineer who gets promoted is asked "and what does it stampede when it does?" — and has already capped the concurrency.

On the job

Turning a standing cost into a per-run one

You · Cloud Engineer · a VM runs a job for eight minutes and bills for a day

A virtual machine exists to run one nightly job and one queue drain, and it bills twenty-four hours a day to do it. You rewrite the two workloads as Functions — a timer trigger and a queue trigger — switch the VM off, and the compute bill for that work collapses to the seconds it actually runs. Same jobs, same output, a line item that finally matches the work — and you are the one who stopped paying for idle.

Lab continuation · Real queue, local worker

Return to the order waiting in Class Forty

The timer lab teaches deployment. This continuation starts a function when a real training order enters Service Bus. Run Functions Core Tools and a supported Python version on your development machine; install them through machine setup and the official local Functions guide. You need a disposable Service Bus namespace and queue you may manage. Those Azure resources can incur charges even though the worker runs locally. This is a receive/log demonstration, not durable fulfilment.

Before you start, check the tools. Each line should print a version, not "command not found":

python3 --version    # 3.11 or later; on Windows try: py --version
func --version       # 4.x
node --version       # needed only to install Azurite
azurite --version

Missing one? The extra tools table has the install line for each. Then read the supplied function before you run it. Here are its working lines, with a note on each:

@app.service_bus_queue_trigger(arg_name="message", queue_name="orders",
                              connection="OrdersBus")       # wake me when a message lands on "orders"
def order_received(message):                                # the message arrives as this argument
    order = json.loads(message.get_body().decode("utf-8"))  # its body is JSON text; turn it into a dictionary
    order_id = order.get("orderId")                         # pick out one field, or None if absent
    if not isinstance(order_id, str) or not order_id:
        raise ValueError(...)                               # refuse loudly; Service Bus will redeliver
    logging.info("Training order received: %s", order_id)   # success: one line in the log

The decorator line is the trigger from §2, and connection="OrdersBus" names the setting that holds the connection string, never the string itself. Raising an error is how the function says "I did not finish", which is what sends a bad message towards the dead-letter queue in step 5.

  1. In a new disposable resource group, create a Service Bus namespace using a currently available tier supporting queues. Create a queue named orders. Set its maximum delivery count to 3 for this exercise. Record your group and namespace for cleanup.

  2. Use a narrow training namespace SAS policy with Listen permission for the local receiver. Create a separate Send policy for the portal or sender if needed. Keep the resulting connection strings local; do not use a production policy or publish them. A deployed Azure worker should instead review an identity-based connection and the required Azure roles.

  3. Create an empty local folder and follow the commands below. Replace its function_app.py with the supplied function. Keep the generated host.json and extension-bundle configuration. In local.settings.json, under Values, set OrdersBus to the listener connection string. Keep that file ignored by Git. Set AzureWebJobsStorage to UseDevelopmentStorage=true. Install and start the local Azurite emulator in a second terminal before running the Functions host, as shown in the official local-development guide. Keep it running during this exercise. The Service Bus connection must be a namespace connection string, not one limited to a specific entity.

  4. Start the worker. In the training queue’s Service Bus Explorer, use the sender permission to send {"orderId":"training-001"}. Expect the log to report Training order received: training-001. Observe the active queue count decrease after completion. Stop if the host reports a binding, network or authentication error; use that exact message to check the configuration.

  5. Send {"notAnOrder":true}. The function throws instead of pretending fulfilment succeeded. After repeated delivery, expect the message in the dead-letter queue under the configured delivery limit. Inspect the count and reason; do not repeatedly resubmit it unchanged.

  6. Send the valid order twice. You may see two handler calls. Explain why this sample must not charge a card or send fulfilment twice: it has no durable deduplication record. Before a real side effect, use a durable idempotency decision keyed to the business operation.

  7. Stop the local host. Delete only the disposable namespace/group created for this exercise and verify deletion. Remove the training connection string from local settings and retire the SAS policies if the namespace is retained.

python -m venv .venv
# Cloud Shell / Linux / macOS:
source .venv/bin/activate
# Git Bash on Windows: source .venv/Scripts/activate
# PowerShell alternative: .venv\Scripts\Activate.ps1
pip install azure-functions
func init --worker-runtime python --model V2
# In a second terminal, install and run the local storage emulator:
# npm install -g azurite
# azurite
# Keep it running while you run func start.
# Replace function_app.py with the supplied file.
func start
Check the story

The queue message starts the handler; a timer does not. The receiver needs network reachability and Listen permission. Throwing leaves a failed message eligible for redelivery and eventual dead-letter handling. This sample records no fulfilment state, so it proves receipt, not exactly-once processing. See Microsoft’s Service Bus trigger reference for connection and binding requirements.

Class Forty-One

Examination

Four drills, then two situations. The situations have no marking scheme — write your answer before you reveal the reasoning, or the exercise is worthless. Nothing is stored; this is between you and the page.

Drill 01Recall · pick the plan
A customer-facing HTTP function must answer in under 200 ms almost every call, reach a database over a private endpoint, and sometimes has to trigger twelve minutes of work behind the scenes. Which hosting plan fits the fast path, and what happens to the twelve minutes?
Marked

B — choose a plan that supports the network path, reduce startup delay where the plan allows it, and move long work out of the waiting HTTP request. Flex Consumption and Premium support VNet integration, and both offer ways to reduce startup delay. That does not guarantee a sub-200 ms response: measure cold and warm requests against the target. Every HTTP-triggered function also has a separate response limit of about 230 seconds, so a twelve-minute job cannot finish inside that request. Return an accepted response and a job ID, then process the work separately and let the caller check its status. A assumes a latency guarantee the plan does not make. C tries to use an execution-timeout setting to keep an HTTP request open beyond its response limit. D ignores that networking and startup behavior vary by plan.

Drill 02Recall · triggers & bindings
A function should run when a message lands on the orders Service Bus subscription, read a customer record from a database, and write a result to a "fulfilled" queue. How many triggers and bindings is that?
Marked

B — a function has exactly one trigger, and everything else it touches is a binding. The one event that wakes the function is the Service Bus message; the database read is an input binding and the queue write is an output binding — declarative connections that hand the function its data without connection code. A is the classic confusion: only the thing that starts the function is a trigger, and there is always precisely one. D repeats the error more subtly — a database read does not start the function, so it cannot be a trigger. C is true only if you refuse the model: you can hand-code with SDKs, but bindings exist so you do not, and choosing boilerplate over a binding is choosing to maintain plumbing the platform offered to carry. "One trigger, zero-or-more bindings" is the sentence to have ready.

Drill 03Select three
Which three are true of an Azure Function and must shape how you write one?
Marked

Stateless, idempotent, and governed by a plan-specific timeout. Keep durable state in storage, make repeated work safe, and check the chosen plan's execution limit. The false choices confuse memory with saved progress and assume every plan keeps compute ready. Some plans can scale to zero; Flex Consumption and Premium offer options that reduce cold-start delays. For a multi-step workflow that pauses or needs recovery, Durable Functions can checkpoint progress between activities.

Drill 04Spot the error
This design for the fulfilment function is about to ship. One line guarantees an incident under load. Which?
# design: fulfilment function (Consumption)
1.  Service Bus trigger on the fulfilment subscription;
    handler is idempotent on the order id.
2.  Keeps a running count of orders processed in a
    module-level variable, logged each run.
3.  On failure it throws, so Service Bus retries and
    eventually dead-letters per Class 40.
4.  Long multi-step fulfilment is moved to a Durable
    Functions orchestration.
Marked

Line two — the one line that quietly assumes a server. A module-level variable holding a running count works perfectly in testing, where one instance handles every message in sequence, and fails silently in production, where Functions scales to many instances that share no memory and any of which may be recycled between runs. The "count" becomes per-instance, resets without warning, and means nothing — a metric that lies. Persistent state belongs in storage, a database, or a metrics backend (Class Thirty's App Insights), never in a variable that outlives a single execution only by accident. The distractors are the design working: idempotency on a Service Bus trigger is not just possible but required (A inverts §4); throwing to drive retry-then-dead-letter is precisely the Class Forty contract (C); and moving multi-step work to Durable Functions is §5's correct call, not overkill (D). The reviewer's reflex: in serverless code, any state that is not written down is a bug that waits for the second instance.

Situation 01Write before you reveal
A teammate's classic Consumption function processes an uploaded video and keeps hitting its ten-minute maximum. Their fix: "Move it to an App Service plan so there's no execution limit and it can run as long as it needs." Is that the right call?
Their fix may remove the timeout limit. Ask what the duration of the work says about how it should be divided, checkpointed, and recovered.
Reasoning

Concede that the fix works, then question what it teaches you to stop noticing. Moving to an App Service plan (or Premium) genuinely removes the ten-minute cap, and for some workloads that is the right answer — so do not reject it reflexively. But the timeout was not an arbitrary obstacle; it was the platform telling you the work does not fit the "wake, do one short thing, end" shape a single function is built for. Lifting the limit lets a long, stateful, all-or-nothing job run inside one execution — and the day it fails at minute nine, you have no idea what already happened, no checkpoint, and no way to resume. You removed the alarm, not the fire.

Name the shapes and match the tool to each. If the video processing is genuinely one long computation that cannot be broken up, then long-running compute is the honest need — but a function is a poor host for it, and a container job (Class Twenty-Seven) or a Durable Functions activity with the right plan is a better one. If, more likely, it is several steps — download, transcode, thumbnail, store — then it wants a Durable Functions orchestration: each step its own short activity, checkpointed, resumable, fanning out across sizes if needed. Either way the question to ask first is not "how do I make one function run longer" but "why is this one function doing so much."

Close on the cost the quick fix hides. An App Service plan does not scale to zero, so the moment they move there to dodge the timeout, they also give up the per-run billing that made Functions worth choosing — the video job now bills for a standing plan whether or not a video is being processed. That may be fine; it should be a decision, not a side effect. The sentence to leave them with: the limit isn't the problem — it's the diagnosis; fix the shape of the work, and pick the plan on purpose.

Situation 02Write before you reveal
Finance flags a bill: a Consumption Function App cost more last month than the VM it replaced. The team's instinct is to move it back to a VM. What do you investigate first, and what is the likely story?
Per-run billing means cost scales with runs. A bill that jumped usually means the run count did — find out why before you architect.
Reasoning

Resist the reflex to re-architect before you have read the invoice. "Move it back to a VM" is a large, slow change proposed before anyone has looked at why the number moved — the exact instinct Class Thirty-Two trains you to distrust. Consumption bills per execution and per gigabyte-second, so a bill that jumped almost always means executions or duration jumped. The first move is the metrics, not the migration: how many invocations, of which function, and did the count or the per-run duration change?

Name the usual suspects, because they are specific and fixable. The classic is a runaway or recursive trigger — a function whose output lands on the queue that triggers it, or a retry storm on a persistently failing message multiplying invocations — turning a bug into a per-run invoice. Next is a chatty trigger firing far more than intended (a timer set to every second, a blob trigger on a hot container). Then a genuine, healthy traffic increase, in which case the bill rose because the business did, and the VM would simply have hit a wall instead of a line item. Each has a targeted fix — cap concurrency, fix the loop, dead-letter the poison, right-size the schedule — none of which is "buy a server."

Only then compare honestly, at the real workload. If, after fixing the anomaly, sustained high-volume traffic genuinely makes Consumption more expensive than a right-sized always-on plan, that is a legitimate finding — Consumption wins on spiky and bursty, and a steady firehose can favour a plan with a fixed floor. But that is a priced comparison at the true run rate, reviewed at the Class Thirty-Two monthly, not a panic migration off one surprising invoice. The sentence that keeps it honest: per-run billing didn't overcharge you — it itemised something, and the first job is to read what.

Examination record · first attempt
0/4
Class Forty-One · Complete
Retain this much

Five things worth carrying out of this class

  1. A Function runs code in response to a trigger. Your hosting plan decides whether compute can scale to zero or stay ready; Azure manages the servers, and you manage the function's code, settings, identity, and behavior.
  2. The model is one trigger, the code, and zero-or-more bindings. The trigger wakes it (queue, Service Bus, timer, HTTP, blob); bindings carry data in and out declaratively, so the code is about the work, not the plumbing.
  3. Choose a hosting plan from measured latency, execution duration, network needs, scaling behavior, and total cost. Flex Consumption is the current starting point for new serverless apps; Premium and App Service plans trade lower startup delay for allocated capacity and cost.
  4. Treat Functions as stateless and make at-least-once work idempotent. In-memory values may survive on one warm instance, but they are neither durable nor shared; persist important state externally.
  5. Multi-step workflows that wait or fan out want Durable Functions — an orchestrator that checkpoints state and survives restarts. Name it when the interviewer describes a process, not a single message.
Notes
  1. Microsoft recommends Flex Consumption as the default serverless hosting plan for new Azure Functions apps. It can scale to zero, and optional always-ready instances can reduce cold-start latency at additional cost. Premium keeps a minimum amount of capacity allocated. These features reduce startup delay, but your app still needs measured latency tests and a response-time budget. Check Microsoft's Flex Consumption guide and hosting-plan comparison for current limits. ↩
  2. On the classic Consumption plan the timeout defaults to five minutes and can be configured up to ten minutes. Flex Consumption and Premium default to thirty minutes; their documented maximum execution timeout is unbounded, subject to platform behavior, plan settings, and trigger limits. For example, an HTTP-triggered response has a separate 230-second limit. A long timeout does not replace checkpoints, retries, or workflow recovery. ↩
  3. This class teaches Azure Functions because it is Azure's serverless compute and the estate runs on it, but the model outranks the product: an event triggers a short, stateless, idempotent unit of work, and long workflows move to a checkpointed orchestrator. Meet AWS Lambda with Step Functions, Google Cloud Functions with Workflows, or Cloudflare Workers and you will find the same shape — learn triggers, bindings, statelessness, and orchestration here once, and the products become configuration. ↩