AWS LambdaからChatworkに定時通知する


# 目標

2. Chatworkに定期的に固定メッセージを流す
3. AWS Lambdaを使う

herokuとheroku scheulerを使えば同じことが、無料で実現できます。AWS Lambdaを使うのは趣味です。
[Heroku Schedulerを使ってChatworkに定期ポストする - Qiita](http://qiita.com/ledsun/items/81d47d934f859a4a8a6b)

# 作戦

1. [Amazon CloudWatch Events](http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/WhatIsCloudWatchEvents.html)を使ってLambda関数を定期実行
2. [AWS Key Management Service](https://aws.amazon.com/jp/kms/)を使ってChatworkのAPI Tokenを安全に管理
2. [ledsun/post-chatwork-message](https://github.com/ledsun/post-chatwork-message)を使ってChatworkに投稿

## KMSを使う必要性について
[Environment Variables - AWS Lambda](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/env_variables.html#env_encrypt)
によると

> 環境変数を使用する Lambda 関数を作成するか、または更新する場合、AWS Lambda は AWS Key Management Service を使用してそれらを暗号化します

とあるので環境変数のkeyとvalueはKMSを使って暗号化されているはずです。ただし

> ただし、環境変数に機密情報を保存する必要がある場合は、Lambda 関数をデプロイする前にその情報を暗号化することを強く推奨します

と、あります。今回は素直にKMSを使ってChatwork API Tokenを暗号化します。


# 必要な権限

- ClowdWatch Events: イベントの作成
- KMS: マスターキーの作成とChatwork API Tokenの暗号化
- Lambda: 作成と実行します
- IAM Role作成: lambda関数作成時にroleを作成します

# 実装
## AWS Key Management ServiceへのChatwork API Tokenの保存
[AWS Key Management Serviceで暗号化した文字列をNode.jsで復号化する - Qiita](http://qiita.com/ledsun/items/e24e60a434dd8a37cef0)の手順でChatwork API Tokenを暗号化した文字列を作成します。

## lambda関数の作成
### ソースコード
```js:index.js
const AWS = require('aws-sdk')
const postChatworkMessage = require('post-chatwork-message')

const encryptedToken = process.env.ENCRYPTED_CHATWORK_API_TOKEN
const message = 'hello'

exports.handler = function(event, context) {
  new AWS.KMS({
      region: 'ap-northeast-1'
    })
    .decrypt({
      CiphertextBlob: new Buffer(encryptedToken, 'base64')
    }, (err, data) => {
      if (err) {
        console.log(err)
        return context.fail(err)
      }
      postChatworkMessage(data.Plaintext.toString(), process.env.CHATWORK_ROOM_ID, message)
    })
}
```

### 圧縮
lambda関数をアップロードするためにzipファイルを作成します。

```
npm i aws-sdk post-chatwork-message
zip deploy_package index.js -r node_modules
```

deploy_package.zipができます。

npmはversion3以降、依存ライブラリーをnode_modulesディレクトリ配下にフラットに展開します。
個別のモジュールではなく、node_modulesディレクトリ全体を圧縮ファイルに入れます。

### アップロード
1. [Lambda Management Console](https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/create/select-blueprint?firstrun=true)を開く
1. Blank Functionを選択
![スクリーンショット 2017-01-04 19.01.43.png](https://qiita-image-store.s3.amazonaws.com/0/15964/6cc84e8e-9ad5-5278-a9f6-30e5ef803e7e.png "スクリーンショット 2017-01-04 19.01.43.png")


1. TriggerにCloudWatch Eventsを選択
2. Rule nameに`hourly`を入力
3. Rule descriptionに`hourly test`を入力
4. Schedule expressionで`rate(1 hour)`を選択
5. Enable triggerをチェック
6. Nextボタンをクリック![スクリーンショット 2017-01-04 19.06.04.png](https://qiita-image-store.s3.amazonaws.com/0/15964/66229ce2-bddd-381e-c1da-4ef229395846.png "スクリーンショット 2017-01-04 19.06.04.png")



6. Nameに`hourly-test`を入力
6. Code entry typeを`Upload a .Zip file`に変更
7. Uploadボタンを押して`deploy_package.zip`を選択![スクリーンショット 2017-01-04 19.12.15.png](https://qiita-image-store.s3.amazonaws.com/0/15964/e4acc091-b68f-c49e-7083-e1b2dd9b0832.png "スクリーンショット 2017-01-04 19.12.15.png")

2. Environment variablesに`ENCRYPTED_CHATWORK_API_TOKEN`:暗号化したChatwork Token文字列を入力
2. Environment variablesに`CHATWORK_ROOM_ID`:投稿したいChatwork room id（数字のみ）を入力![スクリーンショット 2017-01-04 19.44.28.png](https://qiita-image-store.s3.amazonaws.com/0/15964/e3d1e815-3c4e-e7a4-21d3-50ec7bab329d.png "スクリーンショット 2017-01-04 19.44.28.png")

8. Role nameに`chatwork-hourly-test`を入力![スクリーンショット 2017-01-04 19.13.44.png](https://qiita-image-store.s3.amazonaws.com/0/15964/6be92715-7ad4-e979-25a6-f17a73079352.png "スクリーンショット 2017-01-04 19.13.44.png")

9. Nextボタンをクリック
9. Create functionボタンをクリック





### 作成したIAMロールにKMSの参照権限を設定
1. [IAM Management Console](https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/roles/chatwork-hourly-test)を開く
2. インラインポリシーを開いて、「作成するには、ここをクリックしてください。」をクリック![スクリーンショット 2017-01-04 19.17.34.png](https://qiita-image-store.s3.amazonaws.com/0/15964/e34b4463-35f7-fcef-a157-841ce4590685.png "スクリーンショット 2017-01-04 19.17.34.png")

3. 選択ボタンをクリック![スクリーンショット 2017-01-04 19.27.05.png](https://qiita-image-store.s3.amazonaws.com/0/15964/e2a63d75-347b-29a2-9cf9-2af301876a5e.png "スクリーンショット 2017-01-04 19.27.05.png")
4. AWS サービスで`AWS Key Management Service`を選択
5. アクションで`Decrypt`を選択
6. Amazon リソースネーム（ARN）にChatwork API Tokenを暗号化したときに表示されるKey Idの文字列を入力
7. ステートメントを追加ボタンをクリック![スクリーンショット 2017-01-04 19.29.51.png](https://qiita-image-store.s3.amazonaws.com/0/15964/dfed4531-7713-e591-d49f-26a868ca6652.png "スクリーンショット 2017-01-04 19.29.51.png")
8. 次のステップボタンをクリック
9. ポリシーの適用ボタンをクリック

KMSの参照権限がない場合は以下のようなエラーが起きます

```
START RequestId: 586c96ad-cc45-11e6-86ad-a3780462e53e Version: $LATEST
2016-12-27T15:01:30.301Z	586c96ad-cc45-11e6-86ad-a3780462e53e	{ [AccessDeniedException: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.]
  message: 'The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.',
  code: 'AccessDeniedException',
  time: Tue Dec 27 2016 15:01:30 GMT+0000 (UTC),
  requestId: '5929f33e-cc45-11e6-9514-1130ca261caa',
  statusCode: 400,
  retryable: false,
  retryDelay: 55.074393190443516 }
END RequestId: 586c96ad-cc45-11e6-86ad-a3780462e53e
REPORT RequestId: 586c96ad-cc45-11e6-86ad-a3780462e53e	Duration: 977.18 ms	Billed Duration: 1000 ms 	Memory Size: 128 MB	Max Memory Used: 32 MB	
```

#### 追記: Policy templatesを使う
Roleを作成する際に、Policy templatesに`KMS decryption permissions`を選択すると、KMSの参照権限を設定できます。

設定されるポリシーは次の通りです。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:ap-northeast-1:275624216953:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:275624216953:log-group:/aws/lambda/seven-trumpets:*"
            ]
        }
    ]
}
```

Resourceを特定のものに制限したい場合は、手動で設定します。

### 動作確認

Testボタンを押すと、試しに投稿できます。
Input test eventは使っていません、何を入力しても構いません。


# 参考
- [ServerLessで、Amazonのほしい物リストから安売り情報を通知するBotを作ったよ - プログラマでありたい](http://blog.takuros.net/entry/2016/12/12/022750)
- [KMSで認証情報を暗号化しLambda実行時に復号化する ｜ Developers.IO](http://dev.classmethod.jp/cloud/decrypt-sensitive-data-with-kms-on-lambda-invocation/)
- [【アップデート】AWS Lambdaで環境変数を使えるようになりました！！！ ｜ Developers.IO](http://dev.classmethod.jp/cloud/aws/aws-lambda-env-variables/)
- [AWS Lambda環境変数対応をお触り - Qiita](http://qiita.com/TakashiKOYANAGAWA/items/30352b288b23c8c8daae)
- [Simplify Serverless Applications with Environment Variables in AWS Lambda | AWS Compute Blog](https://aws.amazon.com/jp/blogs/compute/simplify-serverless-applications-with-environment-variables-in-aws-lambda/)