Skip to content

Commit

Permalink
Add more AsProvisioningParameter overloads
Browse files Browse the repository at this point in the history
Adding more AsProvisioningParameter overloads to allow for easily referencing endpoints and any custom ReferenceExpression in a bicep parameter.

Fix #6534
  • Loading branch information
eerhardt committed Oct 31, 2024
1 parent 34d6aab commit a90e696
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 11 deletions.
71 changes: 60 additions & 11 deletions src/Aspire.Hosting.Azure/AzureProvisioningResourceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,7 @@ public static ProvisioningParameter AsProvisioningParameter(this IResourceBuilde

infrastructure.AspireResource.Parameters[parameterName] = parameterResourceBuilder.Resource;

var parameter = infrastructure.GetParameters().FirstOrDefault(p => p.BicepIdentifier == parameterName);
if (parameter is null)
{
parameter = new ProvisioningParameter(parameterName, typeof(string))
{
IsSecure = parameterResourceBuilder.Resource.Secret
};
infrastructure.Add(parameter);
}

return parameter;
return GetOrAddParameter(infrastructure, parameterName, parameterResourceBuilder.Resource.Secret);
}

/// <summary>
Expand Down Expand Up @@ -112,10 +102,69 @@ public static ProvisioningParameter AsProvisioningParameter(this BicepOutputRefe

infrastructure.AspireResource.Parameters[parameterName] = outputReference;

return GetOrAddParameter(infrastructure, parameterName);
}

/// <summary>
/// Creates a new <see cref="ProvisioningParameter"/> in <paramref name="infrastructure"/>, or reuses an existing bicep parameter if one with
/// the same name already exists, that corresponds to <paramref name="endpointReference"/>.
/// </summary>
/// <param name="endpointReference">
/// The <see cref="EndpointReference"/> to use for the value of the <see cref="ProvisioningParameter"/>.
/// </param>
/// <param name="infrastructure">The <see cref="AzureResourceInfrastructure"/> that contains the <see cref="ProvisioningParameter"/>.</param>
/// <param name="parameterName">The name of the parameter to be assigned.</param>
/// <returns>
/// The corresponding <see cref="ProvisioningParameter"/> that was found or newly created.
/// </returns>
/// <remarks>
/// This is useful when assigning a <see cref="BicepValue"/> to the value of an Aspire <see cref="EndpointReference"/>.
/// </remarks>
public static ProvisioningParameter AsProvisioningParameter(this EndpointReference endpointReference, AzureResourceInfrastructure infrastructure, string parameterName)
{
ArgumentNullException.ThrowIfNull(endpointReference);
ArgumentNullException.ThrowIfNull(infrastructure);

infrastructure.AspireResource.Parameters[parameterName] = endpointReference;

return GetOrAddParameter(infrastructure, parameterName);
}

/// <summary>
/// Creates a new <see cref="ProvisioningParameter"/> in <paramref name="infrastructure"/>, or reuses an existing bicep parameter if one with
/// the same name already exists, that corresponds to <paramref name="expression"/>.
/// </summary>
/// <param name="expression">
/// The <see cref="ReferenceExpression"/> that represents the value to use for the <see cref="ProvisioningParameter"/>.
/// </param>
/// <param name="infrastructure">The <see cref="AzureResourceInfrastructure"/> that contains the <see cref="ProvisioningParameter"/>.</param>
/// <param name="parameterName">The name of the parameter to be assigned.</param>
/// <returns>
/// The corresponding <see cref="ProvisioningParameter"/> that was found or newly created.
/// </returns>
/// <remarks>
/// This is useful when assigning a <see cref="BicepValue"/> to the value of an Aspire <see cref="EndpointReference"/>.
/// </remarks>
public static ProvisioningParameter AsProvisioningParameter(this ReferenceExpression expression, AzureResourceInfrastructure infrastructure, string parameterName)
{
ArgumentNullException.ThrowIfNull(expression);
ArgumentNullException.ThrowIfNull(infrastructure);

infrastructure.AspireResource.Parameters[parameterName] = expression;

return GetOrAddParameter(infrastructure, parameterName);
}

private static ProvisioningParameter GetOrAddParameter(AzureResourceInfrastructure infrastructure, string parameterName, bool? isSecure = null)
{
var parameter = infrastructure.GetParameters().FirstOrDefault(p => p.BicepIdentifier == parameterName);
if (parameter is null)
{
parameter = new ProvisioningParameter(parameterName, typeof(string));
if (isSecure.HasValue)
{
parameter.IsSecure = isSecure.Value;
};
infrastructure.Add(parameter);
}

Expand Down
2 changes: 2 additions & 0 deletions src/Aspire.Hosting.Azure/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ override Aspire.Hosting.Azure.AzureProvisioningResource.GetBicepTemplateString()
static Aspire.Hosting.AzureBicepResourceExtensions.WithParameter<T>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>! builder, string! name, Aspire.Hosting.ApplicationModel.EndpointReference! value) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>!
static Aspire.Hosting.AzureBicepResourceExtensions.WithParameter<T>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>! builder, string! name, Aspire.Hosting.ApplicationModel.ReferenceExpression! value) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>!
static Aspire.Hosting.AzureProvisioningResourceExtensions.AddAzureInfrastructure(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, System.Action<Aspire.Hosting.Azure.AzureResourceInfrastructure!>! configureInfrastructure) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureProvisioningResource!>!
static Aspire.Hosting.AzureProvisioningResourceExtensions.AsProvisioningParameter(this Aspire.Hosting.ApplicationModel.EndpointReference! endpointReference, Aspire.Hosting.Azure.AzureResourceInfrastructure! infrastructure, string! parameterName) -> Azure.Provisioning.ProvisioningParameter!
static Aspire.Hosting.AzureProvisioningResourceExtensions.AsProvisioningParameter(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.ParameterResource!>! parameterResourceBuilder, Aspire.Hosting.Azure.AzureResourceInfrastructure! infrastructure, string? parameterName = null) -> Azure.Provisioning.ProvisioningParameter!
static Aspire.Hosting.AzureProvisioningResourceExtensions.AsProvisioningParameter(this Aspire.Hosting.ApplicationModel.ReferenceExpression! expression, Aspire.Hosting.Azure.AzureResourceInfrastructure! infrastructure, string! parameterName) -> Azure.Provisioning.ProvisioningParameter!
static Aspire.Hosting.AzureProvisioningResourceExtensions.AsProvisioningParameter(this Aspire.Hosting.Azure.BicepOutputReference! outputReference, Aspire.Hosting.Azure.AzureResourceInfrastructure! infrastructure, string? parameterName = null) -> Azure.Provisioning.ProvisioningParameter!
static Aspire.Hosting.AzureProvisioningResourceExtensions.ConfigureInfrastructure<T>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>! builder, System.Action<Aspire.Hosting.Azure.AzureResourceInfrastructure!>! configure) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>!
static Aspire.Hosting.AzureResourceExtensions.GetBicepIdentifier(this Aspire.Hosting.ApplicationModel.IAzureResource! resource) -> string!
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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.Utils;
using Azure.Provisioning.AppContainers;
using Xunit;
using Xunit.Abstractions;

namespace Aspire.Hosting.Azure.Tests;

public class AzureProvisioningResourceExtensionsTests(ITestOutputHelper output)
{
[Fact]
public async Task AsProvisioningParameterTests()
{
using var builder = TestDistributedApplicationBuilder.Create();

var apiProject = builder.AddProject<Project>("api", launchProfileName: null)
.WithHttpsEndpoint();

var endpointReference = apiProject.GetEndpoint("https");
var referenceExpression = ReferenceExpression.Create($"prefix:{endpointReference.Property(EndpointProperty.Host)}:{endpointReference.Property(EndpointProperty.Port)}");

var resource1 = builder.AddAzureInfrastructure("resource1", infrastructure =>
{
var endpointAddressParam = endpointReference.AsProvisioningParameter(infrastructure, parameterName: "endpointAddressParam");
var someExpressionParam = referenceExpression.AsProvisioningParameter(infrastructure, "someExpressionParam");
var app = new ContainerApp("app");
app.Template.Scale.Rules =
[
new ContainerAppScaleRule()
{
Name = "temp",
Custom = new ContainerAppCustomScaleRule()
{
CustomScaleRuleType= "external",
Metadata =
{
{ "address", endpointAddressParam },
{ "someExpression", someExpressionParam },
}
}
}
];
infrastructure.Add(app);
});

var manifest = await ManifestUtils.GetManifestWithBicep(resource1.Resource);

var expectedManifest = """
{
"type": "azure.bicep.v0",
"path": "resource1.module.bicep",
"params": {
"endpointAddressParam": "{api.bindings.https.url}",
"someExpressionParam": "prefix:{api.bindings.https.host}:{api.bindings.https.port}"
}
}
""";
Assert.Equal(expectedManifest, manifest.ManifestNode.ToString());

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

param endpointAddressParam string

param someExpressionParam string

resource app 'Microsoft.App/containerApps@2024-03-01' = {
name: take('app-${uniqueString(resourceGroup().id)}', 32)
location: location
properties: {
template: {
scale: {
rules: [
{
name: 'temp'
custom: {
type: 'external'
metadata: {
address: endpointAddressParam
someExpression: someExpressionParam
}
}
}
]
}
}
}
}
""";
output.WriteLine(manifest.BicepText);
Assert.Equal(expectedBicep, manifest.BicepText);
}

private sealed class Project : IProjectMetadata
{
public string ProjectPath => "project";
}

}

0 comments on commit a90e696

Please sign in to comment.