หมายเหตุ
การเข้าถึงหน้านี้ต้องได้รับการอนุญาต คุณสามารถลอง ลงชื่อเข้าใช้หรือเปลี่ยนไดเรกทอรีได้
การเข้าถึงหน้านี้ต้องได้รับการอนุญาต คุณสามารถลองเปลี่ยนไดเรกทอรีได้
Aspire is a toolchain for building, running, debugging, and deploying distributed applications. The Aspire Azure Functions integration enables you to develop, debug, and orchestrate an Azure Functions project as part of an Aspire AppHost. The .NET examples in this article use the isolated worker model.
Prerequisites
Set up your development environment for using Azure Functions with Aspire:
Install the Aspire prerequisites, including the .NET SDK required by your AppHost.
Install the Aspire Azure Functions hosting integration from the AppHost directory.
aspire add Aspire.Hosting.Azure.FunctionsInstall the Azure Functions Core Tools.
If you use Visual Studio, install the latest Visual Studio and Azure Functions tooling updates:
- Go to Tools > Options.
- Under Projects and Solutions, select Azure Functions.
- Select Check for updates and install updates as prompted.
For more information about the integration package and supported AppHost APIs, see Set up Azure Functions in the AppHost.
Solution structure
A solution that uses Azure Functions and Aspire has multiple projects, including an AppHost and one or more Functions projects.
The AppHost is the entry point for your application. It orchestrates the setup of the components of your application, including the Functions project.
The solution typically also includes a service defaults project. This project provides a set of default services and configurations to be used across projects in your application.
AppHost project
To successfully configure the integration, ensure that the AppHost project meets the following requirements:
- The AppHost references Aspire.Hosting.Azure.Functions. This package defines the integration.
- A C# AppHost references a Functions project and calls
AddAzureFunctionsProject<TProject>(), or callsAddAzureFunctionsProject(name, projectPath)with the path to the project file. TypeScript AppHosts use the project-path form ofaddAzureFunctionsProject. - Use
AddAzureFunctionsProjectinstead ofAddProject. A Functions project added by usingAddProjectcan't start properly.
The following example shows a minimal AppHost.cs file for a C# AppHost project:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject");
builder.Build().Run();
Azure Functions project
To successfully configure the integration, make sure that the Azure Functions project meets the following requirements:
Target .NET 8 or later, use the .NET 9 SDK or later, and use the isolated worker model.
Reference Microsoft.Azure.Functions.Worker, Microsoft.Azure.Functions.Worker.Sdk, and, for HTTP triggers, Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.
Your
Program.csfile must use theIHostApplicationBuilderversion of the host instance startup. This requirement means that you must useFunctionsApplication.CreateBuilder(args).If your solution includes a service defaults project, ensure that your Functions project is configured to use it:
- The Functions project should include a project reference to the service defaults project.
- Before you build
IHostApplicationBuilderinProgram.cs, include a call tobuilder.AddServiceDefaults().
The following example shows a minimal Program.cs file for a Functions project used in Aspire:
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.ConfigureFunctionsWebApplication();
builder.Build().Run();
This example doesn't include the default Application Insights configuration that appears in many other Program.cs examples and in the Azure Functions templates. Instead, you configure OpenTelemetry integration in Aspire by calling the builder.AddServiceDefaults() method.
To get the most out of the integration, consider the following guidelines:
- Don't include any direct Application Insights integrations in the Functions project. Monitoring in Aspire is instead handled through its OpenTelemetry support. You can configure Aspire to export data to Azure Monitor through the service defaults project.
- When Aspire runs the Functions project, prefer settings injected by the AppHost. You can keep equivalent settings in
local.settings.jsonfor running the project independently withfunc start; Aspire-injected environment variables override them.
Connection configuration with Aspire
The AppHost defines resources and helps you create connections between them by using code. This section shows how to configure and customize connections that your Azure Functions project uses.
Aspire includes default connection permissions that can help you get started. However, these permissions might not be appropriate or sufficient for your application.
For scenarios that use Azure role-based access control (RBAC), you can customize permissions by calling the WithRoleAssignments() method on the project resource. When you call WithRoleAssignments(), all default role assignments are removed, and you must explicitly define the full set role assignments that you want. If you host your application on Azure Container Apps, using WithRoleAssignments() also requires that you call AddAzureContainerAppEnvironment() on DistributedApplicationBuilder.
Azure Functions host storage
Azure Functions requires a host storage connection (AzureWebJobsStorage) for several of its core behaviors. When you call AddAzureFunctionsProject<TProject>() in your AppHost, you create an AzureWebJobsStorage connection by default and provide it to the Functions project. This default connection uses the Azure Storage emulator for local development runs and automatically provisions a storage account when you deploy it. For more control, replace this connection by calling .WithHostStorage() on the Functions project resource.
The default permissions that Aspire sets for the host storage connection depend on whether you call WithHostStorage() or not. Adding WithHostStorage() removes a Storage Account Contributor assignment. The following table lists the default permissions that Aspire sets for the host storage connection:
| Host storage connection | Default roles |
|---|---|
No call to WithHostStorage() |
Storage Blob Data Contributor, Storage Queue Data Contributor, Storage Table Data Contributor, Storage Account Contributor |
Calling WithHostStorage() |
Storage Blob Data Contributor, Storage Queue Data Contributor, Storage Table Data Contributor |
The following example shows a minimal AppHost.cs file that replaces the host storage and specifies a role assignment:
using Azure.Provisioning.Storage;
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureContainerAppEnvironment("myEnv");
var myHostStorage = builder.AddAzureStorage("myHostStorage");
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(myHostStorage)
.WithRoleAssignments(myHostStorage, StorageBuiltInRole.StorageBlobDataOwner);
builder.Build().Run();
Note
Storage Blob Data Owner is the role that we recommend for the basic needs of the host storage connection. Your app might encounter problems if the connection to the blob service has only the Aspire default of Storage Blob Data Contributor.
For production scenarios, include calls to both WithHostStorage() and WithRoleAssignments(). You can then set this role explicitly, along with any others that you need.
Trigger and binding connections
Your triggers and bindings reference connections by name. The following Aspire integrations provide these connections through a call to WithReference() on the project resource:
The following example shows a minimal AppHost.cs file that configures a queue trigger. In this example, the corresponding queue trigger has its Connection property set to MyQueueTriggerConnection, so the call to WithReference() specifies the name.
var builder = DistributedApplication.CreateBuilder(args);
var myAppStorage = builder.AddAzureStorage("myAppStorage").RunAsEmulator();
var queues = myAppStorage.AddQueues("queues");
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithReference(queues, "MyQueueTriggerConnection");
builder.Build().Run();
For other integrations, calls to WithReference set the configuration in a different way. They make the configuration available to Aspire client integrations, but not to triggers and bindings. For these integrations, call WithEnvironment() to pass the connection information for the trigger or binding to resolve.
The following example shows how to set the environment variable MyBindingConnection for a resource that exposes a connection string expression:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection", otherIntegration.Resource.ConnectionStringExpression);
If you want both Aspire client integrations and the system of triggers and bindings to use a connection, you can configure both WithReference() and WithEnvironment().
For some resources, the structure of a connection might be different between when you run it locally and when you publish it to Azure. In the previous example, otherIntegration could be a resource that runs as an emulator, so ConnectionStringExpression would return an emulator connection string. However, when the resource is published, Aspire might set up an identity-based connection, and ConnectionStringExpression would return the service's URI. In this case, to set up identity-based connections for Azure Functions, you might need to provide a different environment variable name.
The following example uses builder.ExecutionContext.IsPublishMode to conditionally add the necessary suffix:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection" + (builder.ExecutionContext.IsPublishMode ? "__serviceUri" : ""), otherIntegration.Resource.ConnectionStringExpression);
For details on the connection formats that each binding supports, and the permissions that those formats require, consult the binding's reference pages.
For more information about how Functions code reads values injected by WithReference, see Azure Functions runtime configuration.
Hosting the application
Aspire supports Azure Container Apps deployment for Functions projects. You can also use the separate preview App Service integration to target a container-capable function app:
- Deploy as a container app
- Deploy as a function app using the preview App Service integration
In both cases, your project is deployed as a container. Aspire takes care of building the container image for you and pushing it to Azure Container Registry.
Deploy as a container app
When your AppHost targets Azure Container Apps, Aspire sets up scaling rules for your Functions project using KEDA. When using Azure Container Apps, you need to perform extra setup for function keys. For more information, see Access keys on Azure Container Apps.
Deploy the configured AppHost by running aspire deploy. For more information, see Deploy to Azure Container Apps and aspire deploy.
Access keys on Azure Container Apps
Several Azure Functions scenarios use access keys to provide a basic mitigation against unwanted access. For example, HTTP trigger functions by default require an access key to be invoked, though this requirement can be disabled using the AuthLevel property. See Work with access keys in Azure Functions for scenarios which may require a key.
When you deploy a Functions project by using Aspire to Azure Container Apps, the system doesn't automatically create or manage Functions access keys. If you need to use access keys, you can manage them as part of your AppHost setup. This section shows you how to create an extension method that you can call from your AppHost's AppHost.cs file to create and manage access keys. This approach uses Azure Key Vault to store the keys and mounts them into the container app as secrets.
Note
The behavior here relies on the ContainerApps secret provider, which requires Functions host version 4.1044.0 or later.
These steps require Bicep version 0.38.3 or later. You can check your Bicep version by running bicep --version from a command prompt. If you have the Azure CLI installed, you can use az bicep upgrade to quickly update Bicep to the latest version.
Add the following NuGet packages to your AppHost project:
Create a new class in your AppHost project and include the following code:
using Aspire.Hosting.Azure;
using Azure.Provisioning.AppContainers;
namespace Aspire.Hosting;
internal static class Extensions
{
private record SecretMapping(string OriginalName, IAzureKeyVaultSecretReference Reference);
public static IResourceBuilder<T> PublishWithContainerAppSecrets<T>(
this IResourceBuilder<T> builder,
IResourceBuilder<AzureKeyVaultResource>? keyVault = null,
string[]? hostKeyNames = null,
string[]? systemKeyExtensionNames = null)
where T : AzureFunctionsProjectResource
{
if (!builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
{
return builder;
}
keyVault ??= builder.ApplicationBuilder.AddAzureKeyVault("functions-keys");
var hostKeysToAdd = (hostKeyNames ?? []).Append("default").Select(k => $"host-function-{k}");
var systemKeysToAdd = systemKeyExtensionNames?.Select(k => $"host-systemKey-{k}_extension") ?? [];
var secrets = hostKeysToAdd.Union(systemKeysToAdd)
.Select(secretName => new SecretMapping(
secretName,
CreateSecretIfNotExists(builder.ApplicationBuilder, keyVault, secretName.Replace("_", "-"))
)).ToList();
return builder
.WithReference(keyVault)
.WithEnvironment("AzureWebJobsSecretStorageType", "ContainerApps")
.PublishAsAzureContainerApp((infra, app) => ConfigureFunctionsContainerApp(infra, app, builder.Resource, secrets));
}
private static void ConfigureFunctionsContainerApp(
AzureResourceInfrastructure infrastructure,
ContainerApp containerApp,
IResource resource,
List<SecretMapping> secrets)
{
const string volumeName = "functions-keys";
const string mountPath = "/run/secrets/functions-keys";
var appIdentityAnnotation = resource.Annotations.OfType<AppIdentityAnnotation>().Last();
var containerAppIdentityId = appIdentityAnnotation.IdentityResource.Id.AsProvisioningParameter(infrastructure);
var containerAppSecretsVolume = new ContainerAppVolume
{
Name = volumeName,
StorageType = ContainerAppStorageType.Secret
};
foreach (var mapping in secrets)
{
var secret = mapping.Reference.AsKeyVaultSecret(infrastructure);
containerApp.Configuration.Secrets.Add(new ContainerAppWritableSecret()
{
Name = mapping.Reference.SecretName.ToLowerInvariant(),
KeyVaultUri = secret.Properties.SecretUri,
Identity = containerAppIdentityId
});
containerAppSecretsVolume.Secrets.Add(new SecretVolumeItem
{
Path = mapping.OriginalName.Replace("-", "."),
SecretRef = mapping.Reference.SecretName.ToLowerInvariant()
});
}
containerApp.Template.Containers[0].Value!.VolumeMounts.Add(new ContainerAppVolumeMount
{
VolumeName = volumeName,
MountPath = mountPath
});
containerApp.Template.Volumes.Add(containerAppSecretsVolume);
}
public static IAzureKeyVaultSecretReference CreateSecretIfNotExists(
IDistributedApplicationBuilder builder,
IResourceBuilder<AzureKeyVaultResource> keyVault,
string secretName)
{
var secretParameter = ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $"param-{secretName}", special: false);
builder.AddBicepTemplateString($"key-vault-key-{secretName}", """
param location string = resourceGroup().location
param keyVaultName string
param secretName string
@secure()
param secretValue string
// Reference the existing Key Vault
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
name: keyVaultName
}
// Deploy the secret only if it does not already exist
@onlyIfNotExists()
resource newSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
parent: keyVault
name: secretName
properties: {
value: secretValue
}
}
""")
.WithParameter("keyVaultName", keyVault.GetOutput("name"))
.WithParameter("secretName", secretName)
.WithParameter("secretValue", secretParameter);
return keyVault.GetSecret(secretName);
}
}
You can then use this method in your AppHost's AppHost.cs file:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(storage)
.WithExternalHttpEndpoints()
.PublishWithContainerAppSecrets(systemKeyExtensionNames: ["mcp"]);
This example uses a default key vault created by the extension method. It results in a default key and a system key for use with the Model Context Protocol extension.
To use these keys from clients, you need to retrieve them from the key vault.
Deploy as a function app
Note
Deploying as a function app requires the Aspire Azure App Service integration, which is currently in preview.
You can configure Aspire to deploy to a function app by using the Aspire Azure App Service integration. Because Aspire deploys the Functions project as a container, the hosting plan for your function app must support deploying containerized applications.
To deploy your Aspire Functions project as a function app, follow these steps:
- From the AppHost directory, run
aspire add Aspire.Hosting.Azure.AppServiceto add the Aspire.Hosting.Azure.AppService NuGet package. - In the
AppHost.csfile, callAddAzureAppServiceEnvironment()on yourIDistributedApplicationBuilderinstance to create an App Service plan. Note that despite the name, this does not provision an App Service Environment resource. - On the Functions project resource, call
.WithExternalHttpEndpoints(). This is required for deploying with the Aspire Azure App Service integration. - On the Functions project resource, call
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux")to customize that project as a function app in the plan.
Important
Make sure that you set the app.Kind property to "functionapp,linux". This setting ensures the resource is created as a function app, which affects experiences for working with your application.
The following example shows a minimal AppHost.cs file that deploys a Functions project as a function app:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureAppServiceEnvironment("functions-env");
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithExternalHttpEndpoints()
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux");
builder.Build().Run();
This configuration creates a Premium V3 plan. When using a dedicated App Service plan SKU, scaling isn't event-based. Instead, scaling is managed through the App Service plan settings.
Considerations and best practices
Consider the following points when you're evaluating the integration of Azure Functions with Aspire:
Trigger and binding configuration through Aspire is currently limited to specific integrations. For details, see Connection configuration with Aspire in this article.
Your function project's
Program.csfile should use theIHostApplicationBuilderversion of host instance startup. By usingIHostApplicationBuilder, you can callbuilder.AddServiceDefaults()to add Aspire Service Defaults to your Functions project.Aspire uses OpenTelemetry for monitoring. You can configure Aspire to export data to Azure Monitor through the service defaults project.
In many other Azure Functions contexts, you might include direct integration with Application Insights by registering the worker service. Don't register a second, direct Application Insights pipeline when you're using Aspire Service Defaults.
For Functions projects enlisted into an Aspire orchestration, the AppHost should provide most application configuration. You can use
local.settings.jsonto run the Functions project independently withfunc start. When Aspire runs the project, Aspire-injected environment variables override values with the same names inlocal.settings.json.Avoid starting a second Azure Storage emulator for connections that the AppHost manages. Competing emulator instances can cause port and storage conflicts.
For more information, see Azure Functions runtime configuration and Aspire telemetry.