こんにちは、エンジニアのyokomachiです。
この記事は、ギークフィードアドベントカレンダー2024の9日目の記事です。
ギークフィードの有志が毎日ブログを投稿しているので是非チェックしてみてください。
さて、皆さんはLambda、書いていますか?
私は以前C#を扱っていたこともあり、数年前まではラムダといえばC#のラムダ式だったのですが、最近は専らAWS Lambda関数を書いています。
というわけで今回は、AWS Lambda関数をC#で実装しつつラムダ式を書いて、Lambda on Lambdaをしようという一発ネタとなります。
とはいえ一応、C#(.NET 8)でAWS Lambda関数を書くまでの手順(2024年12月時点)は詳細に書いていきたいと思います。
目次
早速LambdaコンソールでC#関数を作ってみる
早速AWS LambdaでC#(.NET 8)を指定して関数を作成します。
まずはHello world目的でLambdaコンソールでコードを書こうとしてみましたが…
「コードエディタは .NET 8 (C#/F#/PowerShell) ランタイムをサポートしていません。」…!
というわけでローカルの開発環境を作っていきましょう
ローカル開発環境の構築
C#のIDEには一般にVisual Studio(not Visual Studio Code)が推奨されています。
ボタンポチポチで様々なサンプルやテンプレートをベースにしたプロジェクトを作成することができたり、
最初からテストコードのプロジェクトがついてきたり、高機能なデバッガを利用できたりと、確かにC#というか.NETの開発環境としては非常にリッチです。
ただ今回のように単純なLambda関数を作るだけの場合Visual Studioは大げさすぎるので、VS Codeで開発環境を構築してみたいと思います。
今回使用する環境情報は以下の通りです。
OS, エディタ以外のインストールは今回の記事でやっていきます。
OS/エディタ /パッケージ |
バージョン | 概要 |
---|---|---|
Windows 11 Home | 23H2 | OS |
Visual Studio Code | 1.95.3 | エディタ |
.NET 8.0 | .NET SDK 8.0.404 .NET Runtime 8.0.11 ASP.NET Core Runtime 8.0.11 .NET Windows Desktop Runtime 8.0.11 |
ランタイムやSDK、CLIなど |
.NET Lambda Template | 7.3.0 | AWS Lambda用の.NET プロジェクトテンプレート |
.NET Lambda Global CLI | 5.12.0 | Lambdaプロジェクトの作成やデプロイに使用するCLIツール |
C# for Visual Studio Code | v2.55.29 | C#用のVS Code拡張 |
.NET 8.0のインストール
現在AWS Lambdaでサポートされている.NETの最新バージョンは .NET 8なので、.NET 8.0のSDKをインストールします。
https://dotnet.microsoft.com/ja-jp/download/dotnet
今回はWindowsでの開発になるので画像赤枠をクリックしてインストーラーをダウンロードします。
インストール後、ターミナルでバージョンを確認します。
1 2 3 |
> dotnet --version 8.0.404 |
以上でインストールは完了です。
.NETランタイムの名称は若干複雑なので以下にまとめます。
- .NET Framework
- .NETランタイムのWindows専用バージョン
- .NET Framework 4.8(.1)が最終バージョン
- より細かく2.0/3.0/3.5系と4系に分類されることも。
- .NET Core
- .NET Frameworkをベースにオープンソース化し、Windows以外のプラットフォームにも対応
- .NET Core 3.1が最終バージョン
- .NET
- .NET Core 3.1の次バージョンが.NET 5.0としてリリースされる(4.0はスキップ)
- その後、.NET 6.0, 7.0, 8.0, 9.0と続々リリース
- ので、今の最新バージョンはこの.NETの名前で提供されている
私が以前C#を書いていたときはレガシーなWindows環境での開発だったので、 .NET Coreどころか .NET Framework 3.5だったと記憶しています。
.NET Lambda Templateのインストール
AWS Lambda用の.NET プロジェクトテンプレートをインストールします。
1 2 |
> dotnet new install Amazon.Lambda.Templates |
.NET Lambda Global CLIのインストール
Lambdaプロジェクトの作成やデプロイに使用するCLIツールをインストールします。
1 2 |
> dotnet tool install -g Amazon.Lambda.Tools |
C# for Visual Studio Codeのインストール
VS Code向けのC#拡張をインストールします。
C# Dev Kitという拡張もありますが、こちらはVisual Studioのライセンス下で提供されるさらにリッチな拡張機能となっています。
今回はC# Dev Kitを使う予定がないのでC#拡張のみインストールします。
プロジェクト作成
プロジェクトの作成
ローカルの開発環境が整ったので、実際に.NETプロジェクトを作成してサンプルを見ていきます。
まずはテンプレートを使用してプロジェクトを作成します。
1 2 |
> dotnet new lambda.EmptyFunction --name {project_name} |
作成したプロジェクトはこんな構成になっています。
src/{project_name}
Function.cs: Lambda関数のハンドラーとなるコード
aws-lambda-tools-defaults.json: Lambda関数のデプロイ時に指定するCLIオプション
hello_CSharp_Lambda.csproj: プロジェクトを構成するファイルとアセンブリのリスト
test/{project_name}
テストコードのテンプレート
コードを書く
まずは初期状態のFunction.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 |
using Amazon.Lambda.Core; // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace hello_CSharp_Lambda; public class Function { /// <summary> /// A simple function that takes a string and does a ToUpper /// </summary> /// <param name="input">The event for the Lambda function handler to process.</param> /// <param name="context">The ILambdaContext that provides methods for logging and describing the Lambda environment.</param> /// <returns></returns> public string FunctionHandler(string input, ILambdaContext context) { return input.ToUpper(); } } |
.NETでAWS Lambdaを書く場合、クラスライブラリアプローチと実行可能アセンブリアプローチの2種類から選択して関数をコーディングすることができます。
詳しくは公式ドキュメントを参照ください。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/csharp-handler.html#csharp-handler-execution
初期状態はクラスライブラリアプローチのサンプルとなっています。
FunctionHandlerメソッドの第一引数にevent、第二引数にcontextが渡されます。
現状だとインプットとして文字列を受け、それを大文字にして返すというサンプルが記述されています。
これを今回の検証用に書き換えてみましょう(FunctionHandlerのみ抜粋)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public object FunctionHandler(object input, ILambdaContext context) { // inputログ出力 context.Logger.LogInformation($"input: {input}"); // contextログ出力 context.Logger.LogInformation($"Function name: {context.FunctionName}"); context.Logger.LogInformation($"Remaining time: {context.RemainingTime}"); context.Logger.LogInformation($"Memory limit: {context.MemoryLimitInMB}MB"); // デリゲートprocessInput:objectを引数にとり、文字列化&大文字にしてstringで返す Func<object, string> processInput = (obj) => $"Processed input: {obj?.ToString()?.ToUpper() ?? "NULL"}"; // デリゲートprocessCalc:2つのintを引数にとり、足し算をしてintで返す Func<int, int, int> processCalc = (x, y) => x + y; return new { ProcessedResult = processInput(input), processedCalc = processCalc(3, 5) }; } |
以下の2点を追加しました。
- inputの内容やcontextのFunctionNameなどをログ出力しています。
- ラムダ式を代入した2つのデリゲートを追加しました。
- objectを引数にとり、それを文字列化&大文字にしてstringで返すprocessInput
- 2つのintを引数にとり、足し算をしてintで返すprocessCalc
C#のラムダ式は簡単に言うと匿名メソッドを定義する記法のことです。
この匿名メソッドをデリゲートというメソッドの変数みたいなものに代入して、ほかのメソッドに渡したりLINQ操作を行ったりと便利に使えるというものです。
1 2 3 4 5 6 |
// これがラムダ式 (x, y) => x + y; // これがラムダ式で定義した匿名関数をデリゲートに代入するコード Func<int, int, int> add = (x, y) => x + y; // デリゲートを呼び出すときはこんな感じ int result = add(3, 5); |
以上でサンプルのLambda関数の作成が完了しました。次節でデプロイをしていきます。
デプロイ
CLIオプションの設定
aws-lambda-tools-defaults.jsonにデプロイ時のオプションを記述します。
- profileにAWS CLIを使用するときのProfile名を指定します。
- regionは今回ap-northeast-1を指定します。
- デフォルトだとfunction-memory-sizeが512MBになっていたので、128MBに落とします。
上記以外のオプションは初期のままです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "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": "{AWS CLI Profile}", "region": "ap-northeast-1", "configuration": "Release", "function-architecture": "x86_64", "function-runtime": "dotnet8", "function-memory-size": 128, "function-timeout": 30, "function-handler": "hello_CSharp_Lambda::hello_CSharp_Lambda.Function::FunctionHandler" } |
デプロイコマンド
ターミナルから以下のコマンドでデプロイを行います。
1 2 3 |
> cd src/{project_name} # csprojのあるディレクトリに移動 > dotnet lambda deploy-function {project_name} # デプロイ |
こんな感じで?デプロイプロセスが動きます
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 |
# コマンド実行 > dotnet lambda deploy-function hello_CSharp_Lambda Amazon Lambda Tools for .NET Core applications (5.12.0) Project Home: <https://github.com/aws/aws-extensions-for-dotnet-cli>, <https://github.com/aws/aws-lambda-dotnet> Executing publish command ... invoking 'dotnet publish', working folder 'D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\bin\\Release\\net8.0\\publish' ... dotnet publish "D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda" --output "D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\bin\\Release\\net8.0\\publish" --configuration "Release" --framework "net8.0" /p:GenerateRuntimeConfigurationFiles=true --runtime linux-x64 --self-contained False ... publish: 復元対象のプロジェクトを決定しています... ... publish: D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\hello_CSharp_Lambda.csproj を復元しました (4.06 秒)。 ... publish: hello_CSharp_Lambda -> D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\bin\\Release\\net8.0\\linux-x64\\hello_CSharp_Lambda.dll ... publish: hello_CSharp_Lambda -> D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\bin\\Release\\net8.0\\publish\\ Zipping publish folder D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\bin\\Release\\net8.0\\publish to D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\bin\\Release\\net8.0\\hello_CSharp_Lambda.zip ... zipping: Amazon.Lambda.Core.dll ... zipping: Amazon.Lambda.Serialization.SystemTextJson.dll ... zipping: hello_CSharp_Lambda.deps.json ... zipping: hello_CSharp_Lambda.dll ... zipping: hello_CSharp_Lambda.pdb ... zipping: hello_CSharp_Lambda.runtimeconfig.json Created publish archive (D:\\work\\Workshops\\hello_CSharp_Lambda\\src\\hello_CSharp_Lambda\\bin\\Release\\net8.0\\hello_CSharp_Lambda.zip). Creating new Lambda function hello_CSharp_Lambda # どのIAMロールを付与するか聞かれたので新規作成を選択 Select IAM Role that to provide AWS credentials to your code: 1) *** (中略) 10) *** Create new IAM Role *** > 10 # IAMロールの名前を入力 Enter name of the new IAM Role: > hello_CSharp_Lambda # IAMロールにアタッチするポリシーを選択 Select IAM Policy to attach to the new role and grant permissions 1) AWSLambdaExecute (Provides Put, Get access to S3 and full access to CloudWatch Logs.) 2) AWSLambdaInvocation-DynamoDB (Provides read access to DynamoDB Streams.) 3) AWSLambdaRole (Default policy for AWS Lambda service role.) 4) AWSLambdaBasicExecutionRole (Provides write permissions to CloudWatch Logs.) 5) AWSLambdaDynamoDBExecutionRole (Provides list and read access to DynamoDB streams and writ ...) (中略) 21) *** No policy, add permissions later *** > 4 Waiting for new IAM Role to propagate to AWS regions ............... Done New Lambda function created |
AWSコンソールで確認
デプロイプロセスの完了後、AWSコンソールで無事Lambda関数がデプロイされていることを確認できました。
最後にテスト実行をしてみましょう。
Lambda関数の戻り値として2つのデリゲートの実行結果が出力されています。
また、ログ出力部分にもinput, contextの内容が出力されていることが確認できます。
おわりに
というわけでLambda on Lambda無事達成しました。
実際のところ、C#でLambda関数を書く機会は今後もあまりないとは思いますが、久々にちょっとだけC#触ることができて楽しかったです。
今回作成したサンプルコードは以下に置いてあります。
https://github.com/n-yokomachi/hello_CSharp_Lambda
参考
C# による Lambda 関数の構築 – AWS Lambda
【準備編】.NET6.0でAWS Lambda開発 | Engr w/ a Tie.
Visual Studioの無いWindows環境でC# Lambdaの開発環境を整える | DevelopersIO
- Amazon ConnectでNGワードをリアルタイムに検知してSlackに通知する - 2024-12-16
- AWS Amplify AI KitでAIチャットアプリを爆速で作ってみる - 2024-12-14
- AWS LambdaをC#で実装する(ついでにラムダ式を書く) - 2024-12-09
- Amazon Connect Tasksの手動作成と自己割り当て、タスクテンプレートを試してみる - 2024-12-07
- Remix SPAモードで静的WebサイトをGithub Pagesにデプロイする - 2024-11-05