Skip to content
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 Lambda Support #3866

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 45 additions & 0 deletions Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrleansContracts", "playgro
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Seq.Tests", "tests\Aspire.Seq.Tests\Aspire.Seq.Tests.csproj", "{0505F739-6F85-4502-A554-77E0D7325F26}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.AWS.AppHost", "src\Aspire.Hosting.AWS.AppHost\Aspire.Hosting.AWS.AppHost.csproj", "{0EBD9CF2-52C4-4F8E-92CD-7F72923C2574}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectMetadataGenerator", "src\Tools\ProjectMetadataGenerator\ProjectMetadataGenerator.csproj", "{4EBDDA75-6FE8-440F-B0FA-444EAACD813C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AWSLambda", "AWSLambda", "{9315E936-F9C2-4FA2-BDC9-61BF5C007427}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LambdaRestApi", "playground\AWSLambda\LambdaRestApi\LambdaRestApi.csproj", "{99F4B2EF-CD4A-4D7C-A35E-0C0593E0964C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibraryFunctions", "playground\AWSLambda\ClassLibraryFunctions\ClassLibraryFunctions.csproj", "{B9A382E9-14CE-4DAD-B76D-050CF2E2D65A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWSLambda.AppHost", "playground\AWSLambda\AWSLambda.AppHost\AWSLambda.AppHost.csproj", "{8F91C054-28F6-4D6A-B296-E33AD247D4B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExecutableLambdaHttpApi", "playground\AWSLambda\ExecutableLambdaHttpApi\ExecutableLambdaHttpApi.csproj", "{BD23172D-2B17-4736-9568-0A59290618C5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1179,6 +1193,30 @@ Global
{0505F739-6F85-4502-A554-77E0D7325F26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0505F739-6F85-4502-A554-77E0D7325F26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0505F739-6F85-4502-A554-77E0D7325F26}.Release|Any CPU.Build.0 = Release|Any CPU
{0EBD9CF2-52C4-4F8E-92CD-7F72923C2574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0EBD9CF2-52C4-4F8E-92CD-7F72923C2574}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0EBD9CF2-52C4-4F8E-92CD-7F72923C2574}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0EBD9CF2-52C4-4F8E-92CD-7F72923C2574}.Release|Any CPU.Build.0 = Release|Any CPU
{4EBDDA75-6FE8-440F-B0FA-444EAACD813C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EBDDA75-6FE8-440F-B0FA-444EAACD813C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EBDDA75-6FE8-440F-B0FA-444EAACD813C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EBDDA75-6FE8-440F-B0FA-444EAACD813C}.Release|Any CPU.Build.0 = Release|Any CPU
{99F4B2EF-CD4A-4D7C-A35E-0C0593E0964C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99F4B2EF-CD4A-4D7C-A35E-0C0593E0964C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99F4B2EF-CD4A-4D7C-A35E-0C0593E0964C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99F4B2EF-CD4A-4D7C-A35E-0C0593E0964C}.Release|Any CPU.Build.0 = Release|Any CPU
{B9A382E9-14CE-4DAD-B76D-050CF2E2D65A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9A382E9-14CE-4DAD-B76D-050CF2E2D65A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9A382E9-14CE-4DAD-B76D-050CF2E2D65A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9A382E9-14CE-4DAD-B76D-050CF2E2D65A}.Release|Any CPU.Build.0 = Release|Any CPU
{8F91C054-28F6-4D6A-B296-E33AD247D4B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F91C054-28F6-4D6A-B296-E33AD247D4B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F91C054-28F6-4D6A-B296-E33AD247D4B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F91C054-28F6-4D6A-B296-E33AD247D4B0}.Release|Any CPU.Build.0 = Release|Any CPU
{BD23172D-2B17-4736-9568-0A59290618C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD23172D-2B17-4736-9568-0A59290618C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD23172D-2B17-4736-9568-0A59290618C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD23172D-2B17-4736-9568-0A59290618C5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1393,6 +1431,13 @@ Global
{906B5687-31AD-4364-AD9F-B4B26113462D} = {8BAF2119-8370-4E9E-A887-D92506F8C727}
{75760E8A-7025-4F6A-B152-6622688D0E7D} = {8BAF2119-8370-4E9E-A887-D92506F8C727}
{0505F739-6F85-4502-A554-77E0D7325F26} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{0EBD9CF2-52C4-4F8E-92CD-7F72923C2574} = {B80354C7-BE58-43F6-8928-9F3A74AB7F47}
{4EBDDA75-6FE8-440F-B0FA-444EAACD813C} = {2136E31D-2CBB-41BB-8618-716FF8E46E9E}
{9315E936-F9C2-4FA2-BDC9-61BF5C007427} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{99F4B2EF-CD4A-4D7C-A35E-0C0593E0964C} = {9315E936-F9C2-4FA2-BDC9-61BF5C007427}
{B9A382E9-14CE-4DAD-B76D-050CF2E2D65A} = {9315E936-F9C2-4FA2-BDC9-61BF5C007427}
{8F91C054-28F6-4D6A-B296-E33AD247D4B0} = {9315E936-F9C2-4FA2-BDC9-61BF5C007427}
{BD23172D-2B17-4736-9568-0A59290618C5} = {9315E936-F9C2-4FA2-BDC9-61BF5C007427}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DCEDFEC-988E-4CB3-B45B-191EB5086E0C}
Expand Down
7 changes: 7 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
<PackageVersion Include="AWS.Messaging" Version="0.9.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AWS" Version="1.1.0-beta.3" />
<PackageVersion Include="OpenTelemetry.Extensions.AWS" Version="1.3.0-beta.1" />
<PackageVersion Include="Amazon.Lambda.Core" Version="2.2.0" />
<PackageVersion Include="Amazon.Lambda.SQSEvents" Version="2.2.0" />
<PackageVersion Include="Amazon.Lambda.S3Events" Version="3.1.0" />
<PackageVersion Include="Amazon.Lambda.AspNetCoreServer.Hosting" Version="1.7.0" />
<PackageVersion Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.1" />
<PackageVersion Include="Amazon.Lambda.RuntimeSupport" Version="1.10.0" />
<PackageVersion Include="Amazon.Lambda.ApiGatewayEvents" Version="2.7.0" />
<!-- Azure SDK for .NET dependencies -->
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.16" />
<PackageVersion Include="Azure.Data.Tables" Version="12.8.3" />
Expand Down
23 changes: 23 additions & 0 deletions playground/AWSLambda/AWSLambda.AppHost/AWSLambda.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(SharedDir)KnownResourceNames.cs" Link="KnownResourceNames.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Aspire.Hosting.AWS.AppHost\Aspire.Hosting.AWS.AppHost.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\..\..\src\Aspire.Hosting.AppHost\Aspire.Hosting.AppHost.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\ClassLibraryFunctions\ClassLibraryFunctions.csproj" />
<ProjectReference Include="..\ExecutableLambdaHttpApi\ExecutableLambdaHttpApi.csproj" />
<ProjectReference Include="..\LambdaRestApi\LambdaRestApi.csproj" />
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions playground/AWSLambda/AWSLambda.AppHost/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<!-- NOTE: This line is only required because we are using P2P references, not NuGet. It will not exist in real apps. -->
<Import Project="../../../src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.props" />
<Import Project="../../../src/Aspire.Hosting.AWS.AppHost/build/Aspire.Hosting.AWS.AppHost.props" />

<PropertyGroup>
<MetadataGeneratorPath>$(OutputPath)/../../ProjectMetadataGenerator/Debug/net8.0/ProjectMetadataGenerator.dll</MetadataGeneratorPath>
</PropertyGroup>
</Project>
10 changes: 10 additions & 0 deletions playground/AWSLambda/AWSLambda.AppHost/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />

<!-- NOTE: These lines are only required because we are using P2P references, not NuGet. They will not exist in real apps. -->
<Import Project="..\..\..\src\Aspire.Hosting.AppHost\build\Aspire.Hosting.AppHost.targets" />
<Import Project="..\..\..\src\Aspire.Hosting.AWS.AppHost\build\Aspire.Hosting.AWS.AppHost.targets" />
<Import Project="..\..\..\src\Aspire.Hosting.Sdk\SDK\Sdk.targets" />

</Project>
29 changes: 29 additions & 0 deletions playground/AWSLambda/AWSLambda.AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var builder = DistributedApplication.CreateBuilder(args)
.AddAWSLambdaToolsSupport();

// builder.DisableMockToolLambda();

builder.AddLambdaFunction<LambdaFunctions.LambdaRestApi>("api");

builder.AddLambdaFunction<LambdaFunctions.ExecutableLambdaHttpApi>("executableApi", config =>
{
// Mock Tool Config (Optional).

// config.Disabled = true;
// config.Port = 8090;
// config.EnableLaunchWindow = true;
});

builder.AddLambdaFunction<LambdaFunctions.ClassLibraryFunctions_OrderFunction_HandleAsync>("orderFunction", _ =>
{
// When Class Library, Mock Tool Config must be set in the first Lambda Function created for the project.
})
.SetAsDefaultFunctionInProject();
builder.AddLambdaFunction<LambdaFunctions.ClassLibraryFunctions_EmailFunction_InformCustomer>("sendEmail")
.WithEnvironment("SEND_FROM","no-reply@example.com");
builder.AddLambdaFunction<LambdaFunctions.ClassLibraryFunctions_Authorizer_Authorize>("authorizer");
builder.AddLambdaFunction<LambdaFunctions.ClassLibraryFunctions_Functions_S3Function_Handle>("s3Function");

// builder.AddLambdaFunction("nodeFunction", LambdaRuntime.Custom("nodejs20.x"), "index.handler", "./node-app");

builder.Build().Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15232",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16069"
}
},
"generate-manifest": {
"commandName": "Project",
"commandLineArgs": "--publisher manifest --output-path ./aspire-manifest.json --aws-lambda-tools-create-missing true",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:15069",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16216"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions playground/AWSLambda/AWSLambda.AppHost/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
43 changes: 43 additions & 0 deletions playground/AWSLambda/AWSLambda.AppHost/aspire-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"resources": {
"api": {
"type": "aws.lambda.function.v0",
"path": "../LambdaRestApi/LambdaRestApi.csproj",
"handler": "MyRestApi",
"runtime": "dotnet8"
},
"executableApi": {
"type": "aws.lambda.function.v0",
"path": "../ExecutableLambdaHttpApi/ExecutableLambdaHttpApi.csproj",
"handler": "ExecutableLambdaHttpApi",
"runtime": "dotnet8"
},
"orderFunction": {
"type": "aws.lambda.function.v0",
"path": "../ClassLibraryFunctions/ClassLibraryFunctions.csproj",
"handler": "ClassLibraryFunctions::ClassLibraryFunctions.OrderFunction::HandleAsync",
"runtime": "dotnet8"
},
"sendEmail": {
"type": "aws.lambda.function.v0",
"path": "../ClassLibraryFunctions/ClassLibraryFunctions.csproj",
"handler": "ClassLibraryFunctions::ClassLibraryFunctions.EmailFunction::InformCustomer",
"runtime": "dotnet8",
"env": {
"SEND_FROM": "no-reply@example.com"
}
},
"authorizer": {
"type": "aws.lambda.function.v0",
"path": "../ClassLibraryFunctions/ClassLibraryFunctions.csproj",
"handler": "ClassLibraryFunctions::ClassLibraryFunctions.Authorizer::Authorize",
"runtime": "dotnet8"
},
"s3Function": {
"type": "aws.lambda.function.v0",
"path": "../ClassLibraryFunctions/ClassLibraryFunctions.csproj",
"handler": "ClassLibraryFunctions::ClassLibraryFunctions.Functions.S3Function::Handle",
"runtime": "dotnet8"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace ClassLibraryFunctions;
26 changes: 26 additions & 0 deletions playground/AWSLambda/ClassLibraryFunctions/Authorizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;

namespace ClassLibraryFunctions;

public class Authorizer
{
public APIGatewayCustomAuthorizerResponse Authorize(APIGatewayCustomAuthorizerRequest request,
ILambdaContext context)
{
return new APIGatewayCustomAuthorizerResponse
{
PrincipalID = "Aspire",
PolicyDocument = new APIGatewayCustomAuthorizerPolicy
{
Statement =
[
new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement { Effect = "Allow" }
]
},
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AWSProjectType>Lambda</AWSProjectType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Amazon.Lambda.SQSEvents" />
<PackageReference Include="Amazon.Lambda.S3Events" />
<PackageReference Include="Amazon.Lambda.Core" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" />
<PackageReference Include="Amazon.Lambda.ApiGatewayEvents" />
</ItemGroup>

<ItemGroup>
<None Remove="aws-lambda-tools-authorizer.json" />
<Content Include="aws-lambda-tools-authorizer.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Remove="aws-lambda-tools-defaults.json" />
<Content Include="aws-lambda-tools-defaults.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Remove="aws-lambda-tools-s3Function.json" />
<Content Include="aws-lambda-tools-s3Function.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Remove="aws-lambda-tools-sendEmail.json" />
<Content Include="aws-lambda-tools-sendEmail.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
21 changes: 21 additions & 0 deletions playground/AWSLambda/ClassLibraryFunctions/EmailFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Amazon.Lambda.Core;

namespace ClassLibraryFunctions;

public class EmailFunction
{
public Task<string> InformCustomer(SendEmail value, ILambdaContext context)
{
return Task.FromResult($"Email has been sent to {value.Email}");
}
}

public class SendEmail
{
public required string Email { get; set; }
public required string Subject { get; set; }
public required string Body { get; set; }
}
14 changes: 14 additions & 0 deletions playground/AWSLambda/ClassLibraryFunctions/Functions/S3Function.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Amazon.Lambda.S3Events;

namespace ClassLibraryFunctions.Functions;

public class S3Function
{
public string Handle(S3Event @event)
{
return "S3 Operation Complete";
}
}
11 changes: 11 additions & 0 deletions playground/AWSLambda/ClassLibraryFunctions/OrderFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Amazon.Lambda.SQSEvents;

namespace ClassLibraryFunctions;

public class OrderFunction
{
public Task<string> HandleAsync(SQSEvent @event)
{
return Task.FromResult("Order is now processed");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"information": [
"This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
"To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
"dotnet lambda help",
"All the command line options for the Lambda command can be specified in this file."
],
"configuration": "Release",
"function-memory-size": 512,
"function-timeout": 30,
"function-handler": "ClassLibraryFunctions::ClassLibraryFunctions.Authorizer::Authorize",
"function-runtime": "dotnet8"
}