AWS-CLIで試すLambdaでのHelloWorld!

Hello World Lambda using AWS CLI!

AWS CLIを使ってやります。CLIでのやり方を覚えると繰り返して実行する場合に楽になりますし、AWS SDKから使う場合にも参考になるので何かと便利かと。ただし、最初はマネージメントコンソールからやって概要を把握し、その後CLIで実行すると良いのかと思います。

# やること

+ AWS CLIを使ってAWS Lambdaを体験
+ Lambdaで **Hello World Lambda!** という出力をCloudWatchLogsに出力
+ Lambda実行のイベントはS3へのオブジェクト作成

# 前提

+ AWS CLIの環境設定は完了していること
+ jqのインストールがされていること
+ 動作確認はMaxOSX10.10.3で実施

# 参考

+ [AWS Lambdaを紐解く](http://www.slideshare.net/keisuke69/aws-lambda-46129981)
+ [AWS Lambda ハンズオン 2-Tier アーキテクチャで未来へ](http://www.slideshare.net/AmazonWebServicesJapan/aws-lambda-2tier)
+ [AWS Lambda Walkthroughs](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/walkthroughs.html)


# S3バケットの作成

バケットを作成します。6月1日現在、Lambdaが日本のリージョンで使えないので、作成するS3バケットのリージョンは **us-east-1** とします。 

**toshihirock** となっている部分は適当に自分の名前に置き換えてください。

```
$aws s3 mb s3://toshihirock-lambda-test --region us-east-1
```

確認。

```
# 確認
$aws s3 ls --region us-east-1 |grep lambda

# リージョン。US-standardはnullになるっぽい
$aws s3api get-bucket-location --bucket toshihirock-lambda-test
{
    "LocationConstraint": null
}
```

# Lambda Exec Roleの作成

Lambdaで処理を実行する際にCloudWatchLogsにログを出力するためにLambdaExecRoleを作成します。作成するRoleにはCloudWatchLogsへの権限を付与します。また、今回はS3へのイベントをフックするようにするのでS3へのアクセス権限も設定します。

Lambda用のロールを作成します。最初にポリシー用のJSONを作成します。

```lang:role-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
     {
       "Action": "sts:AssumeRole",
       "Principal": {
         "Service": "lambda.amazonaws.com"
        },
        "Effect": "Allow",
        "Sid": ""
     }
  ]
}
```

作成後、以下のCLIでロールを作成します。

```
# 作成
$aws iam create-role --role-name hello_exec_role --assume-role-policy-document file://role-policy.json

#確認
$aws iam get-role --role-name hello_exec_role
```

また、後の行程で作成したロールのARNを利用するので確認しておきます。

```
$aws iam get-role --role-name hello_exec_role |jq -r  .'Role.Arn'
```


上記で出力される内容は適当にコピペしておきます。

次に作成したロールni
CloudWatchLogsへの書き込みを行う権限を付与します。(LambdaではCloudWatchLogsにログ情報を記載)

最初からAWS側で用意してあるポリシーがあるので、今回はそれを利用します。まずは存在を確認します

```
#CloudWatchLogs
$aws iam get-policy --policy-arn "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
```

上記を先ほどのロールに付与します。

```
#cloudwatchlogs
$aws iam attach-role-policy --role-name hello_exec_role --policy-arn "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
```

確認します。

```
$aws iam list-attached-role-policies --role-name hello_exec_role
```

先ほど付与したポリシーが表示されればOKです。

# Lambdaの設定

Lambdaの設定をしていきます。

最初に **Lambda function** の作成をしていきます。

```lang:index.js
console.log('Loading function');

exports.handler = function(event, context) {
    console.log('value1 =', event.key1);
    console.log('value2 =', event.key2);
    console.log('value3 =', event.key3);
    console.log('hello world!');
    context.succeed(event.key1);  // Echo back the first key value
    // context.fail('Something went wrong');
};
```

なお、上記ファイル名を **hello.js** などにした場合、あとでfunctionを登録する時のhandlerは **index.handler** ではなく、 **hello.handler** というように変更する必要が有ります。(上記の対応をさせないと、Can't find moduleというエラーがでます)

上記はzip圧縮します。

```
$zip -r HelloWorld.zip index.js
```

設定後、functionを作成します。 **--role** には先ほど作成したRoleのARNを指定します。

```
$aws lambda create-function \
--function-name hello_function \
--runtime nodejs \
--role arn:aws:iam::xxxxxxxx:role/hello_exec_role \
--handler index.handler \
--zip-file fileb://HelloWorld.zip \
--region us-east-1 
```

確認します。

```
$aws lambda get-function --function-name hello_function --region us-east-1
```

あとでARNを使うので確認しておきます。

```
$aws lambda get-function --function-name hello_function --region us-east-1 |jq -r .'Configuration.FunctionArn'
```

# CLIからイベントを発火させてみる

[Step 2.3: Upload the Deployment Package and Test](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/walkthrough-s3-events-adminuser-create-test-function-upload-zip-test.html)

以下のファイルを保存します。なお、 **toshihirock-lambda-test** というバケット名の箇所は適宜自分の作成したバケット名に変更すること。

```lang:input.txt
{
   "Records":[
      {
         "eventVersion":"2.0",
         "eventSource":"aws:s3",
         "awsRegion":"us-west-2",
         "eventTime":"1970-01-01T00:00:00.000Z",
         "eventName":"ObjectCreated:Put",
         "userIdentity":{
            "principalId":"AIDAJDPLRKLG7UEXAMPLE"
         },
         "requestParameters":{
            "sourceIPAddress":"127.0.0.1"
         },
         "responseElements":{
            "x-amz-request-id":"C3D13FE58DE4C810",
            "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
         },
         "s3":{
            "s3SchemaVersion":"1.0",
            "configurationId":"testConfigRule",
            "bucket":{
               "name":"toshihirock-lambda-test",
               "ownerIdentity":{
                  "principalId":"A3NL1KOZZKExample"
               },
               "arn":"arn:aws:s3:::toshihirock-lambda-test"
            },
            "object":{
               "key":"HappyFace.jpg",
               "size":1024,
               "eTag":"d41d8cd98f00b204e9800998ecf8427e",
               "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko"
            }
         }
      }
   ]
}
```

以下で実行します。

```
# 実行。invocation-typeがEventの時の返却コードが202らしい
$aws lambda invoke --invocation-type Event --function-name hello_function --region us-east-1 --payload file://input.txt outputfile.txt
{
    "StatusCode": 202
}

```

CloudWatchLogsにログが出力されているか確認します。(console.logの部分がCloudWatchLogsに表示される)
なお、UNIX time変換でawkコマンドを使っていますが、MacOSXだとGNUのawkでないようなので、事前にGNUのawk(gawk)をbrewでインストールしてください。

[macで使いにくいコマンドをLinuxに合わせる](http://qiita.com/toyama0919/items/661437d86a95b02484a2)

```
#該当するロググループを探す。/aws/lambda/function_nameという感じになっているはず
$aws logs describe-log-groups --region us-east-1 | jq .'logGroups[].logGroupName'

# ログストリームを更新時間順に表示。確認したいログストリーム名を探す。/aws/lambda/testの部分は変更する
$aws logs describe-log-streams --log-group-name /aws/lambda/hello_function --region us-east-1 | jq -r .'logStreams[] | "\(.logStreamName) \(.lastIngestionTime)"' |sed -e 's/[0-9][0-9][0-9]$//g' |sort -r -t" " -k2 |gawk -F " " '{print $1} {print strftime("%c",$2)}'

# ログを表示。グループ名、ストリーム名は適宜変更
$aws logs get-log-events --log-group-name /aws/lambda/hello_function --log-stream-name 2015/06/02/acf214a7c84a4cbbb845b971290f43ee --region us-east-1|jq .'events[].message'

```

備忘録として2目のコマンドの作成メモ｡

+ Cloudwatchlogsで取得できるUNIXのtimstampの桁数が多い?くて正しく変換できないので、末尾3文字をsedで削除
+ sortを使って降順にソート
+ awk(gawk)を使って1つ目のフィールドはそのまま表示し、2つ目の表示はUNIX timeを変換して表示

頑張ってコマンド作成したのですが、かなり面倒なので、CloudWatchLogsの確認はマネージメントコンソール見た方が良いかもしれません。。。(APIの勉強になるという点では良いと思いますが)

# S3の設定をする

[Step 3: Configure Amazon S3 to Publish Events](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/walkthrough-s3-events-adminuser-configure-s3.html)

以下のコマンドを実行してhello_functionのlambda:InvokeFunctionにS3の権限を付与します。 **statement-id** には任意の名称を指定し、ARNは **arn:aws:s3:::{bucket_name}** の形式で指定します。

```
$aws lambda add-permission --function-name hello_function --region us-east-1 --statement-id s3_toshihirock-lambda-test --action "lambda:InvokeFunction" --principal s3.amazonaws.com --source-arn "arn:aws:s3:::toshihirock-lambda-test"
```

確認

```
$aws lambda get-policy --function-name hello_function --region us-east-1
```

S3側にもイベントを追加します。
まず、追加したいイベントの内容を示すJSONを作成します。 **LambdaFunctionArn** には先ほど確認したLambdaFunctionのARNを指定し、 **Id** には任意の文字列を指定します。 **Events** 内にはどのタイミングでイベントを発火するか記載しています。(下記例ではObjectが作成された時)

```lang:event.json
{
    "LambdaFunctionConfigurations": [
        {
            "LambdaFunctionArn": "arn:aws:lambda:us-east-1:xxxxxx:function:hello_function",
            "Id": "lambda-hello",
            "Events": [
                "s3:ObjectCreated:*"
            ]
        }
    ]
}
```


作成後、以下を実行してS3の対象バケットにイベントを設定します。

```
$aws s3api put-bucket-notification-configuration --bucket toshihirock-lambda-test --region us-east-1 --notification-configuration file://event.json
```


設定を確認します

```
$aws s3api get-bucket-notification --bucket toshihirock-lambda-test --region us-east-1
```

設定が終わったので、実際にS3にファイルをputしてlambdaが実行されるか確認します。

```
$aws s3 cp event.json s3://toshihirock-lambda-test --region us-east-1
```

先ほどと同じ形でCloudWatchLogsの内容を確認してみます。

```
$aws logs describe-log-groups --region us-east-1 | jq .'logGroups[].logGroupName'

$aws logs describe-log-streams --log-group-name /aws/lambda/hello_function --region us-east-1 | jq -r .'logStreams[] | "\(.logStreamName) \(.lastIngestionTime)"' |sed -e 's/[0-9][0-9][0-9]$//g' |sort -r -t" " -k2 |gawk -F " " '{print $1} {print strftime("%c",$2)}'

$aws logs get-log-events --log-group-name /aws/lambda/hello_function --log-stream-name 2015/06/02/acf214a7c84a4cbbb845b971290f43ee --region us-east-1|jq .'events[].message'
```

現在の時間の内容のログが確認できればOKです！

# お掃除

今回作成したものを削除します。

```
# delete lambda function
$aws lambda delete-function --function-name hello_function --region us-east-1

#detach policy
$aws iam detach-role-policy --role-name hello_exec_role --policy-arn "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"

#delete role
$aws iam delete-role --role-name hello_exec_role

#delete S3 bucket
$aws s3 rm s3://toshihirock-lambda-test --recursive --region us-east-1
$aws s3 rb s3://toshihirock-lambda-test --region us-east-1
```

お疲れ様でしたー！