Updating an AWS Lambda app to .NET 6.0
- 4 minutes read - 714 wordsThe long awaited support for .NET 6.0 on AWS Lambda has finally been released. My Twitter bot runs on .NET Core 3.1 in a Lambda function and I have been meaning to update it to .NET 6 for a while. So let’s do just that!
I’ll be following the steps on the announcement blog post to migrate to 6.0.
Project file
The following items need to be updated in the project file:
TargetFramework
tonet6.0
- If using Top Level Statements (see below), set the
OutputType
toExe
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType>
...
</PropertyGroup>
</Project>
CloudFormation template
The following items need to be updated in the CloudFormation template:
Runtime
todotnet6
- If using Top Level Statements (see below), set the Handler to the name of your assembly
...
Resources:
Retweet:
...
Properties:
Handler: DotNetTwitterBot
Runtime: dotnet6
CodeBuild script
I’m using AWS CodeBuild for CI, but the CodeBuild images do not currently support .NET 6, so I need to install it directly:
version: 0.2
phases:
install:
commands:
- /usr/local/bin/dotnet-install.sh --channel LTS
...
Note: I also updated my CodeBuild image to
aws/codebuild/standard:5.0
as this is the latest version.
NuGet packages
The Lambda NuGet packages also need to be updated the latest versions. In my case I was already on the latest version for Amazon.Lambda.Core, 2.1.0.
New features
Top Level Statements
.NET 6 introduces Top Level Statements which can now be used in Lambda functions to reduce boilerplate code. This can be enabled by being on .NET 6 and adding the following package:
dotnet add package Amazon.Lambda.RuntimeSupport
Previously, my function looked like:
using System;
using System.Linq;
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Microsoft.ML.OnnxRuntime;
using Tweetinvi;
using Tweetinvi.Models;
using Tweetinvi.Parameters;
namespace DotNetTwitterBot
{
public class Functions
{
static readonly string[] SearchTerms = new[]
{
"\".NET AND Framework\"",
"\".NET AND Core\"",
"\".NET AND 5\"",
"\".NET AND 6\"",
"\".NET AND 7\"",
"#dotnet",
"#dotnetcore",
"#dotnet5",
"#dotnet6",
"#dotnet7"
};
public async Task Retweet(ILambdaContext context)
{
var creds = await SecretHelper.GetSecretAsync();
Auth.SetUserCredentials(creds.ConsumerKey, creds.ConsumerSecret, creds.AccessToken, creds.AccessSecret);
var searchSince = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(31));
var me = User.GetAuthenticatedUser();
await SearchAndRetweetTweets(SearchTerms, searchSince, me);
static async Task SearchAndRetweetTweets(string[] terms, DateTime searchSince, IAuthenticatedUser me)
{
var query = string.Join(" OR ", terms);
var param = new SearchTweetsParameters(query)
{
Since = searchSince,
TweetSearchType = TweetSearchType.OriginalTweetsOnly,
Filters = TweetSearchFilters.Safe,
MaximumNumberOfResults = 1000
};
var tweets = await SearchAsync.SearchTweets(param);
var spamFilter = new SpamFilter("spam_filter.onnx");
var isSpam = spamFilter.Run(tweets.Select(t => $"RT @{t.CreatedBy.ScreenName} : {t.Text}")).ToArray();
var tasks = tweets.Select(async (t, i) => {
if (!isSpam[i])
await t.PublishRetweetAsync();
});
await Task.WhenAll(tasks);
}
}
}
}
After incorporating Top Level Statements, my function looks like:
using System;
using System.Linq;
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Amazon.Lambda.RuntimeSupport;
using DotNetTwitterBot;
using Tweetinvi;
using Tweetinvi.Models;
using Tweetinvi.Parameters;
var handler = async (ILambdaContext context) =>
{
string[] searchTerms = new[]
{
"\".NET AND Framework\"",
"\".NET AND Core\"",
"\".NET AND 5\"",
"\".NET AND 6\"",
"\".NET AND 7\"",
"#dotnet",
"#dotnetcore",
"#dotnet5",
"#dotnet6",
"#dotnet7"
};
var creds = await SecretHelper.GetSecretAsync();
Auth.SetUserCredentials(creds.ConsumerKey, creds.ConsumerSecret, creds.AccessToken, creds.AccessSecret);
var searchSince = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(31));
var me = User.GetAuthenticatedUser();
await SearchAndRetweetTweets(searchTerms, searchSince, me);
async Task SearchAndRetweetTweets(string[] terms, DateTime searchSince, IAuthenticatedUser me)
{
var query = string.Join(" OR ", terms);
var param = new SearchTweetsParameters(query)
{
Since = searchSince,
TweetSearchType = TweetSearchType.OriginalTweetsOnly,
Filters = TweetSearchFilters.Safe,
MaximumNumberOfResults = 1000
};
var tweets = await SearchAsync.SearchTweets(param);
var spamFilter = new SpamFilter("spam_filter.onnx");
var isSpam = spamFilter.Run(tweets.Select(t => $"RT @{t.CreatedBy.ScreenName} : {t.Text}")).ToArray();
var tasks = tweets.Select(async (t, i) => {
if (!isSpam[i])
await t.PublishRetweetAsync();
});
await Task.WhenAll(tasks);
}
};
await LambdaBootstrapBuilder.Create(handler).Build().RunAsync();
Much cleaner!
Other new features
There are lots of new features that come with .NET 6.0 that can be utilised in AWS Lambda, such as:
- Improved logging
- Using source generator for JSON serialization
- ASP.NET Core minimal APIs
Cool! That’s it for my app then. Let’s merge this into main and see how it goes!
Summary
In this post, I updated my Twitter bot app from .NET Core 3.1 to .NET 6.0. This was pretty straight forward, just updating the .NET version in a few files, adding/updating some NuGet packages and refactoring the function to support Top Level Statements. It was a very painless exercise and I recommend everyone do it for their apps; there’s lots of performance improvements in .NET 6.0 so if all of our code runs faster, that’s hopefully a lot of CO2 we could be saving!