.NET Aspire Azure 集成概述

Azure 是最常用的云平台,用于构建和部署 .NET 应用程序 Azure 用于 .NET 的 SDK 允许轻松管理和使用 Azure 服务。 .NET Aspire 提供了一组与 Azure 服务的集成,你可以在其中免费添加新资源或连接到现有资源。 本文详细介绍了 Azure 中所有 .NET Aspire 集成的某些常见方面,旨在帮助你了解如何使用这些集成。

添加 Azure 资源

所有 .NET AspireAzure 托管集成都公开 Azure 资源,并按约定使用 AddAzure* API 添加。 将这些资源添加到 .NET Aspire 应用主机时,它们表示 Azure 服务。 AddAzure* API 返回一个 IResourceBuilder<T>,其中 T 是 Azure 资源的类型。 这些 IResourceBuilder<T>(生成器)接口提供了一个流畅的 API,可用于在 Azure中配置基础 资源。 有一些 API 可用于添加新 Azure 资源、将资源标记为现有资源,以及配置资源在各种执行上下文中的行为方式。

典型的开发人员体验

当您的 `.NET Aspire` 应用主机包含 `Azure` 资源,并且在本地运行时(开发人员以典型方式按 `F5` 或进行 `dotnet run` 体验),这些 `Azure` 资源会在您的 `Azure` 订阅中进行预配。 这样,开发人员就可以在应用主机的上下文中在本地对其进行调试。

.NET .NET Aspire 旨在通过默认采用 基本标准库存单位 (SKU) 来尽量降低 Azure 集成的成本。 虽然提供了这些合理的默认值,但你可以 自定义 Azure 资源 以满足你的需求。 此外,某些集成还支持 模拟器容器,这对于本地开发、测试和调试非常有用。 默认情况下,在本地运行应用时,Azure 资源使用实际的 Azure 服务。 但是,可以将它们配置为使用本地模拟器或容器,从而避免在本地开发期间与实际 Azure 服务相关的成本。

本地模拟器

可以模拟某些 Azure 服务在本地运行。 目前,.NET Aspire 支持以下 Azure 模拟器:

托管集成 描述
Azure Cosmos DB 调用 AzureCosmosExtensions.RunAsEmulatorIResourceBuilder<AzureCosmosDBResource> 上配置 Cosmos DB 资源,通过 NoSQL API 进行模拟
Azure Event Hubs AzureEventHubsExtensions.RunAsEmulator 上调用 IResourceBuilder<AzureEventHubsResource>,将事件中心资源配置为 模拟
Azure Service Bus AzureServiceBusExtensions.RunAsEmulator 上调用 IResourceBuilder<AzureServiceBusResource>,将服务总线资源配置为通过服务总线模拟器 模拟
Azure SignalR Service AzureSignalRExtensions.RunAsEmulator 上调用 IResourceBuilder<AzureSignalRResource>,将 SignalR 资源配置为使用 AzureSignalR 模拟器 进行仿真。
Azure 存储 IResourceBuilder<AzureStorageResource> 上调用 AzureStorageExtensions.RunAsEmulator,将存储资源配置为使用 Azurite 进行模拟。

若要让 Azure 资源使用本地模拟器,请在 RunAsEmulator 资源生成器上调用 Azure 方法。 此方法将 Azure 资源配置为使用本地模拟器,而不是实际的 Azure 服务。

重要

对 Azure 资源生成器调用任何可用的 RunAsEmulator API 不会影响 发布清单。 发布应用时,生成的 Bicep 文件 反映实际 Azure 服务,而不是本地模拟器。

本地容器

某些 Azure 资源可以使用开放源代码或本地容器在本地替代。 若要在容器中本地替换 Azure 资源,请在 RunAsContainer 资源生成器上链接对 Azure 方法的调用。 此方法将 Azure 资源配置为使用服务的容器化版本进行本地开发和测试,而不是实际的 Azure 服务。

目前,.NET Aspire 支持以下 Azure 服务作为容器:

托管集成 详细信息
Azure Cache for Redis AzureRedisExtensions.RunAsContainer 调用 IResourceBuilder<AzureRedisCacheResource>,以根据 docker.io/library/redis 映像将其配置为在容器中本地运行。
Azure PostgreSQL 灵活 Server AzurePostgresExtensions.RunAsContainer 调用 IResourceBuilder<AzurePostgresFlexibleServerResource>,以根据 docker.io/library/postgres 映像将其配置为在容器中本地运行。
Azure SQL Server AzureSqlExtensions.RunAsContainer 调用 IResourceBuilder<AzureSqlServerResource>,以根据 mcr.microsoft.com/mssql/server 映像将其配置为在容器中本地运行。

注意

与模拟器一样,对 Azure 资源生成器调用 RunAsContainer 不会影响 发布清单。 发布应用时,生成的 Bicep 文件 反映实际 Azure 服务,而不是本地容器。

了解 Azure 集成 API 接口

.NET .NET Aspire的强项在于其能够提供卓越的开发人员内部工作循环。 Azure 的集成并没有什么不同。 它们提供一组通用 API 和模式,这些 API 和模式在所有 Azure 资源之间共享。 这些 API 和模式旨在以一致的方式轻松处理 Azure 资源。

在前面的容器部分中,你了解了如何在容器中本地运行 Azure 服务。 如果你熟悉 .NET.NET Aspire,你可能会想知道调用 AddAzureRedis("redis").RunAsContainer() 获取本地 docker.io/library/redis 容器与AddRedis("redis")方法有何不同——因为它们最终都会产生相同的本地容器。

答案是,在本地运行时没有区别。 但是,当它们发布时,你会获得各种不同的资源。

API 运行模式 发布模式
AddAzureRedis(“redis”)。RunAsContainer() 本地 Redis 容器 Azure Cache for Redis
AddRedis(“redis”) 本地 Redis 容器 使用Azure映像的Redis容器应用

SQL 和 PostgreSQL 服务也是如此:

应用程序接口 运行模式 发布模式
AddAzurePostgresFlexibleServer(“postgres”)。RunAsContainer() 本地 PostgreSQL 容器 Azure PostgreSQL 灵活 Server
AddPostgres(“postgres”) 本地 PostgreSQL 容器 使用Azure镜像的PostgreSQL容器应用
AddAzureSqlServer(“sql”)。RunAsContainer() 本地 SQL Server 容器 Azure SQL Server
AddSqlServer(“sql”) 本地 SQL Server 容器 使用Azure映像的SQL Server容器应用

有关运行模式和发布模式之间的差异的详细信息,请参阅 .NET.NET Aspire 应用主机:执行上下文

用于以不同模式表达 Azure 资源的 API

分布式应用程序生成器是 应用主机的一部分,它利用生成器模式将资源 AddAzure* 分配到 应用模型。 开发人员可以在不同的执行上下文中配置这些资源并定义其行为。 Azure 托管集成提供 API,用于指定应如何“发布”和“运行”这些资源。

应用主机执行时,执行上下文 用于确定应用主机是处于 Run 还是 Publish 模式。 这些 API 的命名约定指示资源的预期作。

下表汇总了用于表达 Azure 资源的命名约定:

操作 应用程序接口 描述
发布 PublishAsConnectionString<T>(IResourceBuilder<T>) 将资源更改为在清单中作为连接字符串引用进行发布。
发布 PublishAsExisting 部署应用程序时使用现有的 Azure 资源,而不是创建新的资源。
AzureSqlExtensions.RunAsContainer(IResourceBuilder<AzureSqlServerResource>, Action<IResourceBuilder<SqlServerServerResource>>)
AzureRedisExtensions.RunAsContainer(IResourceBuilder<AzureRedisCacheResource>, Action<IResourceBuilder<RedisResource>>)
RunAsContainer(IResourceBuilder<AzurePostgresFlexibleServerResource>, Action<IResourceBuilder<PostgresServerResource>>)
将等效容器配置为在本地运行。 有关详细信息,请参阅 本地容器
AzureCosmosExtensions.RunAsEmulator(IResourceBuilder<AzureCosmosDBResource>, Action<IResourceBuilder<AzureCosmosDBEmulatorResource>>)
AzureSignalRExtensions.RunAsEmulator(IResourceBuilder<AzureSignalRResource>, Action<IResourceBuilder<AzureSignalREmulatorResource>>)
AzureStorageExtensions.RunAsEmulator(IResourceBuilder<AzureStorageResource>, Action<IResourceBuilder<AzureStorageEmulatorResource>>)
AzureEventHubsExtensions.RunAsEmulator(IResourceBuilder<AzureEventHubsResource>, Action<IResourceBuilder<AzureEventHubsEmulatorResource>>)
AzureServiceBusExtensions.RunAsEmulator(IResourceBuilder<AzureServiceBusResource>, Action<IResourceBuilder<AzureServiceBusEmulatorResource>>)
配置要模拟的 Azure 资源。 有关详细信息,请参阅 本地模拟器
RunAsExisting 在应用程序运行时使用现有资源,而不是创建新资源。
发布和运行 AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) 无论操作如何,都使用现有资源。

注意

并非所有 API 都可用于所有 Azure 资源。 例如,某些 Azure 资源可以容器化或模拟,而另一些资源则不能。

有关执行模式的详细信息,请参阅 执行上下文

常规运行模式 API 用例

如果需要在运行时动态与现有资源交互,而无需部署或更新资源,请使用 RunAsExisting。 将现有资源声明为部署配置的一部分时,请使用 PublishAsExisting,确保应用正确的范围和权限。 最后,在两种配置中声明现有资源时使用 AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>),并要求参数化引用。

可以通过对 IsExisting(IResource)调用 IResource 扩展方法来查询资源是否标记为现有资源。 有关详细信息,请参阅 使用现有 Azure 资源

使用现有 Azure 资源

.NET Aspire 支持引用现有 Azure 资源。 通过 PublishAsExistingRunAsExistingAsExisting API 标记现有资源。 通过这些 API,开发人员可以使用 Bicep 模板引用已部署 Azure 资源、对其进行配置和生成适当的部署清单。

可以通过角色分配和其他自定义功能来增强这些 API 引用的现有资源,而这些自定义功能可用于 .NET.NET Aspire的 基础设施即代码能力。 这些 API 仅限于通过 Bicep 模板部署的 Azure 资源。

为运行模式配置现有 Azure 资源

当分布式应用程序在“运行”模式下执行时,将使用 RunAsExisting 方法。 在此模式下,它假定引用 Azure 资源已存在,并在执行期间与它集成,而无需预配资源。 若要将 Azure 资源标记为现有资源,请对资源生成器调用 RunAsExisting 方法。 请考虑以下示例:

var builder = DistributedApplication.CreateBuilder();

var existingServiceBusName = builder.AddParameter("existingServiceBusName");
var existingServiceBusResourceGroup = builder.AddParameter("existingServiceBusResourceGroup");

var serviceBus = builder.AddAzureServiceBus("messaging")
                        .RunAsExisting(existingServiceBusName, existingServiceBusResourceGroup);

serviceBus.AddServiceBusQueue("queue");

前面的代码:

  • 创建新的 builder 实例。
  • 将名为 existingServiceBusName 的参数添加到生成器。
  • 将名为 Azure Service Bus 的 messaging 资源添加到生成器。
  • RunAsExisting 资源生成器调用 serviceBus 方法,传递 existingServiceBusName 参数,或者,可以使用 string 参数重载。
  • 将名为 queue 的队列添加到 serviceBus 资源。

默认情况下,假定服务总线参数引用位于同一 Azure 资源组中。 但是,如果它位于其他资源组中,则可以将资源组显式作为参数传递,以正确指定适当的资源组。

为发布模式配置现有 Azure 资源

当意向是在发布模式下声明和引用已存在的 PublishAsExisting 资源时,Azure 方法在“发布”模式下使用。 此 API 有助于创建清单和模板,这些清单和模板包含映射到 Bicep 中现有资源的资源定义。

若要将 Azure 资源标记为“发布”模式中的现有资源,请对资源生成器调用 PublishAsExisting 方法。 请考虑以下示例:

var builder = DistributedApplication.CreateBuilder();

var existingServiceBusName = builder.AddParameter("existingServiceBusName");
var existingServiceBusResourceGroup = builder.AddParameter("existingServiceBusResourceGroup");

var serviceBus = builder.AddAzureServiceBus("messaging")
                        .PublishAsExisting(existingServiceBusName, existingServiceBusResourceGroup);

serviceBus.AddServiceBusQueue("queue");

前面的代码:

  • 创建新的 builder 实例。
  • 将名为 existingServiceBusName 的参数添加到生成器。
  • 将名为 Azure Service Bus 的 messaging 资源添加到生成器。
  • PublishAsExisting 资源生成器调用 serviceBus 方法,传递 existingServiceBusName 参数,或者,可以使用 string 参数重载。
  • 将名为 queue 的队列添加到 serviceBus 资源。

在发布模式下执行应用主机后,生成的清单文件将包含 existingResourceName 参数,该参数可用于引用现有 Azure 资源。 请考虑以下生成的清单文件的部分代码片段:

"messaging": {
  "type": "azure.bicep.v0",
  "connectionString": "{messaging.outputs.serviceBusEndpoint}",
  "path": "messaging.module.bicep",
  "params": {
    "existingServiceBusName": "{existingServiceBusName.value}",
    "principalType": "",
    "principalId": ""
  }
},
"queue": {
  "type": "value.v0",
  "connectionString": "{messaging.outputs.serviceBusEndpoint}"
}

有关清单文件的详细信息,请参阅部署工具生成器 .NET.NET Aspire 清单格式。

此外,生成的 Bicep 模板包括 existingResourceName 参数,可用于引用现有 Azure 资源。 请审阅以下自动生成的 Bicep 模板。

@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

param existingServiceBusName string

param principalType string

param principalId string

resource messaging 'Microsoft.ServiceBus/namespaces@2024-01-01' existing = {
  name: existingServiceBusName
}

resource messaging_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(messaging.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419'))
  properties: {
    principalId: principalId
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')
    principalType: principalType
  }
  scope: messaging
}

resource queue 'Microsoft.ServiceBus/namespaces/queues@2024-01-01' = {
  name: 'queue'
  parent: messaging
}

output serviceBusEndpoint string = messaging.properties.serviceBusEndpoint

有关生成的 Bicep 模板的详细信息,请参阅 基础结构即代码可以考虑其他发布 API

警告

与需要身份验证的现有资源交互时,请确保在 .NET.NET Aspire 应用程序模型中配置的身份验证策略与现有资源允许的身份验证策略保持一致。 例如,不能对未配置为允许托管标识的现有 AzurePostgreSQL 资源使用托管标识。 同样,如果现有 AzureRedis 资源禁用了访问密钥,则无法使用访问密钥身份验证。

在所有模式中配置现有的 Azure 资源

当分布式应用程序在“run”或“publish”模式下运行时,将使用 AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) 方法。 由于 AsExisting 方法在这两种情况下都运行,因此它仅支持对资源名称或资源组名称的参数化引用。 此方法有助于防止在测试和生产环境中使用相同的资源。

若要将 Azure 资源标记为现有资源,请对资源生成器调用 AsExisting 方法。 请考虑以下示例:

var builder = DistributedApplication.CreateBuilder();

var existingServiceBusName = builder.AddParameter("existingServiceBusName");
var existingServiceBusResourceGroup = builder.AddParameter("existingServiceBusResourceGroup");

var serviceBus = builder.AddAzureServiceBus("messaging")
                        .AsExisting(existingServiceBusName, existingServiceBusResourceGroup);

serviceBus.AddServiceBusQueue("queue");

前面的代码:

  • 创建新的 builder 实例。
  • 将名为 existingServiceBusName 的参数添加到生成器。
  • 将名为 Azure Service Bus 的 messaging 资源添加到生成器。
  • AsExisting 资源生成器调用 serviceBus 方法,并传递 existingServiceBusName 参数。
  • 将名为 queue 的队列添加到 serviceBus 资源。

使用连接字符串添加现有 Azure 资源

.NET .NET Aspire 能够 连接到现有资源,包括 Azure 资源。 表达连接字符串很有用,当您希望在 Azure 应用中使用现有的 .NET Aspire 资源时。 AddConnectionString API 与应用主机的 执行上下文一起使用, 有条件地将连接字符串添加到应用模型。

注意

连接字符串用于表示各种连接信息,包括数据库连接、消息代理、终结点 URI 和其他服务。 在 .NET.NET Aspire 名词中,术语“连接字符串”用于表示任何类型的连接信息。

请考虑以下示例,在 发布 模式下,添加 Azure 存储资源,而在 运行 模式下,将连接字符串添加到现有 Azure 存储:

var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.ExecutionContext.IsPublishMode
    ? builder.AddAzureStorage("storage")
    : builder.AddConnectionString("storage");

builder.AddProject<Projects.Api>("api")
       .WithReference(storage);

// After adding all resources, run the app...

前面的代码:

  • 创建新的 builder 实例。
  • 在“发布”模式下添加名为 Azure 的 storage 存储资源。
  • 在“运行”模式下,将连接字符串添加到现有名为 Azure 的 storage 存储中。
  • 将名为 api 的项目添加到生成器。
  • 无论模式如何,api 项目都会引用 storage 资源。

消费 API 项目使用连接字符串信息,但对应用主机如何配置它一无所知。 在“发布”模式下,代码会添加一个新的 Azure 存储资源,该资源将相应地反映在 部署清单 中。 在“运行”模式下,连接字符串对应于对应用主机可见的配置值。 可以假设目标资源的所有角色分配已配置完毕。 这意味着,可能会配置环境变量或用户机密来存储连接字符串。 配置是从 ConnectionStrings__storage(或 ConnectionStrings:storage)配置密钥解析的。 可以在应用运行时查看这些配置值。 有关详细信息,请参阅 资源详细信息

与使用 一流 AsExisting API建模的现有资源不同,建模为连接字符串的现有资源无法通过额外的角色分配或基础结构自定义进行增强。

发布为 Azure 容器应用

.NET .NET Aspire 允许将基元资源发布为 Azure Container Apps,这是一个可减少基础结构管理的无服务器平台。 支持的资源类型包括:

若要发布这些资源,请使用以下 API:

这些 API 将资源配置为发布为 Azure 容器应用,并隐式调用 AddAzureContainerAppsInfrastructure(IDistributedApplicationBuilder),以将必要的基础结构和 Bicep 文件添加到应用主机。 例如,请考虑以下代码:

var builder = DistributedApplication.CreateBuilder();

var env = builder.AddParameter("env");

var api = builder.AddProject<Projects.AspireApi>("api")
                 .PublishAsAzureContainerApp<Projects.AspireApi>((infra, app) =>
                 {
                     app.Template.Containers[0].Value!.Env.Add(new ContainerAppEnvironmentVariable
                     {
                         Name = "Hello",
                         Value = env.AsProvisioningParameter(infra)
                     });
                 });

前面的代码:

  • 创建新的 builder 实例。
  • 将名为 env 的参数添加到生成器。
  • 将名为 api 的项目添加到生成器。
  • api 资源生成器调用 PublishAsAzureContainerApp 方法,传递配置 Azure 容器应用基础结构的 lambda 表达式,其中 infraAzureResourceInfrastructureappContainerApp
    • 使用 env 参数将名为 Hello 的环境变量添加到容器应用。
    • AsProvisioningParameter 方法用于将 env 视为基础结构中的新 ProvisioningParameter,或者在已存在同名 bicep 参数时重用该参数。

有关详细信息,请参阅 ContainerAppAsProvisioningParameter

基础结构即代码

用于 Azure 的 .NET SDK 提供 📦Azure。预配 NuGet 包和一套特定于服务的 Azure 预配包。 这些 Azure 预配库可以轻松地在 Azure中以声明方式指定 .NET 基础结构。 借助其 API,可以在 C# 中编写面向对象的基础结构,从而生成 Bicep。 Bicep 是一种特定领域语言 (DSL), 用于声明式部署 Azure 资源。

虽然可以手动预配 Azure 资源,但 .NET Aspire 通过提供一组 API 来表达 Azure 资源来简化该过程。 这些 API 可用作托管库 .NET AspireAzure 中的扩展方法,从而扩展 IDistributedApplicationBuilder 接口。 将 Azure 资源添加到应用主机时,它们会隐式添加适当的预配功能。 换句话说,无需直接调用任何预配 API。

由于 .NET Aspire 在 Azure 托管集成中模型化Azure 资源,因此使用 Azure SDK 来预配这些资源。 生成 Bicep 文件,用于定义所需的 Azure 资源。 发布应用时,生成的 Bicep 文件与清单文件一起输出。

有多种方式可以影响生成的 Bicep 文件:

本地预配和 Azure.Provisioning

为了避免混淆术语并帮助消除“预配”的歧义,请务必了解 本地预配Azure 预配之间的区别:

  • 本地预配:

    默认情况下,调用任何用于添加 Azure 资源的 Azure 托管集成 API 时,会隐式调用 AddAzureProvisioning(IDistributedApplicationBuilder) API。 此 API 在依赖项注入(DI)容器中注册服务,用于在应用主机启动时预配 Azure 资源。 此概念称为 本地预配。 有关详细信息,请参阅 本地Azure 预配

  • Azure.Provisioning

    Azure.Provisioning 指 NuGet 包,是一组可用于使用 C# 生成 Bicep 的库。 在 Azure 中,.NET Aspire 托管集成在后台使用这些库来生成定义所需 Azure 资源的 Bicep 文件。 有关详细信息,请参阅 Azure.Provisioning 自定义设置

Azure.Provisioning 自定义

所有 .NET AspireAzure 托管集成都公开各种 Azure 资源,它们都是 AzureProvisioningResource 类型的子类,而 AzureProvisioningResource 类型本身是继承自 。 这样可以启用对该类型进行泛型类型约束的扩展,允许通过“Fluent API”将基础结构自定义成您喜欢的样子。 虽然 .NET.NET Aspire 提供默认值,但你可以使用这些 API 自由地影响生成的 Bicep。

配置基础结构

不论使用哪个 Azure 资源,要配置它的底层基础设施,需要串联调用 ConfigureInfrastructure 扩展方法。 这种方法允许您通过传递一个configure类型的Azure委托来自定义Action<AzureResourceInfrastructure>资源的基础架构。 AzureResourceInfrastructure 类型是 Azure.Provisioning.Infrastructure的子类。 此类型公开了一个庞大的 API 接口,用于配置 Azure 资源的底层基础设施。

请考虑以下示例:

var sku = builder.AddParameter("storage-sku");

var storage = builder.AddAzureStorage("storage")
    .ConfigureInfrastructure(infra =>
    {
        var resources = infra.GetProvisionableResources();

        var storageAccount = resources.OfType<StorageAccount>().Single();

        storageAccount.Sku = new StorageSku
        {
            Name = sku.AsProvisioningParameter(infra)
        };
    });

前面的代码:

  • 添加名为 storage-sku的参数。
  • 使用名为 Azure的 AddAzureStorage API 添加 storage 存储。
  • 将调用方法链接至 ConfigureInfrastructure 以自定义 Azure 存储基础结构:

这演示了将 外部参数 流向 Azure 存储基础结构,从而生成反映所需配置的 Bicep 文件。

添加 Azure 基础结构

并非所有 Azure 服务都公开为 .NET Aspire 集成。 尽管它们可能会在较晚的时候推出,但您仍然可以配置 Azure.Provisioning.* 库中可用的服务。 想象一下一个场景,在这个场景中,您有一个负责管理Azure 容器注册表的工人服务。 现在,设想一个应用程序主机项目依赖于 📦Azure.Provisioning.ContainerRegistry 的 NuGet 包。

可以使用 AddAzureInfrastructure API 将 Azure 容器注册表基础结构添加到应用主机:

var acr = builder.AddAzureInfrastructure("acr", infra =>
{
    var registry = new ContainerRegistryService("acr")
    {
        Sku = new()
        {
            Name = ContainerRegistrySkuName.Standard
        },
    };
    infra.Add(registry);

    var output = new ProvisioningOutput("registryName", typeof(string))
    {
        Value = registry.Name
    };
    infra.Add(output);
});

builder.AddProject<Projects.WorkerService>("worker")
       .WithEnvironment(
            "ACR_REGISTRY_NAME",
            new BicepOutputReference("registryName", acr.Resource));

前面的代码:

  • 调用 AddAzureInfrastructure,其名称为 acr
  • 提供 configureInfrastructure 委托,以自定义 Azure 容器注册表的基础设施。
    • 实例化一个名为 acrContainerRegistryService,使用标准 SKU。
    • 将 Azure 容器注册表服务添加到 infra 变量。
    • 实例化一个名为 registryNameProvisioningOutput,其类型为 string,值为对应 Azure 容器注册表的名称。
    • 将输出添加到 infra 变量。
  • 将名为 worker 的项目添加到生成器。
  • 将一个调用链接到 WithEnvironment,以在项目中将 ACR_REGISTRY_NAME 环境变量设置为 registryName 的输出值。

此功能演示了如何将 Azure 基础设施添加到应用主机项目,即使 Azure 服务没有直接以 .NET Aspire 集成的形式曝光。 它进一步演示如何将 Azure 容器注册表的输出流到依赖项目的环境中。

请查看下列生成的 Bicep 文件:

@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

resource acr 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
  name: take('acr${uniqueString(resourceGroup().id)}', 50)
  location: location
  sku: {
    name: 'Standard'
  }
}

output registryName string = acr.name

Bicep 文件反映了 Azure API 定义的 AddAzureInfrastructure 容器注册表的所需配置。

使用自定义 Bicep 模板

把 Azure 设为所需的云提供商时,可以用 Bicep 把基础结构定义为代码。 它旨在通过更简洁的语法大幅简化创作体验,并更好地支持模块化和代码重用。

虽然 .NET.NET Aspire 提供了一组预生成的 Bicep 模板,但有时你可能想要自定义模板或创建自己的模板。 本部分介绍可用于自定义 Bicep 模板的概念和相应的 API。

重要

本节的目的不是教授 Bicep,而是提供指导,帮助您为 .NET.NET Aspire 创建自定义的 Bicep 模板。

作为 部署故事的一部分,)提供对 项目的了解,并能够将其部署到 azd CLI 使用 Bicep 模板将应用程序部署到 Azure。

安装 Aspire.Hosting.Azure

当您想引用 Bicep 文件时,可能没有使用任何 Azure 主机集成。 在这种情况下,您仍然可以通过安装 Aspire.Hosting.Azure 包来引用 Bicep 文件。 此包提供用于引用 Bicep 文件和自定义 Azure 资源所需的 API。

提示

如果使用任何 Azure 托管集成,则无需安装 Aspire.Hosting.Azure 包,因为它是可传递依赖项。

若要使用这些功能中的任何一项,必须安装 📦Aspire.Hosting.Azure NuGet 包:

dotnet add package Aspire.Hosting.Azure

有关详细信息,请参阅 dotnet add package管理 .NET 应用程序中的包依赖项。

示例中可以预期的内容

本节中的所有示例都假定你使用的是 Aspire.Hosting.Azure 命名空间。 此外,这些示例假定你有一个 IDistributedApplicationBuilder 实例:

using Aspire.Hosting.Azure;

var builder = DistributedApplication.CreateBuilder(args);

// Examples go here...

builder.Build().Run();

默认情况下,调用任何与 Bicep 相关的 API 时,还会调用 AddAzureProvisioning,以便在应用程序启动期间动态生成 Azure 资源。 有关详细信息,请参阅 本地预配和 Azure.Provisioning

引用 Bicep 文件

假设在名为 storage.bicep 的文件中有一个 Bicep 模板,用于配置 Azure 存储帐户:

param location string = resourceGroup().location
param storageAccountName string = 'toylaunch${uniqueString(resourceGroup().id)}'

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

若要在磁盘上添加对 Bicep 文件的引用,请调用 AddBicepTemplate 方法。 请考虑以下示例:

builder.AddBicepTemplate(
    name: "storage",
    bicepFile: "../infra/storage.bicep");

前面的代码添加对位于 ../infra/storage.bicep的 Bicep 文件的引用。 文件路径应相对于 应用主机 项目。 此引用会将名称为 AzureBicepResource"storage" 添加到应用程序的资源集合中,并且 API 会返回一个 IResourceBuilder<AzureBicepResource> 实例,该实例可用于进一步自定义资源。

引用 Bicep 内联

虽然在磁盘上拥有 Bicep 文件是最常见的情况,但也可以直接添加 Bicep 模板。 如果想要在代码中定义模板,或者想要动态生成模板,则内联模板非常有用。 若要添加内联 Bicep 模板,请调用 AddBicepTemplateString 方法,并使用 Bicep 模板作为 string。 请考虑以下示例:

builder.AddBicepTemplateString(
        name: "ai",
        bicepContent: """
        @description('That name is the name of our application.')
        param cognitiveServiceName string = 'CognitiveService-${uniqueString(resourceGroup().id)}'

        @description('Location for all resources.')
        param location string = resourceGroup().location

        @allowed([
          'S0'
        ])
        param sku string = 'S0'

        resource cognitiveService 'Microsoft.CognitiveServices/accounts@2021-10-01' = {
          name: cognitiveServiceName
          location: location
          sku: {
            name: sku
          }
          kind: 'CognitiveServices'
          properties: {
            apiProperties: {
              statisticsEnabled: false
            }
          }
        }
        """
    );

在此示例中,Bicep 模板被定义为内联 string,并将其添加到名为 "ai"的应用程序资源集合中。 此示例配置 Azure AI 资源。

将参数传递给 Bicep 模板

Bicep 支持接受参数,可用于自定义模板的行为。 若要从 .NET.NET Aspire 向 Bicep 模板传递参数,可以按照以下示例,通过链式调用 WithParameter 方法来实现:

var region = builder.AddParameter("region");

builder.AddBicepTemplate("storage", "../infra/storage.bicep")
       .WithParameter("region", region)
       .WithParameter("storageName", "app-storage")
       .WithParameter("tags", ["latest","dev"]);

前面的代码:

  • 将名为 "region" 的参数添加到 builder 实例。
  • 添加对位于 ../infra/storage.bicep的 Bicep 文件的引用。
  • "region" 参数传递给 Bicep 模板,通过标准参数解析进行解析。
  • 使用硬编码值将 "storageName" 参数传递给 Bicep 模板。
  • 使用字符串数组将 "tags" 参数传递给 Bicep 模板。

有关详细信息,请参阅 外部参数

已知参数

.NET .NET Aspire 提供了一组可传递给 Bicep 模板的已知参数。 这些参数用于向 Bicep 模板提供有关应用程序和环境的信息。 以下已知参数可用:

描述 价值
AzureBicepResource.KnownParameters.KeyVaultName 用于存储机密输出的密钥保管库资源的名称。 "keyVaultName"
AzureBicepResource.KnownParameters.Location 资源的位置。 这是所有资源所必需的。 "location"
AzureBicepResource.KnownParameters.LogAnalyticsWorkspaceId Log Analytics 工作区的资源 ID。 "logAnalyticsWorkspaceId"
AzureBicepResource.KnownParameters.PrincipalId 当前用户或托管身份的主体 ID。 "principalId"
AzureBicepResource.KnownParameters.PrincipalName 当前用户或托管身份的主体名称。 "principalName"
AzureBicepResource.KnownParameters.PrincipalType 当前用户或受管理身份的主体类型。 UserServicePrincipal "principalType"

若要使用已知参数,请将参数名称传递给 WithParameter 方法,例如 WithParameter(AzureBicepResource.KnownParameters.KeyVaultName)。 不传递已知参数的值,因为 .NET.NET Aspire 代表你解析它们。

请考虑一个示例,您想要设置一个 Azure Event Grid Webhook。 您可以定义 Bicep 模板,如下所示:

param topicName string
param webHookEndpoint string
param principalId string
param principalType string
param location string = resourceGroup().location

// The topic name must be unique because it's represented by a DNS entry. 
// must be between 3-50 characters and contain only values a-z, A-Z, 0-9, and "-".

resource topic 'Microsoft.EventGrid/topics@2023-12-15-preview' = {
  name: toLower(take('${topicName}${uniqueString(resourceGroup().id)}', 50))
  location: location

  resource eventSubscription 'eventSubscriptions' = {
    name: 'customSub'
    properties: {
      destination: {
        endpointType: 'WebHook'
        properties: {
          endpointUrl: webHookEndpoint
        }
      }
    }
  }
}

resource EventGridRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(topic.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd5a91429-5739-47e2-a06b-3470a27159e7'))
  scope: topic
  properties: {
    principalId: principalId
    principalType: principalType
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd5a91429-5739-47e2-a06b-3470a27159e7')
  }
}

output endpoint string = topic.properties.endpoint

此 Bicep 模板定义了多个参数,包括 topicNamewebHookEndpointprincipalIdprincipalType和可选 location。 若要将这些参数传递给 Bicep 模板,可以使用以下代码片段:

var webHookApi = builder.AddProject<Projects.WebHook_Api>("webhook-api");

var webHookEndpointExpression = ReferenceExpression.Create(
        $"{webHookApi.GetEndpoint("https")}/hook");

builder.AddBicepTemplate("event-grid-webhook", "../infra/event-grid-webhook.bicep")
       .WithParameter("topicName", "events")
       .WithParameter(AzureBicepResource.KnownParameters.PrincipalId)
       .WithParameter(AzureBicepResource.KnownParameters.PrincipalType)
       .WithParameter("webHookEndpoint", () => webHookEndpointExpression);
  • webHookApi 项目添加为对 builder的引用。
  • topicName 参数传递一个固定的名称值。
  • webHookEndpoint 参数作为一个表达式传递,该表达式解析为从 api 项目引用的 "https" 终结点按 /hook 路由生成的 URL。
  • principalIdprincipalType 参数作为已知参数传递。

已知参数基于约定,在使用 WithParameter API 传递时不应附带相应的值。 众所周知的参数能够简化一些常见功能,例如将 角色分配添加到 Bicep 模板时,如前面的示例所示。 事件网格 Webhook 需要进行角色分配才能将事件发送到指定的终结点。 有关详细信息,请参阅 事件网格数据发送方角色分配

获取来自 Bicep 引用的输出

除了将参数传递给 Bicep 模板之外,还可以从 Bicep 模板获取输出。 请考虑以下 Bicep 模板,因为它定义了名为 outputendpoint

param storageName string
param location string = resourceGroup().location

resource myStorageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: storageName
  location: location
  kind: 'StorageV2'
  sku:{
    name:'Standard_LRS'
    tier: 'Standard'
  }
  properties: {
    accessTier: 'Hot'
  }
}

output endpoint string = myStorageAccount.properties.primaryEndpoints.blob

Bicep 脚本定义了一个名为 "endpoint" 的输出变量。 若要从 Bicep 模板获取输出,请对 GetOutput 实例调用 IResourceBuilder<AzureBicepResource> 方法,如以下 C# 代码片段所示:

var storage = builder.AddBicepTemplate(
        name: "storage",
        bicepFile: "../infra/storage.bicep"
    );

var endpoint = storage.GetOutput("endpoint");

在此示例中,检索 Bicep 模板的输出并将其存储在 endpoint 变量中。 通常,将此输出作为环境变量传递给依赖于它的另一个资源。 例如,如果你有一个依赖于此终结点的 ASP.NET Core 最小 API 项目,则可以使用以下代码片段将输出作为环境变量传递给项目:

var storage = builder.AddBicepTemplate(
                name: "storage",
                bicepFile: "../infra/storage.bicep"
            );

var endpoint = storage.GetOutput("endpoint");

var apiService = builder.AddProject<Projects.AspireSample_ApiService>(
        name: "apiservice"
    )
    .WithEnvironment("STORAGE_ENDPOINT", endpoint);

有关详细信息,请参阅 Bicep 输出

从 Bicep 的引用中获取机密的输出

使用 Bicep 时,请务必 避免 机密信息的输出。 如果输出被视为 机密,这意味着它不应在日志或其他位置公开,则可以将其视为此类。 这可以通过将机密存储在 Azure Key Vault 中并在 Bicep 模板中引用它来实现。 .NET Aspire的 Azure 集成提供了一种模式,允许资源使用 keyVaultName 参数将机密存储在 Azure Key Vault中,从而安全地存储来自 Bicep 模板的输出。

请考虑以下 Bicep 模板作为示例,以演示保护秘密输出的概念:

param databaseAccountName string
param keyVaultName string

param databases array = []

@description('Tags that will be applied to all resources')
param tags object = {}

param location string = resourceGroup().location

var resourceToken = uniqueString(resourceGroup().id)

resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
    name: replace('${databaseAccountName}-${resourceToken}', '-', '')
    location: location
    kind: 'GlobalDocumentDB'
    tags: tags
    properties: {
        consistencyPolicy: { defaultConsistencyLevel: 'Session' }
        locations: [
            {
                locationName: location
                failoverPriority: 0
            }
        ]
        databaseAccountOfferType: 'Standard'
    }

    resource db 'sqlDatabases@2023-04-15' = [for name in databases: {
        name: '${name}'
        location: location
        tags: tags
        properties: {
            resource: {
                id: '${name}'
            }
        }
    }]
}

var primaryMasterKey = cosmosDb.listKeys(cosmosDb.apiVersion).primaryMasterKey

resource vault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
    name: keyVaultName

    resource secret 'secrets@2023-07-01' = {
        name: 'connectionString'
        properties: {
            value: 'AccountEndpoint=${cosmosDb.properties.documentEndpoint};AccountKey=${primaryMasterKey}'
        }
    }
}

前面的 Bicep 模板需要 keyVaultName 参数以及其他几个参数。 然后,它定义一个 Azure Cosmos DB 资源,并将机密存储到 Azure Key Vault中,该机密名为 connectionString,表示 Cosmos DB 实例的完全限定连接字符串。 若要访问此机密连接字符串值,可以使用以下代码片段:

var cosmos = builder.AddBicepTemplate("cosmos", "../infra/cosmosdb.bicep")
    .WithParameter("databaseAccountName", "fallout-db")
    .WithParameter(AzureBicepResource.KnownParameters.KeyVaultName)
    .WithParameter("databases", ["vault-33", "vault-111"]);

var connectionString =
    cosmos.GetSecretOutput("connectionString");

builder.AddProject<Projects.WebHook_Api>("api")
    .WithEnvironment(
        "ConnectionStrings__cosmos",
        connectionString);

在前面的代码片段中,作为对 cosmos的引用,builder Bicep 模板被添加。 connectionString 机密输出从 Bicep 模板中检索并存储在某个变量中。 然后,机密输出作为环境变量(ConnectionStrings__cosmos)传递给 api 项目。 此环境变量用于连接到 Cosmos DB 实例。

部署此资源后,基础部署机制将自动引用机密从Azure Key Vault。 为了保证机密隔离,.NET.NET Aspire 为每个源创建 Key Vault。

注释

本地预配 模式下,机密将从 Key Vault 中提取,并将其设置为环境变量。 有关详细信息,请参阅 本地Azure 预配

出版

发布应用时,Azure 会使用 Azure Developer CLI 预配生成的 Bicep 来创建 Azure 订阅中的 Azure 资源。 .NET .NET Aspire 输出 发布清单,这也是发布过程的重要组成部分。 Azure Developer CLI 是一种命令行工具,提供一组用于管理 Azure 资源的命令。

有关发布和部署的详细信息,请参阅 .NET Aspire将 Azure Container Apps 项目部署到 Azure Developer CLI。