※本記事はPowerShell Advent Calendar 2018の24日目になります。
2018年9月にAWS LambdaでPowerShell Coreがサポートされたとリリースが出ました。
AWS Lambda は PowerShell Core をサポートします
今回は環境準備からHelloWorldまでをやってみたいと思います。 一応ですがPowerShell Coreがサポートされたのであって、Windows PowerShellはまったく関係ないのであしからず。
- Windows 10
- PSVersion 6.1.1
AWSのブログ、基本的にこの通りにやればうまくいく。
AWS Lambdaの開発者ドキュメント。こちらもわかりやすく解説されています。
LambdaでPowerShell Coreを利用するために下記のモジュールとSDKのインストールを行います。 そもそもPowerShell Coreをインストールしていない場合はPowerShell Coreのインストールから行ってください。
必要となるモジュールについてはPowerShell Galleryで公開されているため下記コマンドでインストールできます。 ※公式ブログ記事とかだとインストールのスコープがCurrentUserになってますが、そこらへんはお好みで。
Install-Module -Name AWSLambdaPSCore
Install-Module -Name AWSPowerShell.NetCore
AWSLambdaPSCoreモジュールでは下記のようなコマンドが用意されているようです。
コマンドについてはAWSLambdaPSCore モジュールを使用するに説明があります。
余談ですがPublish‑AWSPowerShellLambda
コマンドでLambdaにpublishする際に、内部的にはaws/aws-extensions-for-dotnet-cliを利用しており。
コマンド実行時にdotnet tool install -g Amazon.Lambda.Tools
コマンドで .NET Core グローバル ツール(Amazon.Lambda.Tools)のインストールやらpublishが行われているようです。
AWSLambdaPSCoreモジュールを動かすのに.NET Core SDKが必要な理由はこれがあるかららしい。
まずはImport-Module AWSLambdaPSCore
でモジュールをインポート。
Get-AWSPowershellLambdaTemplate
コマンドで用意されているテンプレートにどんな物があるか確認してみます。
いろいろと用意されているようですが、HelloWorldなのでBasicなテンプレートを利用してみます。
New-AWSPowerShellLambda
コマンドでBasicテンプレートのファイルを生成します。
ファイルが生成されています。
生成されたps1ファイルの中身をみると下記のようになっています。 このサンプルファイルの中身を読んで、どうなっているのか確認していきます。
サンプルスクリプトやドキュメントを読んでみると特殊な変数が用意されている事がわかります。
Variable | Description |
---|---|
$LambdaInput |
Lambdaへの入力データが格納されるオブジェクト。 |
$LambdaContext |
実行しているLambda環境についての情報が格納される。(Amazon.Lambda.Core.ILambdaContext) |
$LambdaContext
の詳細については下記が詳しい。
PowerShell の AWS Lambda Context オブジェクト
戻り値については下記のように書かれています。
The last item in the PowerShell pipeline will be returned as the result of the Lambda function.
どうやら出力ストリームの最後のアイテムを戻り値として返してそうです。
AWSLambdaPSCore モジュールを使用するに下記のようなサンプルがあり、これで$PSVersionTableを戻り値として返せるらしい。
個人的には変数だけでなくWrite-Oputput
を書くほうが好みです……
Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5)
$PSVersionTable
#Requires
ステートメントを利用すると任意のモジュールがLambdaで利用できるようになるようです。
指定したモジュールをpublishするファイルに含めてくれたり色々とうまいことやってくれる様子。
サンプルでは下記のようにAWSPowerShell.NetCoreを読み込んでいる。
#Requires -Modules @{ModuleName='AWSPowerShell.NetCore';ModuleVersion='3.3.390.0'}
サンプルにはコメント外せば入力データがCloudWatch Logsに書き込みされるようなサンプルコードが記載されています。
Write-Host
でCloudWatch Logsに書き込みができそうです。
Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5)
とりえあず今回は下記のように生成したサンプルスクリプト(pwshLambdaHelloWorld.ps1)を書き換えてみました。
- 説明コメント部分を削除したり。
- AWSPowerShell.NetCoreは使わないので
#Requires
ステートメントは削除したり。 - Write-HostでCloudWatch Logsへ入力データをjson形式に変換して出力してみたり。
- 戻り値に$LambdaContextをjson形式に変換して渡してみたり。
だいぶ単純な形におちつきました。
# 入力データをJson形式に変換してWrite-HostでCloudWatch Logsへ書き出す
Write-Host (ConvertTo-Json -InputObject $LambdaInput -Compress -Depth 5)
# HelloWorld
Write-Host "HelloWorld"
# 戻り値として$LambdaContextをjson形式に変換して出力ストリームに書き込み
Write-Output (ConvertTo-Json -InputObject $LambdaContext -Compress -Depth 5)
Lambda関数に割り当てるIAMロールを準備します。
今回のHelloWolrd用に良さそうな標準ポリシー(AWSLambdaBasicExecutionRole)が用意されているので今回はこれを利用してIAMロールを作成します。 Lambdaのアクセス権限については下記ページを参照の事。
IAMのロールの作成でAWSサービスはLambdaを選択して次へ
AWSLambdaBasicExecutionRoleを選択して次へ
次へ
ロール名 lambda_pwsh_helloworld を入力して次へ
IAMロールが作成できました。 Publish時に利用するのでロールARNは控えておいて下さい。
Publish-AWSPowerShellLambda
コマンドでpublish。
下記の例ではProfileNameで接続する資格情報を指定していますが、そこらへんは各自お好きな方法でどうぞ。
Publish-AWSPowerShellLambda -ProfileName プロファイル名 -Name helloworld -ScriptPath 作成したスクリプトのパス -Region ap-northeast-1 -IAMRoleArn 作成したロールのARN
無事、publishできました。
初回のpublishではロールの指定を行う必要があるためIAMRoleArn
で指定を行いましたが、指定しなくても下記のように実行時にコンソール上で選択できたりもします。
自分の端末環境でPublish-AWSPowerShellLambda
コマンドがエラーとなってPublishができない状態に陥りました。
原因を探ってみると、Publish-AWSPowerShellLambda内で行っているdotnet tool install -g Amazon.Lambda.Tools
が失敗していて……なぜ? と思い色々と確認してみると。
過去、Nugetのパッケージソースの設定を自分でぶっこわしていたようで、下記にあるNuget.orgの設定がなくなっていたことが発覚……
Set-PackageSource
コマンドでNuget.orgのソースを追加した所、無事動き出しpublishできるようになりました。
とりあえず今回は下記の三通りの実行方法で実行してみる。
- AWSコンソールから
- Invoke-LMFunctionコマンドから(PowerShell.NetCore)
- Amazon.Lambda.Tools(.NET Core グローバル ツール)から
作成した関数helloworldからテストイベントの設定を選択
下記のようなテストイベントを作成
とりえあずjson形式でhello worldな感じ。
作成したテストイベントHelloWorldを指定してテストを実行
戻り値として$LambdaContextをjson形式に変換した物が赤枠に表示されています。
またログ出力にWrite-HostでCloudWatch Logsにテストイベントで設定した入力データをjson形式に変換したものと、helloworldの文字列が出力されている事が確認できました。
AWSPowerShell.NetCoreのInvoke-LMFunction
コマンドを利用してLambdaを実行してみます。
$inputData = [pscustomobject]@{"hello"="world"} | ConvertTo-Json
$return=Invoke-LMFunction -ProfileName プロファイル名 -FunctionName helloworld -Payload $inputData -Region ap-northeast-1 -LogType tail
$return
StatusCodeが200で正常終了したようです。
戻り値はPayloadプロパティにSystem.IO.MemoryStreamで格納されていて、またLogTypeオプションをtailで実行するとLogResultにはログ最後の4KBをbase64-encodedされた物が格納されるようです。
System.IO.MemoryStreamを文字列に変換したり、base64を変換するのは一手間かかるなと思っていたら、下記のような便利なモジュールを公開している人がいました。
Announcing 'Convert', a PowerShell Module for common object conversions.
このモジュールに含まれるConvertTo-String
を使うとお手軽にSystem.IO.MemoryStreamだったりbase64エンコードから文字列を抽出できます。 便利。
Amazon.Lambda.Tools
を利用してLambdaを実行してみます。
aws/aws-extensions-for-dotnet-cli - Invoke Function
dotnet lambda invoke-function helloworld --profile プロファイル名 --region ap-northeast-1 --Payload '{\"hello\":\"world\"}'
Payloadでエスケープ文字を利用してますが下記を参考しました。Amazon.Lambda.toolsでPayloadにjson形式で渡すにはちょっと小細工が必要な様子。
今回の数KByteのps1でもPublish‑AWSPowerShellLambdaで生成されるzipファイルは11MByteほどありました。
#Requires
ステートメントを利用して任意のモジュールを追加していくとサイズは更に増加します。
(AWSPowerShell.NetCoreを含めると23MByteでした)
Lambdaをちょっと修正してアップロードして確認してというのをぐるぐる回すにはなかなかしびれるファイルサイズな気がします。 ココらへんもう少しファイルサイズが軽くなったりしたら素敵な感じなんですが……
下記のような会話もあったりして将来に期待したいですね。
aws/aws-lambda-dotnet - #issue #319 disappointed with the powershell implementation
自分の端末環境ではNugetのパッケージソース設定をぶっこわしておかしい状態だった為、Publishができず意外とハマってしまいましたが。 基本的にはさっくり環境設定できて簡単に実行できる気がします。