AWS Developer Tools Blog
F# Tooling Support for AWS Lambda
F# is a functional language that runs on .NET and enables you to use packages written in other .NET languages, like the AWS SDK for .NET that’s written in C#. Today we have released a new version of the AWS Toolkit for Visual Studio 2017 with support for writing AWS Lambda functions in F#. These Lambda functions use the same .NET Core Lambda runtime that C# Lambda functions use.
Getting Started
After you update your toolkit version, you can get started creating an F# Lambda function by launching the New Project wizard in Visual Studio. Under the Visual F# node, you’ll see a new node for AWS Lambda. This node provides the same options as our C# project templates, where you have two sets of choices for project types. The first two choices enable you to create an AWS Lambda project for developing a single function for deployment directly to Lambda. The second set of options are for AWS serverless application projects, which enable you to develop multiple functions and deploy them with AWS CloudFormation and any other resources you define in your AWS CloudFormation template. For this post, let’s select the AWS Serverless Application project type, then click OK.
Next, we get a dialog box in which you select a blueprint of code to get started with your F# Lambda function. One of the most interesting blueprints released with the F# update is for running a Giraffe web application. Giraffe describes itself on its website as “a functional ASP.NET Core micro web framework for building rich web applications.” Because it’s built on top of ASP.NET Core, the same Amazon.Lambda.AspNetCoreServer NuGet package that we released to run ASP.NET Core applications on Lambda works for running Giraffe web applications on Lambda. Let’s select the Giraffe blueprint, then click Finish.
Because Giraffe is an ASP.NET Core-based project, you can run it locally just by pressing F5, like any other ASP.NET Core project. The project is also configured to be easily deployed to AWS as a serverless application using Lambda.
Let’s take a look at some of the files created in the project.
- AppHandlers.fs – Defines all of the HTTP handlers for the web application. The webApp function will route the incoming request to the appropriate handler function.
let indexHandler = fun (next : HttpFunc) (ctx : HttpContext) -> text "Serverless Giraffe Web API" next ctx let arrayExampleHandler (itemCount:int) = fun (next : HttpFunc) (ctx : HttpContext) -> let values = seq { for a in 1 .. itemCount do yield sprintf "value%i" a } text (String.concat ", " values) next ctx let webApp:HttpHandler = choose [ GET >=> choose [ route "/" >=> indexHandler route "/array" >=> arrayExampleHandler 2 routef "/array/%i" arrayExampleHandler ] setStatusCode 404 >=> text "Not Found" ]
- Setup.fs – Contains the usual ASP.NET Core startup code. It contains a main function that is the entry point when running the application locally, and an F# type that inherits from Amazon.Lambda.AspNetCore.Server.APIGatewayProxyFunction, just like our C# ASP.NET Core blueprints. This type provides the entry point when running from Lambda. Code in the main function isn’t executed when running the application in Lambda.
// --------------------------------- // This type is the entry point when running in Lambda. It has similar responsiblities // to the main entry point function that can be used for local development. // --------------------------------- type LambdaEntryPoint() = inherit Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction() override this.Init(builder : IWebHostBuilder) = let contentRoot = Directory.GetCurrentDirectory() builder .UseContentRoot(contentRoot) .Configure(Action configureApp) .ConfigureServices(configureServices) |> ignore // --------------------------------- // The main function is used for local development. // --------------------------------- [] let main _ = let contentRoot = Directory.GetCurrentDirectory() let webRoot = Path.Combine(contentRoot, "WebRoot") WebHostBuilder() .UseKestrel() .UseContentRoot(contentRoot) .UseIISIntegration() .UseWebRoot(webRoot) .ConfigureAppConfiguration(Action configureAppConfiguration) .Configure(Action configureApp) .ConfigureServices(configureServices) .ConfigureLogging(configureLogging) .Build() .Run() 0
- aws-lambda-tools-defaults.json – Contains default settings for deployment setup by the blueprint. When you deploy your application, you have the option to persist the settings in the wizard to this file to simplify subsequent deployments or easily switch to command line deployments, as we’ll show later in this post.
{ "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." ], "profile" : "default", "region" : "us-west-2", "configuration" : "Release", "framework" : "netcoreapp2.0", "s3-prefix" : "FSharpLambda/", "template" : "serverless.template" }
- serverless.template – An AWS CloudFormation template. In this case the template contains only one resource, an AWS::Serverless::Function. This resource defines a Lambda function and configures the event sources for the Lambda function, which in this case is Amazon API Gateway.
"AspNetCoreFunction" : { "Type" : "AWS::Serverless::Function", "Properties": { "Handler": "FSharpLambda::Setup+LambdaEntryPoint::FunctionHandlerAsync", "Runtime": "dotnetcore2.0", "CodeUri": "", "MemorySize": 256, "Timeout": 30, "Role": null, "Policies": [ "AWSLambdaFullAccess" ], "Environment" : { "Variables" : { } }, "Events": { "RootResource": { "Type": "Api", "Properties": { "Path": "/", "Method": "ANY" } }, "ProxyResource": { "Type": "Api", "Properties": { "Path": "/{proxy+}", "Method": "ANY" } } } } }
The Handler property indicates the .NET method to call in the package bundle. The string can be broken down to <assembly-name>::<type-name>::<function-name>. If you have used C# to develop Lambda functions, the ‘+’ in the handler string might be surprising. In this case, the LambdaEntryPoint type is defined inside an F# module, and a module, under the covers, is a static type. That means LambdaEntryPoint is an inner type to the Setup type, and to reference inner types with .NET reflection you need to use a ‘+’.
Deployment
Deploying the F# serverless application is the same as C#-based Lambda and serverless applications. Right-click the project in Solution Explorer, then select Publish to AWS Lambda to launch the deployment wizard. For serverless application-based projects, you just need to provide a name for the AWS CloudFormation stack and an Amazon S3 bucket to store the compiled and packaged-up F# project. Lambda fetches the deployed function code from the bucket.
After setting these values, choose Publish. Your F# serverless application will be built, packaged up, and deployed. When the AWS CloudFormation stack is complete, it will present the URL to the F# serverless application.
Command line tooling
For developers who are not using Visual Studio, we have tooling that integrates with the dotnet CLI that is provided as part of .NET Core. To create a project, simply install the Amazon.Lambda.Templates NuGet package by running the following command.
dotnet new -i Amazon.Lambda.Templates
Once this package is installed, the command dotnet new with no arguments will display all of the templates that are available. With the latest version of this library, many of these templates have been updated to support F#.
Templates Short Name Language Tags
----
Order Flowers Chatbot Tutorial lambda.OrderFlowersChatbot [C#] AWS/Lambda/Function
Lambda Detect Image Labels lambda.DetectImageLabels [C#], F# AWS/Lambda/Function
Lambda Empty Function lambda.EmptyFunction [C#], F# AWS/Lambda/Function
Lex Book Trip Sample lambda.LexBookTripSample [C#] AWS/Lambda/Function
Lambda Simple DynamoDB Function lambda.DynamoDB [C#], F# AWS/Lambda/Function
Lambda Simple Kinesis Firehose Function lambda.KinesisFirehose [C#] AWS/Lambda/Function
Lambda Simple Kinesis Function lambda.Kinesis [C#], F# AWS/Lambda/Function
Lambda Simple S3 Function lambda.S3 [C#], F# AWS/Lambda/Function
Lambda ASP.NET Core Web API serverless.AspNetCoreWebAPI [C#], F# AWS/Lambda/Serverless
Lambda ASP.NET Core Web Application with Razor Pages serverless.AspNetCoreWebApp [C#] AWS/Lambda/Serverless
Serverless Detect Image Labels serverless.DetectImageLabels [C#], F# AWS/Lambda/Serverless
Lambda DynamoDB Blog API serverless.DynamoDBBlogAPI [C#] AWS/Lambda/Serverless
Lambda Empty Serverless serverless.EmptyServerless [C#], F# AWS/Lambda/Serverless
Lambda Giraffe Web App serverless.Giraffe F# AWS/Lambda/Serverless
Serverless Simple S3 Function serverless.S3 [C#], F# AWS/Lambda/Serverless
Step Functions Hello World serverless.StepFunctionsHelloWorld [C#], F# AWS/Lambda/Serverless
Lambda Giraffe Web API serverless.GiraffeWebAPI F# AWS/Lambda/Serverless
Console Application console [C#], F#, VB Common/Console
Class library classlib [C#], F#, VB Common/Library
Unit Test Project mstest [C#], F#, VB Test/MSTest
xUnit Test Project xunit [C#], F#, VB Test/xUnit
ASP.NET Core Empty web [C#], F# Web/Empty
ASP.NET Core Web App (Model-View-Controller) mvc [C#], F# Web/MVC
ASP.NET Core Web App razor [C#] Web/MVC/Razor Pages
ASP.NET Core with Angular angular [C#] Web/MVC/SPA
ASP.NET Core with React.js react [C#] Web/MVC/SPA
ASP.NET Core with React.js and Redux reactredux [C#] Web/MVC/SPA
ASP.NET Core Web API webapi [C#], F# Web/WebAPI
global.json file globaljson Config
NuGet Config nugetconfig Config
Web Config webconfig Config
Solution File sln Solution
Razor Page page Web/ASP.NET
MVC ViewImports viewimports Web/ASP.NET
MVC ViewStart viewstart Web/ASP.NET
To create a Lambda project for F#, use the -lang F# switch. Here is an example to create an F# empty function project.
dotnet new lambda.EmptyFunction -lang F# -o FSharpBasicFunction --region us-west-2 --profile default
All of the projects created from Visual Studio or dotnet new have the Amazon.Lambda.Tools CLI tool configured in the project file.
<ItemGroup>
<DotNetCliToolReference Include="Amazon.Lambda.Tools" Version="2.1.3" />
</ItemGroup>
With this tool reference configured, you can perform deployments from within Visual Studio or at the command line with the dotnet CLI. Deployments from either environment will also pick up any settings made in the aws-lambda-tools-defaults.json file, so you don’t have to set them on the command line or in the IDE wizard. For a Lambda project, meaning a project that deploys a single function, use the deploy-function sub-command. For a serverless application like the previous Giraffe example, use the deploy-serverless sub-command. If you haven’t built your project yet, you need to first run dotnet restore to restore the Amazon.Lambda.Tools package, as follows.
dotnet lambda deploy-function MyFSharpFunction
Summary
We know the F# community is a very passionate one, and we’re excited to see what they build with F# and AWS Lambda. With the new F# project templates and the Visual Studio and command line deployment tools, we hope to make it easier to get started with F# and Lambda. We would love to hear from the F# community about their thoughts and ideas for AWS and F#. For any questions or issues, please reach out to us at our GitHub repository.