Set up the local development environment for Functions
Tip
See the Text and images tab for more details!
A productive local development workflow lets you build and test Azure Functions on your own machine before deploying to Azure. Local development eliminates the need to provision cloud resources during the early stages of development, reduces the feedback loop between writing code and seeing results, and helps you catch configuration issues before they reach production. This unit walks through setting up Visual Studio Code with the Azure Functions extension, understanding the project structure, configuring emulators for dependent services, and debugging functions locally.
Install Azure Functions Core Tools
The Azure Functions extension for Visual Studio Code provides an integrated development experience for creating, testing, and deploying functions. The extension depends on Azure Functions Core Tools, which provides the local version of the Azure Functions runtime. Core Tools includes the same trigger and binding processing logic that runs in Azure, so functions behave consistently between local and cloud environments.
You can install Core Tools through several package managers depending on your operating system. On macOS, use Homebrew with brew tap azure/functions && brew install azure-functions-core-tools@4. On Linux, use the appropriate package manager for your distribution. On Windows, use winget with winget install Microsoft.Azure.FunctionsCoreTools. After installation, verify the setup by running func --version in your terminal.
You can create a new Functions project in Visual Studio Code by running the Azure Functions: Create Function... command from the Command Palette. This command walks you through selecting a project folder, language runtime, and trigger template. It scaffolds the project structure, creates a starter function, and generates the .vscode/launch.json, .vscode/tasks.json, and .vscode/extensions.json files that enable integrated debugging and task running. Starting the local runtime with func start or by pressing F5 launches the Functions host and exposes HTTP endpoints at http://localhost:7071 by default.
Note
You can also create projects from the command line with func init and func new. The command func init my-ai-backend --python scaffolds the project structure, and func new --name classify --template "HTTP trigger" adds a function. However, these commands don't generate the .vscode configuration files, so you need to create them manually or run Azure Functions: Create Function... afterward.
Understand local project structure
Azure Functions projects follow a specific directory layout that the runtime uses to discover functions, load configuration, and manage dependencies. Understanding this structure helps you organize your code, troubleshoot startup issues, and configure deployments correctly.
The Python v2 programming model uses a single entry point file, typically function_app.py, where you define functions and their triggers using decorators. This approach consolidates function definitions in one place rather than spreading them across multiple directories with individual function.json files.
A single function_app.py file works well for projects with a handful of functions, such as an HTTP trigger for inference and a queue trigger for batch processing. As the number of functions grows, you can split definitions into separate files using blueprints. Blueprints let you define functions in individual modules and register them with the main function_app.py entry point, which keeps each file focused and easier to maintain.
The host.json file contains global runtime configuration that applies to all functions in the app. Settings in this file control behavior such as function timeout duration, logging levels, extension bundle versions, and concurrency limits for specific trigger types. Changes to host.json affect every function in the app, so treat it as shared infrastructure configuration.
The local.settings.json file stores application settings and connection strings for local development. The runtime reads values from the Values object in this file and exposes them as environment variables to your function code. This file is excluded from source control by the default .gitignore because it typically contains connection strings and other sensitive values. Never commit local.settings.json to a repository.
The .vscode folder contains Visual Studio Code configuration files generated by the Azure Functions extension. The launch.json file configures the debugger to attach to the Core Tools process, tasks.json defines the build and host start tasks, and extensions.json recommends the Azure Functions extension to other developers who open the project. These files enable F5 debugging and should be committed to source control so the team shares a consistent development experience.
Dependency management files, such as requirements.txt for Python or package.json for Node.js, list the packages your functions need. The runtime installs these dependencies during deployment or when you run pip install -r requirements.txt locally. The .funcignore file specifies files and folders to exclude from deployment packages, keeping your deployed artifact lean by omitting test files, documentation, and other development-only resources.
Use Azurite for the Functions runtime storage dependency
The Azure Functions runtime requires a storage account connection (configured through the AzureWebJobsStorage setting) to manage internal operations such as coordinating trigger processing across instances, tracking timer trigger state, and storing function access keys. This dependency exists regardless of whether your application code uses Azure Storage directly. Without a valid storage connection, non-HTTP triggers such as queue triggers and timer triggers don't work.
For local development, you can route the runtime's storage dependency to the Azurite emulator by setting AzureWebJobsStorage to UseDevelopmentStorage=true in local.settings.json. Azurite provides local implementations of Azure Blob Storage, Queue Storage, and Table Storage that behave like the cloud service. This approach eliminates the need to create an Azure Storage account for development purposes.
The simplest way to run Azurite is through the Azurite Visual Studio Code extension, which starts the emulator automatically when you launch a debugging session. You can also install Azurite globally through npm with npm install -g azurite and start it from the command line with azurite --silent. The following configuration in local.settings.json connects the runtime to Azurite:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python"
}
}
When you deploy a function app to Azure, the platform creates or requires a linked storage account automatically. The UseDevelopmentStorage=true setting applies only to local development and has no effect in the cloud.
Use emulators and local services for connected resources
Beyond the runtime storage dependency, AI backends typically connect to databases, caches, and other services for application-level data operations. Several emulators and local alternatives help you test these connections without provisioning cloud resources, keeping your development workflow fast and cost-free during the build phase.
The Azure Cosmos DB emulator runs locally on Windows, Linux, and macOS (through Docker). It supports the NoSQL API and provides a local endpoint that behaves like the cloud service. You can use the emulator's connection string in local.settings.json and switch to the production endpoint when deploying. This approach works well for AI applications that store inference results, session state, or vector embeddings in Cosmos DB.
Azure Database for PostgreSQL doesn't have an Azure-specific emulator, but PostgreSQL is open source. You can run a local instance through Docker with docker run -e POSTGRES_PASSWORD=mysecret -p 5432:5432 postgres or install PostgreSQL natively. Your function code connects identically to a local instance and the Azure-managed service, because both use the same PostgreSQL wire protocol.
Azure Managed Redis doesn't have an Azure-specific emulator either, but Redis is open source. You can run a local instance through Docker with docker run -p 6379:6379 redis or install Redis natively. For AI backends that use Redis for caching inference results or managing rate limiting, local Redis provides a functional development target.
You can configure connection strings for all local services in the Values section of local.settings.json. When deploying to Azure, swap these values to production connection strings or identity-based connections. This separation between local and production configuration keeps your development environment isolated from cloud resources.
Debug locally with Visual Studio Code
The Azure Functions extension for Visual Studio Code provides integrated debugging that lets you set breakpoints, inspect variables, and step through trigger execution in your functions. Debugging locally gives you the same visibility into function behavior that you'd have with any other application, which is essential for troubleshooting AI pipeline logic and data transformation issues.
You can configure launch settings in the .vscode/launch.json file to attach the debugger to the Core Tools process. When you press F5, Visual Studio Code starts Core Tools, attaches the debugger, and opens the terminal showing the function endpoints. You can set breakpoints in your function code and step through execution when a trigger fires.
For testing HTTP triggers, you can use .http files directly in Visual Studio Code with the REST Client extension, or tools like curl from the command line. The following example shows how to test an HTTP trigger locally:
curl -X POST http://localhost:7071/api/classify \
-H "Content-Type: application/json" \
-d '{"document_url": "https://storage.example.com/docs/sample.pdf"}'
You can also test Service Bus triggers locally by configuring a Service Bus connection string in local.settings.json and sending messages through the Azure portal's Service Bus Explorer or the Azure CLI with az servicebus queue message send. Timer triggers fire automatically based on their CRON schedule when Core Tools runs.
Synchronize settings between local and Azure
Keeping local development settings aligned with your Azure function app's configuration prevents environment-specific bugs and simplifies the transition from development to production. The Azure Functions extension and Core Tools both provide ways to synchronize settings between your local environment and Azure.
In Visual Studio Code, you can right-click your function app in the Azure Resources panel and select Download Remote Settings to pull production app settings into your local local.settings.json file. You can also select Upload Local Settings to push local values to Azure. These commands provide a quick way to keep environments aligned without leaving the editor.
The equivalent CLI commands are func azure functionapp fetch-app-settings <app-name> to download settings and func azure functionapp publish <app-name> to deploy code along with local settings. You can encrypt the local settings file with func settings encrypt to protect any secrets stored on your development machine. The encrypted file can only be decrypted by Core Tools on the same machine.
Be mindful of which settings you push to Azure, because overwriting production settings with local development values can break your deployed application. A common practice is to manage production settings through Azure CLI (az functionapp config appsettings set) or infrastructure-as-code templates, and reserve local.settings.json for development-only values.