diff --git a/docs/azure/azure-app-configuration-integration.md b/docs/azure/azure-app-configuration-integration.md index 27b0ec6e5e..4cb22d44f0 100644 --- a/docs/azure/azure-app-configuration-integration.md +++ b/docs/azure/azure-app-configuration-integration.md @@ -77,7 +77,7 @@ The preceding code: - The is assigned to `Free`. - A tag is added to the App Configuration store with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Azure App Configuration resource. For more information, see . For more information, see [Azure.Provisioning customization](integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Azure App Configuration resource. For more information, see . For more information, see [Azure.Provisioning customization](customize-azure-resources.md#azureprovisioning-customization). ### Use existing Azure App Configuration resource diff --git a/docs/azure/configure-aca-environments.md b/docs/azure/configure-aca-environments.md index 86581aed2e..848ca349e8 100644 --- a/docs/azure/configure-aca-environments.md +++ b/docs/azure/configure-aca-environments.md @@ -17,7 +17,7 @@ These APIs automatically create a default ACA environment when you publish your The [.NET Aspire app host](../fundamentals/app-host-overview.md) simplifies infrastructure provisioning by generating code to create Azure resources for your applications. This approach enables you to model and configure deployment-related aspects directly in C#, reducing the need to rely on tools like Bicep. These aspects include configuring ACA environments, which provide a serverless platform for running containerized applications. -By using the APIs (explained in [Infrastructure as code](integrations-overview.md#infrastructure-as-code)), you can configure and customize ACA environments along with related resources, such as container registries and file share volumes. Any available deployment setting can be configured. For more information on the available settings, see [Microsoft.App managedEnvironments](/azure/templates/microsoft.app/managedenvironments). +By using the APIs (explained in [Customize Azure resources](customize-azure-resources.md)), you can configure and customize ACA environments along with related resources, such as container registries and file share volumes. Any available deployment setting can be configured. For more information on the available settings, see [Microsoft.App managedEnvironments](/azure/templates/microsoft.app/managedenvironments). This article guides you through the process of tailoring ACA environments for your .NET Aspire solutions. @@ -43,7 +43,7 @@ This module configures: - A role assignment for the user principal ID to the ACA environment. - Various outputs for the ACA environment. -Using the `acaEnv` variable, you can chain a call to the API to customize the ACA environment to your liking. For more information, see [Configure infrastructure](integrations-overview.md#configure-infrastructure). +Using the `acaEnv` variable, you can chain a call to the API to customize the ACA environment to your liking. For more information, see [Configure infrastructure](customize-azure-resources.md#configure-infrastructure). ## Handle naming conventions diff --git a/docs/azure/customize-azure-resources.md b/docs/azure/customize-azure-resources.md new file mode 100644 index 0000000000..f2a6a9855e --- /dev/null +++ b/docs/azure/customize-azure-resources.md @@ -0,0 +1,256 @@ +--- +title: Customize Azure resources +description: Describes how to customize your Azure infrastructure using code in .NET Aspire solutions. +ms.date: 06/23/2025 +uid: dotnet/aspire/integrations/customize-azure-resources +--- + +# Customize Azure resources + +If you're using Azure to host resources for a .NET Aspire solution, you have granular control over those resources. You can either let .NET Aspire configure them if the default properties suit your needs, or override defaults to control their behavior. Let's examine how you can customize your Azure infrastructure from .NET Aspire code. + +The Azure SDK for .NET provides the [📦 Azure.Provisioning](https://www.nuget.org/packages/Azure.Provisioning) NuGet package and a suite of service-specific [Azure provisioning packages](https://www.nuget.org/packages?q=owner%3A+azure-sdk+description%3A+declarative+resource+provisioning&sortby=relevance). These Azure provisioning libraries make it easy to declaratively specify Azure infrastructure natively in .NET. Their APIs enable you to write object-oriented infrastructure in C#, resulting in Bicep. [Bicep is a domain-specific language (DSL)](/azure/azure-resource-manager/bicep/overview) for deploying Azure resources declaratively. + + + +While it's possible to provision Azure resources manually, .NET Aspire simplifies the process by providing a set of APIs to express Azure resources. These APIs are available as extension methods in .NET Aspire Azure hosting libraries, extending the interface. When you add Azure resources to your app host, they add the appropriate provisioning functionality implicitly. In other words, you don't need to call any provisioning APIs directly. + +Since .NET Aspire models Azure resources within Azure hosting integrations, the Azure SDK is used to provision these resources. Bicep files are generated that define the Azure resources you need. The generated Bicep files are output alongside the manifest file when you publish your app. + +There are several ways to influence the generated Bicep files: + +- [Azure.Provisioning customization](#azureprovisioning-customization): + - [Configure infrastructure](#configure-infrastructure): Customize Azure resource infrastructure. + - [Add Azure infrastructure](#add-azure-infrastructure): Manually add Azure infrastructure to your app host. +- [Use custom Bicep templates](#use-custom-bicep-templates): + - [Reference Bicep files](#reference-bicep-files): Add a reference to a Bicep file on disk. + - [Reference Bicep inline](#reference-bicep-inline): Add an inline Bicep template. + +## Local provisioning and `Azure.Provisioning` + +To avoid conflating terms and to help disambiguate "provisioning," it's important to understand the distinction between _local provisioning_ and _Azure provisioning_: + +- **_Local provisioning:_** + + By default, when you call any of the Azure hosting integration APIs to add Azure resources, the API is called implicitly. This API registers services in the dependency injection (DI) container that are used to provision Azure resources when the app host starts. This concept is known as _local provisioning_. For more information, see [Local Azure provisioning](local-provisioning.md). + +- **_`Azure.Provisioning`:_** + + `Azure.Provisioning` refers to the NuGet package, and is a set of libraries that lets you use C# to generate Bicep. The Azure hosting integrations in .NET Aspire use these libraries under the covers to generate Bicep files that define the Azure resources you need. For more information, see [`Azure.Provisioning` customization](#azureprovisioning-customization). + +## `Azure.Provisioning` customization + +All .NET Aspire Azure hosting integrations expose various Azure resources, and they're all subclasses of the type—which itself inherits the . This enables extensions that are generically type-constrained to this type, allowing for a fluent API to customize the infrastructure to your liking. While .NET Aspire provides defaults, you're free to influence the generated Bicep using these APIs. + +### Configure infrastructure + +Regardless of the Azure resource you're working with, to configure its underlying infrastructure, you chain a call to the extension method. This method allows you to customize the infrastructure of the Azure resource by passing a `configure` delegate of type `Action`. The type is a subclass of the . This type exposes a massive API surface area for configuring the underlying infrastructure of the Azure resource. + +Consider the following example: + +:::code language="csharp" source="../snippets/azure/AppHost/Program.ConfigureInfrastructure.cs" id="infra"::: + +The preceding code: + +- Adds a parameter named `storage-sku`. +- Adds Azure Storage with the API named `storage`. +- Chains a call to `ConfigureInfrastructure` to customize the Azure Storage infrastructure: + - Gets the provisionable resources. + - Filters to a single . + - Assigns the `storage-sku` parameter to the property: + - A new instance of the has its `Name` property assigned from the result of the API. + +This exemplifies flowing an [external parameter](../fundamentals/external-parameters.md) into the Azure Storage infrastructure, resulting in the generated Bicep file reflecting the desired configuration. + +### Add Azure infrastructure + +Not all Azure services are exposed as .NET Aspire integrations. While they might be at a later time, you can still provision services that are available in `Azure.Provisioning.*` libraries. Imagine a scenario where you have worker service that's responsible for managing an Azure Container Registry. Now imagine that an app host project takes a dependency on the [📦 Azure.Provisioning.ContainerRegistry](https://www.nuget.org/packages/Azure.Provisioning.ContainerRegistry) NuGet package. + +You can use the `AddAzureInfrastructure` API to add the Azure Container Registry infrastructure to your app host: + +:::code language="csharp" source="../snippets/azure/AppHost/Program.AddAzureInfra.cs" id="add"::: + +The preceding code: + +- Calls with a name of `acr`. +- Provides a `configureInfrastructure` delegate to customize the Azure Container Registry infrastructure: + - Instantiates a with the name `acr` and a standard SKU. + - Adds the Azure Container Registry service to the `infra` variable. + - Instantiates a with the name `registryName`, a type of `string`, and a value that corresponds to the name of the Azure Container Registry. + - Adds the output to the `infra` variable. +- Adds a project named `worker` to the builder. +- Chains a call to to set the `ACR_REGISTRY_NAME` environment variable in the project to the value of the `registryName` output. + +The functionality demonstrates how to add Azure infrastructure to your app host project, even if the Azure service isn't directly exposed as a .NET Aspire integration. It further shows how to flow the output of the Azure Container Registry into the environment of a dependent project. + +Consider the resulting Bicep file: + +:::code language="bicep" source="../snippets/azure/AppHost/acr.module.bicep"::: + +The Bicep file reflects the desired configuration of the Azure Container Registry, as defined by the `AddAzureInfrastructure` API. + +## Use custom Bicep templates + +When you're targeting Azure as your desired cloud provider, you can use Bicep to define your infrastructure as code. It aims to drastically simplify the authoring experience with a cleaner syntax and better support for modularity and code reuse. + +While .NET Aspire provides a set of prebuilt Bicep templates, there might be times when you either want to customize the templates or create your own. This section explains the concepts and corresponding APIs that you can use to customize the Bicep templates. + +> [!IMPORTANT] +> This section isn't intended to teach you Bicep, but rather to provide guidance on how to create custom Bicep templates for use with .NET Aspire. + +As part of the [Azure deployment story for .NET Aspire](../deployment/overview.md), the Azure Developer CLI (`azd`) provides an understanding of your .NET Aspire project and the ability to deploy it to Azure. The `azd` CLI uses the Bicep templates to deploy the application to Azure. + +### Install `Aspire.Hosting.Azure` package + +When you want to reference Bicep files, it's possible that you're not using any of the Azure hosting integrations. In this case, you can still reference Bicep files by installing the `Aspire.Hosting.Azure` package. This package provides the necessary APIs to reference Bicep files and customize the Azure resources. + +> [!TIP] +> If you're using any of the Azure hosting integrations, you don't need to install the `Aspire.Hosting.Azure` package, as it's a transitive dependency. + +To use any of this functionality, the [📦 Aspire.Hosting.Azure](https://www.nuget.org/packages/Aspire.Hosting.Azure) NuGet package must be installed: + +### [.NET CLI](#tab/dotnet-cli) + +```dotnetcli +dotnet add package Aspire.Hosting.Azure +``` + +### [PackageReference](#tab/package-reference) + +```xml + +``` + +--- + +For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies). + +### What to expect from the examples + +All the examples in this section assume that you're using the namespace. Additionally, the examples assume you have an instance: + +```csharp +using Aspire.Hosting.Azure; + +var builder = DistributedApplication.CreateBuilder(args); + +// Examples go here... + +builder.Build().Run(); +``` + +By default, when you call any of the Bicep-related APIs, a call is also made to that adds support for generating Azure resources dynamically during application startup. For more information, see [Local provisioning and `Azure.Provisioning`](#local-provisioning-and-azureprovisioning). + +### Reference Bicep files + +Imagine that you have a Bicep template in a file named `storage.bicep` that provisions an Azure Storage Account: + +:::code language="bicep" source="snippets/bicep/AppHost.Bicep/storage.bicep"::: + +To add a reference to the Bicep file on disk, call the method. Consider the following example: + +:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.ReferenceBicep.cs" id="addfile"::: + +The preceding code adds a reference to a Bicep file located at `../infra/storage.bicep`. The file paths should be relative to the _app host_ project. This reference results in an being added to the application's resources collection with the `"storage"` name, and the API returns an `IResourceBuilder` instance that can be used to further customize the resource. + +### Reference Bicep inline + +While having a Bicep file on disk is the most common scenario, you can also add Bicep templates inline. Inline templates can be useful when you want to define a template in code or when you want to generate the template dynamically. To add an inline Bicep template, call the method with the Bicep template as a `string`. Consider the following example: + +:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.InlineBicep.cs" id="addinline"::: + +In this example, the Bicep template is defined as an inline `string` and added to the application's resources collection with the name `"ai"`. This example provisions an Azure AI resource. + +### Pass parameters to Bicep templates + +[Bicep supports accepting parameters](/azure/azure-resource-manager/bicep/parameters), which can be used to customize the behavior of the template. To pass parameters to a Bicep template from .NET Aspire, chain calls to the method as shown in the following example: + +:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.PassParameter.cs" id="addparameter"::: + +The preceding code: + +- Adds a parameter named `"region"` to the `builder` instance. +- Adds a reference to a Bicep file located at `../infra/storage.bicep`. +- Passes the `"region"` parameter to the Bicep template, which is resolved using the standard parameter resolution. +- Passes the `"storageName"` parameter to the Bicep template with a _hardcoded_ value. +- Passes the `"tags"` parameter to the Bicep template with an array of strings. + +For more information, see [External parameters](../fundamentals/external-parameters.md). + +#### Well-known parameters + +.NET Aspire provides a set of well-known parameters that can be passed to Bicep templates. These parameters are used to provide information about the application and the environment to the Bicep templates. The following well-known parameters are available: + +| Field | Description | Value | +|--|--|--| +| | The name of the key vault resource used to store secret outputs. | `"keyVaultName"` | +| | The location of the resource. This is required for all resources. | `"location"` | +| | The resource ID of the log analytics workspace. | `"logAnalyticsWorkspaceId"` | +| | The principal ID of the current user or managed identity. | `"principalId"` | +| | The principal name of the current user or managed identity. | `"principalName"` | +| | The principal type of the current user or managed identity. Either `User` or `ServicePrincipal`. | `"principalType"` | + +To use a well-known parameter, pass the parameter name to the method, such as `WithParameter(AzureBicepResource.KnownParameters.KeyVaultName)`. You don't pass values for well-known parameters, as .NET Aspire resolves them on your behalf. + +Consider an example where you want to set up an Azure Event Grid webhook. You might define the Bicep template as follows: + + :::code language="bicep" source="snippets/bicep/AppHost.Bicep/event-grid-webhook.bicep" highlight="3-4,27-35"::: + +This Bicep template defines several parameters, including the `topicName`, `webHookEndpoint`, `principalId`, `principalType`, and the optional `location`. To pass these parameters to the Bicep template, you can use the following code snippet: + +:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.PassParameter.cs" id="addwellknownparams"::: + +- The `webHookApi` project is added as a reference to the `builder`. +- The `topicName` parameter is passed a hardcoded name value. +- The `webHookEndpoint` parameter is passed as an expression that resolves to the URL from the `api` project references' "https" endpoint with the `/hook` route. +- The `principalId` and `principalType` parameters are passed as well-known parameters. + +The well-known parameters are convention-based and shouldn't be accompanied with a corresponding value when passed using the `WithParameter` API. Well-known parameters simplify some common functionality, such as _role assignments_, when added to the Bicep templates, as shown in the preceding example. Role assignments are required for the Event Grid webhook to send events to the specified endpoint. For more information, see [Event Grid Data Sender role assignment](/azure/role-based-access-control/built-in-roles/integration#eventgrid-data-sender). + +## Get outputs from Bicep references + +In addition to passing parameters to Bicep templates, you can also get outputs from the Bicep templates. Consider the following Bicep template, as it defines an `output` named `endpoint`: + +:::code language="bicep" source="snippets/bicep/AppHost.Bicep/storage-out.bicep"::: + +The Bicep defines an output named `endpoint`. To get the output from the Bicep template, call the method on an `IResourceBuilder` instance as demonstrated in following C# code snippet: + +:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.GetOutputReference.cs" id="getoutput"::: + +In this example, the output from the Bicep template is retrieved and stored in an `endpoint` variable. Typically, you would pass this output as an environment variable to another resource that relies on it. For instance, if you had an ASP.NET Core Minimal API project that depended on this endpoint, you could pass the output as an environment variable to the project using the following code snippet: + +```csharp +var storage = builder.AddBicepTemplate( + name: "storage", + bicepFile: "../infra/storage.bicep" + ); + +var endpoint = storage.GetOutput("endpoint"); + +var apiService = builder.AddProject( + name: "apiservice" + ) + .WithEnvironment("STORAGE_ENDPOINT", endpoint); +``` + +For more information, see [Bicep outputs](/azure/azure-resource-manager/bicep/outputs). + +## Get secret outputs from Bicep references + +It's important to [avoid outputs for secrets](/azure/azure-resource-manager/bicep/scenarios-secrets#avoid-outputs-for-secrets) when working with Bicep. If an output is considered a _secret_, meaning it shouldn't be exposed in logs or other places, you can treat it as such. This can be achieved by storing the secret in Azure Key Vault and referencing it in the Bicep template. .NET Aspire's Azure integration provides a pattern for securely storing outputs from the Bicep template by allows resources to use the `keyVaultName` parameter to store secrets in Azure Key Vault. + +Consider the following Bicep template as an example the helps to demonstrate this concept of securing secret outputs: + +:::code language="bicep" source="snippets/bicep/AppHost.Bicep/cosmosdb.bicep" highlight="2,41"::: + +The preceding Bicep template expects a `keyVaultName` parameter, among several other parameters. It then defines an Azure Cosmos DB resource and stashes a secret into Azure Key Vault, named `connectionString` which represents the fully qualified connection string to the Cosmos DB instance. To access this secret connection string value, you can use the following code snippet: + +:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.cs" id="secrets"::: + +In the preceding code snippet, the `cosmos` Bicep template is added as a reference to the `builder`. The `connectionString` secret output is retrieved from the Bicep template and stored in a variable. The secret output is then passed as an environment variable (`ConnectionStrings__cosmos`) to the `api` project. This environment variable is used to connect to the Cosmos DB instance. + +When this resource is deployed, the underlying deployment mechanism will automatically [Reference secrets from Azure Key Vault](/azure/container-apps/manage-secrets?tabs=azure-portal#reference-secret-from-key-vault). To guarantee secret isolation, .NET Aspire creates a Key Vault per source. + +> [!NOTE] +> In _local provisioning_ mode, the secret is extracted from Key Vault and set it in an environment variable. For more information, see [Local Azure provisioning](local-provisioning.md). diff --git a/docs/azure/integrations-overview.md b/docs/azure/integrations-overview.md index 5d1bceebf5..fbc8c23d77 100644 --- a/docs/azure/integrations-overview.md +++ b/docs/azure/integrations-overview.md @@ -17,7 +17,7 @@ All .NET Aspire Azure hosting integrations expose Azure resources and by convent When your .NET Aspire app host contains Azure resources, and you run it locally (typical developer F5 or `dotnet run` experience), the [Azure resources are provisioned](local-provisioning.md) in your Azure subscription. This allows you as the developer to debug against them locally in the context of your app host. -.NET Aspire aims to minimize costs by defaulting to _Basic_ or _Standard_ [Stock Keeping Unit (SKU)](/partner-center/developer/product-resources#sku) for its Azure integrations. While these sensible defaults are provided, you can [customize the Azure resources](#azureprovisioning-customization) to suit your needs. Additionally, some integrations support [emulators](#local-emulators) or [containers](#local-containers), which are useful for local development, testing, and debugging. By default, when you run your app locally, the Azure resources use the actual Azure service. However, you can configure them to use local emulators or containers, avoiding costs associated with the actual Azure service during local development. +.NET Aspire aims to minimize costs by defaulting to _Basic_ or _Standard_ [Stock Keeping Unit (SKU)](/partner-center/developer/product-resources#sku) for its Azure integrations. While these sensible defaults are provided, you can [customize the Azure resources](customize-azure-resources.md#azureprovisioning-customization) to suit your needs. Additionally, some integrations support [emulators](#local-emulators) or [containers](#local-containers), which are useful for local development, testing, and debugging. By default, when you run your app locally, the Azure resources use the actual Azure service. However, you can configure them to use local emulators or containers, avoiding costs associated with the actual Azure service during local development. ### Local emulators @@ -34,7 +34,7 @@ Some Azure services can be emulated to run locally. Currently, .NET Aspire suppo To have your Azure resources use the local emulators, chain a call the `RunAsEmulator` method on the Azure resource builder. This method configures the Azure resource to use the local emulator instead of the actual Azure service. > [!IMPORTANT] -> Calling any of the available `RunAsEmulator` APIs on an Azure resource builder doesn't effect the [publishing manifest](../deployment/manifest-format.md). When you publish your app, [generated Bicep file](#infrastructure-as-code) reflects the actual Azure service, not the local emulator. +> Calling any of the available `RunAsEmulator` APIs on an Azure resource builder doesn't effect the [publishing manifest](../deployment/manifest-format.md). When you publish your app, [the generated Bicep file](customize-azure-resources.md) reflects the actual Azure service, not the local emulator. ### Local containers @@ -49,7 +49,7 @@ Currently, .NET Aspire supports the following Azure services as containers: | Azure SQL Server | Call on the `IResourceBuilder` to configure it to run locally in a container, based on the `mcr.microsoft.com/mssql/server` image. | > [!NOTE] -> Like emulators, calling `RunAsContainer` on an Azure resource builder doesn't effect the [publishing manifest](../deployment/manifest-format.md). When you publish your app, the [generated Bicep file](#infrastructure-as-code) reflects the actual Azure service, not the local container. +> Like emulators, calling `RunAsContainer` on an Azure resource builder doesn't effect the [publishing manifest](../deployment/manifest-format.md). When you publish your app, the [generated Bicep file](customize-azure-resources.md) reflects the actual Azure service, not the local container. ### Understand Azure integration APIs @@ -107,7 +107,7 @@ You can query whether a resource is marked as an existing resource, by calling t .NET Aspire provides support for referencing existing Azure resources. You mark an existing resource through the `PublishAsExisting`, `RunAsExisting`, and `AsExisting` APIs. These APIs allow developers to reference already-deployed Azure resources, configure them, and generate appropriate deployment manifests using Bicep templates. -Existing resources referenced with these APIs can be enhanced with [role assignments](role-assignments.md) and other customizations that are available with .NET Aspire's [infrastructure as code capabilities](#infrastructure-as-code). These APIs are limited to Azure resources that can be deployed with Bicep templates. +Existing resources referenced with these APIs can be enhanced with [role assignments](role-assignments.md) and other customizations that are available with .NET Aspire's [infrastructure as code capabilities](customize-azure-resources.md). These APIs are limited to Azure resources that can be deployed with Bicep templates. ### Configure existing Azure resources for run mode @@ -216,7 +216,7 @@ resource queue 'Microsoft.ServiceBus/namespaces/queues@2024-01-01' = { output serviceBusEndpoint string = messaging.properties.serviceBusEndpoint ``` -For more information on the generated Bicep templates, see [Infrastructure as code](#infrastructure-as-code) and [consider other publishing APIs](#publish-as-azure-container-app). +For more information on the generated Bicep templates, see [Customize Azure resources](customize-azure-resources.md) and [consider other publishing APIs](#publish-as-azure-container-app). > [!WARNING] > When interacting with existing resources that require authentication, ensure the authentication strategy that you're configuring in the .NET Aspire application model aligns with the authentication strategy allowed by the existing resource. For example, it's not possible to use managed identity against an existing Azure PostgreSQL resource that isn't configured to allow managed identity. Similarly, if an existing Azure Redis resource disabled access keys, it's not possible to use access key authentication. @@ -326,254 +326,6 @@ To configure the Azure Container App environment, see [Configure Azure Container > [!TIP] > If you're working with Azure Container Apps, you might also be interested in the [.NET Aspire Azure Container Registry integration](container-registry-integration.md). -## Infrastructure as code - -The Azure SDK for .NET provides the [📦 Azure.Provisioning](https://www.nuget.org/packages/Azure.Provisioning) NuGet package and a suite of service-specific [Azure provisioning packages](https://www.nuget.org/packages?q=owner%3A+azure-sdk+description%3A+declarative+resource+provisioning&sortby=relevance). These Azure provisioning libraries make it easy to declaratively specify Azure infrastructure natively in .NET. Their APIs enable you to write object-oriented infrastructure in C#, resulting in Bicep. [Bicep is a domain-specific language (DSL)](/azure/azure-resource-manager/bicep/overview) for deploying Azure resources declaratively. - - - -While it's possible to provision Azure resources manually, .NET Aspire simplifies the process by providing a set of APIs to express Azure resources. These APIs are available as extension methods in .NET Aspire Azure hosting libraries, extending the interface. When you add Azure resources to your app host, they add the appropriate provisioning functionality implicitly. In other words, you don't need to call any provisioning APIs directly. - -Since .NET Aspire models Azure resources within Azure hosting integrations, the Azure SDK is used to provision these resources. Bicep files are generated that define the Azure resources you need. The generated Bicep files are output alongside the manifest file when you publish your app. - -There are several ways to influence the generated Bicep files: - -- [Azure.Provisioning customization](#azureprovisioning-customization): - - [Configure infrastructure](#configure-infrastructure): Customize Azure resource infrastructure. - - [Add Azure infrastructure](#add-azure-infrastructure): Manually add Azure infrastructure to your app host. -- [Use custom Bicep templates](#use-custom-bicep-templates): - - [Reference Bicep files](#reference-bicep-files): Add a reference to a Bicep file on disk. - - [Reference Bicep inline](#reference-bicep-inline): Add an inline Bicep template. - -### Local provisioning and `Azure.Provisioning` - -To avoid conflating terms and to help disambiguate "provisioning," it's important to understand the distinction between _local provisioning_ and _Azure provisioning_: - -- **_Local provisioning:_** - - By default, when you call any of the Azure hosting integration APIs to add Azure resources, the API is called implicitly. This API registers services in the dependency injection (DI) container that are used to provision Azure resources when the app host starts. This concept is known as _local provisioning_. For more information, see [Local Azure provisioning](local-provisioning.md). - -- **_`Azure.Provisioning`:_** - - `Azure.Provisioning` refers to the NuGet package, and is a set of libraries that lets you use C# to generate Bicep. The Azure hosting integrations in .NET Aspire use these libraries under the covers to generate Bicep files that define the Azure resources you need. For more information, see [`Azure.Provisioning` customization](#azureprovisioning-customization). - -### `Azure.Provisioning` customization - -All .NET Aspire Azure hosting integrations expose various Azure resources, and they're all subclasses of the type—which itself inherits the . This enables extensions that are generically type-constrained to this type, allowing for a fluent API to customize the infrastructure to your liking. While .NET Aspire provides defaults, you're free to influence the generated Bicep using these APIs. - -#### Configure infrastructure - -Regardless of the Azure resource you're working with, to configure its underlying infrastructure, you chain a call to the extension method. This method allows you to customize the infrastructure of the Azure resource by passing a `configure` delegate of type `Action`. The type is a subclass of the . This type exposes a massive API surface area for configuring the underlying infrastructure of the Azure resource. - -Consider the following example: - -:::code language="csharp" source="../snippets/azure/AppHost/Program.ConfigureInfrastructure.cs" id="infra"::: - -The preceding code: - -- Adds a parameter named `storage-sku`. -- Adds Azure Storage with the API named `storage`. -- Chains a call to `ConfigureInfrastructure` to customize the Azure Storage infrastructure: - - Gets the provisionable resources. - - Filters to a single . - - Assigns the `storage-sku` parameter to the property: - - A new instance of the has its `Name` property assigned from the result of the API. - -This exemplifies flowing an [external parameter](../fundamentals/external-parameters.md) into the Azure Storage infrastructure, resulting in the generated Bicep file reflecting the desired configuration. - -#### Add Azure infrastructure - -Not all Azure services are exposed as .NET Aspire integrations. While they might be at a later time, you can still provision services that are available in `Azure.Provisioning.*` libraries. Imagine a scenario where you have worker service that's responsible for managing an Azure Container Registry. Now imagine that an app host project takes a dependency on the [📦 Azure.Provisioning.ContainerRegistry](https://www.nuget.org/packages/Azure.Provisioning.ContainerRegistry) NuGet package. - -You can use the `AddAzureInfrastructure` API to add the Azure Container Registry infrastructure to your app host: - -:::code language="csharp" source="../snippets/azure/AppHost/Program.AddAzureInfra.cs" id="add"::: - -The preceding code: - -- Calls with a name of `acr`. -- Provides a `configureInfrastructure` delegate to customize the Azure Container Registry infrastructure: - - Instantiates a with the name `acr` and a standard SKU. - - Adds the Azure Container Registry service to the `infra` variable. - - Instantiates a with the name `registryName`, a type of `string`, and a value that corresponds to the name of the Azure Container Registry. - - Adds the output to the `infra` variable. -- Adds a project named `worker` to the builder. -- Chains a call to to set the `ACR_REGISTRY_NAME` environment variable in the project to the value of the `registryName` output. - -The functionality demonstrates how to add Azure infrastructure to your app host project, even if the Azure service isn't directly exposed as a .NET Aspire integration. It further shows how to flow the output of the Azure Container Registry into the environment of a dependent project. - -Consider the resulting Bicep file: - -:::code language="bicep" source="../snippets/azure/AppHost/acr.module.bicep"::: - -The Bicep file reflects the desired configuration of the Azure Container Registry, as defined by the `AddAzureInfrastructure` API. - -### Use custom Bicep templates - -When you're targeting Azure as your desired cloud provider, you can use Bicep to define your infrastructure as code. It aims to drastically simplify the authoring experience with a cleaner syntax and better support for modularity and code reuse. - -While .NET Aspire provides a set of prebuilt Bicep templates, there might be times when you either want to customize the templates or create your own. This section explains the concepts and corresponding APIs that you can use to customize the Bicep templates. - -> [!IMPORTANT] -> This section isn't intended to teach you Bicep, but rather to provide guidance on how to create custom Bicep templates for use with .NET Aspire. - -As part of the [Azure deployment story for .NET Aspire](../deployment/overview.md), the Azure Developer CLI (`azd`) provides an understanding of your .NET Aspire project and the ability to deploy it to Azure. The `azd` CLI uses the Bicep templates to deploy the application to Azure. - -#### Install `Aspire.Hosting.Azure` package - -When you want to reference Bicep files, it's possible that you're not using any of the Azure hosting integrations. In this case, you can still reference Bicep files by installing the `Aspire.Hosting.Azure` package. This package provides the necessary APIs to reference Bicep files and customize the Azure resources. - -> [!TIP] -> If you're using any of the Azure hosting integrations, you don't need to install the `Aspire.Hosting.Azure` package, as it's a transitive dependency. - -To use any of this functionality, the [📦 Aspire.Hosting.Azure](https://www.nuget.org/packages/Aspire.Hosting.Azure) NuGet package must be installed: - -### [.NET CLI](#tab/dotnet-cli) - -```dotnetcli -dotnet add package Aspire.Hosting.Azure -``` - -### [PackageReference](#tab/package-reference) - -```xml - -``` - ---- - -For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies). - -#### What to expect from the examples - -All the examples in this section assume that you're using the namespace. Additionally, the examples assume you have an instance: - -```csharp -using Aspire.Hosting.Azure; - -var builder = DistributedApplication.CreateBuilder(args); - -// Examples go here... - -builder.Build().Run(); -``` - -By default, when you call any of the Bicep-related APIs, a call is also made to that adds support for generating Azure resources dynamically during application startup. For more information, see [Local provisioning and `Azure.Provisioning`](#local-provisioning-and-azureprovisioning). - -#### Reference Bicep files - -Imagine that you have a Bicep template in a file named `storage.bicep` that provisions an Azure Storage Account: - -:::code language="bicep" source="snippets/bicep/AppHost.Bicep/storage.bicep"::: - -To add a reference to the Bicep file on disk, call the method. Consider the following example: - -:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.ReferenceBicep.cs" id="addfile"::: - -The preceding code adds a reference to a Bicep file located at `../infra/storage.bicep`. The file paths should be relative to the _app host_ project. This reference results in an being added to the application's resources collection with the `"storage"` name, and the API returns an `IResourceBuilder` instance that can be used to further customize the resource. - -#### Reference Bicep inline - -While having a Bicep file on disk is the most common scenario, you can also add Bicep templates inline. Inline templates can be useful when you want to define a template in code or when you want to generate the template dynamically. To add an inline Bicep template, call the method with the Bicep template as a `string`. Consider the following example: - -:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.InlineBicep.cs" id="addinline"::: - -In this example, the Bicep template is defined as an inline `string` and added to the application's resources collection with the name `"ai"`. This example provisions an Azure AI resource. - -#### Pass parameters to Bicep templates - -[Bicep supports accepting parameters](/azure/azure-resource-manager/bicep/parameters), which can be used to customize the behavior of the template. To pass parameters to a Bicep template from .NET Aspire, chain calls to the method as shown in the following example: - -:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.PassParameter.cs" id="addparameter"::: - -The preceding code: - -- Adds a parameter named `"region"` to the `builder` instance. -- Adds a reference to a Bicep file located at `../infra/storage.bicep`. -- Passes the `"region"` parameter to the Bicep template, which is resolved using the standard parameter resolution. -- Passes the `"storageName"` parameter to the Bicep template with a _hardcoded_ value. -- Passes the `"tags"` parameter to the Bicep template with an array of strings. - -For more information, see [External parameters](../fundamentals/external-parameters.md). - -##### Well-known parameters - -.NET Aspire provides a set of well-known parameters that can be passed to Bicep templates. These parameters are used to provide information about the application and the environment to the Bicep templates. The following well-known parameters are available: - -| Field | Description | Value | -|--|--|--| -| | The name of the key vault resource used to store secret outputs. | `"keyVaultName"` | -| | The location of the resource. This is required for all resources. | `"location"` | -| | The resource ID of the log analytics workspace. | `"logAnalyticsWorkspaceId"` | -| | The principal ID of the current user or managed identity. | `"principalId"` | -| | The principal name of the current user or managed identity. | `"principalName"` | -| | The principal type of the current user or managed identity. Either `User` or `ServicePrincipal`. | `"principalType"` | - -To use a well-known parameter, pass the parameter name to the method, such as `WithParameter(AzureBicepResource.KnownParameters.KeyVaultName)`. You don't pass values for well-known parameters, as .NET Aspire resolves them on your behalf. - -Consider an example where you want to set up an Azure Event Grid webhook. You might define the Bicep template as follows: - - :::code language="bicep" source="snippets/bicep/AppHost.Bicep/event-grid-webhook.bicep" highlight="3-4,27-35"::: - -This Bicep template defines several parameters, including the `topicName`, `webHookEndpoint`, `principalId`, `principalType`, and the optional `location`. To pass these parameters to the Bicep template, you can use the following code snippet: - -:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.PassParameter.cs" id="addwellknownparams"::: - -- The `webHookApi` project is added as a reference to the `builder`. -- The `topicName` parameter is passed a hardcoded name value. -- The `webHookEndpoint` parameter is passed as an expression that resolves to the URL from the `api` project references' "https" endpoint with the `/hook` route. -- The `principalId` and `principalType` parameters are passed as well-known parameters. - -The well-known parameters are convention-based and shouldn't be accompanied with a corresponding value when passed using the `WithParameter` API. Well-known parameters simplify some common functionality, such as _role assignments_, when added to the Bicep templates, as shown in the preceding example. Role assignments are required for the Event Grid webhook to send events to the specified endpoint. For more information, see [Event Grid Data Sender role assignment](/azure/role-based-access-control/built-in-roles/integration#eventgrid-data-sender). - -### Get outputs from Bicep references - -In addition to passing parameters to Bicep templates, you can also get outputs from the Bicep templates. Consider the following Bicep template, as it defines an `output` named `endpoint`: - -:::code language="bicep" source="snippets/bicep/AppHost.Bicep/storage-out.bicep"::: - -The Bicep defines an output named `endpoint`. To get the output from the Bicep template, call the method on an `IResourceBuilder` instance as demonstrated in following C# code snippet: - -:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.GetOutputReference.cs" id="getoutput"::: - -In this example, the output from the Bicep template is retrieved and stored in an `endpoint` variable. Typically, you would pass this output as an environment variable to another resource that relies on it. For instance, if you had an ASP.NET Core Minimal API project that depended on this endpoint, you could pass the output as an environment variable to the project using the following code snippet: - -```csharp -var storage = builder.AddBicepTemplate( - name: "storage", - bicepFile: "../infra/storage.bicep" - ); - -var endpoint = storage.GetOutput("endpoint"); - -var apiService = builder.AddProject( - name: "apiservice" - ) - .WithEnvironment("STORAGE_ENDPOINT", endpoint); -``` - -For more information, see [Bicep outputs](/azure/azure-resource-manager/bicep/outputs). - -### Get secret outputs from Bicep references - -It's important to [avoid outputs for secrets](/azure/azure-resource-manager/bicep/scenarios-secrets#avoid-outputs-for-secrets) when working with Bicep. If an output is considered a _secret_, meaning it shouldn't be exposed in logs or other places, you can treat it as such. This can be achieved by storing the secret in Azure Key Vault and referencing it in the Bicep template. .NET Aspire's Azure integration provides a pattern for securely storing outputs from the Bicep template by allows resources to use the `keyVaultName` parameter to store secrets in Azure Key Vault. - -Consider the following Bicep template as an example the helps to demonstrate this concept of securing secret outputs: - -:::code language="bicep" source="snippets/bicep/AppHost.Bicep/cosmosdb.bicep" highlight="2,41"::: - -The preceding Bicep template expects a `keyVaultName` parameter, among several other parameters. It then defines an Azure Cosmos DB resource and stashes a secret into Azure Key Vault, named `connectionString` which represents the fully qualified connection string to the Cosmos DB instance. To access this secret connection string value, you can use the following code snippet: - -:::code language="csharp" source="snippets/bicep/AppHost.Bicep/Program.cs" id="secrets"::: - -In the preceding code snippet, the `cosmos` Bicep template is added as a reference to the `builder`. The `connectionString` secret output is retrieved from the Bicep template and stored in a variable. The secret output is then passed as an environment variable (`ConnectionStrings__cosmos`) to the `api` project. This environment variable is used to connect to the Cosmos DB instance. - -When this resource is deployed, the underlying deployment mechanism will automatically [Reference secrets from Azure Key Vault](/azure/container-apps/manage-secrets?tabs=azure-portal#reference-secret-from-key-vault). To guarantee secret isolation, .NET Aspire creates a Key Vault per source. - -> [!NOTE] -> In _local provisioning_ mode, the secret is extracted from Key Vault and set it in an environment variable. For more information, see [Local Azure provisioning](local-provisioning.md). - ## Publishing When you publish your app, the Azure provisioning generated Bicep is used by the Azure Developer CLI to create the Azure resources in your Azure subscription. .NET Aspire outputs a [publishing manifest](../deployment/manifest-format.md), that's also a vital part of the publishing process. The Azure Developer CLI is a command-line tool that provides a set of commands to manage Azure resources. diff --git a/docs/azure/role-assignments.md b/docs/azure/role-assignments.md index e336ab448e..292e08e20e 100644 --- a/docs/azure/role-assignments.md +++ b/docs/azure/role-assignments.md @@ -6,7 +6,7 @@ ms.date: 03/31/2025 # Manage Azure role assignments -All .NET Aspire Azure hosting integrations define Azure resources. [These resources](integrations-overview.md#add-azure-resources) come with default role assignments. You can replace these default role assignments with built-in role [or custom role assignments](integrations-overview.md#infrastructure-as-code). In this article, you learn how to manage Azure role assignments on .NET Aspire resources. +All .NET Aspire Azure hosting integrations define Azure resources. [These resources](integrations-overview.md#add-azure-resources) come with default role assignments. You can replace these default role assignments with built-in role [or custom role assignments](customize-azure-resources.md). In this article, you learn how to manage Azure role assignments on .NET Aspire resources. ## Default built-in role assignments @@ -54,7 +54,7 @@ For more information, see [Azure built-in roles](/azure/role-based-access-contro ## Built-in role assignment reference -All built-in roles are defined within the namespaces and are included in the corresponding [📦 Azure.Provisioning.*](https://www.nuget.org/packages?q=Azure.Provisioning) NuGet packages. Each .NET Aspire Azure hosting integration automatically depends on the appropriate provisioning package. For more information, see [Infrastructure as code](integrations-overview.md#infrastructure-as-code). +All built-in roles are defined within the namespaces and are included in the corresponding [📦 Azure.Provisioning.*](https://www.nuget.org/packages?q=Azure.Provisioning) NuGet packages. Each .NET Aspire Azure hosting integration automatically depends on the appropriate provisioning package. For more information, see [Customized Azure resources](customize-azure-resources.md). The following sections list the built-in roles for each Azure provisioning type that can be used as a parameter to the `WithRoleAssignments` API. diff --git a/docs/azureai/azureai-search-document-integration.md b/docs/azureai/azureai-search-document-integration.md index e85dc856c4..8510eb8298 100644 --- a/docs/azureai/azureai-search-document-integration.md +++ b/docs/azureai/azureai-search-document-integration.md @@ -80,7 +80,7 @@ The preceding code: - The is set to . - A tag is added to the Cognitive Services resource with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Azure AI Search resource. For more information, see [`Azure.Provisioning` customization](../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Azure AI Search resource. For more information, see [`Azure.Provisioning` customization](../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure AI Search service diff --git a/docs/caching/includes/azure-redis-app-host.md b/docs/caching/includes/azure-redis-app-host.md index 939f2a250a..5de87f202f 100644 --- a/docs/caching/includes/azure-redis-app-host.md +++ b/docs/caching/includes/azure-redis-app-host.md @@ -71,7 +71,7 @@ The preceding code: - The `Sku` is set with a family of `BasicOrStandard`, a name of `Standard`, and a capacity of `1`. - A tag is added to the Redis resource with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Azure Cache for Redis resource. For more information, see . For more information, see [Azure.Provisioning customization](../../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Azure Cache for Redis resource. For more information, see . For more information, see [Azure.Provisioning customization](../../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure Cache for Redis diff --git a/docs/database/includes/cosmos-app-host.md b/docs/database/includes/cosmos-app-host.md index 12cadff99c..202aa58b46 100644 --- a/docs/database/includes/cosmos-app-host.md +++ b/docs/database/includes/cosmos-app-host.md @@ -72,7 +72,7 @@ The preceding code: - The is assigned to a . - A tag is added to the Cosmos DB account with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Azure Cosmos DB resource. For more information, see . For more information, see [Azure.Provisioning customization](../../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Azure Cosmos DB resource. For more information, see . For more information, see [Azure.Provisioning customization](../../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure Cosmos DB account diff --git a/docs/database/includes/postgresql-flexible-server.md b/docs/database/includes/postgresql-flexible-server.md index 58bb9033fd..5637b96b95 100644 --- a/docs/database/includes/postgresql-flexible-server.md +++ b/docs/database/includes/postgresql-flexible-server.md @@ -84,7 +84,7 @@ The preceding code: - The high availability properties are set with in standby availability zone `"2"`. - A tag is added to the flexible server with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the PostgreSQL flexible server resource. For more information, see and [Azure.Provisioning customization](../../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the PostgreSQL flexible server resource. For more information, see and [Azure.Provisioning customization](../../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure PostgreSQL flexible server diff --git a/docs/messaging/azure-event-hubs-integration.md b/docs/messaging/azure-event-hubs-integration.md index 0de97b2531..394c0f3a54 100644 --- a/docs/messaging/azure-event-hubs-integration.md +++ b/docs/messaging/azure-event-hubs-integration.md @@ -87,7 +87,7 @@ The preceding code: - The property is assigned to `SecuredByPerimeter`. - A tag is added to the Event Hubs resource with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Event Hubs resource resource. For more information, see . For more information, see [`Azure.Provisioning` customization](../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Event Hubs resource resource. For more information, see . For more information, see [`Azure.Provisioning` customization](../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure Event Hubs namespace diff --git a/docs/messaging/azure-service-bus-integration.md b/docs/messaging/azure-service-bus-integration.md index 7545c9eb91..3dc3c4b7fb 100644 --- a/docs/messaging/azure-service-bus-integration.md +++ b/docs/messaging/azure-service-bus-integration.md @@ -83,7 +83,7 @@ The preceding code: - The created with a - A tag is added to the Service Bus namespace with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Azure Service Bus resource. For more information, see . For more information, see [Azure.Provisioning customization](../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Azure Service Bus resource. For more information, see . For more information, see [Azure.Provisioning customization](../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure Service Bus namespace diff --git a/docs/messaging/azure-web-pubsub-integration.md b/docs/messaging/azure-web-pubsub-integration.md index bf29949ba2..bc7fdd8385 100644 --- a/docs/messaging/azure-web-pubsub-integration.md +++ b/docs/messaging/azure-web-pubsub-integration.md @@ -122,7 +122,7 @@ The preceding code: - The object has its name and capacity properties set to `Standard_S1` and `5`, respectively. - A tag is added to the Web PubSub resource with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Web PubSub resource. For more information, see . For more information, see [`Azure.Provisioning` customization](../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Web PubSub resource. For more information, see . For more information, see [`Azure.Provisioning` customization](../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure Web PubSub instance diff --git a/docs/security/azure-security-key-vault-integration.md b/docs/security/azure-security-key-vault-integration.md index f49187b649..3eb92782c5 100644 --- a/docs/security/azure-security-key-vault-integration.md +++ b/docs/security/azure-security-key-vault-integration.md @@ -83,7 +83,7 @@ The preceding code: - The property is set to `true`. - A tag is added to the resource with a key of `ExampleKey` and a value of `Example value`. -There are many more configuration options available to customize the Key Vault resource. For more information, see and [Azure.Provisioning customization](../azure/integrations-overview.md#azureprovisioning-customization). +There are many more configuration options available to customize the Key Vault resource. For more information, see and [Azure.Provisioning customization](../azure/customize-azure-resources.md#azureprovisioning-customization). ### Connect to an existing Azure Key Vault instance diff --git a/docs/toc.yml b/docs/toc.yml index 4e4bdefe24..8545ea27b3 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -156,6 +156,8 @@ items: items: - name: Overview href: azure/integrations-overview.md + - name: Customize Azure resources + href: azure/customize-azure-resources.md - name: Local Azure provisioning href: azure/local-provisioning.md - name: Configure Azure Container Apps environments