Azure Functions

Azure Functions

How to run small pieces of code in the cloud without managing a single server: what triggers them, how they scale, how to keep them safe and affordable, and when a function is the right tool.

Imagine a small task that must happen every time something occurs. A customer uploads a photo and it must be shrunk to a thumbnail. An order arrives and a confirmation email must be sent. A clock strikes midnight and a report must be built. Each task is tiny, but it must run reliably, whether it happens once a day or ten thousand times in a minute.

Doing this the old way meant renting a computer, installing software, keeping it updated, and paying for it around the clock, even while it sat idle. Azure Functions removes that burden. You write only the small piece of code, tell Azure what should wake it up, and Azure finds a computer, runs your code, and puts it away again. You pay for the work done, not for waiting.

In this guide you will learn what a function is, how triggers and bindings connect it to the world, how the hosting plans differ, how scaling and cold starts work, how long-running workflows are built, how to secure, watch, and price your functions, and how to decide when a function is the right choice. Every new word is explained when it first appears.

1The Big Picture: Code Without Servers

Before we open the box, let us understand what “serverless” really means, why it exists, and where Azure Functions fits among Azure’s many services.

What is Azure Functions?

Azure Functions is Microsoft’s service for running small units of code in response to events. An event is simply something that happens: a message arrives, a file appears, a timer fires, a web request lands. A function is a short program that reacts to one kind of event, does its job, and finishes.

The service is called serverless, which is a slightly misleading name. Servers certainly exist. The word means that you never see, choose, patch, or pay for them directly. Microsoft owns the machines, installs the operating system, applies the security updates, and starts or stops capacity according to demand. You bring the code.

Analogy

Think of the difference between owning a car and calling a taxi. Owning a car means you pay for insurance, parking, and repairs even when it sits in the garage. A taxi appears when you call, takes you where you need to go, and you pay only for the trip. Azure Functions is the taxi: code arrives on demand, does one job, and you pay for that job alone.

Why does it exist?

  • Focus. Teams spend their time on business logic instead of operating systems and patching.
  • Elasticity. The service grows from zero copies to many and back again without a human touching anything.
  • Cost. In the pay-per-use plans, an idle function costs nothing.
  • Speed of delivery. A working function can be written, tested, and published in an afternoon.

A short history

Cloud computing began by renting virtual computers. The next step was renting only the platform, and the step after that was renting only the execution of a single function. Amazon introduced the idea to the mainstream with its Lambda service in 2014. Microsoft announced Azure Functions in 2016, built on top of the earlier WebJobs technology and its software development kit. Since then the service has added more programming languages, richer hosting plans, and a workflow extension called Durable Functions.

0
servers for you to manage
2
pieces every function needs: a trigger and your code
1 M
free executions each month on the Consumption plan
i
Memory trick

Whenever you design a function, ask three questions: What wakes it up (trigger)? What does it read or write (bindings)? Where does it run (hosting plan)?

2Core Concepts: Function, Function App, and Host

Azure Functions uses a few words with very specific meanings. Learning them once makes everything later easier to follow.

The vocabulary

Unit of code

Function

One piece of code with exactly one trigger. It does one job, such as resizing an image or sending a message.

Unit of deployment

Function app

A container for one or more functions that are published, scaled, and configured together. Think of it as the folder that holds the whole project.

Wake-up call

Trigger

The event that starts a function. Each function has exactly one trigger.

Plumbing

Binding

A declared connection to data, either coming in (input) or going out (output), so your code does not need connection logic.

Engine

Functions host

The runtime that listens for triggers, calls your code, and handles retries, logging, and scaling signals.

Run

Invocation

One execution of one function. A busy app can have thousands of invocations a minute.

Settings

App settings

Named configuration values, such as connection details, stored outside the code.

Bookkeeping

Storage account

A companion Azure storage area the host uses to keep internal records, such as timer state and coordination data.

Function versus function app

New learners often mix these two up. A function app is the thing you create in Azure and deploy to. It owns the hosting plan, the settings, the identity, and the URL. Inside it live many functions. All functions in one app share the same resources and scale together, so it is wise to group functions that belong to the same job and keep unrelated ones apart.

flowchart TB
    Plan["Hosting plan: where the app runs"] --> App["Function app"]
    App --> F1["Function 1: HTTP trigger"]
    App --> F2["Function 2: Queue trigger"]
    App --> F3["Function 3: Timer trigger"]
    App --> Set["App settings and identity"]
    App --> Stor["Storage account for host records"]
    F1 --> B1["Bindings: input and output"]
    F2 --> B2["Bindings: input and output"]
Fig 1. A function app contains several functions. Each has one trigger and any number of bindings, and all share the app’s plan, settings, and storage.
!
Watch out

Because every function in an app scales together, one very busy function can use capacity the others need. If two jobs have very different loads or security needs, give them separate function apps.

3Triggers: What Wakes a Function Up

A function never runs by accident. Every run begins with a trigger, and choosing the right one is the most important design decision you make.

The common triggers

TriggerFires whenTypical use
HTTPA web request arrivesSmall web interfaces, webhooks, backend for a web page
TimerA schedule is reachedNightly reports, cleanups, regular checks
Queue (Azure Storage)A message appears in a storage queueBackground jobs and work lists
Service BusA message arrives in a queue or topicReliable business messaging between systems
Blob storageA file is added or changedImage processing, file validation
Event GridAn Azure service raises an eventReacting to resource changes
Event HubsA stream of events arrivesTelemetry, logs, high-volume data
Cosmos DBA document is added or changedReacting to database changes

Pull and push triggers

Some triggers are pushed to the function. A web request or an Event Grid notification arrives by itself and the host simply hands it over. Others are polled: the host must keep checking a queue or a stream to see whether anything is waiting. Polling triggers are the reason scaling needs a clever helper, which we meet in a later chapter.

One trigger per function

A function has exactly one trigger. If the same code must react to two different events, you write two functions that share a common helper. This rule keeps each function simple to reason about, and makes it clear exactly why any given run started.

i
Good to know

Timer schedules use a six-field expression that includes seconds. A common mistake is writing the familiar five-field form from other schedulers. Always check the number of fields.

Example: a thumbnail maker

A photo website stores each upload in blob storage. A blob trigger function wakes up when a new file lands, reads it through an input binding, creates a small version, and saves it through an output binding. No one has to run a server that polls for new photos. The same code handles one photo or one million.

4Bindings: Declared Connections to Data

If triggers are the doorbell, bindings are the delivery slots in the wall. They let your code receive and send data without writing the connection plumbing yourself.

Input and output bindings

An input binding reads extra data for your function before it runs, for example fetching a database record named in an incoming message. An output binding sends the result somewhere after it finishes, for example putting a message on a queue or writing a row to a table. The trigger itself is a special kind of input binding.

Bindings are optional. They exist to save effort, and you can always talk to a service directly through its own client library when you need more control, such as complex queries or fine-grained error handling.

Why bindings help

  • Less code. No connection setup, no authentication code, and no clean-up code for the common cases.
  • Clear intent. A reader can see at a glance what the function reads and writes.
  • Safer configuration. Connection details live in app settings, not in the source code.

Where bindings fall short

Bindings cover the common, simple actions. They do not cover everything. When you need to run a custom query, handle a partial failure of a batch, or use a service feature the binding does not expose, you use the service’s own client library instead. Many production functions mix both styles.

flowchart LR
    T["Trigger: new message on a queue"] --> Fn["Your function code"]
    IB["Input binding: read a customer record"] --> Fn
    Fn --> OB1["Output binding: write result row"]
    Fn --> OB2["Output binding: send confirmation message"]
Fig 2. One trigger starts the function. An input binding supplies extra data. Two output bindings deliver the results.
Triggers decide when your code runs. Bindings decide what it can touch without extra plumbing.

5Hosting Plans: Where Your Functions Run

The hosting plan decides how your app scales, how fast it starts, how it connects to private networks, and how you are billed. It is the biggest cost and performance choice after the code itself.

The main options

PlanScalesBilling ideaBest for
ConsumptionAutomatically, down to zeroPay per execution and resource use, with a monthly free grantLight, spiky, low-cost workloads
Flex ConsumptionAutomatically, per function group, down to zeroPay for execution, plus optional always-ready instancesMost new serverless apps needing private networking and faster starts
PremiumAutomatically, with warm instances always readyPay for reserved and active instancesSteady traffic, no cold start, long runs
Dedicated (App Service)Manually or by ruleFixed price for the reserved machinesSpare capacity on existing plans, predictable load
Container AppsAutomatically, with container rulesContainer Apps pricing modelTeams that want functions beside other containers

Consumption: the original

The Consumption plan is the classic pay-per-use choice. The app can shrink to zero copies when idle and grows when work appears. It is cheap and simple, but it has trade-offs: a limited run time per invocation (five minutes by default, ten at most), more limited private network features, and the so-called cold start we study later.

Flex Consumption: the newer serverless

The Flex Consumption plan is Microsoft’s newer serverless option, aimed at fixing the main complaints about Consumption. It supports private virtual network connections, lets you choose the memory size of each instance, can keep a number of instances always ready to remove cold starts, and scales groups of functions independently. For many new projects it is now the sensible starting point, although you should check the current feature list in the documentation for your region.

Premium and Dedicated

The Premium plan keeps at least one instance running and warms extra ones ahead of demand, so users rarely wait for a start. It also offers private networking and longer run times, at a higher price. The Dedicated plan runs your functions on a regular App Service plan, the same machines that host web apps. It makes sense when you already pay for spare capacity there, but it does not scale to zero by itself.

!
Check the current limits

Plan features, memory sizes, and time limits change as Azure evolves. Treat the numbers in any article as a guide and confirm them in the official documentation before you commit to a design.

flowchart TB
    Q1{"Is traffic spiky or often idle?"}
    Q1 -->|"Yes"| Q2{"Need private network or no cold start?"}
    Q1 -->|"No, steady"| Prem["Premium or Dedicated"]
    Q2 -->|"Yes"| Flex["Flex Consumption with always-ready instances"]
    Q2 -->|"No"| Cons["Consumption or Flex Consumption"]
Fig 3. A simple first guide for picking a hosting plan. Real decisions also weigh cost, region support, and team skills.

6Languages and Programming Models

Azure Functions does not tie you to one language. You can usually write functions in the language your team already knows.

Supported languages

The service supports C#, JavaScript, TypeScript, Python, Java, and PowerShell as first-class options. For anything else, the custom handler feature lets any program that can serve web requests act as a function, which opens the door to languages such as Go or Rust. Supported language versions change over time, so always check which runtime versions your chosen region and plan allow.

The runtime version

The Functions runtime has major versions. The current generation is version 4, and older versions have been retired. Your function app is pinned to a major version through a setting, and moving between versions can require code changes. Keep an eye on the support calendar so you are never running on a version that is about to lose support.

C# models: in-process and isolated

For C#, there used to be two ways to run. The older in-process model ran your code inside the same process as the host. The newer isolated worker model runs your code in its own process, which gives you control over dependencies and lets you use newer .NET versions on your own schedule. Microsoft has announced that support for the in-process model is ending, so new C# projects should use the isolated worker model. Confirm the exact end date in the official lifecycle pages.

Programming style inside the languages

Several languages offer a more modern programming model in which triggers and bindings are declared in code using decorators or attributes, rather than in a separate configuration file. Python and JavaScript both adopted this style in recent versions. It keeps everything about a function in one place and is easier to read than the older split between a code file and a configuration file.

.NET

C#

Strongest tooling and the most mature binding library. Use the isolated worker model.

Script

JavaScript and TypeScript

Natural for teams already using web technology. TypeScript adds type safety.

Data

Python

Popular for data tasks, automation, and machine learning glue.

Enterprise

Java

Fits teams with existing Java skills and libraries.

7How It Works Inside: Host, Workers, and the Scale Controller

Understanding what happens behind the curtain explains why functions behave as they do: why they sometimes start slowly, why they run twice, and why they scale the way they do.

The host and the worker

Every running instance of your app has a Functions host. The host is a program written by Microsoft. It reads the list of functions in your app, connects to the services that supply triggers, and waits. When an event arrives, the host calls your code, which runs in a language worker process suited to your chosen language. The host handles the logging, the retries, the lease records, and the bookkeeping, while the worker simply runs your logic.

The scale controller

Who decides how many instances to run? A Microsoft-managed component called the scale controller. It watches the sources of your triggers, such as the length of a queue or the rate of incoming events, and decides when to add or remove instances. When nothing is waiting, it can remove every instance in plans that scale to zero. When work piles up, it adds more, and your host instances start pulling items in parallel.

The life of one invocation

1

An event happens

A message lands in a queue, a file is saved, or a web request arrives.

2

The scale controller notices

If no instance is running, it starts one. If instances are overloaded, it adds more.

3

The host picks up the event

It reads input bindings and prepares the data your code needs.

4

Your code runs

The worker process executes your function and produces a result.

5

Outputs and clean-up

The host writes output bindings, marks the message as handled, and records logs.

sequenceDiagram
    participant S as Event Source
    participant SC as Scale Controller
    participant H as Functions Host
    participant W as Language Worker
    S->>SC: New work appears
    SC->>H: Start or add an instance
    H->>S: Pull the event
    H->>W: Call your function with data
    W-->>H: Return result
    H->>S: Mark event complete
Fig 4. The path of one event from its source to completion. The scale controller watches the source and sizes the fleet of hosts.
i
Why this matters

Most triggers give at-least-once delivery. The same event can occasionally arrive twice, for example after a crash. Design your functions to be safe when repeated, a property called idempotency. We return to this in the chapter on reliability.

8Scaling and Cold Starts

The promise of serverless is that capacity follows demand. The price of that promise is a delay called the cold start. Knowing both sides helps you design well.

How scaling works

When more events arrive than the running instances can handle, the scale controller adds instances. Each new instance runs its own host and begins pulling work. When the pressure drops, instances are removed after a period of quiet. This is called scale out and scale in. It is horizontal scaling: more copies, not bigger machines.

Each plan has limits on how far it will go, and these limits differ by operating system, region, and plan type. The Consumption plan has a maximum number of instances per app. Flex Consumption allows a much higher ceiling and lets you set your own cap, which is helpful when a downstream database cannot handle unlimited callers. Always confirm current numbers in the documentation.

The cold start

If no instance is running, the first event must wait while Azure finds a machine, loads your code, starts the language worker, and runs any start-up logic you wrote. This delay is the cold start. It can range from a fraction of a second to several seconds, depending on language, package size, and the plan.

Analogy

A cold start is like walking into a restaurant that is closed but will open when the first guest rings the bell. The first guest waits while the cook arrives and lights the stove. Later guests, who arrive while the kitchen is warm, are served at once. A warm instance is a kitchen with the stove already hot.

Ways to reduce cold starts

  • Keep instances warm. Premium keeps a minimum number of instances running. Flex Consumption can keep “always-ready” instances for chosen triggers.
  • Make the package small. Fewer and lighter dependencies load faster.
  • Do less at start-up. Avoid heavy work when the app loads. Create expensive clients once and reuse them.
  • Pick the language wisely. Languages with heavy runtimes may start slower than lightweight ones, though your own code often matters more.
  • Accept it where it does not matter. Background jobs rarely care about a two-second delay.

Scale in and shutdown behavior

When instances are removed, the host is given a short period to finish the work it already started. A function that is halfway through a long task when its instance is taken away may be cut off, and the event will be delivered again to another instance. This is another reason to keep each run short and to make the work safe to repeat. Splitting a large job into many small messages means that losing one instance costs you only a small slice of progress, not the whole job.

Measuring what users feel

Do not guess about cold starts. Measure the time between the arrival of the first request after a quiet period and the moment your code begins. Compare that number with what your users and your service goals can tolerate. Many teams discover that the delay is irrelevant for background jobs and only matters for a few user-facing paths, which can then be given warm instances while everything else stays on the cheapest setting.

flowchart LR
    Idle["No instances running"] -->|"First event"| Alloc["Azure allocates a machine"]
    Alloc --> Load["Load code and start worker"]
    Load --> Run["Run the function"]
    Run --> Warm["Instance stays warm for a while"]
    Warm -->|"Next event"| Run
    Warm -. "Long quiet period" .-> Idle
Fig 5. The cold start path (top row) versus the warm path. Warm instances skip the allocation and loading steps.
!
Watch out

A user-facing web interface that must answer in under a second every time is a poor match for a plan that scales to zero. Either keep instances warm or choose another hosting model.

9Concurrency, Batching, and Throughput

Scaling out is only half the story. Each instance can also do many things at once, and tuning that parallelism protects both speed and the systems your function calls.

Concurrency inside one instance

One instance may process several events at the same moment. For queue triggers the host fetches a batch of messages and runs them in parallel, then fetches more when the batch is mostly finished. Settings in the host configuration control the batch size and the point at which a new batch is fetched. Some triggers, such as Service Bus and Event Hubs, have their own concurrency settings too.

Newer versions of the runtime also offer dynamic concurrency, where the host adjusts parallelism by itself based on how busy the machine is. It reduces the need for hand-tuning, though you may still want to set firm upper limits.

Protect what lies downstream

A function that scales to dozens of instances, each handling many messages, can overwhelm a database or an external service that was built for a handful of callers. The result is slow responses, timeouts, and retries, which add even more load. This is called a retry storm.

  • Cap the maximum number of instances for the app.
  • Lower the batch size or concurrency for the trigger.
  • Put a queue in front of fragile services so work arrives at a steady pace.
  • Use timeouts and sensible retry delays in your own calls.

Reuse connections

A common mistake is to create a new web client or database connection on every run. Under heavy load this wastes time and can exhaust the available network ports. Create the client once, outside the function body, so every run on the same instance shares it.

Example: a gentle importer

A retail company receives a nightly file of fifty thousand price updates and writes each one to a database that comfortably handles a few hundred writes a second. A queue trigger function would happily scale far beyond that. By capping instances and lowering concurrency, the team lets the import finish in minutes without ever slowing the database for shoppers. This scenario is illustrative, not taken from a named company.

10Durable Functions: Workflows That Remember

Ordinary functions are short and forgetful. Real business processes are long and have many steps. Durable Functions bridges the gap.

The problem

Suppose an order requires five steps: check stock, charge the card, reserve a courier, send an email, and update the books. Each step could be a function, but who keeps track of which step is done, retries a failed step, or waits three days for a customer to approve something? Writing that bookkeeping by hand is painful and error-prone.

The idea

Durable Functions is an extension that lets you write a whole workflow as ordinary code, while the framework saves progress to storage after each step. If the app restarts or the machine disappears, the workflow resumes exactly where it stopped. You write the story; the framework remembers the page.

Conductor

Orchestrator function

Describes the order of steps. It calls other functions and waits for them. It must be predictable and free of side effects.

Worker

Activity function

Does one real piece of work, such as charging a card or calling a service. It may do anything an ordinary function can do.

Starter

Client function

An ordinary function, often HTTP triggered, that begins an orchestration and can check on it or stop it.

Memory

Entity function

A small stateful object, such as a counter or a shopping basket, that processes one operation at a time.

How it remembers: replay

The orchestrator saves a history of everything that happened. When it wakes after waiting, the framework replays the orchestrator code from the beginning, feeding it the saved results so it reaches the same point without redoing the work. For this to work, orchestrator code must be deterministic: it must make the same decisions given the same history. That means no reading the clock directly, no random numbers, no direct network calls, and no unguarded loops. Use the special helpers the framework provides for time and waiting.

!
The classic mistake

Calling the current time or a random number generator inside an orchestrator breaks replay, because the second run gets a different answer than the first. Put such work in an activity function, or use the framework’s own safe versions.

The patterns it supports

1

Function chaining

Run steps in order, passing each result to the next.

2

Fan-out and fan-in

Start many steps in parallel, then wait for all of them and combine the results.

3

Async HTTP API

Return a status address immediately while a long job runs, so callers can check progress.

4

Monitor

Check something repeatedly until a condition is met, without a permanent running process.

5

Human interaction

Pause for an approval, with a timeout that triggers a fallback action.

sequenceDiagram
    participant C as Client Function
    participant O as Orchestrator
    participant A as Activity Functions
    participant S as Storage History
    C->>O: Start the workflow
    O->>A: Run step one
    A-->>O: Result
    O->>S: Save progress
    O->>A: Run steps two and three in parallel
    A-->>O: Both results
    O->>S: Save progress
    O-->>C: Final result when asked
Fig 6. An orchestrator hands work to activities and saves progress after each stage. If the app restarts, the saved history lets it continue.

Durable Functions can keep its history in different back ends. The classic choice uses Azure Storage, and newer back ends aim at higher throughput and easier operation. Check the current documentation to see which options suit your plan and region.

11Reliability: Retries, Poison Messages, and Idempotency

Networks fail, services hiccup, and machines restart. A reliable function expects trouble and handles it gracefully.

What happens when a function fails

If your code throws an error while handling a queue message, the host does not mark the message as handled. The message becomes visible again and is retried. After a set number of attempts, it is moved aside to a poison queue so it cannot block the rest of the work. Service Bus follows a similar idea using its own dead-letter queue after a maximum delivery count. Someone should look at those side queues regularly, because they hold real business events that were never processed.

Retry policies

Some triggers support built-in retry policies, either with a fixed delay between tries or an increasing delay, called exponential back-off. Retries help with brief problems such as a short network glitch. They do not help with permanent ones, such as a message in the wrong format. For those, fail quickly and send the item to the side queue.

At-least-once means possibly twice

Most triggers promise that an event will be processed at least once. After a crash or a lost acknowledgement, the same event can be delivered again. If your function sends an email or charges a card, running it twice is a real problem. The cure is idempotency: designing the function so repeating it produces the same final result as running it once.

  • Use a unique identifier for each event and record which ones are already done.
  • Prefer “set the value to X” over “add one to the value”.
  • Use the downstream service’s own duplicate protection when it has one.
  • Write results in a way that a second write simply replaces the first.
DECISION 1Recommended
Make every queue-driven function idempotent

Because delivery can repeat, record a unique event identifier before performing any side effect and skip events already recorded. This costs a small extra lookup but prevents duplicate emails, charges, and records.

flowchart TB
    Msg["Message arrives"] --> Try["Function runs"]
    Try -->|"Success"| Done["Message marked complete"]
    Try -->|"Error"| Retry{"Tries left?"}
    Retry -->|"Yes"| Wait["Wait, then deliver again"]
    Wait --> Try
    Retry -->|"No"| Poison["Move to poison or dead-letter queue"]
    Poison --> Human["Person or tool inspects it"]
Fig 7. The life of a message that fails. Retries handle brief faults; the poison queue protects the rest of the work from a bad item.

12Networking and Private Access

By default a function app lives on the public internet. Many organizations need it to reach private resources, or to be reachable only from inside their network.

Two directions

Outbound access is about your function calling out to private resources, such as a database that has no public address. Inbound access is about who may call your function. They are configured separately, and mixing them up is a frequent source of confusion.

Outbound: virtual network integration

With virtual network integration, your function app sends its outgoing traffic through a subnet in your Azure virtual network. From there it can reach private endpoints, databases, and on-premises systems linked by a VPN or a private circuit. Flex Consumption, Premium, and Dedicated plans support it. The classic Consumption plan does not offer the same level of private networking.

Inbound: restrictions and private endpoints

  • Access restrictions allow or deny traffic by address range or network rule.
  • Private endpoints give the function app a private address inside your network, so it is not exposed to the internet at all.
  • Front doors and gateways can sit in front of an HTTP-triggered app to add a web firewall and traffic rules.

Do not forget the companion storage

The storage account used by the host is part of the picture. In a locked-down design, you may need to restrict that account too, and give the app private access to it. Missing this step is a classic reason why a freshly locked-down app fails to start.

i
Good to know

Private networking usually pushes you toward Flex Consumption, Premium, or Dedicated plans. Decide this early, because changing plans later can mean rebuilding the app.

13Security: Keys, Identity, and Secrets

A function is small, but it may hold the keys to important data. Security means deciding who may call it and what it may touch.

Who may call it: authorization levels

HTTP-triggered functions have an authorization level. At the anonymous level anyone who knows the address may call it. At the function level the caller must present a key specific to that function or the app. At the admin level only a master key works. Keys are simple shared secrets. They are fine for casual protection, but they are not a replacement for real user sign-in.

Real sign-in: built-in authentication

The platform includes a feature often called Easy Auth that lets users sign in with Microsoft Entra ID or other identity providers before a request reaches your code. For serious applications, rely on this or on a gateway rather than on shared keys.

What it may touch: managed identity

A managed identity is an automatically managed sign-in identity for your function app. Instead of storing a password in settings, you give the identity permission on the target service and let the platform handle the credentials. Many triggers and bindings now support identity-based connections, which replace connection strings with a service address and a role assignment. Following the principle of least privilege, grant only the narrow roles the function really needs, such as permission to read messages from one queue, not to manage the whole storage account.

Secrets that must remain

When a secret cannot be avoided, keep it in Azure Key Vault and refer to it from app settings using a Key Vault reference. The value never appears in your code or deployment files, and rotating it does not require redeploying.

Identity-based access

  • No passwords to store, leak, or rotate
  • Permissions are visible and auditable
  • Works with Azure role assignments

Connection strings and keys

  • Secrets can leak through logs or code
  • Rotation needs coordination
  • Broad access if the key is powerful
flowchart LR
    User["Caller"] -->|"Signs in"| Auth["Built-in authentication"]
    Auth --> App["Function app"]
    App -->|"Managed identity"| Q["Storage queue"]
    App -->|"Managed identity"| KV["Key Vault"]
    KV -. "Role: read secrets only" .-> App
    App -->|"Managed identity"| DB["Database"]
Fig 8. A secure shape: users sign in at the front, and the app reaches other services using its managed identity with narrow roles.

14Monitoring and Troubleshooting with Application Insights

You cannot log into a serverless machine and look around. Telemetry is how you see what your functions are doing.

Application Insights

Application Insights is Azure’s application monitoring service, and it plugs into Functions with a single setting. Once connected, it records each invocation, its duration, whether it succeeded, the logs your code wrote, calls to other services, and unhandled errors. The Azure portal also offers a live metrics view that shows requests and failures within seconds, useful during a release.

What to watch

SignalWhat it tells youTypical action
Failure count and rateHow often runs end in errorAlert when above normal
DurationHow long each run takesInvestigate slow outliers and timeouts
Queue length or age of oldest itemWhether work is piling upScale, tune, or fix a slow downstream service
Cold start delayHow long the first run waitsConsider warm instances
Dependency failuresProblems in databases or APIs you callAdd retries, timeouts, and alerts
Cost trendSpend per day or per appSet budgets and review changes

Asking questions of the logs

Application Insights stores data in tables you can query with the Kusto language, usually shortened to KQL. Typical tables hold requests, traces, exceptions, and dependencies. Because every run carries an operation identifier, you can follow one single event across several functions and see exactly where it went wrong.

Alerts that matter

Dashboards are only useful when someone looks at them, so turn the most important signals into alerts. A rising failure rate, a queue whose oldest message keeps getting older, a sudden jump in duration, and any message arriving in a poison queue all deserve a notification to the people who can act. Keep the list short. A team that receives dozens of noisy alerts soon learns to ignore every one of them, including the one that matters.

Pair alerts with a simple written guide for the team: what each alert means, where to look first, and how to stop the damage. During an incident, a calm checklist beats clever improvisation every time.

Mind the volume

Telemetry is billed by volume. A busy function that logs every detail can cost more in monitoring than in compute. Use sampling to keep a representative portion of routine records, and set log levels so that detailed messages are not kept in production unless needed.

i
Practical habit

Write one meaningful log line at the start and end of each important step, including the unique event identifier. When something fails at three in the morning, that identifier is the thread you pull.

15Development and Deployment

Good tooling makes functions pleasant to build. A good release process keeps them safe to change.

Building locally

You do not need the cloud to start. Azure Functions Core Tools runs the real host on your own computer, so you can write code, trigger it, set breakpoints, and see logs. Combined with a local storage emulator, you can test queues, blobs, and timers offline. Editor extensions for Visual Studio Code and Visual Studio add project templates and one-click publishing.

Getting code to Azure

  • Zip deployment and run from package. The app runs straight from a single deployment package, which is quick and reliable.
  • Continuous delivery. GitHub Actions or Azure Pipelines build, test, and publish on each change.
  • Containers. You can package the app as a container image where plans and hosting support it.
  • Infrastructure as code. Bicep or Terraform describe the function app, plan, storage, identity, and settings, so environments can be rebuilt identically.

Safer releases: slots

On some plans, deployment slots let you publish a new version beside the live one, test it, and then swap. If it misbehaves, swap back. Slot support depends on the plan, so check before relying on it. Where slots are not available, use separate apps and a gateway, or roll out gradually with feature switches.

Testing functions properly

Because a good function is a thin wrapper around ordinary code, most of its logic can be tested with normal unit tests that need no Azure at all. Add a smaller number of integration tests that run the real host against a test queue or storage area, and a handful of end-to-end checks in a test environment before each release. Test the unhappy paths as well: a malformed message, a slow downstream service, and a repeated delivery of the same event.

Keeping environments apart

Use separate function apps, and ideally separate resource groups or subscriptions, for development, testing, and production. Keep settings per environment, and never let a test app point at production data.

flowchart LR
    Dev["Write and test locally"] --> Repo["Source repository"]
    Repo --> Build["Build and automated tests"]
    Build --> Test["Deploy to test app"]
    Test --> Approve{"Checks pass?"}
    Approve -->|"Yes"| Prod["Deploy to production"]
    Approve -->|"No"| Fix["Fix and retry"]
    Fix --> Dev
Fig 9. A typical release path. Automated checks guard each step, and the test app mirrors production settings.

16Pricing and Cost Control

Functions can cost almost nothing, or surprise you. Understanding what you pay for keeps you in the first group.

How the Consumption plan bills

You pay for two things: the number of executions and the resources consumed, measured in gigabyte-seconds. A gigabyte-second is the memory your function uses multiplied by how long it runs. Each month a generous free grant is included, which in the Consumption plan has been one million executions and four hundred thousand gigabyte-seconds. Small workloads often remain entirely free. Always confirm current prices and grants on the official pricing page for your region.

How other plans bill

  • Flex Consumption. Billed for execution time and memory size, with a separate rate for always-ready instances if you use them.
  • Premium. Billed for the cores and memory of reserved and active instances, even when idle, since at least one stays warm.
  • Dedicated. A fixed price for the machines in the plan, regardless of how much your functions run.

The hidden costs

Often forgotten

Storage account

Every app needs one, and heavy host activity or Durable history adds transactions.

Often large

Telemetry

Verbose logging can exceed the compute bill. Sample and trim.

Easy to miss

Downstream services

Each run may call databases, queues, or paid APIs that bill by use.

Network

Data leaving Azure

Data sent out to the internet or across regions can be billed.

Ways to keep the bill small

  • Finish quickly. Time is the main driver of cost, so remove waiting and slow calls.
  • Right-size memory. Too little memory slows the run, but too much wastes money.
  • Cap instance counts so a mistake or an attack cannot scale spending without limit.
  • Avoid infinite loops where a function writes to the thing that triggers it.
  • Set budgets and alerts in Azure cost management.
!
The runaway loop

A blob-triggered function that saves its output into the same container it watches will trigger itself forever. Each cycle costs money and creates files. Use separate containers for input and output.

2
main meters on Consumption: executions and gigabyte-seconds
0
compute cost while idle on scale-to-zero plans
3
side costs to watch: storage, telemetry, downstream calls

17Strengths and Limits: The Honest Trade-offs

No tool is perfect. Knowing where Azure Functions shines and where it struggles saves you from forcing it into the wrong job.

Where it shines

  • No server management or patching
  • Scales from zero to many instances automatically
  • Pay only for work done on usage-based plans
  • Rich set of triggers and bindings for Azure services
  • Fast to build, test, and publish small pieces of logic
  • Durable Functions for long-running workflows

Where it struggles

  • Cold starts on plans that scale to zero
  • Run-time limits on some plans
  • Less control over the underlying machine
  • Many tiny functions can become hard to see as a whole
  • Costs can climb with constant heavy load
  • Local state is lost between runs and instances

Stateless by design

Your function may run on a different instance every time, and an instance may disappear at any moment. Do not keep important data in memory or on the local disk between runs. Put state in a database, a queue, a cache, or in Durable Functions.

The cost curve

For light and uneven work, pay-per-use is wonderfully cheap. For heavy, constant work around the clock, a reserved machine can be cheaper than paying per execution. The crossover point depends on the workload, so estimate both before choosing.

DECISION 2Guideline
Prefer functions for event-driven and uneven work

Choose Azure Functions when work arrives in bursts, can be split into small steps, and tolerates a brief start-up delay. Choose a reserved, always-on service when traffic is constant, latency is strict, or the process needs unusual system access.

flowchart LR
    Low["Low or uneven load"] --> Cheap["Pay-per-use is cheapest"]
    Mid["Moderate steady load"] --> Even["Costs are similar, compare both"]
    High["Heavy constant load"] --> Reserved["Reserved capacity often cheaper"]
Fig 10. A rough picture of the cost crossover. Always model your own numbers rather than trusting a rule of thumb.

18Patterns and Anti-patterns

Teams that use Functions for a while discover the same helpful habits and the same traps. Here are the most common of both.

Patterns that work

Pattern

Queue-based load leveling

Put a queue between a busy producer and a function. The queue absorbs bursts and the function drains it at a steady pace.

Pattern

Event-driven reaction

Let Event Grid tell a function when something changes, instead of polling for changes on a schedule.

Pattern

Fan-out and fan-in

Split a big job into many small tasks, run them in parallel, then gather the results.

Pattern

Thin trigger, shared logic

Keep the function a small wrapper and place business rules in a normal library that is easy to test.

Pattern

Dead-letter and review

Send failures to a side queue and alert someone, so bad messages are never silently lost.

Pattern

Saga with compensation

Undo earlier steps with matching reverse steps when a later step fails, using Durable Functions to coordinate.

Anti-patterns to avoid

Anti-patternWhy it hurtsBetter approach
One giant function doing everythingHard to test, slow, and fails as a wholeSplit into small steps or use an orchestration
Functions calling functions over HTTP in chainsLatency adds up, failures cascade, and costs doubleUse queues or Durable Functions between steps
Waiting inside a function for a long jobYou pay for idle time and risk timeoutsStart the job, return, and be notified when it ends
Creating clients on every callExhausts connections and slows each runCreate once and reuse
Secrets in code or settings filesLeaks and difficult rotationManaged identity and Key Vault references
Ignoring the poison queueReal events are lost unnoticedAlert on side queues and review them
Output that triggers the same functionEndless loop and runaway costSeparate input and output locations
Small, single-purpose, repeat-safe functions joined by queues and events are the shape that scales gracefully.

19Real-World Uses

Functions appear in almost every kind of cloud system, usually as the glue between larger services. These are common shapes. They are illustrative scenarios built from publicly documented capabilities, not claims about any named company.

Web and mobile back ends

A small HTTP-triggered function can serve as the interface for a single-page website or a mobile application: saving a form, returning a list, or calling a payment provider. It pairs naturally with static website hosting in Azure, where the front page is served from storage and functions provide the dynamic parts.

File and image processing

Uploads trigger functions that validate files, scan them, resize images, extract text from documents, or convert formats. Because the work only exists when files arrive, the cost follows the upload volume.

Streaming and Internet of Things data

Devices send readings to Event Hubs, and functions clean, combine, and store them. Batch processing by the trigger lets one instance handle many readings per call.

Scheduled housekeeping

Timer-triggered functions run nightly reports, delete old records, rotate keys, send reminders, and check the health of other systems. These tasks once needed a small always-on server to hold a scheduler.

Integration and glue

When one system must tell another that something happened, a function receives the webhook, reshapes the data, and forwards it. Combined with Service Bus, this creates dependable links between older and newer systems.

AI and data workflows

Functions are often used to call language models or other AI services, prepare documents for search, or run steps of a data pipeline. Durable Functions suits multi-step flows that call models, wait for approvals, and retry on failure. Check current documentation for newer AI-specific bindings and samples.

flowchart LR
    Up["Customer uploads a document"] --> Blob["Blob storage"]
    Blob -->|"Event Grid"| F1["Function: validate and scan"]
    F1 --> Q["Service Bus queue"]
    Q --> F2["Function: extract text"]
    F2 --> DB["Database or search index"]
    F2 -->|"On failure"| DL["Dead-letter queue and alert"]
Fig 11. An illustrative document pipeline. Each function does one job, a queue separates the stages, and failures land somewhere visible.

20Choosing: Functions or Something Else?

Azure has several ways to run code. The right one depends on how your work behaves, not on fashion.

The neighbors

ServiceWhat it isPick it when
Azure FunctionsEvent-driven code snippets, serverlessWork is triggered by events and is split into small steps
Logic AppsVisual workflows with many ready-made connectorsYou prefer designing flows visually and connecting business apps with little code
App ServiceHosting for full web applicationsYou run a complete, always-on web site or interface
Container AppsServerless containers and microservicesYou already ship containers or need custom runtimes and long-running services
Virtual MachinesWhole computers you manageYou need full operating system control or must run legacy software
Batch or AKSLarge compute jobs or full container orchestrationYou need heavy parallel computation or complex cluster control

Functions and Logic Apps together

These two are often combined. A Logic App handles the visual flow and the connectors to business tools, while a function does any custom calculation the connectors cannot. Choose the one your team can maintain with confidence.

flowchart TB
    Start{"What is the work like?"}
    Start -->|"Reacts to events, small steps"| Fn["Azure Functions"]
    Start -->|"Visual flow, many connectors"| LA["Logic Apps"]
    Start -->|"Whole web site, always on"| AS["App Service"]
    Start -->|"Containers or microservices"| CA["Container Apps"]
    Start -->|"Full machine control"| VM["Virtual Machines"]
Fig 12. A first-pass chooser. Many real systems use several of these services together.
i
A simple test

If you can describe the job as “when X happens, do Y, then stop”, Azure Functions is likely a good fit. If you describe it as “keep this running all day”, look at the other services first.

21Frequently Asked Questions

Short answers to the questions people ask most often when they meet Azure Functions.

Q1Is serverless really without servers?
No. Servers exist, but Microsoft runs them. You do not choose, patch, or watch them, and on usage-based plans you do not pay for them while idle.
Q2What is the difference between a function and a function app?
A function is one piece of code with one trigger. A function app is the container that holds one or more functions and gives them a shared plan, settings, identity, and address.
Q3Can a function have more than one trigger?
No. Each function has exactly one trigger. If the same logic must react to two events, create two functions that share a common library.
Q4Why is my first request slow?
You are probably seeing a cold start. No instance was running, so Azure had to allocate one and load your code. Warm instances, a smaller package, or a plan with always-ready instances reduce it.
Q5Which hosting plan should I start with?
For most new serverless apps, Flex Consumption is a sensible default because it supports private networking and optional warm instances. Use Consumption for the lightest workloads, Premium or Dedicated for steady load, and always check current regional support.
Q6Why did my function run twice for one message?
Most triggers deliver at least once, so a crash or a lost acknowledgement can cause a repeat. Make your function idempotent by recording event identifiers and skipping those already handled.
Q7How long can a function run?
It depends on the plan. Consumption has a short default and a maximum of ten minutes, while other plans allow much longer. For truly long processes, use Durable Functions rather than one huge run.
Q8How do I keep secrets out of my code?
Prefer managed identity so there is no secret at all. Where one is unavoidable, store it in Key Vault and reference it from app settings.
Q9Can functions reach a private database?
Yes, through virtual network integration, which is available on Flex Consumption, Premium, and Dedicated plans. Remember to secure the companion storage account too.
Q10What is Durable Functions for?
It lets you write multi-step, long-running workflows as normal code. The framework saves progress, retries failed steps, and resumes after restarts, supporting patterns such as chaining, fan-out, and human approval.
Q11Why is my bill higher than expected?
Look beyond compute. Telemetry volume, storage transactions, downstream services, and runaway loops are common causes. Cap instance counts, sample logs, and set budget alerts.
Q12Can I test functions on my own computer?
Yes. Core Tools runs the real host locally, and a storage emulator supplies queues, blobs, and tables, so you can develop and debug before publishing.
Q13Should I use Functions or Logic Apps?
Use Logic Apps when you want a visual flow and ready-made connectors. Use Functions when you need custom code or fine control. Many solutions use both.

22Summary and Key Takeaways

Azure Functions lets you run small pieces of code in response to events, while Azure handles the machines, the scaling, and the patching. Used well, it is a cheap, quick, and flexible building block.

Remember these seven points

  • A function has exactly one trigger, and a function app groups functions that share a plan, settings, and identity.
  • Triggers decide when code runs, and bindings give declared access to data without extra plumbing.
  • The hosting plan sets scaling, cold start behavior, private networking, and billing, so choose it early and deliberately.
  • Design for repeat delivery: make functions idempotent and watch the poison or dead-letter queues.
  • Use Durable Functions for long, multi-step workflows, and keep orchestrator code deterministic.
  • Secure with managed identity, narrow roles, Key Vault references, and real sign-in instead of shared keys.
  • Watch telemetry and cost together, cap scaling to protect downstream systems, and prefer other services when work is constant or needs full machine control.