/
CloudFormationExtensions.cs
164 lines (146 loc) · 7.77 KB
/
CloudFormationExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Amazon.CloudFormation;
using Amazon.CloudFormation.Model;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.AWS.CloudFormation;
using Aspire.Hosting.Lifecycle;
using Microsoft.Extensions.Logging;
namespace Aspire.Hosting;
/// <summary>
/// Extension methods for adding AWS CloudFormation as a provisioning resource.
/// </summary>
public static class CloudFormationExtensions
{
/// <summary>
/// Add a CloudFormation stack for provisioning application resources.
/// </summary>
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
/// <param name="stackName">The name of the CloudFormation stack.</param>
/// <param name="templatePath">The path to the CloudFormation template that defines the CloudFormation stack.</param>
/// <returns></returns>
public static IResourceBuilder<ICloudFormationTemplateResource> AddAWSCloudFormationTemplate(this IDistributedApplicationBuilder builder, string stackName, string templatePath)
{
var resource = new CloudFormationTemplateResource(stackName, templatePath);
var cfBuilder = builder.AddResource(resource)
.WithManifestPublishingCallback(resource.WriteToManifest);
builder.Services.TryAddLifecycleHook<CloudFormationProvisioner>();
return cfBuilder;
}
/// <summary>
/// Add parameters to be provided to CloudFormation when creating the stack for the template.
/// </summary>
/// <param name="builder"></param>
/// <param name="parameterName">Name of the CloudFormation parameter.</param>
/// <param name="parameterValue">Value of the CloudFormation parameter.</param>
/// <returns></returns>
public static IResourceBuilder<ICloudFormationTemplateResource> WithParameter(this IResourceBuilder<ICloudFormationTemplateResource> builder, string parameterName, string parameterValue)
{
builder.Resource.AddParameter(parameterName, parameterValue);
return builder;
}
/// <summary>
/// Add a CloudFormation stack for provisioning application resources.
/// </summary>
/// <param name="builder"></param>
/// <param name="stackName">The name of the CloudFormation stack.</param>
/// <returns></returns>
public static IResourceBuilder<ICloudFormationStackResource> AddAWSCloudFormationStack(this IDistributedApplicationBuilder builder, string stackName)
{
var resource = new CloudFormationStackResource(stackName);
var cfBuilder = builder.AddResource(resource)
.WithManifestPublishingCallback(resource.WriteToManifest);
builder.Services.TryAddLifecycleHook<CloudFormationProvisioner>();
return cfBuilder;
}
/// <summary>
/// Gets a reference to a output from the CloudFormation stack.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="name">Name of the output.</param>
/// <returns>A <see cref="StackOutputReference"/> that represents the output.</returns>
public static StackOutputReference GetOutput(this IResourceBuilder<ICloudFormationResource> builder, string name)
{
return new StackOutputReference(name, builder.Resource);
}
/// <summary>
/// Adds an environment variable to the resource with the value of the output from the CloudFormation stack.
/// </summary>
/// <typeparam name="T">The resource type.</typeparam>
/// <param name="builder">The resource builder.</param>
/// <param name="name">The name of the environment variable.</param>
/// <param name="stackOutputReference">The reference to the CloudFormation stack output.</param>
/// <returns>An <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<T> WithEnvironment<T>(this IResourceBuilder<T> builder, string name, StackOutputReference stackOutputReference)
where T : IResourceWithEnvironment
{
return builder.WithEnvironment(async ctx =>
{
if (ctx.ExecutionContext.IsPublishMode)
{
ctx.EnvironmentVariables[name] = stackOutputReference.ValueExpression;
return;
}
ctx.Logger?.LogInformation("Getting CloudFormation stack output {Name} from resource {ResourceName}", stackOutputReference.Name, stackOutputReference.Resource.Name);
ctx.EnvironmentVariables[name] = await stackOutputReference.GetValueAsync(ctx.CancellationToken).ConfigureAwait(false) ?? "";
});
}
/// <summary>
/// Override the CloudFormation service client the ICloudFormationStackResource would create to interact with the CloudFormation service. This can be used for pointing the
/// CloudFormation service client to a non-standard CloudFormation endpoint like an emulator.
/// </summary>
/// <param name="builder"></param>
/// <param name="cloudFormationClient">The AWS CloudFormation service client.</param>
public static IResourceBuilder<TDestination> WithReference<TDestination>(this IResourceBuilder<ICloudFormationResource> builder, IAmazonCloudFormation cloudFormationClient)
where TDestination : ICloudFormationResource
{
builder.Resource.CloudFormationClient = cloudFormationClient;
return (IResourceBuilder<TDestination>)builder;
}
/// <summary>
/// Add a reference of a CloudFormations stack to a project. The output parameters of the CloudFormation stack are added to the project IConfiguration.
/// </summary>
/// <typeparam name="TDestination"></typeparam>
/// <param name="builder"></param>
/// <param name="cloudFormationResourceBuilder">The CloudFormation resource.</param>
/// <param name="configSection">The config section in IConfiguration to add the output parameters.</param>
/// <param name="filter"></param>
/// <param name="keySelector"></param>
/// <returns></returns>
public static IResourceBuilder<TDestination> WithReference<TDestination>(this IResourceBuilder<TDestination> builder, IResourceBuilder<ICloudFormationResource> cloudFormationResourceBuilder, string configSection = "AWS::Resources",
Func<Output, bool>? filter = null,
Func<Output, string>? keySelector = null)
where TDestination : IResourceWithEnvironment
{
cloudFormationResourceBuilder.WithAnnotation(new CloudFormationReferenceAnnotation(builder.Resource));
builder.WithEnvironment(async context =>
{
if (context.ExecutionContext.IsPublishMode)
{
return;
}
if (cloudFormationResourceBuilder.Resource.ProvisioningTaskCompletionSource is not null)
{
context.Logger?.LogInformation("Waiting on CloudFormation resource {Name} ...", cloudFormationResourceBuilder.Resource.Name);
await cloudFormationResourceBuilder.Resource.ProvisioningTaskCompletionSource.Task.WaitAsync(context.CancellationToken).ConfigureAwait(false);
}
if (cloudFormationResourceBuilder.Resource.Outputs == null)
{
return;
}
configSection = configSection.Replace(':', '_');
var outputs = cloudFormationResourceBuilder.Resource.Outputs;
if (filter != null)
{
outputs = outputs.Where(filter).ToList();
}
foreach (var output in outputs)
{
var key = keySelector != null ? keySelector(output) : output.OutputKey;
var envName = $"{configSection}__{key}";
context.EnvironmentVariables[envName] = output.OutputValue;
}
});
return builder.WithAWSSDKConfig(cloudFormationResourceBuilder);
}
}