Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Aspire est une chaîne d’outils dédiée à la création, l’exécution, le débogage et le déploiement d’applications distribuées. L’intégration Aspire Azure Functions vous permet de développer, déboguer et orchestrer un projet Azure Functions dans le cadre d’un Aspire AppHost. Les exemples .NET de cet article utilisent le modèle de travailleur isolé.
Prerequisites
Configurez votre environnement de développement pour l’utilisation d’Azure Functions avec Aspire :
Installez les prérequis Aspire, y compris le SDK .NET requis par votre AppHost.
Installez l’intégration d’hébergement Aspire Azure Functions depuis le répertoire AppHost.
aspire add Aspire.Hosting.Azure.FunctionsInstallez Azure Functions Core Tools.
Si vous utilisez Visual Studio, installez les dernières mises à jour des outils Visual Studio et Azure Functions :
- Accédez à Outils>.
- Sous Projets et solutions, sélectionnez Azure Functions.
- Sélectionnez Rechercher les mises à jour et installez les mises à jour comme indiqué.
Pour plus d’informations sur le paquet d’intégration et les API AppHost prises en charge, consultez Configurer Azure Functions dans l’AppHost.
Structure de la solution
Une solution utilisant Azure Functions et Aspire possède plusieurs projets, dont un AppHost et un ou plusieurs projets Functions.
L’AppHost est le point d’entrée pour votre candidature. Il orchestre la configuration des composants de votre application, y compris le projet Functions.
La solution inclut généralement un projet de service par défaut . Ce projet fournit un ensemble de services et de configurations par défaut à utiliser dans les projets de votre application.
Projet AppHost
Pour configurer correctement l’intégration, assurez-vous que le projet AppHost répond aux exigences suivantes :
- L’AppHost fait référence à Aspire.Hosting.Azure. Functions. Ce package définit l’intégration.
- Un AppHost C# inclut une référence à un projet Functions et appelle
AddAzureFunctionsProject<TProject>(), ou appelleAddAzureFunctionsProject(name, projectPath)avec le chemin du fichier de projet. Les AppHosts TypeScript utilisent la forme de chemin de projet deaddAzureFunctionsProject. - Utilisez
AddAzureFunctionsProjectau lieu deAddProject. Un projet Fonctions ajouté par l’utilisationAddProjectne peut pas démarrer correctement.
L’exemple suivant montre un fichier minimal AppHost.cs pour un projet AppHost C# :
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject");
builder.Build().Run();
Projet Azure Functions
Pour configurer l’intégration, vérifiez que le projet Azure Functions répond aux exigences suivantes :
Ciblez .NET 8 ou une version ultérieure, utilisez le SDK .NET 9 ou une version ultérieure, et utilisez le modèle de travailleur isolé.
Référencez Microsoft.Azure.Functions.Worker, Microsoft.Azure.Functions.Worker.Sdk et, pour les déclencheurs HTTP, Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.
Votre
Program.csfichier doit utiliser laIHostApplicationBuilderversion du démarrage de l’instance hôte. Cette exigence signifie que vous devez utiliserFunctionsApplication.CreateBuilder(args).Si votre solution inclut un projet par défaut de service, vérifiez que votre projet Functions est configuré pour l’utiliser :
- Le projet Functions doit inclure une référence de projet au projet par défaut du service.
- Avant de construire
IHostApplicationBuilderdansProgram.cs, incluez un appel àbuilder.AddServiceDefaults().
L’exemple suivant montre un fichier minimal Program.cs pour un projet Functions utilisé dans Aspire :
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.ConfigureFunctionsWebApplication();
builder.Build().Run();
Cet exemple n’inclut pas la configuration Application Insights par défaut qui apparaît dans de nombreux autres Program.cs exemples et dans les modèles Azure Functions. Au lieu de cela, vous configurez l’intégration d’OpenTelemetry dans Aspire en appelant la builder.AddServiceDefaults() méthode.
Pour tirer le meilleur parti de l’intégration, tenez compte des instructions suivantes :
- N’incluez pas d’intégrations Application Insights directes dans le projet Functions. La surveillance dans Aspire est plutôt gérée par le biais de sa prise en charge d’OpenTelemetry. Vous pouvez configurer Aspire pour exporter des données vers Azure Monitor via le projet par défaut du service.
- Lorsque Aspire exécute le projet Functions, il faut privilégier les paramètres injectés par l’AppHost. Vous pouvez conserver des paramètres équivalents dans
local.settings.jsonpour exécuter le projet indépendamment avecfunc start; les variables d’environnement injectées par Aspire les remplacent.
Configuration de connexion avec Aspire
L’AppHost définit les ressources et vous aide à créer des connexions entre elles en utilisant du code. Cette section montre comment configurer et personnaliser les connexions utilisées par votre projet Azure Functions.
Aspire inclut des autorisations de connexion par défaut qui peuvent vous aider à commencer. Toutefois, ces autorisations peuvent ne pas être appropriées ou suffisantes pour votre application.
Pour les scénarios qui utilisent le contrôle d’accès en fonction du rôle Azure (RBAC), vous pouvez personnaliser les autorisations en appelant la WithRoleAssignments() méthode sur la ressource de projet. Lorsque vous appelez WithRoleAssignments(), toutes les attributions de rôles par défaut sont supprimées et vous devez définir explicitement les attributions de rôles de jeu complet souhaitées. Si vous hébergez votre application sur Azure Container Apps, l’utilisation de WithRoleAssignments() nécessite également que vous appeliez AddAzureContainerAppEnvironment() sur DistributedApplicationBuilder.
Stockage hôte Azure Functions
Azure Functions nécessite une connexion de stockage hôte (AzureWebJobsStorage) pour plusieurs de ses comportements de base. Lorsque vous appelez AddAzureFunctionsProject<TProject>() dans votre AppHost, vous créez par défaut une connexion AzureWebJobsStorage et la mettez à disposition du projet Functions. Cette connexion par défaut utilise l’émulateur stockage Azure pour les runs de développement local et provisionne automatiquement un compte de stockage lors du déploiement. Pour plus de contrôle, remplacez cette connexion en appelant .WithHostStorage() la ressource du projet Functions.
Les autorisations par défaut qu’Aspire définit pour la connexion de stockage hôte dépendent du fait que vous appeliez WithHostStorage() ou non. L’ajout WithHostStorage() supprime une attribution contributeur de compte de stockage . Le tableau suivant répertorie les autorisations par défaut que Aspire définit pour la connexion de stockage hôte :
| Connexion de stockage hôte | Rôles par défaut |
|---|---|
Aucun appel à WithHostStorage() |
Contributeur aux données Blob du stockage, Contributeur aux données en file d’attente du stockage, Contributeur de données de table de stockage Contributeur au compte de stockage |
Appel de WithHostStorage() |
Contributeur aux données Blob du stockage, Contributeur aux données en file d’attente du stockage, Contributeur de données de Storage Table |
L’exemple suivant montre un fichier minimal AppHost.cs qui remplace le stockage hôte et spécifie une attribution de rôle :
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();
Remarque
Propriétaire des données blob du stockage est le rôle que nous recommandons pour les besoins de base de la connexion de stockage d’hôte. Votre application peut rencontrer des problèmes si la connexion au service d’objets blob ne dispose que du rôle par défaut Contributeur aux données Blob du stockage dans Aspire.
Pour les scénarios de production, incluez des appels à WithHostStorage() et à WithRoleAssignments(). Vous pouvez ensuite définir ce rôle explicitement, ainsi que les autres dont vous avez besoin.
Connexions de déclencheur et de liaison
Vos déclencheurs et liaisons référencent les connexions par nom. Les intégrations Aspire suivantes fournissent ces connexions via un appel à WithReference() sur la ressource de projet.
L’exemple suivant montre un fichier minimal AppHost.cs qui configure un déclencheur de file d’attente. Dans cet exemple, le déclencheur de file d’attente correspondant a sa propriété Connection définie sur MyQueueTriggerConnection, de sorte que l’appel à WithReference() spécifie le nom.
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();
Pour d'autres intégrations, les appels à WithReference définissent la configuration d'une manière différente. Ils rendent la configuration disponible pour les intégrations des clients Aspire, mais pas pour les déclencheurs et les liaisons. Pour ces intégrations, appelez WithEnvironment() pour passer les informations de connexion pour le déclencheur ou la liaison à résoudre.
L’exemple suivant montre comment définir la variable MyBindingConnection d’environnement d’une ressource qui expose une expression de chaîne de connexion :
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection", otherIntegration.Resource.ConnectionStringExpression);
Si vous souhaitez que les intégrations clientes Aspire et le système de déclencheurs et de liaisons utilisent une connexion, vous pouvez configurer à la fois WithReference() et WithEnvironment().
Pour certaines ressources, la structure d’une connexion peut être différente entre le moment où vous l’exécutez localement et celui où vous la publiez sur Azure. Dans l’exemple précédent, otherIntegration peut être une ressource qui s’exécute en tant qu’émulateur de sorte que ConnectionStringExpression retourne une chaîne de connexion d’émulateur. Toutefois, lorsque la ressource est publiée, Aspire peut configurer une connexion basée sur l’identité et ConnectionStringExpression retournerait l’URI du service. Dans ce cas, pour configurer des connexions basées sur des identités pour Azure Functions, vous devrez peut-être fournir un autre nom de variable d’environnement.
L’exemple suivant utilise builder.ExecutionContext.IsPublishMode pour ajouter conditionnellement le suffixe nécessaire :
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection" + (builder.ExecutionContext.IsPublishMode ? "__serviceUri" : ""), otherIntegration.Resource.ConnectionStringExpression);
Pour plus d’informations sur les formats de connexion pris en charge par chaque liaison et les autorisations requises par ces formats, consultez les pages de référence de la liaison.
Pour plus d’informations sur la façon dont le code Functions lit les valeurs injectées par WithReference, voir Azure Functions runtime configuration.
Hébergement de l’application
Aspire prend en charge le déploiement Azure Container Apps pour les projets Functions. Vous pouvez également utiliser l’intégration séparée du service d’aperçu pour cibler une application fonctionnelle compatible conteneur :
- Déploiement en tant qu’application conteneur
- Déployer en tant qu’application de fonction à l’aide de l’intégration App Service en préversion
Dans les deux cas, votre projet est déployé en tant que conteneur. Aspire s’occupe de la création de l’image conteneur pour vous et de son envoi vers Azure Container Registry.
Déploiement en tant qu’application conteneur
Lorsque votre AppHost cible Azure Container Apps, Aspire met en place des règles de mise à l’échelle pour votre projet Functions en utilisant KEDA. Lorsque vous utilisez Azure Container Apps, vous devez effectuer une configuration supplémentaire pour les touches de fonction. Pour plus d’informations, voir Clés d’accès sur Azure Container Apps.
Déploie l’AppHost configuré en exécutant aspire deploy. Pour plus d’informations, voir Déployer vers Azure Container Apps et aspire deploy.
Clés d’accès sur Azure Container Apps
Plusieurs scénarios Azure Functions utilisent des clés d’accès pour fournir une atténuation de base contre les accès indésirables. Par exemple, les fonctions de déclencheur HTTP nécessitent par défaut l’appel d’une clé d’accès, bien que cette exigence puisse être désactivée à l’aide de la AuthLevel propriété. Consultez Utiliser des clés d’accès dans Azure Functions pour les scénarios qui peuvent nécessiter une clé.
Lorsque vous déployez un projet Fonctions en utilisant Aspire vers Azure Container Apps, le système ne crée ni ne gère automatiquement les clés d'accès Fonctions. Si vous devez utiliser des clés d’accès, vous pouvez les gérer dans le cadre de votre configuration AppHost. Cette section vous montre comment créer une méthode d’extension que vous pouvez appeler depuis le fichier de AppHost.cs votre AppHost pour créer et gérer des clés d’accès. Cette approche utilise Azure Key Vault pour stocker les clés et les monter dans l’application conteneur en tant que secrets.
Remarque
Le comportement ici repose sur le fournisseur de secrets ContainerApps, qui nécessite la version 4.1044.0 ou ultérieure de l’hôte Functions.
Ces étapes nécessitent la version de Bicep 0.38.3 ou une version ultérieure. Vous pouvez vérifier votre version de Bicep en exécutant bicep --version à partir d'une invite de commandes. Si l’interface de ligne de commande Azure est installée, vous pouvez utiliser az bicep upgrade pour mettre à jour rapidement Bicep vers la dernière version.
Ajoutez les packages NuGet suivants à votre projet AppHost :
Créez une nouvelle classe dans votre projet AppHost et incluez le code suivant :
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);
}
}
Vous pouvez alors utiliser cette méthode dans le AppHost.cs fichier de votre AppHost :
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(storage)
.WithExternalHttpEndpoints()
.PublishWithContainerAppSecrets(systemKeyExtensionNames: ["mcp"]);
Cet exemple utilise un coffre de clés par défaut créé par la méthode d’extension. Elle génère une clé par défaut et une clé système à utiliser avec l’extension Model Context Protocol.
Pour utiliser ces clés à partir de clients, vous devez les récupérer à partir du coffre de clés.
Déploiement en tant qu’application de fonctions
Remarque
Le déploiement en tant qu’application de fonction nécessite l’intégration Aspire Azure App Service, qui est actuellement en préversion.
Vous pouvez configurer Aspire pour qu’il soit déployé dans une application fonctionnelle en utilisant l’intégration Aspire Azure App Service. Parce qu’Aspire déploie le projet Fonctions sous forme de conteneur, le plan d’hébergement de votre application de fonctions doit supporter le déploiement d’applications conteneurisées.
Pour déployer votre projet Aspire Functions en application de fonctions, suivez ces étapes :
- Depuis le répertoire AppHost, exécutez
aspire add Aspire.Hosting.Azure.AppServicepour ajouter le package NuGet Aspire.Hosting.Azure.AppService. - Dans le fichier
AppHost.cs, appelezAddAzureAppServiceEnvironment()sur votre instanceIDistributedApplicationBuilderpour créer un plan App Service. Notez que malgré le nom, cela ne provisionne pas de ressource App Service Environment. - Sur la ressource de projet Functions, appelez
.WithExternalHttpEndpoints(). Cela est nécessaire pour le déploiement avec l’intégration d’Aspire Azure App Service. - Dans la ressource de projet Functions, utilisez
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux")pour configurer ce projet comme une application de fonction dans le plan.
Important
Vérifiez que vous définissez la app.Kind propriété sur "functionapp,linux". Ce paramètre garantit que la ressource est créée en tant qu’application de fonction, ce qui affecte les expériences d’utilisation de votre application.
L’exemple suivant montre un fichier minimal AppHost.cs qui déploie un projet Functions en tant qu’application de fonctions :
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();
Cette configuration crée un plan Premium V3. Lorsque vous utilisez une référence SKU de plan App Service dédiée, la mise à l’échelle n’est pas basée sur les événements. Au lieu de cela, la mise à l’échelle est gérée via les paramètres du plan App Service.
Considérations et bonnes pratiques
Tenez compte des points suivants lorsque vous évaluez l’intégration d’Azure Functions à Aspire :
La configuration du déclencheur et de la liaison via Aspire est actuellement limitée à des intégrations spécifiques. Pour plus d’informations, consultez La configuration de connexion avec Aspire dans cet article.
Le fichier de votre projet de
Program.csfonction doit utiliser laIHostApplicationBuilderversion du démarrage de l’instance hôte. En utilisantIHostApplicationBuilder, vous pouvez appelerbuilder.AddServiceDefaults()pour ajouter des valeurs par défaut du service Aspire à votre projet Functions.Aspire utilise OpenTelemetry pour la surveillance. Vous pouvez configurer Aspire pour exporter des données vers Azure Monitor via le projet par défaut du service.
Dans de nombreux autres contextes Azure Functions, vous pouvez inclure l’intégration directe à Application Insights en inscrivant le service worker. N’enregistrez pas de second pipeline direct pour Application Insights lorsque vous utilisez Aspire Service Defaults.
Pour les projets Fonctions intégrés à une orchestration Aspire, l’AppHost doit fournir la plupart des configurations d’applications. Vous pouvez utiliser
local.settings.jsonpour exécuter le projet Functions de manière indépendante avecfunc start. Lorsque Aspire exécute le projet, les variables d’environnement injectées par Aspire supplantent les valeurs portant les mêmes noms danslocal.settings.json.Évitez de lancer un second émulateur stockage Azure pour les connexions gérées par l’AppHost. Des instances concurrentes d’émulateurs peuvent provoquer des conflits de port et de stockage.
Pour plus d’informations, voir la configuration à l’exécution d’Azure Functions et la télémétrie Aspire.