|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure;
using Aspire.Hosting.Foundry;
using Azure.Provisioning;
using Azure.Provisioning.ApplicationInsights;
using Azure.Provisioning.Authorization;
using Azure.Provisioning.CognitiveServices;
using Azure.Provisioning.ContainerRegistry;
using Azure.Provisioning.CosmosDB;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.KeyVault;
using Azure.Provisioning.Primitives;
using Azure.Provisioning.Resources;
using Azure.Provisioning.Roles;
using Azure.Provisioning.Search;
using Azure.Provisioning.Storage;
namespace Aspire.Hosting;
/// <summary>
/// Extension methods for adding Azure Cognitive Services project resources to the distributed application model.
/// </summary>
public static class AzureCognitiveServicesProjectExtensions
{
/// <summary>
/// Adds an Azure Cognitive Services project resource to the application model.
///
/// This will also attach the project as a deployment target for agents.
/// </summary>
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for the parent Azure Cognitive Services account resource.</param>
/// <param name="name">The name of the Azure Cognitive Services project resource.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/> for the Azure Cognitive Services project resource.</returns>
[AspireExport("addProject", Description = "Adds a Microsoft Foundry project resource to a Microsoft Foundry resource.")]
public static IResourceBuilder<AzureCognitiveServicesProjectResource> AddProject(
this IResourceBuilder<FoundryResource> builder,
string name)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(name);
var project = builder.ApplicationBuilder.AddResource(new AzureCognitiveServicesProjectResource(name, ConfigureInfrastructure, builder.Resource));
project.Resource.DefaultContainerRegistry = CreateDefaultRegistry(builder.ApplicationBuilder, $"{name}-acr");
return project;
}
/// <summary>
/// Adds an Azure Cognitive Services project resource to the application model.
///
/// This will create a default Microsoft Foundry account resource.
/// This will also set the project as a deployment target for agents.
/// </summary>
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for the parent Azure Cognitive Services account resource.</param>
/// <param name="name">The name of the Azure Cognitive Services project resource.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/> for the Azure Cognitive Services project resource.</returns>
[AspireExport("addFoundryProject", Description = "Adds a Microsoft Foundry project resource and its parent Microsoft Foundry resource to the application model.")]
public static IResourceBuilder<AzureCognitiveServicesProjectResource> AddFoundryProject(
this IDistributedApplicationBuilder builder,
[ResourceName] string name)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(name);
var account = builder.AddFoundry($"{name}-foundry");
return account.AddProject(name);
}
/// <summary>
/// Associates a container registry with the Azure Cognitive Services project resource for
/// publishing and locating hosted agents.
/// </summary>
[AspireExport("withContainerRegistry", Description = "Associates a container registry with a Microsoft Foundry project resource.")]
public static IResourceBuilder<AzureCognitiveServicesProjectResource> WithContainerRegistry(
this IResourceBuilder<AzureCognitiveServicesProjectResource> builder,
IResourceBuilder<AzureContainerRegistryResource> registryBuilder)
{
return builder.WithContainerRegistry(registryBuilder.Resource);
}
/// <summary>
/// Associates a container registry with the Azure Cognitive Services project resource for
/// publishing and locating hosted agents.
/// </summary>
/// <remarks>This overload is not available in polyglot app hosts. Use the resource-builder overload instead.</remarks>
[AspireExportIgnore(Reason = "IContainerRegistry is not ATS-compatible. Use the resource-builder overload instead.")]
public static IResourceBuilder<AzureCognitiveServicesProjectResource> WithContainerRegistry(
this IResourceBuilder<AzureCognitiveServicesProjectResource> builder,
IContainerRegistry registry)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(registry);
// This will be queried during the "publish" phase
builder.Resource.Annotations.Add(new ContainerRegistryReferenceAnnotation(registry));
return builder;
}
/// <summary>
/// Adds a reference to an Azure Cognitive Services project resource to the destination resource.
/// </summary>
/// <remarks>This overload is not available in polyglot app hosts. Use the standard <c>WithReference</c> overload instead.</remarks>
[AspireExportIgnore(Reason = "The standard WithReference export already covers this polyglot scenario.")]
public static IResourceBuilder<TDestination> WithReference<TDestination>(this IResourceBuilder<TDestination> builder, IResourceBuilder<AzureCognitiveServicesProjectResource> project)
where TDestination : IResourceWithEnvironment
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(project);
// Add standard references and environment variables
ResourceBuilderExtensions.WithReference(builder, project);
if (builder is IResourceBuilder<IResourceWithWaitSupport> waitableBuilder)
{
waitableBuilder.WaitFor(project);
}
return builder;
}
/// <summary>
/// Adds a Key Vault connection to the Azure Cognitive Services project.
/// </summary>
/// <param name="builder">The resource builder for the Azure Cognitive Services project.</param>
/// <param name="keyVault">The Key Vault resource to associate with the project.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/> for chaining.</returns>
/// <exception cref="InvalidOperationException">Thrown when the project already has a Key Vault connection configured.</exception>
[AspireExport("withKeyVault", Description = "Associates an Azure Key Vault resource with a Microsoft Foundry project.")]
public static IResourceBuilder<AzureCognitiveServicesProjectResource> WithKeyVault(
this IResourceBuilder<AzureCognitiveServicesProjectResource> builder,
IResourceBuilder<AzureKeyVaultResource> keyVault)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(keyVault);
if (builder.Resource.KeyVaultConn is not null)
{
throw new InvalidOperationException($"Azure Cognitive Services project resource '{builder.Resource.Name}' already has a Key Vault connection configured.");
}
var conn = builder.AddConnection(keyVault);
// We need to keep a reference to the connection resource for dependency tracking
builder.Resource.KeyVaultConn = conn.Resource;
return builder.WithRoleAssignments(keyVault, KeyVaultBuiltInRole.KeyVaultSecretsOfficer);
}
/// <summary>
/// Adds an Application Insights resource to the Azure Cognitive Services project,
/// overriding the default (which is to create a new Application Insights resource).
/// </summary>
/// <param name="builder">The resource builder for the Azure Cognitive Services project.</param>
/// <param name="appInsights">The Application Insights resource to associate with the project.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/> for chaining.</returns>
[AspireExport("withAppInsights", Description = "Associates an Azure Application Insights resource with a Microsoft Foundry project.")]
public static IResourceBuilder<AzureCognitiveServicesProjectResource> WithAppInsights(
this IResourceBuilder<AzureCognitiveServicesProjectResource> builder,
IResourceBuilder<AzureApplicationInsightsResource> appInsights)
{
builder.Resource.AppInsights = appInsights.Resource;
return builder;
}
/// <summary>
/// Adds a capability host to the Azure Cognitive Services project, enabling agent capabilities
/// with external Azure resources such as CosmosDB, Storage, and Search.
/// </summary>
/// <param name="builder">The resource builder for the Azure Cognitive Services project.</param>
/// <param name="name">The name of the capability host.</param>
/// <returns>A <see cref="CapabilityHostBuilder"/> for fluent configuration of the capability host resources.</returns>
/// <example>
/// <code lang="csharp">
/// project.AddCapabilityHost("cap-host")
/// .WithCosmosDB(cosmosDb)
/// .WithStorage(storage)
/// .WithSearch(search)
/// .WithAzureOpenAI(foundry);
/// </code>
/// </example>
/// <remarks>This method is not available in polyglot app hosts.</remarks>
[AspireExportIgnore(Reason = "CapabilityHostBuilder is not ATS-compatible.")]
public static CapabilityHostBuilder AddCapabilityHost(
this IResourceBuilder<AzureCognitiveServicesProjectResource> builder,
string name)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(name);
var config = new CapabilityHostConfiguration(name);
builder.Resource.CapabilityHostConfiguration = config;
return new CapabilityHostBuilder(builder, config);
}
/// <summary>
/// Adds a model deployment to the parent Microsoft Foundry of the Azure Cognitive Services project.
/// </summary>
/// <param name="builder">Aspire resource builder for a project</param>
/// <param name="name">Name to give the model deployment</param>
/// <param name="model">The <see cref="FoundryModel"/> to deploy.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/> for the deployment resource.</returns>
[AspireExport("addModelDeploymentFromModel", Description = "Adds a model deployment to the parent Microsoft Foundry resource by using a model descriptor.")]
public static IResourceBuilder<FoundryDeploymentResource> AddModelDeployment(
this IResourceBuilder<AzureCognitiveServicesProjectResource> builder,
[ResourceName] string name,
FoundryModel model)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(name);
return builder.ApplicationBuilder.CreateResourceBuilder(builder.Resource.Parent).AddDeployment(name, model);
}
/// <summary>
/// Adds a model deployment to the parent Microsoft Foundry of the Azure Cognitive Services project.
/// </summary>
[AspireExport("addModelDeployment", Description = "Adds a model deployment to the parent Microsoft Foundry resource.")]
public static IResourceBuilder<FoundryDeploymentResource> AddModelDeployment(
this IResourceBuilder<AzureCognitiveServicesProjectResource> builder,
[ResourceName] string name,
string modelName,
string modelVersion,
string format)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentException.ThrowIfNullOrEmpty(name);
return builder.ApplicationBuilder.CreateResourceBuilder(builder.Resource.Parent).AddDeployment(name, modelName, modelVersion, format);
}
internal static void ConfigureInfrastructure(AzureResourceInfrastructure infra)
{
var prefix = infra.AspireResource.Name;
var aspireResource = (AzureCognitiveServicesProjectResource)infra.AspireResource;
var tags = new ProvisioningParameter("tags", typeof(object))
{
Value = new BicepDictionary<string>()
};
infra.Add(tags);
// This tells azd to avoid creating infrastructure
var userPrincipalId = new ProvisioningParameter(AzureBicepResource.KnownParameters.UserPrincipalId, typeof(string)) { Value = new BicepValue<string>(string.Empty) };
infra.Add(userPrincipalId);
/*
* Create managed identity
*/
ManagedServiceIdentity managedIdentity;
if (aspireResource.TryGetAppIdentityResource(out var idResource) && idResource is AzureUserAssignedIdentityResource identityResource)
{
managedIdentity = new ManagedServiceIdentity()
{
ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned,
// We hack in this dictionary because the CDK doesn't take BicepValues as
// keys.
UserAssignedIdentities =
{
{ ((UserAssignedIdentity)identityResource.AddAsExistingResource(infra)).Id.Compile().ToString(), new UserAssignedIdentityDetails() }
}
};
}
else
{
managedIdentity = new ManagedServiceIdentity()
{
ManagedServiceIdentityType = ManagedServiceIdentityType.SystemAssigned
};
}
var account = aspireResource.Parent.AddAsExistingResource(infra);
/*
* Create the project
*/
var project = AzureProvisioningResource.CreateExistingOrNewProvisionableResource(
infra,
(identifier, resourceName) =>
{
var resource = aspireResource.FromExisting(identifier);
resource.Parent = account;
resource.Name = resourceName;
return resource;
},
infra =>
{
var resource = new CognitiveServicesProject(infra.AspireResource.GetBicepIdentifier())
{
Parent = account,
Name = aspireResource.Name,
Identity = managedIdentity,
Properties = new CognitiveServicesProjectProperties
{
DisplayName = aspireResource.Name
},
Tags = { { "aspire-resource-name", infra.AspireResource.Name } }
};
return resource;
});
var projectPrincipalId = project.Identity.PrincipalId;
infra.Add(new ProvisioningOutput("id", typeof(string))
{
Value = project.Id
});
infra.Add(new ProvisioningOutput("name", typeof(string)) { Value = project.Name });
infra.Add(new ProvisioningOutput("endpoint", typeof(string))
{
Value = (BicepValue<string>)new IndexExpression((BicepExpression)project.Properties.Endpoints!, "AI Foundry API")
});
infra.Add(new ProvisioningOutput("principalId", typeof(string))
{
Value = projectPrincipalId
});
/*
* Container registry for hosted agents
*
* TODO: only provision if we need to create a Hosted Agent
*/
AzureProvisioningResource? registry = null;
if (aspireResource.TryGetLastAnnotation<ContainerRegistryReferenceAnnotation>(out var registryReferenceAnnotation) && registryReferenceAnnotation.Registry is AzureProvisioningResource r)
{
registry = r;
}
else if (aspireResource.DefaultContainerRegistry is not null)
{
registry = aspireResource.DefaultContainerRegistry;
}
else
{
throw new InvalidOperationException($"No container registry configured for Azure Cognitive Services project resource '{aspireResource.Name}'. A container registry is required to publish and run hosted agents.");
}
var containerRegistry = (ContainerRegistryService)registry.AddAsExistingResource(infra);
// Why do we need this?
infra.Add(containerRegistry);
// Project needs this to pull hosted agent images and run them
var pullRa = containerRegistry.CreateRoleAssignment(ContainerRegistryBuiltInRole.AcrPull, RoleManagementPrincipalType.ServicePrincipal, projectPrincipalId);
// There's a bug in the CDK, see https://github.com/Azure/azure-sdk-for-net/issues/47265
pullRa.Name = BicepFunction.CreateGuid(containerRegistry.Id, project.Id, pullRa.RoleDefinitionId);
infra.Add(pullRa);
infra.Add(containerRegistry);
infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_ENDPOINT", typeof(string))
{
Value = containerRegistry.LoginServer
});
infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_NAME", typeof(string))
{
Value = containerRegistry.Name
});
infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID", typeof(string))
{
Value = projectPrincipalId
});
// Implicit dependencies for capability hosts
List<ProvisionableResource> capHostDeps = [];
var keyVaultConn = aspireResource.KeyVaultConn?.AddAsExistingResource(infra);
/*
* Application Insights for telemetry
*/
ApplicationInsightsComponent appInsights;
if (aspireResource.AppInsights is not null && !aspireResource.AppInsights.IsEmulator())
{
appInsights = (ApplicationInsightsComponent)aspireResource.AppInsights.AddAsExistingResource(infra);
}
else
{
appInsights = new ApplicationInsightsComponent(Infrastructure.NormalizeBicepIdentifier($"{prefix}-ai"))
{
ApplicationType = ApplicationInsightsApplicationType.Web,
Name = $"{aspireResource.Name}-ai",
Kind = "web",
Tags = tags
};
infra.Add(appInsights);
}
var pubRoleRa = appInsights.CreateRoleAssignment(ApplicationInsightsBuiltInRole.MonitoringMetricsPublisher, RoleManagementPrincipalType.ServicePrincipal, projectPrincipalId);
pubRoleRa.Name = BicepFunction.CreateGuid(appInsights.Id, project.Id, pubRoleRa.RoleDefinitionId);
infra.Add(pubRoleRa);
// This is for passing into hosted agent application code
infra.Add(new ProvisioningOutput("APPLICATION_INSIGHTS_CONNECTION_STRING", typeof(string))
{
Value = appInsights.ConnectionString
});
// Project needs a connection to send server-side telemetry
var appInsightsConn = new CognitiveServicesProjectConnection($"{aspireResource.GetBicepIdentifier()}_ai_conn")
{
Parent = project,
Name = $"{aspireResource.Name}-ai-conn",
Properties = new AppInsightsConnectionProperties()
{
Target = appInsights.Id,
IsSharedToAll = false,
CredentialsKey = appInsights.ConnectionString,
Metadata =
{
{ "ApiType", "Azure" },
{ "ResourceId", appInsights.Id },
{ "location", appInsights.Location }
}
}
};
if (keyVaultConn is not null)
{
appInsightsConn.DependsOn.Add(keyVaultConn);
}
infra.Add(appInsightsConn);
/*
* Capability host for BYO resources.
* These resources are all-or-nothing (except for Azure OpenAI), and will replace the public hosting
* caphost.
* TODO: private network
*/
if (aspireResource.CapabilityHostConfiguration is null)
{
return;
}
aspireResource.CapabilityHostConfiguration.Validate(aspireResource.Name);
var capHostConfig = aspireResource.CapabilityHostConfiguration;
var capHostProps = new PublicHostingCognitiveServicesCapabilityHostProperties()
{
CapabilityHostKind = CapabilityHostKind.Agents
};
/*
* Storage
*/
var storage = (StorageAccount)capHostConfig.Storage!.AddAsExistingResource(infra);
var storageConn = new CognitiveServicesProjectConnection($"{aspireResource.GetBicepIdentifier()}_storage_conn")
{
Parent = project,
Name = BicepFunction.Interpolate($"{project.Name}-{storage.Name}"),
Properties = new AzureStorageAccountConnectionProperties()
{
Target = capHostConfig.Storage!.BlobEndpoint.AsProvisioningParameter(infra),
Metadata =
{
{ "ApiType", "Azure" },
{ "ResourceId", storage.Id },
{ "location", storage.Location }
}
}
};
if (keyVaultConn is not null)
{
storageConn.DependsOn.Add(keyVaultConn);
}
infra.Add(storageConn);
var storageRoleRa = storage.CreateRoleAssignment(StorageBuiltInRole.StorageBlobDataContributor, RoleManagementPrincipalType.ServicePrincipal, projectPrincipalId);
storageRoleRa.Name = BicepFunction.CreateGuid(storage.Id, project.Id, storageRoleRa.RoleDefinitionId);
infra.Add(storageRoleRa);
capHostDeps.Add(storage);
capHostDeps.Add(storageRoleRa);
capHostProps.StorageConnections = [storageConn.Name];
/*
* CosmosDB
*/
var cosmosDb = (CosmosDBAccount)capHostConfig.CosmosDB!.AddAsExistingResource(infra);
var cosmosDbConn = new CognitiveServicesProjectConnection($"{aspireResource.GetBicepIdentifier()}_cosmosdb_conn")
{
Parent = project,
Name = BicepFunction.Interpolate($"{project.Name}-{cosmosDb.Name}"),
Properties = new AadAuthTypeConnectionProperties()
{
Category = CognitiveServicesConnectionCategory.CosmosDB,
// This is the document endpoint
Target = capHostConfig.CosmosDB!.ConnectionStringOutput.AsProvisioningParameter(infra),
Metadata =
{
{ "ApiType", "Azure" },
{ "ResourceId", cosmosDb.Id },
{ "location", cosmosDb.Location }
}
}
};
if (keyVaultConn is not null)
{
cosmosDbConn.DependsOn.Add(keyVaultConn);
}
infra.Add(cosmosDbConn);
var cosmosDbRoleRa = cosmosDb.CreateRoleAssignment(
// Data Contributor
new CosmosDBBuiltInRole("00000000-0000-0000-0000-000000000002"),
RoleManagementPrincipalType.ServicePrincipal,
projectPrincipalId
);
cosmosDbRoleRa.Name = BicepFunction.CreateGuid(cosmosDb.Id, project.Id, cosmosDbRoleRa.RoleDefinitionId);
infra.Add(cosmosDbRoleRa);
capHostDeps.Add(cosmosDb);
capHostDeps.Add(cosmosDbRoleRa);
capHostProps.ThreadStorageConnections = [cosmosDbConn.Name];
/*
* Azure Search
*/
var searchService = (SearchService)capHostConfig.Search!.AddAsExistingResource(infra);
var searchConn = new CognitiveServicesProjectConnection($"{aspireResource.GetBicepIdentifier()}_search_conn")
{
Parent = project,
Name = BicepFunction.Interpolate($"{project.Name}-{searchService.Name}"),
Properties = new AadAuthTypeConnectionProperties()
{
Category = CognitiveServicesConnectionCategory.CognitiveSearch,
Target = BicepFunction.Interpolate($"https://{searchService.Name}.search.windows.net"),
Metadata =
{
{ "ApiType", "Azure" },
{ "ResourceId", searchService.Id },
{ "location", searchService.Location }
}
}
};
if (keyVaultConn is not null)
{
searchConn.DependsOn.Add(keyVaultConn);
}
infra.Add(searchConn);
var contributor = searchService.CreateRoleAssignment(SearchBuiltInRole.SearchServiceContributor, RoleManagementPrincipalType.ServicePrincipal, projectPrincipalId);
contributor.Name = BicepFunction.CreateGuid(searchService.Id, project.Id, contributor.RoleDefinitionId);
infra.Add(contributor);
var indexDataContrib = searchService.CreateRoleAssignment(SearchBuiltInRole.SearchIndexDataContributor, RoleManagementPrincipalType.ServicePrincipal, projectPrincipalId);
indexDataContrib.Name = BicepFunction.CreateGuid(searchService.Id, project.Id, indexDataContrib.RoleDefinitionId);
infra.Add(indexDataContrib);
capHostDeps.Add(searchService);
capHostDeps.Add(contributor);
capHostDeps.Add(indexDataContrib);
capHostProps.VectorStoreConnections = [searchConn.Name];
/*
* Azure OpenAI Account (optional)
*/
CognitiveServicesProjectConnection? aoaiConn = null;
if (capHostConfig.AzureOpenAI is not null)
{
var aoaiAccount = capHostConfig.AzureOpenAI.AddAsExistingResource(infra);
aoaiConn = new CognitiveServicesProjectConnection($"{aspireResource.GetBicepIdentifier()}_aoai_conn")
{
Parent = project,
Name = BicepFunction.Interpolate($"{project.Name}-{aoaiAccount.Name}"),
Properties = new AadAuthTypeConnectionProperties()
{
Category = CognitiveServicesConnectionCategory.AzureOpenAI,
Target = aoaiAccount.Properties.Endpoint,
Metadata =
{
{ "ApiType", "Azure" },
{ "ResourceId", aoaiAccount.Id },
{ "location", aoaiAccount.Location }
}
}
};
if (keyVaultConn is not null)
{
aoaiConn.DependsOn.Add(keyVaultConn);
}
infra.Add(aoaiConn);
var aoaiRoleRa = aoaiAccount.CreateRoleAssignment(CognitiveServicesBuiltInRole.CognitiveServicesOpenAIUser, RoleManagementPrincipalType.ServicePrincipal, projectPrincipalId);
aoaiRoleRa.Name = BicepFunction.CreateGuid(aoaiAccount.Id, project.Id, aoaiRoleRa.RoleDefinitionId);
infra.Add(aoaiRoleRa);
capHostDeps.Add(aoaiAccount);
capHostDeps.Add(aoaiRoleRa);
capHostProps.AiServicesConnections = [aoaiConn.Name];
}
// Necesssary to have parameter enablePublicHostingEnvironment
var capHost = new CognitiveServicesProjectCapabilityHost(Infrastructure.NormalizeBicepIdentifier($"{prefix}-caphost"), "2025-10-01-preview")
{
Parent = project,
Name = $"{project.Name}-ch",
Properties = capHostProps,
};
if (keyVaultConn is not null)
{
capHost.DependsOn.Add(keyVaultConn);
}
foreach (var dep in capHostDeps)
{
capHost.DependsOn.Add(dep);
}
infra.Add(capHost);
}
private static AzureContainerRegistryResource CreateDefaultRegistry(IDistributedApplicationBuilder builder, string name)
{
static void configureInfrastructure(AzureResourceInfrastructure infrastructure)
{
var registry = AzureProvisioningResource.CreateExistingOrNewProvisionableResource(infrastructure,
(identifier, resourceName) =>
{
var resource = ContainerRegistryService.FromExisting(identifier);
resource.Name = resourceName;
return resource;
},
(infra) => new ContainerRegistryService(infra.AspireResource.GetBicepIdentifier())
{
Sku = new ContainerRegistrySku { Name = ContainerRegistrySkuName.Basic },
Tags = { { "aspire-resource-name", infra.AspireResource.Name } }
});
infrastructure.Add(registry);
infrastructure.Add(new ProvisioningOutput("name", typeof(string)) { Value = registry.Name });
infrastructure.Add(new ProvisioningOutput("loginServer", typeof(string)) { Value = registry.LoginServer });
}
var resource = new AzureContainerRegistryResource(name, configureInfrastructure);
if (builder.ExecutionContext.IsPublishMode)
{
builder.AddResource(resource);
}
return resource;
}
}
|