Python
AWS
lambda
Slack

AWSのLambdaからLambdaを呼んで、Slackにメッセージを送信する

今回のお題

機能単位でLambdaを作成し、Lambda内からLambdaを実行する。
また、Requestで必要なデータを送信し、Responseで結果を得る。
本来はAWSのサービスのStep FunctionsやSNS使うことを考える必要があるのだろうけど、
チュートリアルなので次の課題とさせていただきます。

Requestで受けた情報を元にSlackへメッセージを送信するLambdaの作成

LambdaからWebHookを使用してSlackにメッセージを送信してみる

こちらを元に、下記のように定義しました。
少しずつ最終形に近づいています。

slack.py
# coding: UTF-8
import requests
import json
import os
from base64 import b64decode
import boto3
from datetime import datetime

def lambda_handler(event, context):
    # 時間を設定
    now = datetime.now()
    msg = now.strftime("%Y/%m/%d %H:%M:%S")

    # メンションを設定
    if event['user_id'] != None:
        msg += "\r\n<@%s>" % event['user_id']

    # メッセージを設定
    msg += u"\r\n%s" % event["message"]
    # 実行
    send_slack(u'カイジ', u':kaiji:', event['channel'], msg)

    return {"ret": True}

# slack送信、仮メソッド
def send_slack(user_name, icon, channel, msg):
    # WEBHOOK URLを複合化
    kms = boto3.client('kms')
    webhook = kms.decrypt(CiphertextBlob=b64decode(os.environ['WEBHOOK_URL']))['Plaintext']

    requests.post(webhook, data = json.dumps({
        'text': msg, # 投稿するテキスト
        'username': user_name, # 投稿のユーザー名
        'link_names': 1, # メンションを有効にする
        'channel': channel, # チャンネル
        'icon_emoji': icon, # アイコン
    }))

ポイント

  1. eventの中にはRequest内のパラメータが入っていますので、そこからSlackに使用する情報を取得しました。
    1. user_id : メンションするユーザID(Slackで自動で振られるMemberIDを使用)
    2. message : 本文
    3. channel : 送信先のチャンネル
  2. WebhookのURLを環境変数に入れて暗号化しました。使用する際に複合化してます。
    1. Lambdaの環境変数をKMSで暗号化して、Pythonで複合化する を元に実施しました。定数にしたり、DynamoDBに入れたりとかが正かもしれませんが、今回は環境変数にしました。

テスト実行。

テストイベントで、Requestパラメータを下記のように設定します。
スクリーンショット 2017-11-21 0.08.48.png

この状態でテストを実行します。
スクリーンショット 2017-11-21 0.14.23.png
チャンネルやちゃんとした名前はボカしてますが、無事にRequestから取得した内容でSlack通信できました。
(テストデータにゴミが入ってますね・・・失礼しました・・・)

別のLambdaから実行する

AWSでPythonを使用したLambdaを作ってみる
を元に、新規で1つLambdaを作成し、下記のようなソースを記載します。

lambda.py
# -*- coding: utf-8 -*-
import boto3
import json

def lambda_handler(event, context):
    clientLambda = boto3.client("lambda")
    # 引数
    params = {
        "user_id": "1234567890",
        "message": "lambdaからlambdaを実行する",
        "channel": "test"
    }
    res = clientLambda.invoke(
        FunctionName="呼び出すLambdaの関数名",
        InvocationType="RequestResponse",
        Payload=json.dumps(params)
    )

    # ResponseをJson形式に変換    
    pay=json.loads(res['Payload'].read())
    # 表示
    print(pay["ret"])

ポイント

  1. boto3.client("lambda")を使用して、lambdaをソースから実行できるようにしてます。
  2. RequestパラメータはJSON形式をエンコードして渡します。
  3. Response情報は、clientLambda.invokeでの結果を受け取り、Payloadの中に入ります。そちらを読み取り、JSON形式に変換してます。

テスト実行するとエラーに・・・

こちらで一度実行して見ました。
するとエラーになり、下記の文言が出てきました。

 is not authorized to perform

どうやら、デフォルトで用意されているロールの「lambda_basic_execution」を設定している場合は、他のLambdaを実行する権限が無いようです。

ロールを作成する

IAM>ロール>ロールの作成ボタンを押下します。
スクリーンショット 2017-11-21 0.25.01.png

ロールの作成

  1. AWSを選択します。 スクリーンショット 2017-11-21 0.26.15.png
  2. Lambdaを選択し、次のステップへ スクリーンショット 2017-11-21 0.27.00.png

ポリシーの選択

画面上部でlambdaと入力し、必要なものを絞り込みます。
スクリーンショット 2017-11-21 0.27.48.png

  1. Lambdaでは、Cloud Watchが必要なようなので、「AWSLambdaBasicExecutionRole」を選択します。 スクリーンショット 2017-11-21 0.28.49.png
  2. 他のLambdaを呼ぶために、「AWSLambdaRole」も追加します。 スクリーンショット 2017-11-21 0.29.58.png おそらく、Resourceも指定して設定するのが本来の用途だと思いますが、今回は割愛します 他にも必要に応じて、DynamoDBやRDSなど、必要なものを追加します。

ロール名と説明を入力して、完了

スクリーンショット 2017-11-21 0.32.12.png
説明は空でも構いません。

作成が完了すると、一覧に表示されます。

スクリーンショット 2017-11-21 0.33.07.png

Lambdaに戻って、ロールの設定をする。

先ほどのエラーになったLambdaに戻り、ロールを選択して保存します。
スクリーンショット 2017-11-21 0.34.42.png

テスト実行

テスト成功(returnを書いてないためnullになっている)
スクリーンショット 2017-11-21 0.35.39.png

ログにも、retの値のTrueが出る
スクリーンショット 2017-11-21 0.35.31.png

Slackにも送信結果が出力される。
スクリーンショット 2017-11-21 0.38.04.png

以上で、LambdaからLambdaを呼ぶことができました。

boto3については下記を参照願います。
http://boto3.readthedocs.io/en/latest/reference/services/lambda.html