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.