Skip to content

Commit d3fce4b

Browse files
authored
Fix Azure roles resources always redeploying (#12901)
* Fix Azure roles resources always redeploying Everytime we check if we need to redeploy an Azure "roles" resource in run mode, we are getting a different CheckSum. This is because we are overwriting the "known parameters" like PrincipalId and PrincipalType with 'null' values, because these values aren't available yet. The fix is to skip setting those known parameters, like we did in previous versions. Fix #12651
1 parent ef7081e commit d3fce4b

File tree

2 files changed

+59
-4
lines changed

2 files changed

+59
-4
lines changed

src/Aspire.Hosting.Azure/Provisioning/BicepUtilities.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,34 @@ namespace Aspire.Hosting.Azure.Provisioning;
1414
/// </summary>
1515
internal static class BicepUtilities
1616
{
17+
// Known values since they will be filled in by the provisioner
18+
private static readonly string[] s_knownParameterNames =
19+
[
20+
AzureBicepResource.KnownParameters.PrincipalName,
21+
AzureBicepResource.KnownParameters.PrincipalId,
22+
AzureBicepResource.KnownParameters.PrincipalType,
23+
AzureBicepResource.KnownParameters.UserPrincipalId,
24+
AzureBicepResource.KnownParameters.Location,
25+
];
26+
1727
/// <summary>
1828
/// Converts the parameters to a JSON object compatible with the ARM template.
1929
/// </summary>
20-
public static async Task SetParametersAsync(JsonObject parameters, AzureBicepResource resource, CancellationToken cancellationToken = default)
30+
public static async Task SetParametersAsync(JsonObject parameters, AzureBicepResource resource, bool skipKnownValues = false, CancellationToken cancellationToken = default)
2131
{
2232
// Convert the parameters to a JSON object
2333
foreach (var parameter in resource.Parameters)
2434
{
2535
// Execute parameter values which are deferred.
2636
var parameterValue = parameter.Value is Func<object?> f ? f() : parameter.Value;
2737

38+
// Skip known parameters with 'null' values, like PrincipalType and PrincipalId, since they are filled in by the provisioner
39+
// and are not available at this time. If we don't do this, the "roles" resources will be re-deployed every run.
40+
if (skipKnownValues && s_knownParameterNames.Contains(parameter.Key) && parameterValue is null)
41+
{
42+
continue;
43+
}
44+
2845
parameters[parameter.Key] = new JsonObject()
2946
{
3047
["value"] = parameterValue switch
@@ -109,7 +126,7 @@ public static string GetChecksum(AzureBicepResource resource, JsonObject paramet
109126
_ = resource.GetBicepTemplateString();
110127

111128
// Now overwrite with live object values skipping known values.
112-
await SetParametersAsync(parameters, resource, cancellationToken: cancellationToken).ConfigureAwait(false);
129+
await SetParametersAsync(parameters, resource, skipKnownValues: true, cancellationToken: cancellationToken).ConfigureAwait(false);
113130
if (scope is not null)
114131
{
115132
await SetScopeAsync(scope, resource, cancellationToken).ConfigureAwait(false);
@@ -130,4 +147,4 @@ public static string GetChecksum(AzureBicepResource resource, JsonObject paramet
130147
(resource.TryGetLastAnnotation<ExistingAzureResourceAnnotation>(out var existingResource) ?
131148
existingResource.ResourceGroup :
132149
null);
133-
}
150+
}

tests/Aspire.Hosting.Azure.Tests/BicepUtilitiesTests.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,49 @@ public async Task GetCurrentChecksumAsync_ReturnsValidChecksumForValidParameters
428428
Assert.NotEmpty(result);
429429
}
430430

431+
/// <summary>
432+
/// Ensures that known parameters are not overwritten when calculating the checksum.
433+
/// This is important because if these known parameters are overwritten, it means the "roles"
434+
/// resources will be redeployed every time the app is run.
435+
/// </summary>
436+
[Fact]
437+
public async Task GetCurrentChecksumAsync_DoesNotOverwriteKnownParameters()
438+
{
439+
// Arrange
440+
using var builder = TestDistributedApplicationBuilder.Create();
441+
var bicep = builder.AddBicepTemplateString("test", "param name string").Resource;
442+
bicep.Parameters[AzureBicepResource.KnownParameters.PrincipalType] = null;
443+
bicep.Parameters[AzureBicepResource.KnownParameters.PrincipalId] = null;
444+
445+
var parameters = new JsonObject
446+
{
447+
[AzureBicepResource.KnownParameters.PrincipalType] = new JsonObject { ["value"] = "User" },
448+
[AzureBicepResource.KnownParameters.PrincipalId] = new JsonObject { ["value"] = "1234" },
449+
};
450+
451+
var configurationBuilder = new ConfigurationBuilder();
452+
configurationBuilder.AddInMemoryCollection(new Dictionary<string, string?>
453+
{
454+
["Parameters"] = parameters.ToJsonString()
455+
});
456+
var config = configurationBuilder.Build();
457+
458+
// Act
459+
var result = await BicepUtilities.GetCurrentChecksumAsync(bicep, config);
460+
461+
// Assert
462+
Assert.NotNull(result);
463+
464+
// verify the checksum is the same as using the config parameters directly
465+
var expected = BicepUtilities.GetChecksum(bicep, parameters, scope: null);
466+
Assert.Equal(expected, result);
467+
}
468+
431469
private sealed class ResourceWithConnectionString(string name, string connectionString) :
432470
Resource(name),
433471
IResourceWithConnectionString
434472
{
435473
public ReferenceExpression ConnectionStringExpression =>
436474
ReferenceExpression.Create($"{connectionString}");
437475
}
438-
}
476+
}

0 commit comments

Comments
 (0)