Description
Background and Motivation
To support Azure Functions worker projects, the Azure Functions host needs to be able to construct its own clients for Azure-based resources to facilitate acting as the middleman between the resource and triggers defined on the function.
Aspire doesn't currently provide any APIs for accessing the configuration properties associated with a resource (including the specific resource type) to support the construction of non-Aspired injected clients.
To mitigate this, we propose adding a new IResource interface to support emitting the configuration used by a given resource to consumers.
Proposed API
The proposed API adds a new IResourceWithAzureFunctionsConfig to the Azure hosting package and implementations of the interface to the following resources: AzureQueueStorageResource
, AzureBlobStorageResource
, AzureTableStorageResource
, AzureServiceBusResource
, and AzureEventHubsResource
.
// Package: Aspire.Hosting.Azure
namespace Aspire.Hosting.ApplicationModel;
+ public interface IResourceWithAzureFunctionsConfig : IResource
+ {
+ void ApplyAzureFunctionsConfiguration(Dictionary<string, object> target, string? connectionName = null)
+ }
// Package: Aspire.Hosting.Azure.Storage
namespace Aspire.Hosting.Azure;
public class AzureQueueStorageResource(string name, AzureStorageResource storage) : Resource(name),
IResourceWithConnectionString,
IResourceWithParent<AzureStorageResource>,
+ IResourceWithAzureFunctionsConfig
{
+ public void ApplyAzureFunctionsConfiguration(Dictionary<string, object> target, string? connectionName = null)
}
public class AzureBlobStorageResource(string name, AzureStorageResource storage) : Resource(name),
IResourceWithConnectionString,
IResourceWithParent<AzureStorageResource>,
+ IResourceWithAzureFunctionsConfig
{
+ public void ApplyAzureFunctionsConfiguration(Dictionary<string, object> target, string? connectionName = null)
}
public class AzureTableStorageResource(string name, AzureStorageResource storage) : Resource(name),
IResourceWithConnectionString,
IResourceWithParent<AzureStorageResource>,
+ IResourceWithAzureFunctionsConfig
{
+ public void ApplyAzureFunctionsConfiguration(Dictionary<string, object> target, string? connectionName = null)
}
// Package: Aspire.Hosting.Azure.ServiceBus
namespace Aspire.Hosting.Azure;
public class AzureServiceBusResource(string name, Action<ResourceModuleConstruct> configureConstruct)
: AzureConstructResource(name, configureConstruct),
IResourceWithConnectionString,
+ IResourceWithAzureFunctionsConfig
{
+ public void ApplyAzureFunctionsConfiguration(Dictionary<string, object> target, string? connectionName = null)
}
// Package: Aspire.Hosting.Azure.EventHubs
namespace Aspire.Hosting.Azure;
public class AzureEventHubsResource(string name, Action<ResourceModuleConstruct> configureConstruct) :
AzureConstructResource(name, configureConstruct),
IResourceWithConnectionString,
IResourceWithEndpoints,
+ IResourceWithAzureFunctionsConfig
{
+ public void ApplyAzureFunctionsConfiguration(Dictionary<string, object> target, string? connectionName = null)
}
Usage Examples
// User Code
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage");
var queue = storage.AddQueues("queue");
var blob = storage.AddBlobs("blob");
var funcApp = builder.AddAzureFunctionsProject<Projects.AspirePlusFunctions_Functions>("myfuncapp")
.WithReference(queue)
.WithReference(blob);
builder.Build().Run();
// Framework Code
public class AzureFunctionsProjectResource(string name)
: ProjectResource(name), IResourceWithEnvironment, IResourceWithArgs, IResourceWithServiceDiscovery { }
public static class AzureFunctionsProjectResourceExtensions
{
public static IResourceBuilder<AzureFunctionsProjectResource> WithReference(this IResourceBuilder<AzureFunctionsProjectResource> builder, IResourceBuilder<IResourceWithAzureFunctionConfig> source, string? name = null)
{
return builder.WithEnvironment(delegate (EnvironmentCallbackContext context)
=> source.Resource.ApplyAzureFunctionsConfiguration(context.EnvironmentVariables, name));
}
Alternative Designs
- To future proof this interface, the type returned by the GetAzureFunctionsConfiguration is an opaque dictionary. The contract about what properties resources emit is informal between the target resources and the Functions app resource.
Risks
- Because the Azure Functions host is using the original configuration information to construct its own clients, it's not able to take advantage of the features that come with Aspire-injected resources. We think this is fine given the requirements of the Functions host.
- This interface is designed specifically for consumption by Azure Functions (because of the implied contract above) but is accessible by anyone. There's no inherent risks to this other than confusion and mismatched expectations.