Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's drop this file. I kinda of want to work out how we could do better aspire cli support, but until then we should avoid adding the files.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"appHostPath": "../CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.csproj"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@


builder.AddProject<Projects.CommunityToolkit_Aspire_Hosting_Dapr_ServiceA>("servicea")
.WithReference(redis)
.PublishAsAzureContainerApp((i,c)=> { })
.WithDaprSidecar(sidecar => sidecar.WithReference(stateStore).WithReference(pubSub))
.WaitFor(redis);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
using Azure.Provisioning.Expressions;
using Azure.Provisioning.KeyVault;
using Azure.Provisioning.Roles;
using Azure.ResourceManager.Authorization;
using CommunityToolkit.Aspire.Hosting.Azure.Dapr;
using CommunityToolkit.Aspire.Hosting.Dapr;
using AzureRedisResource = Azure.Provisioning.Redis.RedisResource;
using RedisBuiltInRole = Azure.Provisioning.Redis.RedisBuiltInRole;

namespace Aspire.Hosting;

Expand Down Expand Up @@ -91,16 +93,12 @@ private static IResourceBuilder<IDaprComponentResource> ConfigureRedisPubSubComp

private static void ConfigureForManagedIdentityAuthentication(this IResourceBuilder<IDaprComponentResource> builder, IResourceBuilder<AzureRedisCacheResource> redisBuilder, string componentType)
{
var principalIdParam = new ProvisioningParameter(AzureBicepResource.KnownParameters.PrincipalId, typeof(string));

var configureInfrastructure = (AzureResourceInfrastructure infrastructure) =>
var configureInfrastructure = (AzureResourceInfrastructure infrastructure, UserAssignedIdentity daprIdentity) =>
{
var redisHostParam = redisBuilder.GetOutput(daprConnectionStringKey).AsProvisioningParameter(infrastructure, redisHostKey);

var provisionableResources = infrastructure.GetProvisionableResources();
if (provisionableResources.OfType<ContainerAppManagedEnvironment>().FirstOrDefault()
is ContainerAppManagedEnvironment managedEnvironment &&
provisionableResources.OfType<UserAssignedIdentity>().FirstOrDefault() is UserAssignedIdentity identity)
if (provisionableResources.OfType<ContainerAppManagedEnvironment>().FirstOrDefault() is ContainerAppManagedEnvironment managedEnvironment)
{
var daprComponent = AzureDaprHostingExtensions.CreateDaprComponent(
builder.Resource.Name,
Expand All @@ -115,7 +113,7 @@ is ContainerAppManagedEnvironment managedEnvironment &&
new() { Name = redisHostKey, Value = redisHostParam },
new() { Name = "enableTLS", Value = "true" },
new() { Name = "useEntraID", Value = "true" },
new() { Name = "azureClientId", Value = identity.PrincipalId }
new() { Name = "azureClientId", Value = daprIdentity.PrincipalId }
};

// Add state-specific metadata
Expand All @@ -132,9 +130,11 @@ is ContainerAppManagedEnvironment managedEnvironment &&
infrastructure.Add(daprComponent);

infrastructure.TryAdd(redisHostParam);

}
};

builder.WithRoleAssignments(redisBuilder, RedisBuiltInRole.GetBuiltInRoleName, [RedisBuiltInRole.RedisCacheContributor]);
builder.WithAnnotation(new AzureDaprComponentPublishingAnnotation(configureInfrastructure));

// Configure the Redis resource to output the connection string
Expand Down Expand Up @@ -162,7 +162,7 @@ private static void ConfigureForAccessKeyAuthentication(this IResourceBuilder<ID
// Configure Key Vault secret store component - this adds the annotation to the same resource
builder.ConfigureKeyVaultSecretsComponent(kvNameParam);

var configureInfrastructure = (AzureResourceInfrastructure infrastructure) =>
var configureInfrastructure = (AzureResourceInfrastructure infrastructure, UserAssignedIdentity daprIdentity) =>
{
var redisHostParam = redisBuilder.GetOutput(daprConnectionStringKey).AsProvisioningParameter(infrastructure, redisHostKey);

Expand All @@ -183,6 +183,7 @@ private static void ConfigureForAccessKeyAuthentication(this IResourceBuilder<ID
new() { Name = "redisPassword", SecretRef = "redis-password" }
};

// TODO: Make this configurable - perhaps AddDaprStateStore().AsActorStateStore() or similar
// Add state-specific metadata
if (componentType == "state.redis")
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure;
using Aspire.Hosting.Azure.AppContainers;
using Azure.Provisioning;
using Azure.Provisioning.AppContainers;
using Azure.Provisioning.Authorization;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.Roles;
using CommunityToolkit.Aspire.Hosting.Azure.Dapr;
using CommunityToolkit.Aspire.Hosting.Dapr;

Expand All @@ -12,11 +16,14 @@ namespace Aspire.Hosting;
/// </summary>
public static class AzureContainerAppEnvironmentResourceBuilderExtensions
{
private const string DaprManagedIdentityKey = "daprManagedIdentity";

/// <summary>
/// Configures the Azure Container App Environment resource to use Dapr.
/// This method creates a dedicated managed identity for Dapr components and configures all Dapr components to use it.
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
/// <param name="builder">The Azure Container App Environment resource builder.</param>
/// <returns>The configured Azure Container App Environment resource builder.</returns>
public static IResourceBuilder<AzureContainerAppEnvironmentResource> WithDaprComponents(
this IResourceBuilder<AzureContainerAppEnvironmentResource> builder)
{
Expand Down Expand Up @@ -53,13 +60,39 @@ public static IResourceBuilder<AzureContainerAppEnvironmentResource> WithDaprCom

return builder.ConfigureInfrastructure(infrastructure =>
{
// Create the Dapr managed identity once
var daprIdentity = new UserAssignedIdentity(DaprManagedIdentityKey);

infrastructure.Add(daprIdentity);

var daprComponentResources = builder.ApplicationBuilder.Resources.OfType<IDaprComponentResource>();

foreach (var daprComponentResource in daprComponentResources)
{
if (daprComponentResource.TryGetLastAnnotation<RoleAssignmentAnnotation>(out var roleAssignmentAnnotation))
{
var target = roleAssignmentAnnotation.Target.AddAsExistingResource(infrastructure);

foreach (var roleDefinition in roleAssignmentAnnotation.Roles)
{
var id = new MemberExpression(new IdentifierExpression(roleAssignmentAnnotation.Target.GetBicepIdentifier()), "id");
var roleAssignment = new RoleAssignment($"{daprComponentResource.Name}{roleDefinition.Name}")
{
Name = BicepFunction.CreateGuid(id, daprIdentity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", roleDefinition.Id)),
RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", roleDefinition.Id),
PrincipalId = daprIdentity.PrincipalId,
PrincipalType = RoleManagementPrincipalType.ServicePrincipal,
Scope = new IdentifierExpression(target.BicepIdentifier)
};

infrastructure.Add(roleAssignment);
}
}

daprComponentResource.TryGetLastAnnotation<AzureDaprComponentPublishingAnnotation>(out var publishingAnnotation);
publishingAnnotation?.PublishingAction(infrastructure, daprIdentity);


publishingAnnotation?.PublishingAction(infrastructure);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure;
using Azure.Provisioning.Roles;

namespace CommunityToolkit.Aspire.Hosting.Azure.Dapr;
/// <summary>
Expand All @@ -10,4 +11,4 @@ namespace CommunityToolkit.Aspire.Hosting.Azure.Dapr;
/// allowing customization of the resource infrastructure.</remarks>
/// <param name="PublishingAction">The action to be executed on the <see cref="AzureResourceInfrastructure"/> during the publishing process. This
/// action allows for customization of the infrastructure configuration.</param>
public record AzureDaprComponentPublishingAnnotation(Action<AzureResourceInfrastructure> PublishingAction) : IResourceAnnotation;
public record AzureDaprComponentPublishingAnnotation(Action<AzureResourceInfrastructure, UserAssignedIdentity> PublishingAction) : IResourceAnnotation;
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ public static IResourceBuilder<AzureDaprComponentResource> AddAzureDaprResource(
.WithManifestPublishingCallback(azureDaprComponentResource.WriteToManifest);
}

/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TTarget"></typeparam>
/// <typeparam name="TBuiltInRole"></typeparam>
/// <param name="builder"></param>
/// <param name="target"></param>
/// <param name="getName"></param>
/// <param name="roles"></param>
/// <returns></returns>
public static IResourceBuilder<T> WithRoleAssignments<T, TTarget, TBuiltInRole>(this IResourceBuilder<T> builder, IResourceBuilder<TTarget> target, Func<TBuiltInRole, string> getName, TBuiltInRole[] roles)
where T : IResource
where TTarget : AzureProvisioningResource
where TBuiltInRole : notnull
{
builder.WithAnnotation(new RoleAssignmentAnnotation(target.Resource, CreateRoleDefinitions(roles, getName)));
return builder;
}


/// <summary>
/// Adds scopes to the specified Dapr component in a container app managed environment.
/// </summary>
Expand Down Expand Up @@ -96,4 +117,10 @@ public static ContainerAppManagedEnvironmentDaprComponent CreateDaprComponent(
Version = version
};
}

private static HashSet<RoleDefinition> CreateRoleDefinitions<TBuiltInRole>(IReadOnlyList<TBuiltInRole> roles, Func<TBuiltInRole, string> getName)
where TBuiltInRole : notnull
{
return [.. roles.Select(r => new RoleDefinition(r.ToString()!, getName(r)))];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Azure.Provisioning;
using Azure.Provisioning.AppContainers;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.KeyVault;
using Azure.Provisioning.Roles;
using CommunityToolkit.Aspire.Hosting.Azure.Dapr;
using CommunityToolkit.Aspire.Hosting.Dapr;

Expand All @@ -17,6 +17,8 @@ public static class AzureKeyVaultDaprHostingExtensions
private const string secretStoreComponentKey = "secretStoreComponent";
private const string secretStore = nameof(secretStore);



/// <summary>
/// Configures the Key Vault secret store component for the Dapr component resource.
/// </summary>
Expand All @@ -27,9 +29,9 @@ public static IResourceBuilder<IDaprComponentResource> ConfigureKeyVaultSecretsC
{
ArgumentNullException.ThrowIfNull(builder, nameof(builder));

var principalIdParameter = new ProvisioningParameter(AzureBicepResource.KnownParameters.PrincipalId, typeof(string));
//TODO: We may need to actually add the key vault resource here as well - I'm not sure if aspire automatically adds it anymore or not

var configureInfrastructure = (AzureResourceInfrastructure infrastructure) =>
var configureInfrastructure = (AzureResourceInfrastructure infrastructure, UserAssignedIdentity daprIdentity) =>
{
if (infrastructure.GetProvisionableResources().OfType<ContainerAppManagedEnvironment>().FirstOrDefault() is ContainerAppManagedEnvironment managedEnvironment)
{
Expand All @@ -43,12 +45,11 @@ public static IResourceBuilder<IDaprComponentResource> ConfigureKeyVaultSecretsC
daprComponent.Scopes = [];
daprComponent.Metadata = [
new ContainerAppDaprMetadata { Name = "vaultName", Value = kvNameParam },
new ContainerAppDaprMetadata { Name = "azureClientId", Value = principalIdParameter }
new ContainerAppDaprMetadata { Name = "azureClientId", Value = daprIdentity.PrincipalId }
];

infrastructure.Add(daprComponent);
infrastructure.Add(kvNameParam);
infrastructure.Add(principalIdParameter);

infrastructure.Add(new ProvisioningOutput(secretStoreComponentKey, typeof(string))
{
Expand Down
Loading