Compartir a través de


Visión general de las integraciones de .NET AspireAzure

Azure es la plataforma en la nube más popular para compilar e implementar aplicaciones .NET. El SDK de Azure para .NET permite administrar y usar fácilmente los servicios de Azure. .NET Aspire proporciona un conjunto de integraciones con servicios Azure, donde puede agregar nuevos recursos o conectarse a los existentes. En este artículo se detallan algunos aspectos comunes de todas las integraciones de Azure en .NET Aspire y se pretende ayudarle a comprender cómo usarlos.

Agregar Azure recursos

Todas las integraciones de hospedaje de .NET AspireAzure exponen recursos Azure y por convención se agregan mediante AddAzure* API. Al añadir estos recursos a tu host de aplicación .NET Aspire, representan un servicio Azure. La API de AddAzure* devuelve un IResourceBuilder<T> donde T es el tipo de recurso de Azure. Estas interfaces de IResourceBuilder<T> (constructor) proporcionan una API fluida que permite configurar el recurso subyacente Azure dentro del modelo de aplicación. Hay API para agregar nuevos recursos de Azure, marcar los recursos como existentes y configurar cómo se comportan los recursos en varios contextos de ejecución.

Experiencia típica para desarrolladores

Cuando el host de la aplicación de .NET Aspire contiene recursos de Azure y lo ejecuta localmente (experiencia típica del desarrollador con F5 o dotnet run), los recursos de Azure se aprovisionan en la suscripción de Azure. Esta funcionalidad le permite a usted como desarrollador depurar estos elementos localmente en el contexto del host de su aplicación.

.NET .NET Aspire pretende minimizar los costos por defecto optando por Basic o StandardStock Keeping Unit (SKU) para sus integraciones de Azure. Aunque se proporcionan estos valores predeterminados razonables, puede personalizar los recursos de Azure para satisfacer sus necesidades. Además, algunas integraciones admiten emuladores o contenedores, que son útiles para el desarrollo, las pruebas y la depuración locales. De forma predeterminada, al ejecutar la aplicación localmente, los recursos de Azure usan el servicio Azure real. Sin embargo, puede configurarlos para que usen emuladores o contenedores locales, evitando los costos asociados con el servicio Azure real durante el desarrollo local.

Emuladores locales

Algunos servicios de Azure se pueden emular para ejecutarse localmente. Actualmente, .NET Aspire admite los siguientes emuladores de Azure:

Integración de hospedaje Descripción
Azure Cosmos DB Llame a AzureCosmosExtensions.RunAsEmulator en el IResourceBuilder<AzureCosmosDBResource> para configurar el recurso Cosmos DB y que sea emulado con la API NoSQL.
Azure Event Hubs Llame a AzureEventHubsExtensions.RunAsEmulator en el IResourceBuilder<AzureEventHubsResource> para configurar el recurso de Event Hubs para emularlo.
Azure Service Bus Llame a AzureServiceBusExtensions.RunAsEmulator en el IResourceBuilder<AzureServiceBusResource> para configurar el recurso de Service Bus de manera que sea emulado con el emulador de Service Bus.
Azure SignalR Service Llame al AzureSignalRExtensions.RunAsEmulator en el IResourceBuilder<AzureSignalRResource> para configurar el recurso SignalR para que se emule con el emulador AzureSignalR.
Azure almacenamiento Utilice AzureStorageExtensions.RunAsEmulator en IResourceBuilder<AzureStorageResource> para configurar el recurso de almacenamiento que se va a emular con Azurite.

Para que los recursos de Azure usen los emuladores locales, encadena una llamada al método RunAsEmulator del generador de recursos Azure. Este método configura el recurso Azure para usar el emulador local en lugar del servicio Azure real.

Importante

Llamar a cualquiera de las API de RunAsEmulator disponibles en un generador de recursos de Azure no afecta al manifiesto de publicación . Cuando publicas tu aplicación, el archivo Bicep generado refleja el servicio Azure real, no el emulador local.

Contenedores locales

Algunos recursos Azure se pueden sustituir localmente mediante contenedores de código abierto o en las instalaciones. Para sustituir un recurso de Azure localmente en un contenedor, encadene una llamada al método RunAsContainer en el generador de recursos de Azure. Este método configura el recurso de Azure para usar una versión en contenedor del servicio para el desarrollo y las pruebas locales, en lugar del servicio Azure real.

Actualmente, .NET Aspire admite los siguientes servicios Azure como contenedores:

Integración de hospedaje Detalles
Azure Cache for Redis Ejecute AzureRedisExtensions.RunAsContainer en IResourceBuilder<AzureRedisCacheResource> para configurarlo y ejecutarlo localmente en un contenedor, basado en la imagen de docker.io/library/redis.
Azure PostgreSQL Flexible Server Ejecute AzurePostgresExtensions.RunAsContainer en IResourceBuilder<AzurePostgresFlexibleServerResource> para configurarlo y ejecutarlo localmente en un contenedor, basado en la imagen de docker.io/library/postgres.
Azure SQL Server Ejecute AzureSqlExtensions.RunAsContainer en IResourceBuilder<AzureSqlServerResource> para configurarlo y ejecutarlo localmente en un contenedor, basado en la imagen de mcr.microsoft.com/mssql/server.

Nota

Al igual que los emuladores, invocar a RunAsContainer en un constructor de recursos Azure no afecta al manifiesto de publicación . Al publicar tu aplicación, el archivo Bicep generado refleja el servicio Azure real, no el contenedor local.

Descripción de las API de integración de Azure

La fuerza de .NET.NET Aspirereside en su capacidad para proporcionar un ciclo interno asombroso para desarrolladores. Las integraciones de Azure no son diferentes. Proporcionan un conjunto de API y patrones comunes que se comparten en todos los recursos de Azure. Estas APIs y patrones están diseñados para facilitar el trabajo con recursos Azure de manera coherente.

En la sección de contenedores precedentes, has visto cómo ejecutar Azure servicios en contenedores de forma local. Si está familiarizado con .NET.NET Aspire, es posible que se pregunte cómo llamar a AddAzureRedis("redis").RunAsContainer() para obtener un contenedor de docker.io/library/redis local difiere de AddRedis("redis"), ya que ambos dan como resultado el mismo contenedor local.

La respuesta es que no hay ninguna diferencia al ejecutarse localmente. Sin embargo, cuando se publican, consigues recursos diferentes:

API (Interfaz de Programación de Aplicaciones) Modo de ejecución Modo de publicación
AddAzureRedis("redis").RunAsContainer() Contenedor local Redis Azure Cache for Redis
AddRedis("redis") Contenedor local Redis Azure app de contenedor con Redis imagen

Lo mismo sucede con los servicios SQL y PostgreSQL:

API (Interfaz de Programación de Aplicaciones) Modo de ejecución Modo de publicación
AddAzurePostgresFlexibleServer("postgres").RunAsContainer() Contenedor local PostgreSQL Azure PostgreSQL Server flexible
AddPostgres("postgres") Contenedor local PostgreSQL Azure aplicación de contenedores con PostgreSQL imagen
AddAzureSqlServer("sql").RunAsContainer() Contenedor local SQL Server Azure SQL Server
AddSqlServer("sql") Contenedor local SQL Server Azure Aplicación de contenedores con SQL Server imagen

Para obtener más información sobre la diferencia entre los modos de ejecución y publicación, consulte .NET.NET Aspire host de la aplicación: Contexto de ejecución.

API para expresar recursos de Azure en distintos modos

El constructor de aplicaciones distribuidas, que forma parte del host de aplicación, utiliza el patrón de constructor para AddAzure* recursos al modelo de aplicación. Los desarrolladores pueden configurar estos recursos y definir su comportamiento en distintos contextos de ejecución. Azure las integraciones de hospedaje proporcionan API para especificar cómo se deben "publicar" y "ejecutar" estos recursos.

Cuando se ejecuta el host de la aplicación, se usa el contexto de ejecución para determinar si el host de la aplicación está en modo Run o Publish. Las convenciones de nomenclatura de estas API indican la acción prevista para el recurso.

En la tabla siguiente se resumen las convenciones de nomenclatura que se usan para expresar los recursos de Azure:

Operación API (Interfaz de Programación de Aplicaciones) Descripción
Publicar PublishAsConnectionString<T>(IResourceBuilder<T>) Cambia el recurso para que se publique como una referencia de cadena de conexión en el manifiesto.
Publicar PublishAsExisting Usa un recurso de Azure existente cuando se implementa la aplicación en lugar de crear uno nuevo.
Correr AzureSqlExtensions.RunAsContainer(IResourceBuilder<AzureSqlServerResource>, Action<IResourceBuilder<SqlServerServerResource>>)
AzureRedisExtensions.RunAsContainer(IResourceBuilder<AzureRedisCacheResource>, Action<IResourceBuilder<RedisResource>>)
RunAsContainer(IResourceBuilder<AzurePostgresFlexibleServerResource>, Action<IResourceBuilder<PostgresServerResource>>)
Configura un contenedor equivalente para que se ejecute localmente. Para obtener más información, consulte contenedores locales.
Correr 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>>)
Configura el recurso de Azure que se va a emular. Para obtener más información, consulte emuladores locales.
Correr RunAsExisting Usa un recurso existente cuando se ejecuta la aplicación en lugar de crear uno nuevo.
Publicar y ejecutar AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) Usa un recurso existente independientemente de la operación.

Nota

No todas las API están disponibles en todos los recursos de Azure. Por ejemplo, algunos recursos de Azure se pueden incluir en contenedores o emularlos, mientras que otros no.

Para obtener más información sobre los modos de ejecución, vea contexto de ejecución.

Casos de uso de la API del modo de ejecución general

Use RunAsExisting cuando necesite interactuar dinámicamente con un recurso existente durante el tiempo de ejecución sin necesidad de implementarlo ni actualizarlo. Use PublishAsExisting al declarar los recursos existentes como parte de una configuración de implementación, lo que garantiza que se aplican los ámbitos y permisos correctos. Por último, use AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) al declarar los recursos existentes en ambas configuraciones, con un requisito para parametrizar las referencias.

Puede consultar si un recurso está marcado como un recurso existente llamando al método de extensión IsExisting(IResource) en el IResource. Para obtener más información, consulte Uso de Azure recursos existentes.

Usar recursos existentes Azure

.NET Aspire proporciona soporte para referenciar los recursos existentes de Azure. Marca un recurso existente a través de las API de PublishAsExisting, RunAsExistingy AsExisting. Estas API permiten a los desarrolladores hacer referencia a recursos de Azure ya implementados, configurarlos y generar manifiestos de implementación adecuados mediante plantillas de Bicep.

Los recursos existentes a los que se hace referencia con estas API se pueden mejorar con asignaciones de roles y otras personalizaciones que están disponibles con la infraestructura de de .NET.NET Aspirecomo funcionalidades de código. Estas APIs se limitan a recursos Azure que se pueden implementar con plantillas de Bicep.

Configurar los recursos de Azure existentes para el modo de ejecución

El método RunAsExisting se usa cuando una aplicación distribuida se ejecuta en modo "ejecutar". En este modo, se supone que el recurso de Azure al que se hace referencia ya existe y se integra con él durante la ejecución sin aprovisionar el recurso. Para marcar un recurso Azure como existente, llame al método RunAsExisting en el generador de recursos. Tenga en cuenta el ejemplo siguiente:

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");

El código anterior:

  • Crea una nueva instancia de builder.
  • Agrega un parámetro denominado existingServiceBusName al constructor.
  • Agrega un recurso Azure Service Bus denominado messaging al constructor.
  • Llama al método RunAsExisting en el generador de recursos serviceBus, pasando el parámetro existingServiceBusName; alternativamente, puede usar la sobrecarga del parámetro string.
  • Agrega una cola denominada queue al recurso serviceBus.

De forma predeterminada, se supone que la referencia de parámetros de Service Bus está en el mismo grupo de recursos Azure. Sin embargo, si se encuentra en otro grupo de recursos, puede pasar el grupo de recursos explícitamente como parámetro para especificar correctamente la agrupación de recursos adecuada.

Configurar los recursos de Azure existentes para el modo de publicación

El método PublishAsExisting se usa en modo "publicar" cuando la intención es declarar y hacer referencia a un recurso de Azure ya existente durante el modo de publicación. Esta API facilita la creación de manifiestos y plantillas que incluyen definiciones de recursos que se asignan a los recursos existentes en Bicep.

Para marcar un recurso de Azure como existente en para el modo "publicar", llame al método PublishAsExisting en el generador de recursos. Tenga en cuenta el ejemplo siguiente:

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");

El código anterior:

  • Crea una nueva instancia de builder.
  • Agrega un parámetro denominado existingServiceBusName al constructor.
  • Agrega un recurso Azure Service Bus denominado messaging al constructor.
  • Llama al método PublishAsExisting en el generador de recursos serviceBus, pasando el parámetro existingServiceBusName; alternativamente, puede usar la sobrecarga del parámetro string.
  • Agrega una cola denominada queue al recurso serviceBus.

Una vez ejecutado el host de la aplicación en modo de publicación, el archivo de manifiesto generado incluirá el parámetro existingResourceName, que se puede usar para hacer referencia al recurso Azure existente. Tenga en cuenta el siguiente fragmento de código parcial generado del archivo de manifiesto:

"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}"
}

Para obtener más información sobre el archivo de manifiesto, vea .NET.NET Aspire formato de manifiesto para los generadores de herramientas de implementación.

Además, la plantilla de Bicep generada incluye el parámetro existingResourceName, que se puede usar para hacer referencia al recurso Azure existente. Considere la siguiente plantilla generada en 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

Para obtener más información sobre las plantillas de Bicep generadas, consulte Infrastructure as code y considere otras API de publicación.

Advertencia

Al interactuar con los recursos existentes que requieren autenticación, asegúrese de que la estrategia de autenticación que está configurando en el modelo de aplicación .NET.NET Aspire se alinea con la estrategia de autenticación permitida por el recurso existente. Por ejemplo, no es posible usar la identidad administrada en un recurso de AzurePostgreSQL existente que no esté configurado para permitir la identidad administrada. De forma similar, si un recurso existente de AzureRedis deshabilitó las claves de acceso, no es posible usar la autenticación mediante clave de acceso.

Configurar los recursos de Azure existentes en todos los modos

El método AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) se usa cuando la aplicación distribuida se ejecuta en modo "ejecutar" o "publicar". Dado que el método AsExisting funciona en ambos escenarios, solo admite una referencia con parámetros al nombre del recurso o al nombre del grupo de recursos. Este enfoque ayuda a evitar el uso del mismo recurso en entornos de prueba y producción.

Para marcar un recurso Azure como existente, llame al método AsExisting en el generador de recursos. Tenga en cuenta el ejemplo siguiente:

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");

El código anterior:

  • Crea una nueva instancia de builder.
  • Agrega un parámetro denominado existingServiceBusName al constructor.
  • Agrega un recurso Azure Service Bus denominado messaging al constructor.
  • Llama al método AsExisting en el generador de recursos de serviceBus y pasa el parámetro existingServiceBusName.
  • Agrega una cola denominada queue al recurso serviceBus.

Añadir recursos existentes de Azure con cadenas de conexión

.NET .NET Aspire proporciona la capacidad de conectarse a los recursos existentes, incluidos los recursos Azure. Expresar cadenas de conexión resulta útil cuando tiene recursos Azure existentes que desea usar en la aplicación de .NET Aspire. La API de AddConnectionString se usa con el contexto de ejecución del host de la aplicación para agregar condicionalmente una cadena de conexión al modelo de aplicación.

Nota

Las cadenas de conexión se usan para representar una amplia gama de información de conexión, incluidas las conexiones de base de datos, los agentes de mensajes, los URI de punto de conexión y otros servicios. En .NET.NET Aspire nomenclatura, el término "cadena de conexión" se usa para representar cualquier tipo de información de conexión.

Tenga en cuenta el ejemplo siguiente, donde en modo de publicación agrega un recurso de Almacenamiento Azure, mientras que en modo de ejecución agrega una cadena de conexión a un Almacenamiento Azure existente.

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...

El código anterior:

  • Crea una nueva instancia de builder.
  • Agrega un recurso de Azure Storage denominado storage en modo "publicar".
  • Agrega una cadena de conexión en modo 'run' a un almacenamiento Azure existente denominado storage.
  • Agrega un proyecto llamado api al constructor.
  • El proyecto api hace referencia al recurso storage independientemente del modo.

El proyecto de API de consumo usa la información de la cadena de conexión sin saber cómo lo configuró el host de la aplicación. En el modo "publicar", el código agrega un nuevo recurso de almacenamiento Azure, que se reflejaría adecuadamente en el manifiesto de implementación de . Cuando está en modo "ejecutar", la cadena de conexión corresponde a un valor de configuración visible para el host de la aplicación. Se supone que todas las asignaciones de roles para el recurso de destino están configuradas. Esto significa que es probable que configure una variable de entorno o un secreto de usuario para almacenar la cadena de conexión. La configuración se resuelve desde la clave de configuración ConnectionStrings__storage (o ConnectionStrings:storage). Estos valores de configuración se pueden ver cuando se ejecuta la aplicación. Para obtener más información, consulte Detalles del recurso.

A diferencia de los recursos existentes modelados con la API AsExisting de primera clase, los recursos existentes modelados como cadenas de conexión no se pueden mejorar con asignaciones de roles adicionales ni personalizaciones de infraestructura.

Publicar como aplicación contenedora Azure

.NET .NET Aspire permite publicar recursos primitivos como Azure Container Apps, una plataforma sin servidor que reduce la administración de infraestructuras. Los tipos de recurso admitidos incluyen:

Para publicar estos recursos, use las SIGUIENTES API:

Estas APIs configuran el recurso que se va a publicar como una Aplicación de Contenedor Azure y llaman implícitamente a AddAzureContainerAppsInfrastructure(IDistributedApplicationBuilder) para agregar la infraestructura necesaria y los archivos de Bicep al host de la aplicación. Por ejemplo, considere el código siguiente:

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)
                     });
                 });

El código anterior:

  • Crea una nueva instancia de builder.
  • Agrega un parámetro denominado env al constructor.
  • Agrega un proyecto llamado api al constructor.
  • Llama al método PublishAsAzureContainerApp en el generador de recursos de api, pasando una expresión lambda que configura la infraestructura de Azure Container App, donde infra es el AzureResourceInfrastructure y app es el ContainerApp.
    • Agrega una variable de entorno denominada Hello a la aplicación contenedora mediante el parámetro env.
    • El método AsProvisioningParameter se usa para tratar env como una nueva ProvisioningParameter en la infraestructura o reutiliza un parámetro bicep existente si ya existe uno con el mismo nombre.

Para obtener más información, consulte ContainerApp y AsProvisioningParameter.

Infraestructura como código

El Azure SDK para .NET proporciona el paquete NuGet de 📦Azure.Provisioning y un conjunto de paquetes de aprovisionamiento específicos para servicios Azure. Estas Azure bibliotecas de aprovisionamiento facilitan la especificación declarativa de Azure infraestructura de forma nativa en .NET. Sus API le permiten escribir infraestructura orientada a objetos en C#, lo que da lugar a Bicep. Bicep es un lenguaje específico del dominio (DSL) para implementar Azure recursos de manera declarativa.

Aunque es posible aprovisionar Azure recursos manualmente, .NET Aspire simplifica el proceso proporcionando un conjunto de API para expresar recursos Azure. Estas API están disponibles como métodos de extensión en .NET AspireAzure bibliotecas de hospedaje, ampliando la interfaz IDistributedApplicationBuilder. Al añadir recursos Azure al host de la aplicación, se añade implícitamente la funcionalidad de aprovisionamiento adecuada. En otras palabras, no es necesario llamar directamente a ninguna API de aprovisionamiento.

Dado que .NET Aspire modela recursos Azure dentro de integraciones de hospedaje Azure, el SDK de Azure se utiliza para aprovisionar estos recursos. Se generan archivos Bicep que definen los recursos Azure necesarios. Los archivos Bicep generados se generan junto al archivo de manifiesto cuando publicas tu aplicación.

Hay varias maneras de influir en los archivos Bicep generados:

Provisión local y Azure.Provisioning

Para evitar la confusión de términos y ayudar a aclarar el "aprovisionamiento", es importante comprender la distinción entre el aprovisionamiento local y el aprovisionamientoAzure:

  • Aprovisionamiento local:

    De forma predeterminada, cuando se llama a cualquiera de las API de integración de hospedaje de Azure para agregar recursos de Azure, se llama implícitamente a la API de AddAzureProvisioning(IDistributedApplicationBuilder). Esta API registra los servicios en el contenedor de inyección de dependencias (DI) que se utilizan para proveer los recursos Azure cuando se inicia el host de la aplicación. Este concepto se conoce como aprovisionamiento local. Para obtener más información, consulte Azure aprovisionamiento local.

  • Azure.Provisioning:

    Azure.Provisioning hace referencia al paquete NuGet y es un conjunto de bibliotecas que permite usar C# para generar Bicep. Las integraciones de alojamiento en Azure utilizan internamente estas bibliotecas para generar archivos Bicep que definen los recursos Azure que necesita. Para obtener más información, vea Azure.Provisioning personalización.

Personalización Azure.Provisioning

Todas las integraciones de alojamiento de .NET AspireAzure exponen varios recursos de Azure, y todas son subclases del tipo AzureProvisioningResource, que a su vez hereda de AzureBicepResource. Esto habilita extensiones que están restringidas genéricamente a este tipo, permitiendo que una API fluida personalice la infraestructura según sus preferencias. Aunque .NET.NET Aspire proporciona valores predeterminados, eres libre de influir en el Bicep generado mediante estas API.

Configuración de la infraestructura

Independientemente del recurso Azure con el que trabajes, encadenas una llamada al método de extensión ConfigureInfrastructure para configurar su infraestructura subyacente. Este método permite personalizar la infraestructura del recurso Azure pasando un delegado configure de tipo Action<AzureResourceInfrastructure>. El tipo AzureResourceInfrastructure es una subclase del Azure.Provisioning.Infrastructure. Este tipo expone una superficie de API masiva para configurar la infraestructura subyacente del recurso Azure.

Tenga en cuenta el ejemplo siguiente:

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)
        };
    });

El código anterior:

  • Agrega un parámetro denominado storage-sku.
  • Agrega almacenamiento Azure con la API AddAzureStorage, denominada storage.
  • Encadena una llamada a ConfigureInfrastructure para personalizar la infraestructura de almacenamiento de Azure:

Esto ejemplifica introducir un parámetro externo en la infraestructura de almacenamiento de Azure, lo que da como resultado el archivo Bicep generado que refleja la configuración deseada.

Agregar Azure a la infraestructura

No todos los servicios Azure se exponen como integraciones de .NET Aspire. Aun cuando podrían estar disponibles en otro momento, todavía puede habilitar servicios que están disponibles en bibliotecas Azure.Provisioning.*. Imagina un escenario en el que tienes un servicio de trabajador responsable de administrar un Azure Registro de Contenedores. Ahora imagine que un proyecto host de aplicación tiene una dependencia del paquete NuGet 📦Azure.Provisioning.ContainerRegistry.

Puede usar la API de AddAzureInfrastructure para agregar la infraestructura de Azure Container Registry al host de la aplicación:

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));

El código anterior:

  • Realiza una llamada a AddAzureInfrastructure con el nombre acr.
  • Proporciona un delegado configureInfrastructure para personalizar la infraestructura del Azure Registro de Contenedores.
    • Crea una instancia de un ContainerRegistryService con el nombre acr y una SKU estándar.
    • Agrega el servicio Azure Container Registry a la variable infra.
    • Crea una instancia de un ProvisioningOutput con el nombre registryName, un tipo de stringy un valor que corresponde al nombre del Azure Container Registry.
    • Agrega la salida a la variable infra.
  • Agrega un proyecto llamado worker al compilador.
  • Encadena una llamada a WithEnvironment para establecer la variable de entorno ACR_REGISTRY_NAME del proyecto con el valor del resultado de registryName.

La funcionalidad demuestra cómo añadir la infraestructura de Azure al proyecto host de la aplicación, incluso si el servicio Azure no se expone directamente como una .NET Aspire integración. Además, muestra cómo canalizar la salida del Azure Container Registry hacia el entorno de un proyecto dependiente.

Observa el archivo Bicep resultante.

@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

El archivo Bicep refleja la configuración deseada de Azure Container Registry, tal como se define en la API de AddAzureInfrastructure.

Uso de plantillas personalizadas de Bicep

Cuando tenga como destino Azure como proveedor de nube deseado, puede usar Bicep para definir la infraestructura como código. Tiene como objetivo simplificar drásticamente la experiencia de creación con una sintaxis más limpia y una mejor compatibilidad con la modularidad y la reutilización de código.

Aunque .NET.NET Aspire proporciona un conjunto de plantillas de Bicep prediseñadas, puede haber ocasiones en las que desee personalizar las plantillas o crear las suyas propias. En esta sección se explican los conceptos y las API correspondientes que puede usar para personalizar las plantillas de Bicep.

Importante

Esta sección no está pensada para enseñarte Bicep, sino para proporcionar una guía sobre cómo crear plantillas personalizadas de Bicep para su uso con .NET.NET Aspire.

Como parte de la historia de implementación de Azure para .NET Aspire, el Azure Developer CLI (azd) ofrece una comprensión de su proyecto .NET Aspire y la capacidad para implementarlo en Azure. La CLI de azd usa las plantillas de Bicep para implementar la aplicación en Azure.

Instalación de Aspire.Hosting.Azure paquete

Cuando quieres hacer referencia a archivos de Bicep, es posible que no estés usando ninguna de las integraciones de hospedaje Azure. En este caso, puede seguir haciendo referencia a los archivos Bicep instalando el paquete Aspire.Hosting.Azure. Este paquete proporciona las API necesarias para hacer referencia a archivos de Bicep y personalizar los recursos de Azure.

Propina

Si usa cualquiera de las Azure integraciones de hospedaje, no es necesario instalar el paquete de Aspire.Hosting.Azure, ya que es una dependencia transitiva.

Para usar cualquiera de estas funcionalidades, el paquete NuGet 📦Aspire.Hosting.Azure debe estar instalado.

dotnet add package Aspire.Hosting.Azure

Para obtener más información, consulte dotnet add package o Manage package dependencies in .NET applications.

Qué esperar de los ejemplos

En todos los ejemplos de esta sección, se asume que estás usando el espacio de nombres Aspire.Hosting.Azure. Además, en los ejemplos se supone que tiene una instancia de IDistributedApplicationBuilder:

using Aspire.Hosting.Azure;

var builder = DistributedApplication.CreateBuilder(args);

// Examples go here...

builder.Build().Run();

De forma predeterminada, cuando se llama a cualquiera de las API relacionadas con Bicep, también se realiza una llamada a AddAzureProvisioning que agrega compatibilidad para generar recursos de Azure dinámicamente durante el inicio de la aplicación. Para obtener más información, consulte Aprovisionamiento Local y Azure.Provisioning.

Archivos de referencia de Bicep

Imagine que tiene una plantilla de Bicep en un archivo denominado storage.bicep que aprovisiona una Cuenta de Almacenamiento 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'
  }
}

Para agregar una referencia al archivo de Bicep en el disco, llame al método AddBicepTemplate. Tenga en cuenta el ejemplo siguiente:

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

El código anterior agrega una referencia a un archivo de Bicep ubicado en ../infra/storage.bicep. Las rutas de acceso de archivo deben ser relativas al proyecto de hospedaje de la aplicación. Esta referencia da como resultado que se agregue un AzureBicepResource a la colección de recursos de la aplicación con el nombre "storage" y la API devuelve una instancia de IResourceBuilder<AzureBicepResource> que se puede usar para personalizar aún más el recurso.

Referencia en línea para Bicep

Aunque tener un archivo de Bicep en el disco es el escenario más común, también puede agregar plantillas de Bicep en línea. Las plantillas insertadas pueden ser útiles cuando se desea definir una plantilla en el código o cuando se desea generar la plantilla de forma dinámica. Para agregar una plantilla Bicep integrada, llame al método AddBicepTemplateString con la plantilla Bicep como string. Tenga en cuenta el ejemplo siguiente:

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
            }
          }
        }
        """
    );

En este ejemplo, la plantilla de Bicep se define en línea como string y se agrega a la colección de recursos de la aplicación con el nombre "ai". En este ejemplo, se aprovisiona un recurso de Inteligencia Artificial (IA) Azure.

Pasar parámetros a plantillas de Bicep

Bicep admite la aceptación de parámetros, que se pueden usar para personalizar el comportamiento de la plantilla. Para pasar parámetros a una plantilla de Bicep desde .NET.NET Aspire, encadene llamadas al método WithParameter como se muestra en el ejemplo siguiente:

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

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

El código anterior:

  • Agrega un parámetro denominado "region" a la instancia de builder.
  • Agrega una referencia a un archivo de Bicep ubicado en ../infra/storage.bicep.
  • Pasa el parámetro "region" a la plantilla de Bicep, que se resuelve utilizando la resolución estándar de parámetros.
  • Pasa el parámetro "storageName" a la plantilla de Bicep con un valor codificado de forma fija.
  • Pasa el parámetro "tags" a la plantilla de Bicep con un array de cadenas de caracteres.

Para obtener más información, vea Parámetros externos.

Parámetros conocidos

.NET .NET Aspire proporciona un conjunto de parámetros conocidos que se pueden pasar a plantillas de Bicep. Estos parámetros se usan para proporcionar información sobre la aplicación y el entorno a las plantillas de Bicep. Están disponibles los siguientes parámetros conocidos:

Campo Descripción Valor
AzureBicepResource.KnownParameters.KeyVaultName Nombre del recurso del almacén de claves que se utiliza para guardar outputs confidenciales. "keyVaultName"
AzureBicepResource.KnownParameters.Location La ubicación del recurso. Esto es necesario para todos los recursos. "location"
AzureBicepResource.KnownParameters.LogAnalyticsWorkspaceId Identificador de recurso del área de trabajo de Log Analytics. "logAnalyticsWorkspaceId"
AzureBicepResource.KnownParameters.PrincipalId El identificador principal del usuario actual o de la identidad gestionada. "principalId"
AzureBicepResource.KnownParameters.PrincipalName Nombre principal del usuario actual o identidad administrada. "principalName"
AzureBicepResource.KnownParameters.PrincipalType El tipo principal del usuario actual o la identidad administrada. User o ServicePrincipal. "principalType"

Para usar un parámetro conocido, pase el nombre del parámetro al método WithParameter, como WithParameter(AzureBicepResource.KnownParameters.KeyVaultName). No se pasan valores para parámetros conocidos, ya que .NET.NET Aspire los resuelve por usted.

Considere un ejemplo en el que desea configurar un webhook de Azure Event Grid. Puede definir la plantilla de Bicep de la siguiente manera:

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

Esta plantilla de Bicep define varios parámetros, incluidos los topicName, webHookEndpoint, principalId, principalTypey el locationopcional . Para pasar estos parámetros a la plantilla de Bicep, puede usar el siguiente fragmento de código:

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);
  • El proyecto de webHookApi se agrega como referencia al builder.
  • Al parámetro topicName se le pasa un valor de nombre fijo.
  • El parámetro webHookEndpoint se pasa como una expresión que se convierte en la URL desde las referencias del proyecto api en el punto de conexión "https" con la ruta /hook.
  • Los parámetros principalId y principalType se pasan como parámetros conocidos.

Los parámetros conocidos se basan en convenciones y no deben ir acompañados de un valor correspondiente cuando se pasan mediante la API de WithParameter. Los parámetros conocidos simplifican algunas funcionalidades comunes, como las asignaciones de roles , cuando se agregan a las plantillas de Bicep, como se muestra en el ejemplo anterior. Las asignaciones de roles son necesarias para que el webhook de Event Grid envíe eventos al punto de conexión especificado. Para obtener más información, consulte asignación de roles del remitente de datos de Event Grid.

Obtención de resultados a partir de referencias de Bicep

Además de pasar parámetros a las plantillas de Bicep, también puede obtener resultados de estas. Considere la siguiente plantilla de Bicep, ya que define un output denominado endpoint:

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

El Bicep define una salida denominada endpoint. Para obtener la salida de la plantilla de Bicep, llame al método GetOutput en una instancia de IResourceBuilder<AzureBicepResource> como se muestra en el siguiente fragmento de código de C#:

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

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

En este ejemplo, la salida de la plantilla de Bicep se recupera y almacena en una variable endpoint. Normalmente, pasarías esta salida como una variable de entorno a otro recurso que se basa en ella. Por ejemplo, si tenía un proyecto de API mínima ASP.NET Core que dependía de este punto de conexión, podría pasar la salida como una variable de entorno al proyecto mediante el siguiente fragmento de código:

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);

Para obtener más información, consulte los resultados de Bicep.

Obtener resultados secretos a partir de referencias de Bicep

Es importante evitar la salida de secretos cuando se trabaja con Bicep. Si una salida se considera un secreto, lo que significa que no debe exponerse en registros u otros sitios, se puede tratar como tal. Esto se puede lograr almacenando el secreto en Azure Key Vault y haciendo referencia a él en la plantilla de Bicep. La integración de .NET Aspire con Azure ofrece un patrón para almacenar de manera segura las salidas de la plantilla de Bicep, permitiendo que los recursos utilicen el parámetro keyVaultName para guardar secretos en Azure Key Vault.

Considere la siguiente plantilla de Bicep como ejemplo que ayuda a demostrar este concepto de protección de salidas secretas:

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}'
        }
    }
}

La plantilla de Bicep anterior espera un parámetro keyVaultName, entre varios otros parámetros. A continuación, define un recurso de Azure Cosmos DB y guarda un secreto en Azure Key Vault, el cual se denomina connectionString y representa la cadena de conexión completa a la instancia de Cosmos DB. Para acceder a este valor de cadena de conexión secreta, puede usar el siguiente fragmento de código:

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);

En el fragmento de código anterior, la plantilla Bicep de cosmos se agrega como referencia al builder. La salida secreta connectionString se recupera de la plantilla de Bicep y se almacena en una variable. A continuación, se pasa el resultado secreto como una variable de entorno (ConnectionStrings__cosmos) al proyecto api. Esta variable de entorno se usa para conectarse a la instancia de Cosmos DB.

Cuando se implemente este recurso, el mecanismo de implementación subyacente referenciará automáticamente secretos de Azure Key Vault. Para garantizar el aislamiento de secretos, .NET.NET Aspire crea un Key Vault por origen.

Nota

En modo de aprovisionamiento local, el secreto se extrae de Key Vault y se establece en una variable de entorno. Para obtener más información, consulte Azure aprovisionamiento local.

Publicación

Al publicar su aplicación, el aprovisionamiento generado por Azure Bicep es utilizado por Azure Developer CLI para crear los recursos Azure en su suscripción Azure. .NET .NET Aspire genera una manifiesto de publicación, que también es una parte fundamental del proceso de publicación. El Azure Developer CLI es una herramienta de línea de comandos que proporciona un conjunto de comandos para administrar Azure recursos.

Para obtener más información sobre la publicación e implementación, consulte Implementación de un proyecto de .NET Aspire en Azure Container Apps mediante la Azure Developer CLI (guía detallada).