小さく始めるScheduled Lambda

# はじめに

コマンドラインベースで、Lambda関数を作成してアップロードし、実行する方法と、スケジュール登録して定期実行させる方法をまとめておきます。Cloud Watch Eventが最近1分単位で実行できるようになったのですこぶる便利なわけですが、マネジメントコンソール毎回開いてポチポチやるのもそれはそれで辛いのです。内容については、自分向けのメモ書きなので残念ながらなんの保証もありません。嘘が書いてたら教えてくれると助かります。

前提条件としてデフォルトリージョンは設定されているものとします。

# 本文

## Lambdaの一覧をリストする

```bash
aws lambda list-functions --output table --query 'Functions[].[FunctionName,Description,Runtime,LastModified]'
```

## ディレクトリの作成

```bash
mkdir log/ roles/
```

## ロールの準備

ここでは、実行用にlambda-exec-role というロールを作成する。存在する場合はこの手順はいらない。

以下のような lambda-exec-role.jsonを作成し、

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

ロールを作成する。

```bash
# ロールを作成する
aws iam create-role --role-name lambda-exec-role --assume-role-policy-document file://roles/lambda-exec-role.json

# ログを書き出すための権限が必要なので AWSLambdaBasicExecutionRole をアタッチする
aws iam attach-role-policy --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" --role-name lambda-exec-role

# ARNを取得する
aws iam get-role --role-name lambda-exec-role --query "Role.Arn" --output text
```

Lambda関数の中からS3などAWSのリソースへアクセスしたければ、このタイミングでロールに権限を設定する。 

例えば、以下のようにしたりできる。(元々用意されているポリシーはざっくりしているので、あとから細かく調整するとよい)

```bash
aws iam attach-role-policy --policy-arn "arn:aws:iam::aws:policy/AmazonS3FullAccess" --role-name lambda-exec-role
```

## Lambda Functionの作成とテストデータの作成

こんな感じの index.js と、

```index.js
exports.handler = (event, context, callback) => {
   console.log("value1 = " + event.key1);
   console.log("value2 = " + event.key2);
   console.log("env.USER = " + process.env.USER);
   callback(null, "some success message");
};
```

こんな test.json を作成する。

```test.json
{
  "key1": "hello world",
  "key2": "foo bar com"
}
```

ごく簡単に動作確認したいならこんな感じにできる。

```bash
$ node -e "require('./index.js').handler({'key1':'hello', 'key2':'foobar'},{},function(o,m){ console.log(m)})"
value1 = hello
value2 = foobar
env.USER = <Your User Name>
some success message
```

## Lambda Functionのアップロードと実行

[create-function](http://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html) で Lambda関数を作成し、 [invoke](http://docs.aws.amazon.com/cli/latest/reference/lambda/invoke.html) で同期実行する。

```bash
# ソースコードを圧縮する
zip -r lambda-sample.zip index.js

# ラムダ関数を作成する
aws lambda create-function --function-name lambda-sample --runtime nodejs --role $(aws iam get-role --role-name lambda-exec-role --query "Role.Arn" --output text) --handler index.handler --runtime nodejs4.3 --zip-file fileb://lambda-sample.zip --environment "Variables={USER=michel}"

# 実行する
aws lambda invoke --function-name lambda-sample --payload file://test.json log/output.txt
aws lambda invoke --function-name lambda-sample --payload file://test.json log/output.txt --log-type Tail --query 'LogResult' --output text | base64 -D | tee -a log/lambda.log
```

console.log で出力したものは CloudWatch から拾えるけど、--log-type Tailを指定することで直接取得できる。base64エンコードされてるので適宜展開する。

runtimeは nodejs でやると 0.10 になっちゃうので nodejs4.3を指定する。

## 更新する

[update-function-code](http://docs.aws.amazon.com/cli/latest/reference/lambda/update-function-code.html) でコードを更新する。

```bash
# コードを更新する
zip -r lambda-sample.zip index.js
aws lambda update-function-code --function-name lambda-sample --zip-file fileb://lambda-sample.zip
```

なお、環境変数を変更したい場合は [update-function-configuration](http://docs.aws.amazon.com/cli/latest/reference/lambda/update-function-configuration.html)です。

## スケジュールを作成する

動作確認のために毎分実行させる。

```bash
# イベント作成
aws events put-rule --name "lambda-sample-schedule" --schedule-expression "cron(* * * * ? *)" --state ENABLED

# イベントのArn取得
aws events describe-rule --name "lambda-sample-schedule" --query 'Arn' --output text
```

lambdaにパーミッションを設定する。(この辺はマネジメントコンソールからだと明示的にやってないのでハマった)

```bash
aws lambda add-permission \
--function-name "lambda-sample" \
--statement-id "lambda-sample-schedule-permission" \
--action 'lambda:InvokeFunction' \
--principal events.amazonaws.com \
--source-arn `aws events describe-rule --name "lambda-sample-schedule" --query 'Arn' --output text`
```

イベントにターゲットとしてlambdaを指定する。

```bash
aws events put-targets --rule "lambda-sample-schedule" --targets Arn=`aws lambda get-function --function-name lambda-sample --query 'Configuration.FunctionArn' --output text`,Id=lambda-sample-schedule-target
```

## 消す

Roleは使いまわす気がするので残しておく。

CLIから叩く場合、イベントのルールを削除する前にターゲットを全部消す必要がある。これは罠だ。

```bash
aws events remove-targets --rule lambda-sample-schedule --ids lambda-sample-schedule-target
aws events delete-rule --name lambda-sample-schedule
aws lambda delete-function --function-name lambda-sample
```


# おわりに

簡単! …だろうか? いや、そうでもないかな。意外に面倒ですね。

ここでは単一ファイルでやってますが、複数ファイルも大丈夫。npm でインストールしたパッケージを利用したい場合、zipでまとめるときに `zip -r index.js node_modules` とします。