File: ExistingAzureResourceExtensions.cs
Web Access
Project: src\src\Aspire.Hosting.Azure\Aspire.Hosting.Azure.csproj (Aspire.Hosting.Azure)
// 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;
 
namespace Aspire.Hosting;
 
/// <summary>
/// Extension methods for interacting with resources that are not managed by Aspire's provisioning or
/// container management layer.
/// </summary>
public static class ExistingAzureResourceExtensions
{
    /// <summary>
    /// Determines if the resource is an existing resource.
    /// </summary>
    /// <param name="resource">The resource to check.</param>
    /// <returns>True if the resource is an existing resource, otherwise false.</returns>
    /// <remarks>This method is not available in polyglot app hosts. Use the Azure resource-specific polyglot surface instead.</remarks>
    [AspireExportIgnore(Reason = "Use the Azure resource-specific polyglot export instead.")]
    public static bool IsExisting(this IResource resource)
    {
        ArgumentNullException.ThrowIfNull(resource);
 
        return resource.Annotations.OfType<ExistingAzureResourceAnnotation>().LastOrDefault() is not null;
    }
 
    /// <summary>
    /// Determines whether the Azure resource is marked as existing.
    /// </summary>
    /// <param name="resource">The Azure resource to check.</param>
    /// <returns><see langword="true"/> if the resource is marked as existing; otherwise, <see langword="false"/>.</returns>
    [AspireExport("isExisting")]
    internal static bool IsExistingForPolyglot(this IAzureResource resource)
    {
        return ((IResource)resource).IsExisting();
    }
 
    /// <summary>
    /// Marks the resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="resourceGroupParameter">The name of the existing resource group, or <see langword="null"/> to use the current resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExisting overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExisting<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource>? resourceGroupParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, nameParameter.Resource, resourceGroupParameter?.Resource);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group, or <see langword="null"/> to use the current resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExisting overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExisting<T>(this IResourceBuilder<T> builder, string name, string? resourceGroup)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, name, resourceGroup);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("runAsExisting")]
    internal static IResourceBuilder<T> RunAsExistingForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object? resourceGroup = null)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(resourceGroup, nameof(resourceGroup), allowNull: true);
 
        return RunAsExistingCore(builder, name, resourceGroup);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="resourceGroupParameter">The name of the existing resource group, or <see langword="null"/> to use the current resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExisting overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExisting<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource>? resourceGroupParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, nameParameter.Resource, resourceGroupParameter?.Resource);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group, or <see langword="null"/> to use the current resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExisting overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExisting<T>(this IResourceBuilder<T> builder, string name, string? resourceGroup)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, name, resourceGroup);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("publishAsExisting")]
    internal static IResourceBuilder<T> PublishAsExistingForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object? resourceGroup = null)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(resourceGroup, nameof(resourceGroup), allowNull: true);
 
        return PublishAsExistingCore(builder, name, resourceGroup);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="resourceGroupParameter">The name of the existing resource group, or <see langword="null"/> to use the current resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot asExisting overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> AsExisting<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource>? resourceGroupParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return AsExistingCore(builder, nameParameter.Resource, resourceGroupParameter?.Resource);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("asExisting")]
    internal static IResourceBuilder<T> AsExistingForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object? resourceGroup = null)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(resourceGroup, nameof(resourceGroup), allowNull: true);
 
        return AsExistingCore(builder, name, resourceGroup);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="subscriptionParameter">The subscription identifier containing the resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Use this only for Azure resources that are deployed at subscription scope. Most Azure services are resource-group scoped and should use <c>RunAsExistingInResourceGroup</c>.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExistingInSubscription overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExistingInSubscription<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource> subscriptionParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, nameParameter.Resource, resourceGroup: null, subscription: subscriptionParameter.Resource);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Use this only for Azure resources that are deployed at subscription scope. Most Azure services are resource-group scoped and should use <c>RunAsExistingInResourceGroup</c>.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExistingInSubscription overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExistingInSubscription<T>(this IResourceBuilder<T> builder, string name, string subscription)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, name, resourceGroup: null, subscription: subscription);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("runAsExistingInSubscription")]
    internal static IResourceBuilder<T> RunAsExistingInSubscriptionForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object subscription)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(subscription, nameof(subscription));
 
        return RunAsExistingCore(builder, name, resourceGroup: null, subscription: subscription);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="subscriptionParameter">The subscription identifier containing the resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Use this only for Azure resources that are deployed at subscription scope. Most Azure services are resource-group scoped and should use <c>PublishAsExistingInResourceGroup</c>.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExistingInSubscription overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExistingInSubscription<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource> subscriptionParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, nameParameter.Resource, resourceGroup: null, subscription: subscriptionParameter.Resource);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Use this only for Azure resources that are deployed at subscription scope. Most Azure services are resource-group scoped and should use <c>PublishAsExistingInResourceGroup</c>.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExistingInSubscription overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExistingInSubscription<T>(this IResourceBuilder<T> builder, string name, string subscription)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, name, resourceGroup: null, subscription: subscription);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("publishAsExistingInSubscription")]
    internal static IResourceBuilder<T> PublishAsExistingInSubscriptionForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object subscription)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(subscription, nameof(subscription));
 
        return PublishAsExistingCore(builder, name, resourceGroup: null, subscription: subscription);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="subscriptionParameter">The subscription identifier containing the resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Use this only for Azure resources that are deployed at subscription scope. Most Azure services are resource-group scoped and should use <c>AsExistingInResourceGroup</c>.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot asExistingInSubscription overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> AsExistingInSubscription<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource> subscriptionParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return AsExistingCore(builder, nameParameter.Resource, resourceGroup: null, subscription: subscriptionParameter.Resource);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Use this only for Azure resources that are deployed at subscription scope. Most Azure services are resource-group scoped and should use <c>AsExistingInResourceGroup</c>.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot asExistingInSubscription overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> AsExistingInSubscription<T>(this IResourceBuilder<T> builder, string name, string subscription)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return AsExistingCore(builder, name, resourceGroup: null, subscription: subscription);
    }
 
    /// <summary>
    /// Marks the subscription-scoped resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("asExistingInSubscription")]
    internal static IResourceBuilder<T> AsExistingInSubscriptionForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object subscription)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(subscription, nameof(subscription));
 
        return AsExistingCore(builder, name, resourceGroup: null, subscription: subscription);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="resourceGroupParameter">The name of the existing resource group.</param>
    /// <param name="subscriptionParameter">The subscription identifier containing the resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExistingInResourceGroup overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExistingInResourceGroup<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource> resourceGroupParameter, IResourceBuilder<ParameterResource> subscriptionParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, nameParameter.Resource, resourceGroupParameter.Resource, subscriptionParameter.Resource);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group.</param>
    /// <param name="subscription">The subscription identifier containing the resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExistingInResourceGroup overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExistingInResourceGroup<T>(this IResourceBuilder<T> builder, string name, string resourceGroup, string subscription)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, name, resourceGroup, subscription);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="resourceGroup">The resource group containing the existing resource as a string or parameter resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource group as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("runAsExistingInResourceGroup")]
    internal static IResourceBuilder<T> RunAsExistingInResourceGroupForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object resourceGroup,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object subscription)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(resourceGroup, nameof(resourceGroup));
        ValidateExistingResourceArgument(subscription, nameof(subscription));
 
        return RunAsExistingCore(builder, name, resourceGroup, subscription);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="resourceGroupParameter">The name of the existing resource group.</param>
    /// <param name="subscriptionParameter">The subscription identifier containing the resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExistingInResourceGroup overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExistingInResourceGroup<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource> resourceGroupParameter, IResourceBuilder<ParameterResource> subscriptionParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, nameParameter.Resource, resourceGroupParameter.Resource, subscriptionParameter.Resource);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group.</param>
    /// <param name="subscription">The subscription identifier containing the resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExistingInResourceGroup overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExistingInResourceGroup<T>(this IResourceBuilder<T> builder, string name, string resourceGroup, string subscription)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, name, resourceGroup, subscription);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="resourceGroup">The resource group containing the existing resource as a string or parameter resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource group as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("publishAsExistingInResourceGroup")]
    internal static IResourceBuilder<T> PublishAsExistingInResourceGroupForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object resourceGroup,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object subscription)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(resourceGroup, nameof(resourceGroup));
        ValidateExistingResourceArgument(subscription, nameof(subscription));
 
        return PublishAsExistingCore(builder, name, resourceGroup, subscription);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <param name="resourceGroupParameter">The name of the existing resource group.</param>
    /// <param name="subscriptionParameter">The subscription identifier containing the resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot asExistingInResourceGroup overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> AsExistingInResourceGroup<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter, IResourceBuilder<ParameterResource> resourceGroupParameter, IResourceBuilder<ParameterResource> subscriptionParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return AsExistingCore(builder, nameParameter.Resource, resourceGroupParameter.Resource, subscriptionParameter.Resource);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <param name="resourceGroup">The name of the existing resource group.</param>
    /// <param name="subscription">The subscription identifier containing the resource group.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExportIgnore(Reason = "Use the polyglot asExistingInResourceGroup overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> AsExistingInResourceGroup<T>(this IResourceBuilder<T> builder, string name, string resourceGroup, string subscription)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return AsExistingCore(builder, name, resourceGroup, subscription);
    }
 
    /// <summary>
    /// Marks the resource as an existing resource in a specific resource group and subscription in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <param name="resourceGroup">The resource group containing the existing resource as a string or parameter resource.</param>
    /// <param name="subscription">The subscription identifier containing the resource group as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("asExistingInResourceGroup")]
    internal static IResourceBuilder<T> AsExistingInResourceGroupForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object resourceGroup,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object subscription)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
        ValidateExistingResourceArgument(resourceGroup, nameof(resourceGroup));
        ValidateExistingResourceArgument(subscription, nameof(subscription));
 
        return AsExistingCore(builder, name, resourceGroup, subscription);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Tenant scope targets the current tenant. Bicep doesn't support selecting a different tenant with the <c>tenant()</c> scope function.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExistingInTenant overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExistingInTenant<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, nameParameter.Resource, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Tenant scope targets the current tenant. Bicep doesn't support selecting a different tenant with the <c>tenant()</c> scope function.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot runAsExistingInTenant overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> RunAsExistingInTenant<T>(this IResourceBuilder<T> builder, string name)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return RunAsExistingCore(builder, name, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource when the application is running.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("runAsExistingInTenant")]
    internal static IResourceBuilder<T> RunAsExistingInTenantForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
 
        return RunAsExistingCore(builder, name, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Tenant scope targets the current tenant. Bicep doesn't support selecting a different tenant with the <c>tenant()</c> scope function.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExistingInTenant overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExistingInTenant<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, nameParameter.Resource, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Tenant scope targets the current tenant. Bicep doesn't support selecting a different tenant with the <c>tenant()</c> scope function.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot publishAsExistingInTenant overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> PublishAsExistingInTenant<T>(this IResourceBuilder<T> builder, string name)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return PublishAsExistingCore(builder, name, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource when the application is deployed.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("publishAsExistingInTenant")]
    internal static IResourceBuilder<T> PublishAsExistingInTenantForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
 
        return PublishAsExistingCore(builder, name, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="nameParameter">The name of the existing resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Tenant scope targets the current tenant. Bicep doesn't support selecting a different tenant with the <c>tenant()</c> scope function.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot asExistingInTenant overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> AsExistingInTenant<T>(this IResourceBuilder<T> builder, IResourceBuilder<ParameterResource> nameParameter)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return AsExistingCore(builder, nameParameter.Resource, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    /// <remarks>Tenant scope targets the current tenant. Bicep doesn't support selecting a different tenant with the <c>tenant()</c> scope function.</remarks>
    [AspireExportIgnore(Reason = "Use the polyglot asExistingInTenant overload that accepts string or ParameterResource values instead.")]
    public static IResourceBuilder<T> AsExistingInTenant<T>(this IResourceBuilder<T> builder, string name)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        return AsExistingCore(builder, name, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    /// <summary>
    /// Marks the current-tenant-scoped resource as an existing resource in both run and publish modes.
    /// </summary>
    /// <typeparam name="T">The type of the resource.</typeparam>
    /// <param name="builder">The resource builder.</param>
    /// <param name="name">The name of the existing resource as a string or parameter resource.</param>
    /// <returns>The resource builder with the existing resource annotation added.</returns>
    [AspireExport("asExistingInTenant")]
    internal static IResourceBuilder<T> AsExistingInTenantForPolyglot<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(ParameterResource))] object name)
        where T : IAzureResource
    {
        ValidateExistingResourceArgument(name, nameof(name));
 
        return AsExistingCore(builder, name, resourceGroup: null, subscription: null, isTenantScope: true);
    }
 
    private static IResourceBuilder<T> RunAsExistingCore<T>(IResourceBuilder<T> builder, object name, object? resourceGroup, object? subscription = null, bool isTenantScope = false)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        if (!builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
        {
            builder.WithAnnotation(CreateExistingAzureResourceAnnotation(name, resourceGroup, subscription, isTenantScope));
        }
 
        return builder;
    }
 
    private static IResourceBuilder<T> PublishAsExistingCore<T>(IResourceBuilder<T> builder, object name, object? resourceGroup, object? subscription = null, bool isTenantScope = false)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        if (builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
        {
            builder.WithAnnotation(CreateExistingAzureResourceAnnotation(name, resourceGroup, subscription, isTenantScope));
        }
 
        return builder;
    }
 
    private static IResourceBuilder<T> AsExistingCore<T>(IResourceBuilder<T> builder, object name, object? resourceGroup, object? subscription = null, bool isTenantScope = false)
        where T : IAzureResource
    {
        ArgumentNullException.ThrowIfNull(builder);
 
        builder.WithAnnotation(CreateExistingAzureResourceAnnotation(name, resourceGroup, subscription, isTenantScope));
 
        return builder;
    }
 
    private static ExistingAzureResourceAnnotation CreateExistingAzureResourceAnnotation(object name, object? resourceGroup, object? subscription, bool isTenantScope)
    {
        if (isTenantScope)
        {
            return new ExistingAzureResourceAnnotation(name, isTenantScope: true);
        }
 
        if (subscription is not null)
        {
            return new ExistingAzureResourceAnnotation(name, resourceGroup, subscription);
        }
 
        return new ExistingAzureResourceAnnotation(name, resourceGroup);
    }
 
    private static void ValidateExistingResourceArgument(object? value, string paramName, bool allowNull = false)
    {
        if (value is null)
        {
            if (allowNull)
            {
                return;
            }
 
            throw new ArgumentNullException(paramName);
        }
 
        if (value is not string && value is not ParameterResource)
        {
            throw new ArgumentException("Value must be a string or ParameterResource.", paramName);
        }
    }
}