Aspire は、分散アプリケーションの構築、実行、デバッグ、デプロイのためのツールチェーンです。 Aspire Azure Functionsの連携により、Aspire AppHostの一部としてAzure Functionsプロジェクトの開発、デバッグ、オーケストレーションが可能です。 この記事の.NET例は孤立ワーカーモデルを使用しています。
前提条件
Azure Functions とアスパイアを使用するための開発環境を設定します。
AppHostで必要な.NET SDKを含むAspireの前提条件をインストールしてください。
AppHost directory から Aspire Azure Functions ホスティング整合をインストールしてください。
aspire add Aspire.Hosting.Azure.FunctionsAzure Functions Core Tools をインストールします。
Visual Studioを使う場合は、最新のVisual StudioおよびAzure Functionsツールのアップデートをインストールしてください:
- [ ツール>オプション] に移動します。
- [ プロジェクトとソリューション] で、[ Azure Functions] を選択します。
- [更新プログラムの確認] を選択し、指示に従って更新プログラムをインストールします。
統合パッケージおよび対応するAppHost APIの詳細については、「App HostのAzure Functions設定」をご覧ください。
ソリューションの構造
Azure FunctionsとAspireを使うソリューションは、AppHostや1つ以上のFunctionsプロジェクトを含む複数のプロジェクトを持っています。
AppHostはアプリケーションの入口です。 Functions プロジェクトを含め、アプリケーションのコンポーネントのセットアップを調整します。
このソリューションには、通常、 サービスの既定の プロジェクトも含まれます。 このプロジェクトには、アプリケーション内のプロジェクト間で使用される既定のサービスと構成のセットが用意されています。
AppHost プロジェクト
統合を成功裏に設定するには、AppHostプロジェクトが以下の要件を満たしていることを確認しましょう。
- AppHost は Aspire.Hosting.Azure.Functions を参照します。 このパッケージは統合を定義します。
- C# AppHostはFunctionsプロジェクトを参照し、
AddAzureFunctionsProject<TProject>()を呼び出したり、プロジェクトファイルのパスをAddAzureFunctionsProject(name, projectPath)呼び出したりします。 TypeScript AppHostsはプロジェクトパス形式のaddAzureFunctionsProjectを使用します。 -
AddAzureFunctionsProjectではなくAddProjectを使用します。AddProjectを使って追加したFunctionsプロジェクトが正常に起動できません。
以下の例は、C# AppHostプロジェクト用の最小限の AppHost.cs ファイルを示しています。
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject");
builder.Build().Run();
Azure Functions プロジェクト
統合を正常に構成するには、Azure Functions プロジェクトが次の要件を満たしていることを確認します。
ターゲットは.NET 8以降で、.NET 9 SDK以降を使用し、アイソレーテッドワーカーモデルを用いましょう。
Microsoft.Azure.Functions.Worker、Microsoft.Azure.Functions.Worker.Sdk、および HTTP トリガーの場合は Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore を参照してください。
Program.csファイルでは、IHostApplicationBuilderのバージョンを使用する必要があります。 この要件は、FunctionsApplication.CreateBuilder(args)を使用する必要があることを意味します。ソリューションにサービスの既定のプロジェクトが含まれている場合は、Functions プロジェクトがそのプロジェクトを使用するように構成されていることを確認します。
- Functions プロジェクトには、サービスの既定のプロジェクトへのプロジェクト参照が含まれている必要があります。
-
IHostApplicationBuilderでProgram.csをビルドする前に、builder.AddServiceDefaults()の呼び出しを含めます。
次の例は、アスパイアで使用される Functions プロジェクトの最小 Program.cs ファイルを示しています。
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.ConfigureFunctionsWebApplication();
builder.Build().Run();
この例には、他の多くの Program.cs の例や Azure Functions テンプレートに表示される既定の Application Insights 構成は含まれていません。 代わりに、 builder.AddServiceDefaults() メソッドを呼び出して、Aspire で OpenTelemetry 統合を構成します。
統合を最大限に活用するには、次のガイドラインを考慮してください。
- Functions プロジェクトには、Application Insights の直接統合を含めないでください。 代わりに、アスパイアでの監視は、OpenTelemetry のサポートを通じて処理されます。 サービスの既定のプロジェクトを使用して Azure Monitor にデータをエクスポートするように、Aspire を構成できます。
- Aspireが関数プロジェクトを実行する際は、AppHostによって注入された設定を優先してください。 プロジェクトを独立して実行するために、
local.settings.jsonに同等の設定を保持できますfunc start;Aspireで注入された環境変数がそれらを上書きします。
アスパイアを使用した接続構成
AppHostはリソースを定義し、コードを使ってそれらの間の接続を作るのを支援します。 このセクションでは、Azure Functions プロジェクトで使用する接続を構成およびカスタマイズする方法について説明します。
アスパイアには、作業の開始に役立つ既定の接続アクセス許可が含まれています。 ただし、これらのアクセス許可は、アプリケーションに適していないか、十分ではない可能性があります。
Azure ロールベースのアクセス制御 (RBAC) を使用するシナリオでは、プロジェクト リソースで WithRoleAssignments() メソッドを呼び出してアクセス許可をカスタマイズできます。
WithRoleAssignments()を呼び出すと、すべての既定のロールの割り当てが削除され、必要な完全なセット ロールの割り当てを明示的に定義する必要があります。 Azure Container Apps でアプリケーションをホストする場合、WithRoleAssignments()を使用するには、AddAzureContainerAppEnvironment()でDistributedApplicationBuilderを呼び出す必要もあります。
Azure Functions ホスト ストレージ
Azure Functions では、いくつかのコア動作にホスト ストレージ接続 (AzureWebJobsStorage) が必要です。 AppHostで AddAzureFunctionsProject<TProject>() を呼び出すと、デフォルトで AzureWebJobsStorage 接続を作成し、それをFunctionsプロジェクトに提供します。 このデフォルト接続はローカル開発時にAzure Storageエミュレーターを使用し、展開時に自動的にストレージアカウントをプロビジョニングします。 より細かい制御を目的として、この接続をFunctionsプロジェクトリソースで .WithHostStorage() 呼び出すことで置き換えてください。
Aspireがホストストレージ接続に設定するデフォルトの権限は、 WithHostStorage() を呼ぶかどうかによって異なります。
WithHostStorage()を追加すると、ストレージ アカウント共同作成者の割り当てが削除されます。 Aspire がホストストレージ接続に対して設定する既定のアクセス許可を、次の表に示します。
| ホスト ストレージ接続 | 既定のロール |
|---|---|
WithHostStorage() の呼び出しなし |
ストレージ BLOB データ共同作成者、 ストレージ キュー データ共同作成者、 ストレージ テーブル データ コンストリビューター、 ストレージ アカウント共同作成者 |
WithHostStorage() を呼び出す |
ストレージ BLOB データ共同作成者、 ストレージ キュー データ共同作成者、 ストレージ テーブル データ共同作成者 |
以下の例は、ホストストレージを置き換え、役割割り当てを指定する最小限の AppHost.cs ファイルを示しています。
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
ストレージ BLOB データ所有者 は、 ホスト ストレージ接続の基本的なニーズに推奨されるロールです。 BLOB サービスへの接続に、Aspire の既定値であるストレージ BLOB データ共同作成者のみが設定されている場合、アプリで問題が発生する可能性があります。
運用環境のシナリオでは、 WithHostStorage() と WithRoleAssignments()の両方の呼び出しを含めます。 その後、必要な他のロールと共に、このロールを明示的に設定できます。
トリガーとバインドの接続
トリガーとバインドは、名前で接続を参照します。 次のアスパイア統合は、プロジェクト リソースの WithReference() 呼び出しを通じてこれらの接続を提供します。
以下の例は、キュートリガーを設定する最小限の AppHost.cs ファイルを示しています。 この例では、対応するキュー トリガーの Connection プロパティが MyQueueTriggerConnection に設定されているため、 WithReference() の呼び出しで名前が指定されます。
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();
他の統合の場合は、 WithReference 呼び出しによって構成が別の方法で設定されます。 この構成は 、アスパイア クライアント統合では使用できますが、トリガーとバインドには使用できません。 これらの統合の場合は、 WithEnvironment() を呼び出して、解決するトリガーまたはバインドの接続情報を渡します。
次の例は、接続文字列式を公開するリソースの環境変数 MyBindingConnection を設定する方法を示しています。
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection", otherIntegration.Resource.ConnectionStringExpression);
アスパイア クライアント統合とトリガーとバインドのシステムの両方で接続を使用する場合は、 WithReference() と WithEnvironment()の両方を構成できます。
一部のリソースでは、接続の構造が、ローカルで実行する場合と Azure に発行する場合とで異なる可能性があります。 前の例では、otherIntegration はエミュレーターとして実行されるリソースである可能性があるため、ConnectionStringExpression からエミュレーター接続文字列が返されます。 ただし、リソースが発行されると、Aspire によって ID ベースの接続が設定され、 ConnectionStringExpression はサービスの URI を返す可能性があります。 この場合、 Azure Functions の ID ベースの接続を設定するには、別の環境変数名を指定する必要がある場合があります。
次の例では、必要なサフィックスを条件付きで追加するため builder.ExecutionContext.IsPublishMode が使用されています。
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection" + (builder.ExecutionContext.IsPublishMode ? "__serviceUri" : ""), otherIntegration.Resource.ConnectionStringExpression);
各バインドでサポートされる接続形式と、それらの形式に必要なアクセス許可の詳細については、バインディングの 参照ページを参照してください。
関数コードがWithReferenceによって注入された値を読み取る方法の詳細については、Azure Functionsランタイム構成を参照してください。
アプリケーションのホスト
Aspire は Functions プロジェクトの Azure Container Apps デプロイメントをサポートしています。 また、別のプレビュー版App Service統合を使ってコンテナ対応機能アプリをターゲットにすることもできます:
- コンテナアプリとしてデプロイ
- プレビューのApp Service統合を使って関数としてデプロイする
どちらの場合も、プロジェクトはコンテナーとしてデプロイされます。 アスパイアは、コンテナーイメージを構築し、それをAzure Container Registryにプッシュします。
コンテナアプリとしてデプロイ
AppHostがAzure Container Appsをターゲットにする場合、AspireはKEDAを使ってFunctionsプロジェクトのスケーリングルールを設定します。 Azure Container Appsを使う場合は、ファンクションキーの追加設定が必要です。 詳細については、Azure Container AppsのAccess Keysをご覧ください。
設定済みのAppHostを aspire deploy実行してデプロイします。 詳細については、「Azure Container Appsへの展開」および「aspire deploy」をご覧ください。
Azure Container Apps のアクセス キー
いくつかの Azure Functions シナリオでは、アクセス キーを使用して、不要なアクセスに対する基本的な軽減策を提供します。 たとえば、HTTP トリガー関数は既定でアクセス キーを呼び出す必要がありますが、この要件は AuthLevel プロパティを使用して無効にすることができます。 キーが必要になる可能性があるシナリオについては、「 Azure Functions でのアクセス キーの操作 」を参照してください。
Aspireを使ってAzure Container AppsにFunctionsプロジェクトをデプロイすると、システムは自動的にFunctionsのアクセスキーを作成または管理しません。 アクセスキーを使う必要がある場合は、AppHostの設定の一部として管理できます。 このセクションでは、AppHostの AppHost.cs ファイルから呼び出してアクセスキーを作成・管理できる拡張メソッドの作成方法を示しています。 この方法では、Azure Key Vault を使用してキーを格納し、シークレットとしてコンテナー アプリにマウントします。
Note
ここでの動作は ContainerApps シークレットプロバイダーに依存しており、Functionsホストバージョン 4.1044.0 以降が必要です。
これらの手順には、Bicep バージョン 0.38.3 以降が必要です。 コマンド プロンプトから bicep --version を実行して、Bicep のバージョンを確認できます。 Azure CLI がインストールされている場合は、 az bicep upgrade を使用して、Bicep を最新バージョンにすばやく更新できます。
以下のNuGetパッケージをAppHostプロジェクトに追加してください:
AppHostプロジェクトで新しいクラスを作成し、以下のコードを含めてください:
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);
}
}
その後、AppHostの AppHost.cs ファイルで以下の方法を利用できます:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(storage)
.WithExternalHttpEndpoints()
.PublishWithContainerAppSecrets(systemKeyExtensionNames: ["mcp"]);
この例では、拡張メソッドによって作成された既定のキー コンテナーを使用します。 その結果、 モデル コンテキスト プロトコル拡張機能で使用する既定のキーとシステム キーが作成されます。
クライアントからこれらのキーを使用するには、キー コンテナーからキーを取得する必要があります。
関数アプリとしてデプロイ
Note
関数型アプリとしてデプロイするには、現在プレビュー段階のAspire Azure App Service統合が必要です。
Aspire Azure App Service 連携を使って、関数型アプリにデプロイするよう設定できます。 AspireはFunctionsプロジェクトをコンテナとしてデプロイするため、関数アプリのホスティングプランはコンテナ化アプリケーションのデプロイに対応しなければなりません。
Aspire Functionsプロジェクトを関数アプリとして展開するには、以下の手順に従ってください:
- AppHost ディレクトリから、Aspire.Hosting.Azure.AppService NuGet パッケージを追加するには
aspire add Aspire.Hosting.Azure.AppServiceを実行します。 -
AppHost.csファイルで、AddAzureAppServiceEnvironment()インスタンスのIDistributedApplicationBuilderを呼び出して App Service プランを作成します。 名前に関わらず、App Service Environment リソースはプロビジョニングされないことに注意してください。 - Functions プロジェクト リソースで、
.WithExternalHttpEndpoints()を呼び出します。 これは、Azure App Service 統合を利用してデプロイする場合に必要です。 - Functionsプロジェクトリソースで
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux")を呼び出して、そのプロジェクトを計画内の関数アプリとしてカスタマイズしてください。
重要
必ず、 app.Kind プロパティを "functionapp,linux" に設定してください。 この設定により、リソースが関数アプリとして作成されます。これは、アプリケーションを操作するためのエクスペリエンスに影響します。
以下の例は、関数プロジェクトを関数アプリとして展開する最小限の AppHost.cs ファイルを示しています:
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();
この構成では、Premium V3 プランが作成されます。 専用の App Service プラン SKU を使用する場合、スケーリングはイベントベースではありません。 代わりに、スケーリングは App Service プランの設定を通じて管理されます。
考慮事項とベスト プラクティス
Azure Functions と Aspire の統合を評価するときは、次の点を考慮してください。
現在、Aspire を使用したトリガーとバインドの構成は、特定の統合に制限されています。 詳細については、この記事の 「アスパイアとの接続の構成 」を参照してください。
関数プロジェクトの
Program.csファイルでは、IHostApplicationBuilderのバージョンを使用する必要があります。IHostApplicationBuilderを使うことで、builder.AddServiceDefaults()を呼び出してAspire Service DefaultsをFunctionsプロジェクトに追加できます。アスパイアは、監視に OpenTelemetry を使用します。 サービスの既定のプロジェクトを使用して Azure Monitor にデータをエクスポートするように、Aspire を構成できます。
他の多くの Azure Functions コンテキストでは、ワーカー サービスを登録することで Application Insights との直接統合を含めることができます。 Aspire Service Defaultsを使う際には、2つ目の直接的なApplication Insightsパイプラインを登録しないでください。
Aspireオーケストレーションに組み込まれたFunctionsプロジェクトでは、AppHostがほとんどのアプリケーション構成を提供すべきです。
local.settings.jsonを使ってFunctionsプロジェクトをfunc startと独立して実行できます。 Aspireがプロジェクトを実行すると、Aspireが注入した環境変数がlocal.settings.json内の同じ名前の値を上書きします。AppHostが管理する接続用に2つ目のAzure Storageエミュレーターを起動するのは避けてください。 競合するエミュレーターインスタンスがポートやストレージの競合を引き起こすことがあります。
詳細については、Azure Functions ランタイム構成およびAspire telemetryをご覧ください。