📝

Lambda で Python を使って、Slack に通知するハンズオンを勝手に作ってみた

2022/03/12に公開
15

AWS にはたくさんの初心者向けハンズオンがあります。
どれも丁寧で、初心者以外でも勉強になるハンズオンばかりです。

僕は現状で上記のハンズオンをすべて実施したので、今度は自分でハンズオンを作ろうと思いました。
Lambda の入門レベルという内容なので、興味のある方は試して頂ければ幸いです。

概要

(★) がついているところは、手を動かすところです。
所要時間は 30 分程度です。

  1. 今回のハンズオンで目指す構成の紹介
  2. 各サービスについての簡単な紹介
  3. Slack の設定 (★)
  4. Lambda の作成 (★)
  5. EventBridge の設定 (★)
  6. まとめ
  7. リソース削除 (★)

サーバレスかつ低リクエストなので、課金額はほぼ 0 です。

事前準備

Lambda で Python を使用しますが、プログラムに関する知識はなくても大丈夫です。

注意点

1. 今回のハンズオンで目指す構成の紹介

それではハンズオンを始めましょう。
まずは今回のハンズオンで目指す構成を紹介します。

上記の通り、非常にシンプルな内容です。

・EventBridge のスケジュール指定で Lambda を実行
・Lambda から Slack に通知
・Slack で通知を確認

次に、Lambda、EventBridge、Slack を簡単に紹介します。

2. 各サービスについての簡単な紹介

それでは、今回使用する各サービスについて、簡単に紹介します。

AWS Lambda

よくある質問 - AWS Lambda |AWS より

Q: AWS Lambda はどのようなサービスですか?

AWS Lambda を使用することで、サーバーのプロビジョニングや管理をすることなく、コードを実行できます。課金は実際に使用したコンピューティング時間に対してのみ発生し、コードが実行されていないときには料金も発生しません。Lambda を使用すれば、実質どのようなタイプのアプリケーションやバックエンドサービスでも管理を必要とせずに実行できます。コードさえアップロードすれば、高可用性を実現しながらコードを実行およびスケーリングするために必要なことは、すべて Lambda により行われます。コードは、他の AWS のサービスから自動的にトリガーするよう設定することも、ウェブやモバイルアプリケーションから直接呼び出すよう設定することもできます。

以下がポイントです。
・コードさえあれば処理を実行できる
・課金はコードを実行した時間に対してのみ発生する
・他の AWS サービスやアプリケーションとも連携可能

Amazon EventBridge

よくある質問 - Amazon EventBridge | AWS より

Q: Amazon EventBridge とは何ですか?

Amazon EventBridge は、コードを作成せずに、AWS のサービス、独自のアプリケーション、サービス型ソフトウェア (SaaS) アプリケーションのデータの変更にリアルタイムにアクセスできるサービスです。使用を開始するには、Amazon EventBridge コンソールでイベントソースを選択し、AWS Lambda、Amazon SNS、Amazon Kinesis Data Firehose といった数々の AWS のサービスの中からターゲットを選択します。Amazon EventBridge により、イベントがほぼリアルタイムで自動的に送信されます。

以下がポイントです。
・コーディング不要
・AWS サービスやアプリケーションのデータの変更を検知
・発生したイベントを、様々な AWS サービスに送信可能

Slack

What is Slack? | Slack より

What is Slack?
Slack は、人々をそれぞれが必要とする情報につなげる、ビジネス用のメッセージングアプリです。Slack を使うことで、メンバーが 1 つの場所に集まり、チームが一体となって働くことができ、組織のコミュニケーションの方法が変わります。

メッセージングアプリの 1 つです。
ブラウザでも使用できますし、OS のアプリとしても使用できます。
今回は、ブラウザ / OS のどちらでも実施できる内容です。

3. Slack の設定 (★)

それではここからは手を動かしていきましょう。
まずは、Slack の設定を行います。

3-1
事前に作成したワークスペースにサインインしてください。
今回は、デフォルトで存在する「general」というチャンネルを使用します。

3-2
チャンネル一覧から「その他」→ 「App」をクリックします。

3-3
右上の「App ディレクトリ」をクリックします。

3-4
ブラウザで以下の画面が表示されるので、右上の「ビルド」をクリックします。

3-5
「Create an app」をクリックします。

3-6
以下のポップアップが表示されたら、「From scratch」をクリックします。

3-7
・App Name に任意の名前を入力します。
・今回使用するワークスペースを選択します。
・「Create App」をクリックします。

3-8
Basic Information の中から「Incoming Webhooks」をクリックします。

3-9
デフォルトでは右上のトグルが「Off」になっているので、クリックして「On」にします。

3-10
「Add New Webhook to Workspace」をクリックします。

3-11
以下の画面が表示されたら、「general」チャンネルを選択し、「許可する」をクリックします。

3-12
Webhook URL が払い出されます。
こちらは後で使用するので、コピーしておいてください。

ここまでで Slack の設定は完了です。
次に Lambda を作成します。

4. Lambda の作成 (★)

ここからは AWS 側での作業になるので、AdministratorAccess を付与した IAM ユーザー情報でサインインしてください。

4-1
まずはリージョンと表示言語を確認します。
今回は東京リージョンを使用し、表示言語は日本語とします。

4-2
AWS マネジメントコンソール上部の検索窓に「lambda」と入力し、検索結果から「Lambda」をクリックします。

4-3
Lambda コンソールから「関数の作成」をクリックします。

4-4
・関数名に任意の名前を入力します。
・ランタイムは「Python 3.9」を選択します。
・「関数の作成」をクリックします。

4-5
作成された Lambda 関数のコードソースに記載されている、デフォルトのコードをすべて削除し、以下のコードを貼り付けます。
コードは、AWS 公式資料 を参考にしています。

import urllib3
import json
http = urllib3.PoolManager()


def lambda_handler(event, context):
    url = "Slack Incoming Webhooks URL"
    msg = {
        "channel": "#general",
        "username": "",
        "text": "Hello From Lambda",
        "icon_emoji": ""
    }

    encoded_msg = json.dumps(msg).encode('utf-8')
    resp = http.request('POST', url, body=encoded_msg)
    print({
        "message": "Hello From Lambda", 
        "status_code": resp.status, 
        "response": resp.data
    })

上記のうち、Slack Incoming Webhooks URL を、3-12 でコピーした URL に変更してください。

4-6
「Deploy」をクリックします。

4-7
「テスト」タブをクリックします。
テストイベントはデフォルトのままでよいので、「名前」に任意の名前を入力します。
最後に「テスト」をクリックします。

4-8
テスト実行により、Slack の「general」チャンネルにメッセージが来ます。

これで、Lambda から Slack に通知することができました。
text のメッセージを変更することで、Slack に通知するメッセージを変更することができるので、ぜひ色々遊んでみてください。

msg = {
        "channel": "#general",
        "username": "",
        "text": "お昼です。そろそろ休憩しましょう。",
        "icon_emoji": ""
    }

4-9
Lambda からの出力は、CloudWatch Logs で確認できます。
AWS マネジメントコンソール上部の検索窓に「cloudwatch」と入力し、検索結果から「CloudWatch」をクリックします。

4-10
CloudWatch ダッシュボードのサイドバーから「ロググループ」をクリックします。

4-11
ロググループの検索窓に「handson」と入力すると、作成した Lambda 関数の出力が記録されているロググループが表示されるので、ロググループ名をクリックします。

4-12
何回かテスト実行をしている方は、いくつかログストリームが表示されていると思いますので、どれか 1 つをクリックしてください。

4-13
Python コードで print() を使用して出力した値が表示されていることが確認できます。

Node.js での console.log() などの出力も、同様に確認することができます。
エラーメッセージも出力されるので、エラーが発生した際には、CloudWatch Logs のロググループを確認しましょう。

ここまででこのハンズオンの核となる部分はできました。
次にスケジュール実行を実装するため、EventBridge の設定を行います。

5. EventBridge の設定 (★)

ここまでの手順では Lambda を コンソール上から手動で実行していました。
しかし、毎日実行したい処理がある場合、毎日コンソールから手動で実行するのは手間がかかります。
そこで、EventBridge と連携して、決まった時間に実行されるよう、スケジュール実行の設定をしていきます。

5-1
今回作成した Lambda 関数のコンソールから「トリガーを追加」をクリックします。

5-2
トリガーから「EventBridge (CloudWatch Events)」を選択します。

5-3
「新規ルールの作成」を選択し、ルール名とルールの説明を入力します。

5-4
ルールタイプは「スケジュール式」を選択します。
スケジュール式に、以下のように入力します。

cron(0 3 ? * MON-FRI *)

左から、(分 時 日 月 曜日 年) の形式で指定します。
詳しくは、AWS 公式ドキュメント をご覧ください。

今回のように、cron(0 3 ? * MON-FRI *) と指定すると、月曜日 ~ 金曜日の 12:00 に実行されるルールとなります。

ただ、今回はすぐに実行されることを確かめたいので、皆さんがハンズオンを実施されている時間に合わせて、以下のように修正してください。

例:日付や曜日に関係なく、22:50 に実行したい場合
cron(50 13 * * ? *)

5-5
スケジュールの設定後、「追加」をクリックします。

5-6
Lambda 関数のコンソール上に、EventBridge のトリガーが追加されたことを確認します。
その後、指定した時間になったら、Slack に通知がくることを確認します。

少し待つと、指定した 22:50 に通知が来ることが確認できました。
cron の時間を自由に設定し、お好きなタイミングで Lambda 関数を実行してみてください。

これで、今回の構成をすべて実装することができました。

6. まとめ

今回は、Lambda で Python を使って、Slack に通知するハンズオンでしたが、いかがでしたか?

改めて構成図を見てみましょう。

シンプルな内容なので、それほど時間をかけずにできたと思います。
「Lambda は触ったことないから何ができるかよくわからない」という方も、AWS サービスとの連携や、組み合わせによる自動化のイメージがつかめたのではないでしょうか。

もちろんまだまだできることはたくさんありますが、今回は入門なので、シンプルな内容で Lambda のイメージをつかんで頂ければ幸いです。

サーバレスの中心サービスともいえる Lambda なので、サーバレスに興味のある方は、ぜひたくさん触って色々な機能を試してみてください。

今回の構成ではほとんどコストはかかっていませんが、念のため次のセクションでリソースの削除を行います。

7. リソース削除 (★)

それでは今回作成したリソースを削除していきます。
・EventBridge ルール
・Lambda 実行ロール (自動生成された IAM ロール)
・Lambda 関数
・CloudWatch Logs ロググループ
・Slack アプリ
・Slack ワークスペース

7-1
Lambda 関数のコンソールから、EventBridge のトリガーを選択し、「削除」をクリックします。

7-2
Lambda 関数のコンソールから「設定タブ」→「アクセス権限」→「ロール名」をクリックします。

7-3
IAM ロールの画面が表示されるので、アタッチされているポリシーをクリックします。

7-4
IAM ポリシーの画面が表示されるので、右上の「ポリシーの削除」をクリックします。

7-5
IAM ロールの画面に戻り、右上の「削除」をクリックします。

IAM ロール名を入力し、「削除」をクリックします。

7-6
Lambda 関数のコンソールから、「アクション」→「関数の削除」をクリックします。

7-7
CloudWatch のコンソールで、サイドバーから「ロググループ」をクリックします。

7-8
今回作成されたロググループを選択し、「アクション」→「ロググループの削除」をクリックします。

7-9
Slack の 「App」→「作成したアプリ名」をクリックします。

7-10
上部のアプリ名→「設定」をクリックします。

7-11
ブラウザでアプリの概要が表示されるので、下部の「アプリを削除する」をクリックします。

7-12

「ワークスペース名」→「設定と管理」→「ワークスペースの設定」をクリックします。

ブラウザでワークスペースの設定と権限のページが表示されます。

以上でリソース削除は完了です。

最後に

お疲れさまでした。
本ハンズオンを実施頂き、ありがとうございました。
フィードバックがあればご連絡ください。
まだまだ本家のハンズオンのクオリティには及びませんが、本ハンズオンが、これから Lambda を触ってみたいという方のお役に立てば幸いです。

参考資料

ハンズオン資料 | AWS クラウドサービス活用資料集
AWS Hands-on for Beginners | AWS
Slack ワークスペースを作成する | Slack
よくある質問 - AWS Lambda |AWS
よくある質問 - Amazon EventBridge | AWS
What is Slack? | Slack
ウェブフックを使用して Amazon SNS メッセージを発行する
Amazon CloudWatch Events とは - Amazon CloudWatch Events

Discussion

sssakasssaka

はじめまして!SRE業務している者です。
こちらの記事参考にさせていただき、正常にSlack通知まで動作確認できました!

一点ご質問があります。
こちらの記事の通り設定を行ったのですが、
Slackの通知内容に変数を埋め込むことは可能なのでしょうか?

現在TerraformにてLambdaの登録を行っているのですが、
個社ごとに通知内容を変えたいと思っておりまして、
外部ファイルから取得してPythonコード内に変数として取得したいと思っております。

お忙しいところ恐れ入りますが、ご教示いただけますと幸いです。

mn87mn87

sssaka さん

本記事の内容をお試し頂きありがとうございます。

Slackの通知内容に変数を埋め込むことは可能なのでしょうか?

まだ試していませんが、Node.js ではやったことがあるので、Python でも可能だと思われます。
実現されたい内容は、以下のような内容であると認識しております。
認識に齟齬がございましたら、ご指摘頂ければ幸いです。

例:
外部ファイル A: A 社の情報
外部ファイル B: B 社の情報
Lambda から外部ファイルを取得し、外部ファイルに含まれる社名が A 社であれば、A 社の情報を Slack に通知、B 社であれば B 社の情報を Slack に通知する

私の方で一度試してみるので、試した後に結果と実装例をご連絡いたします。

よろしくお願いいたします。

sssakasssaka

mn87さん

ご回答頂きありがとうございます。
大変助かります。

はい、ご認識頂いている通りとなります。
具体的に渡したい情報としましては、以下となります。
 ・お客様名
 ・お客様のURL

上記の情報が個社ごとに異なる情報となります。
また、今後、お客様の追加に伴いTerraformで実行してSlack登録したいと考えているため、
Lambdaのpythonコードには手を加えずに、外部ファイルから取得し管理したいと思っております。

お手数おかけしますが、宜しくお願いいたします。

mn87mn87

sssaka さん

認識のご確認ありがとうございます。
頂いた情報をもとに、簡単ではありますがコードを書いてみました。

import urllib3
import json
http = urllib3.PoolManager()


def lambda_handler(event, context):
    # 外部ファイル取得処理
    
    # 外部ファイルからお客様名とお客様の URL を取得 (必要に応じて個社名も取得)
    user_name = "user-name"
    user_url = "https://example.com"
    # company_name = "A"
    
    # 必要に応じて if 文
    # if company_name == "A":
    #     user_name = "user-A"
    #     user_url = "https://example-a.com"
    # elif company_name == "B":
    #     user_name = "user-B"
    #     user_url = "https://example-b.com"
    # else:
    #     user_name = "user-C"
    #     user_url = "https://example-c.com"
    
    url = "Slack Incoming Webhooks URL"
    msg = {
        "channel": "#general",
        "username": "",
        "text": f'user_name: {user_name}\nuser_url: {user_url}', # 変数を投稿メッセージに指定
        "icon_emoji": ""
    }

    encoded_msg = json.dumps(msg).encode('utf-8')
    resp = http.request('POST', url, body=encoded_msg)
    print({
        "message": "Hello From Lambda", 
        "status_code": resp.status, 
        "response": resp.data
    })

上記コードを実行すると、Slack には以下のように投稿されます。

Lambdaのpythonコードには手を加えずに、外部ファイルから取得し管理したいと思っております。

上記のコードでこちらのご要件を満たせれば幸いです。
なお、外部ファイル取得処理や、取得した外部ファイルから情報を抽出する処理は任意のロジックで書き足して頂きますようお願いいたします。

上記について、ご不明な点がございましたらご連絡ください。
よろしくお願いいたします。

sssakasssaka

mn87さん

早速のご対応ありがとうございました。
動作確認し、意図通りの動作となること確認いたしました。
大変助かりました。

mn87mn87

sssaka さん

動作確認頂きありがとうございます。
お役に立てたのであれば光栄です。

sssakasssaka

mn87さん

度々のご質問となり申し訳ありません。。

もしご存じでしたら教えて頂きたいのですが、
Lambdaのハンドラーについて教えてください。

ソースコードに
def lambda_handler(event, context):

を利用しているかと思いますが、こちらのeventの情報には何の情報が送られているのか
ご存じだったりしますでしょうか?
恐れ入りますが、宜しくお願いいたします。

mn87mn87

sssaka さん

追加でのご質問ありがとうございます。

eventの情報には何の情報が送られているのか

結論からご説明すると、Lambda を呼び出したときの呼び出し元の情報などが入っています。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-event

イベントは、処理する Lambda 関数のデータを含む JSON 形式のドキュメントです。イベントは、ランタイムによりオブジェクトに変換された上で、関数のコードに渡されます。関数を呼び出すときは、イベントの構造とコンテンツを決定します。

今回のハンズオンだと、Lambda の呼び出し元が EventBridge なので、以下のような情報が送られています。

{
    "version": "0",
    "id": "217c16b2-26e7-7a0a-e907-815da4d8b986",
    "detail-type": "Scheduled Event",
    "source": "aws.events",
    "account": "0123456789",
    "time": "2022-06-30T10:38:00Z",
    "region": "ap-northeast-1",
    "resources": [
        "arn:aws:events:ap-northeast-1:0123456789:rule/test"
    ],
    "detail": {}
}

上記は、ソースコードの中で event を出力し、CloudWatch Logs で確認した結果です。

print(json.dumps(event))

その他のイベント情報については、以下の AWS 公式ドキュメントに記載されています。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-services.html

また、context については以下の AWS 公式ドキュメントに記載されています。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-context.html

ちなみに Lambda にはテスト実行の機能も備わっているので、テンプレートから様々なイベント例を試すことができます。

上記で回答になりますでしょうか。
ご確認をよろしくお願いいたします。

sssakasssaka

mn87 さん

詳細にご回答いただきありがとうございます。
期待以上の回答で大変助かりました。。

今後とも宜しくお願いいたします。

okadangookadango

大変わかりやすい記事でした。
ありがとうございます。

時間があれば、
AWS利用料のSLACKへの定期通知のハンズオンも紹介して頂きたいです。

mn87mn87

dango さん

本記事をご覧頂きありがとうございます。
AWS 利用料を SLACK に週次や月次で通知する方法も時間があったら紹介したいと思います。

okadangookadango

ぜひお願いします。

コマンドライン構築ばかりのネット情報で困っています。
また、AWS学習を始めたばかりなので、毎日利用料が気になります、、

mn87mn87

dango さん

ご要望頂いていた AWS 利用料を SLACK に定期通知についてのご報告です。
基本的には本ハンズオンの手順のうち、以下の点を変更することで実現可能でした。

  1. Lambda のコードを変更する
  2. Lambda の IAM ロールに「CloudWatchReadOnlyAccess」権限を追加する

1. Lambda のコードを変更する

本ハンズオンの手順 4-5 で紹介しているコードを以下のコードに変更してください。


import datetime
import json
import urllib3
import boto3
http = urllib3.PoolManager()

def lambda_handler(event, context):

    response = boto3.client('cloudwatch', region_name='us-east-1')

    get_metric_statistics = response.get_metric_statistics(
        Namespace='AWS/Billing',
        MetricName='EstimatedCharges',
        Dimensions=[
            {
                'Name': 'Currency',
                'Value': 'USD'
            }
        ],
        StartTime=datetime.datetime.today() - datetime.timedelta(days=1),
        EndTime=datetime.datetime.today(),
        Period=86400,
        Statistics=['Maximum']
    )

    cost = get_metric_statistics['Datapoints'][0]['Maximum']
    date = get_metric_statistics['Datapoints'][0]['Timestamp'].strftime('%Y年%m月%d日')


    text = "%sまでのAWSの料金は、$%sです。" % (date, cost)
    
    url = "Slack Incoming Webhooks URL"
    msg = {
        "channel": "#general",
        "username": "",
        "text": text,
        "icon_emoji": ""
    }
    
    encoded_msg = json.dumps(msg).encode('utf-8')
    
    resp = http.request('POST', url, body=encoded_msg)
    print({
        "message": text, 
        "status_code": resp.status, 
        "response": resp.data
    })

以下はコードの簡単な説明です。

・CloudWatch の料金メトリクスから現在の料金を取得
・取得した前日までの AWS 利用料を Slack に通知

参考にしたのは以下の記事なのでよろしければご参照ください。

https://qiita.com/ymktmk/items/14bbdf2562bde2fa3a1d

2. Lambda の IAM ロールに「CloudWatchReadOnlyAccess」権限を追加する

権限追加の手順も上記参考記事内の「LambdaにIAMロールをアタッチ」で紹介されていましたので、基本的には上記参考記事に手順で OK です。

Lambda の IAM ロールには以下の手順で権限を追加できます。

  1. Lambda コンソールを開く
  2. 作成した Lambda 関数を選択する
  3. Lambda 関数の「設定」タブを選択する
  4. 「アクセス権限」をクリックし、「実行ロール」に記載されている「ロール名」の IAM ロールをクリックする
  5. IAM ロールの設定画面に移動するので、「許可を追加」から「CloudWatchReadOnlyAccess」を追加する

なお、本ハンズオンでは EventBridge での通知を平日のみにしていますが、もし通知間隔を毎日にしたい場合には、EventBridge の設定も上記参考記事の「Eventbridgeの設定」をご参照ください。

ちなみに私の環境で検証した際の Slack への通知結果は以下の通りです。

もしうまく動作しない場合にはご連絡ください。

上記についてご確認頂ければ幸いです。

okadangookadango

無事実行できました。
ありがとうございました!
後ほどバッジ送らせていただきます。

mn87mn87

ご確認頂きありがとうございます。
無事実行できたとのことで何よりです!