# How to Host Agents on Azure Functions with Stateful Execution in Agent Framework

> Deploy stateful AI agents on Azure Functions with Agent Framework by wrapping your workflow in AgentFunctionApp. Persist data across steps using set_state and get_state without bloating message payloads.

- Repository: [Microsoft/agent-framework](https://github.com/microsoft/agent-framework)
- Tags: how-to-guide
- Published: 2026-04-05

---

**Wrap your Agent Framework workflow in an `AgentFunctionApp` to deploy stateful AI agents on Azure Functions, using `WorkflowContext.set_state()` and `get_state()` to persist data across orchestration steps without bloating message payloads.**

Hosting agents on Azure Functions with stateful execution in Agent Framework enables serverless, durable AI workflows that maintain context across multiple steps. By leveraging Durable Functions orchestration combined with the `AgentFunctionApp` bootstrap class in the `microsoft/agent-framework` repository, you can build scalable agent systems where large payloads like email bodies or documents persist in a **SharedState** dictionary rather than passing through every message.

## Architecture of Stateful Agent Execution

The stateful execution model relies on three core components working together inside a Durable Functions orchestration.

**AgentFunctionApp** extends the Durable Functions `DFAppBase` class to automatically register HTTP endpoints, health checks, and activity triggers. When you wrap a `Workflow` inside this app, the framework handles serialization and deserialization of the orchestration state between function activations.

**SharedState** is a per-orchestration dictionary that survives across activity invocations. Unlike standard message passing where large payloads travel through every executor step, SharedState lives in-memory and is serialized only when the function scales or checkpoints.

**WorkflowContext** provides the interface to this state through `set_state(key, value)` and `get_state(key)` methods. In [`python/samples/04-hosting/azure_functions/09_workflow_shared_state/function_app.py`](https://github.com/microsoft/agent-framework/blob/main/python/samples/04-hosting/azure_functions/09_workflow_shared_state/function_app.py), the `store_email` executor demonstrates this pattern by storing a UUID-keyed `Email` object and updating a `CURRENT_EMAIL_ID_KEY` reference for downstream steps to retrieve.

## Prerequisites for Local Development

Before running stateful agents locally, ensure your environment includes:

- **Azure Functions Core Tools 4.x** – Required to run `func start` and publish to Azure
- **Azurite storage emulator** – Provides the durable task hub’s storage tables and blobs for local orchestration persistence
- **Azure AI Foundry project** – Must have an OpenAI model deployed (referenced as `gpt-4o` or similar) for the `FoundryChatClient`
- **Azure CLI authentication** – Run `az login` to enable `AzureCliCredential` for seamless authentication without embedded secrets
- **Python 3.9+** – With a virtual environment to isolate dependencies listed in [`requirements.txt`](https://github.com/microsoft/agent-framework/blob/main/requirements.txt)

## Implementation Steps

Follow these steps to run the stateful workflow sample locally.

1. **Clone and configure the repository**

   ```bash
   git clone https://github.com/microsoft/agent-framework.git
   cd agent-framework/python/samples/04-hosting/azure_functions/09_workflow_shared_state
   python -m venv .venv
   source .venv/bin/activate  # On Windows: .venv\Scripts\activate

   pip install -r requirements.txt
   ```

2. **Configure local settings**

   Create [`local.settings.json`](https://github.com/microsoft/agent-framework/blob/main/local.settings.json) in the sample root with your Azure AI Foundry endpoint:

   ```json
   {
     "Values": {
       "FOUNDRY_PROJECT_ENDPOINT": "https://<your-project>.services.ai.azure.com/api/projects/<your-project>",
       "FOUNDRY_MODEL": "gpt-4o"
     }
   }
   ```

3. **Start the storage emulator**

   ```bash
   azurite --silent
   ```

4. **Launch the function host**

   ```bash
   func start
   ```

5. **Trigger the workflow**

   Send a test request to the HTTP endpoint created by `AgentFunctionApp`:

   ```bash
   curl -X POST http://localhost:7071/api/workflow/run \
        -H "Content-Type: application/json" \
        -d '"URGENT! You have won $1,000,000! Click here to claim!"'
   ```

   The orchestration will either return a spam warning or a drafted email response, depending on the detection agent’s analysis.

## Code Example: Persisting State Across Executors

The core logic in [`function_app.py`](https://github.com/microsoft/agent-framework/blob/main/function_app.py) demonstrates how to keep large payloads out of message traffic. The `store_email` executor receives raw email text, generates a UUID, and persists the full content in SharedState while passing only the ID through the workflow.

```python

# python/samples/04-hosting/azure_functions/09_workflow_shared_state/function_app.py

import os
from uuid import uuid4
from dataclasses import dataclass
from typing import Any

from agent_framework import (
    Agent, AgentExecutorRequest, AgentExecutorResponse,
    Message, Workflow, WorkflowBuilder, WorkflowContext, executor,
)
from agent_framework.foundry import FoundryChatClient
from agent_framework_azurefunctions import AgentFunctionApp
from azure.identity.aio import AzureCliCredential

EMAIL_STATE_PREFIX = "email:"
CURRENT_EMAIL_ID_KEY = "current_email_id"

@dataclass
class Email:
    """Object stored in SharedState to avoid passing large payloads."""
    email_id: str
    email_content: str

@executor(id="store_email")
async def store_email(email_text: str, ctx: WorkflowContext[AgentExecutorRequest]) -> None:
    """Persist raw email and kick off spam detection."""
    new_email = Email(email_id=str(uuid4()), email_content=email_text)
    
    # Store large payload in shared state

    ctx.set_state(f"{EMAIL_STATE_PREFIX}{new_email.email_id}", new_email)
    ctx.set_state(CURRENT_EMAIL_ID_KEY, new_email.email_id)

    # Pass only lightweight metadata to next step

    await ctx.send_message(
        AgentExecutorRequest(
            messages=[Message(role="user", contents=[new_email.email_content])],
            should_respond=True
        )
    )

```

Subsequent executors retrieve the original email using `ctx.get_state(f"{EMAIL_STATE_PREFIX}{email_id}")` without requiring the payload to transit through the Durable Functions message queue.

## Deploying to Azure

To move from local development to production:

1. **Create a Function App** using the Consumption plan or Premium plan (required for Durable Functions) in your target Azure region
2. **Configure application settings** in the Azure portal or via CLI:
   - `FOUNDRY_PROJECT_ENDPOINT`
   - `FOUNDRY_MODEL`
   - `TASKHUB_NAME` (optional, for custom task hub isolation)
3. **Publish the code** using Azure Functions Core Tools:

   ```bash
   func azure functionapp publish <APP_NAME>
   ```

4. **Configure authentication** – Ensure the Function App’s managed identity has `Reader` role on your Azure AI Foundry project, or store service principal credentials in Azure Key Vault and reference them in the application settings.

## Summary

- **AgentFunctionApp** bootstraps Agent Framework workflows inside Durable Functions orchestrations, handling HTTP triggers and activity routing automatically.
- **SharedState** provides durable, per-orchestration storage via `WorkflowContext.set_state()` and `get_state()`, keeping large payloads out of message queues.
- The `store_email` pattern in [`function_app.py`](https://github.com/microsoft/agent-framework/blob/main/function_app.py) demonstrates production-ready state management: generate a UUID, persist the object, and pass only the reference ID.
- Local development requires Azurite for durable task hub storage and `AzureCliCredential` for seamless Azure AI Foundry authentication.
- Deployment relies on standard Azure Functions Core Tools with additional app settings for Foundry endpoints.

## Frequently Asked Questions

### What is SharedState and how does it differ from regular message payloads?

SharedState is an in-memory dictionary unique to each Durable Functions orchestration instance. Unlike message payloads, which pass through the Durable Functions message queue and have size limits and serialization overhead, SharedState persists arbitrary Python objects via `WorkflowContext.set_state()` and survives across activity invocations without leaving the execution context.

### How does AgentFunctionApp differ from a standard Azure Functions app?

According to the `microsoft/agent-framework` source code, `AgentFunctionApp` inherits from `DFAppBase` (the Durable Functions base class) and extends it with Agent Framework-specific registration logic. It automatically wires HTTP endpoints to workflow runners, registers health checks, and injects the `WorkflowContext` into executor functions, whereas a standard Functions app requires manual trigger binding and state management.

### What authentication method does the sample use for Azure AI Foundry?

The sample uses `AzureCliCredential` from the Azure Identity library, which picks up the authentication context from `az login`. This eliminates the need to store secrets in [`local.settings.json`](https://github.com/microsoft/agent-framework/blob/main/local.settings.json) during development. In production, you should configure the Function App’s managed identity or reference service principal credentials from Azure Key Vault.

### Can I test stateful workflows without deploying to Azure?

Yes. The repository includes support for local execution via the Azurite storage emulator, which provides the durable task hub’s tables and blobs required for stateful orchestration. Running `func start` with Azurite active allows full testing of `set_state` and `get_state` persistence before cloud deployment.