-
Notifications
You must be signed in to change notification settings - Fork 475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AWS CloudFormation Provisioning and SDK Configuration #1905
Conversation
@@ -3,6 +3,15 @@ | |||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<!-- AWS SDK for .NET dependencies --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to get these packages added to the Aspire NuGet feeds. Some of these are newer versions of packages that are already in there. Currently I'm testing with hacking the NuGet.config
to allow the public NuGet feed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these the versions you need?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently I'm testing with hacking the NuGet.config to allow the public NuGet feed.
Would it be possible to use the "hack" while the PR was in draft, and adding more dependencies? (and possibly updating new versions that are released). Then once the PR is ready for review, we can revert the nuget.config change, and mirror the necessary packages to the trusted feed.
.AddHttpClientInstrumentation() | ||
// Add instrumentation for the AWS .NET SDK. | ||
.AddAWSInstrumentation(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding the AWS .NET SDK Otel provider. Wonder how a component could possible add it without access to the TracerProviderBuilder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding the AWS .NET SDK Otel provider. Wonder how a component could possible add it without access to the TracerProviderBuilder.
Multiple calls to AddOpenTelemetry().WithTracing(t => ...)
compose. This is what our aspire components do.
What about a CDK based approach? That might gel better with the code focused model aspire has. |
What about for those who use Terraform or other IaC tooling? |
I don't view this as either CloudFormation or CDK. CloudFormation is the lowest level and is very common to use. So I wanted to tackle that first. I suspect we should also add CDK support as well but that would be a separate PR in my opinion. What we can learn here though is if the association of CloudFormation output parameter to projects works for users. CDK at the end of day is still CloudFormation so the association mechanism would be the same. |
You wouldn't use aspire for deployment. You would continue to use terraform (until somebody writes a manifest -> terraform translation layer). |
I would leave Terraform up to the fine folks at Hashicorp or the community. I know Terraform is popular but I have very little experience with it as I have a bias for AWS tools and services 😄. That being said if there are any extensions or mechanism we need to make to help a Terraform component I happy to help. |
OK I think I like this low-level model. High level comments:
|
playground/AWS/Frontend/Program.cs
Outdated
builder.Services.AddAWSMessageBus(messageBuilder => | ||
{ | ||
// Get the SQS queue URL that was created from AppHost and assigned to the project. | ||
var chatTopicArn = builder.Configuration.GetSection("AWS:Resources")["ChatTopicArn"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var chatTopicArn = builder.Configuration.GetSection("AWS:Resources")["ChatTopicArn"]; | |
var chatTopicArn = builder.Configuration["AWS:Resources:ChatTopicArn"]; |
This is one of the things we're going to have to wrangle with aspire. Passing connection information in a consistent way and consuming it in a consistent way is part of the opinionated stack and what makes it easy to use.
The low-level infrastructure is fine, but I hope we can build something a bit more like the other components use a single config section with a fixed schema per component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the code.
The configuration section will be hard to have a fixed schema since the output of a CloudFormation stack is dynamic to whatever the user defines in the template.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That breaks the ease of use in the application code. We want free form config to work as well. but part of the value of aspire beyond the orchestration is having a component on the other side that picks up this configuration and does something with it.
Consider this example that uses mysql in the app host:
var builder = DistributedApplication.CreateBuilder(args);
var db = builder.AddMySql("server").AddDatabase("db");
builder.AddProject<Projects.WebApplication1>("api")
.WithReference(db);
builder.Build().Run();
Then consumes it in the app using the same names:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.AddMySqlDbContext<AppDb>("db"); // pick up the db configuration
var app = builder.Build();
app.MapGet("/", async (AppDb db) =>
{
await db.Database.EnsureCreatedAsync();
db.Entries.Add(new Entry { Name = Guid.NewGuid().ToString() });
await db.SaveChangesAsync();
return await db.Entries.ToListAsync();
});
app.Run();
I removed the extra Here is an example of what the manifest looks like now. The references section is a list of objects instead of resource names to give myself room for future additions.
|
Yes! Also I can tell you that azd parses the env section for expressions and uses it to determine dependencies. It's more implicit, but it works well. In the above manifest, the "FrontEnd" resource reference's env section references "AspireSampleDevResources"'s outputs so there's a dependency between FrontEnd and AspireSampleDevResources.
|
* Use change sets which allows CloudFormation transforms to run * Handle stack status when status is not in a ready state but is recoverable * Separate CloudFormation provisioning logic from the Aspire update logic. * Add advanced CloudFormation options to the ICloudFormationTemplateResource * Include template parameter values when computing the SHA 256
tests/Aspire.Hosting.Tests/AWS/AWSCloudFormationResourceTests.cs
Outdated
Show resolved
Hide resolved
This PR is ready for review. The |
This comment was marked as resolved.
This comment was marked as resolved.
This should be fixed now. It builds locally for me. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Thank you @normj ! |
I’m looking for feedback on this approach that will help support provisioning AWS application resources and also configure service clients from the AWS SDK for .NET. I view these features as the minimum level to build higher components or integrations like CDK and deployments.
For .NET developer that use AWS, please let me know via 👍 or comments if you find this useful. Your feedback will help us understand if there is enough interest from our customers to prioritize more AWS Aspire work.
CloudFormation was chosen so that new AWS features will be available to Aspire components as they are released by AWS. This eliminates the need for Aspire components to be updated which could create a blocker for some customers. Granted sometimes there is a delay for CloudFormation support but that delay has gotten pretty small. Also using CloudFormation makes easy to tear all of the applications resources as a single unit.
SDK Service Client Config
To configure service clients you would define an
IAWSSDKConfig
and using fluent programming flow add the properties for service client. Currently the only thing supported is credential profile and region.The
WithReference
extension method forIResourceBuilder<ProjectResource>
is used to attach the SDK config to the project.During the startup of AppHost environment variables are applied to the project where AWSSDK.Extensions.NETCore.Setup used in the project will look for via IConfiguration.
This approach allows multiple configurations to be defined in the AppHost and assign them to the appropriate projects. I suspect it is common users will want a default/global
IAWSSDKConfig
that should be tied to all projects automatically unless the project was given a specificIAWSSDKConfig
. I haven't seen a way for an object likeIAWSSDKConfig
to be able to iterate through all other projects and apply environment variables. If there is a way I would appreciate hearing how.Provisioning
Provisioning is done by adding a CloudFormation template to the AppHost, either JSON or YAML. As part of the
IDistributedApplicationBuilder
inProgram.cs
you would add the CloudFormation provisioning using theAddAWSCloudFormationTemplate
. The first parameter is the name of the CloudFormation stack and the second is the CloudFormation template file used to create the stack.During AppHost startup a
IDistributedApplicationLifecycleHook
is used to check if the stack exists and if not create the stack and wait till the stack is complete which means the AWS resources are available for projects to use. Depending on the types and number of resources defined in the CloudFormation template this can take some time to allocation.To keep the F5 dev cycle quick a tag is assigned to the CloudFormation stack with a SHA256 of the CloudFormation template. During subsequent starts of the AppHost the SHA256 of the CloudFormation template is compared and if it is the same as the value in the tag it skips CloudFormation updates.
In a possible deployment story in the future I could see the CloudFormation template of applications resources be a nested stack in a deployment CloudFormation stack.
Associated AWS resources to a project
CloudFormation templates have output parameters that are often used to return the identifiers of resources to use in code. For example this CloudFormation template puts the identifiers for a queue and a topic as output parameters.
A CloudFormation stack can be associated to project using the
WithReference
extension method.This takes all of the output parameters from the stack and assigns them as environment variables that applications can pick up through IConfiguration. The default section in IConfiguration that
WithReference
puts the output parameters in isAWS:Resources
but can be overridden with theWithReference
method.Alternatively a single output parameter can be bound to a project via an environment variable.
Referencing existing stacks
The
AddAWSCloudFormationStack
is used to include the output parameters of an existing stack. This allows users to handle provision their resources outside of their AppHost but bind the outputs to projects using the sameWithReference
andWithEnvironment
methods. This is particular useful when developers do not have write permissions to CloudFormation.Sample
To demonstrate how these hosting components work and give me a playground to try things out I added a new Aspire AWS sample application. I'm a terrible frontend developer but the frontend has a page that I use basically as diagnostics to see the AppHost configuration filter through AppHost to the application.
The Message Publisher page is a sanity check that the service clients are working with the configs passed in.
AppHost
Here is the full code of the
Program.cs
for the AppHost using AWS to get an idea how you would use it.Example Startup
This animated gif gives a high level view of the experience this AWS component in the Aspire Dashboard.
Microsoft Reviewers: Open in CodeFlow