🚀

【AWS】各サービスを触ってみる EventBridge①(EC2の状態変更を検知して通知を行う)

2022/07/01に公開


はじめに

ご覧いただきありがとうございます。阿河です。

当ブログでもEventBridgeを扱うことが多いですが、改めてサービスの中身を掘り下げてみようと思います。
第一弾として「EC2が特定の状態になったことをEventBridgeで自動検知」を行い「LambdaでSlack通知を実行」させます。

対象者

  • AWSを運用中
  • 運用を自動化したい
  • AWSとSlackの連携方法について知りたい

概要

  1. Lambda関数の作成
  2. EventBridgeルールの作成

今回やりたいことは以下の通りです。

  • EC2インスタンスが「stopped」「terminate」「shutting-down」いずれかの状態となったことをEvnetBridgeが検知。
  • 上記を検知した場合、Lambdaを経由してSlackに通知を飛ばす

EventBridgeは、サーバレスのイベントバスサービスです。
イベント駆動アーキテクチャを用意に構築でき、イベントの送信元/送信先がお互いの実装を意識する必要が無くなります。

今回は下記①~④のフローで処理が流れていきます。

①イベントソースである「AWSサービス」からイベントが送信される
②イベントバス(default)がイベントを受けつける
③イベントバスに紐づけたルールが後続処理に送信するイベントを選択
④後続処理を行うターゲット(今回はLambda)にイベントが送信され、イベントが処理されます。

EventBridgeの詳細はBlackBeltを参照頂ければと思います。

事前準備

  • AWSアカウント作成
  • AdministratorAccessを付与したIAMユーザーの作成
  • EC2を2台作成して「running」の状態としておく

1. Lambda関数の作成

EventBridgeのサンプルイベントを確認する

イベントとは「状態の変化」を表します。
今回はEC2インスタンスが特定の状態に移行した場合に、EvnetBridgeで検知できるようにします。

AWSマネジメントコンソールのEventBridgeのページで、ルール作成を行う過程で、サンプルイベントを確認できる箇所があります。
こちらでサンプルイベントの構造を手っ取り早く確認できます。

複数のAWSサービスのサンプルイベントが確認できますが、今回はEC2の状態変更に関わる「EC2 Instance State-change Notification」を選択します。

{
  "version": "0",
  "id": "651d5f8b-947c-4c0f-acb7-5ac4e41a1b8a",
  "detail-type": "EC2 Instance State-change Notification",
  "source": "aws.ec2",
  "account": "123456789012",
  "time": "2015-11-11T21:33:19Z",
  "region": "us-east-1",
  "resources": ["arn:aws:ec2:us-east-1:123456789012:instance/i-abcd3333"],
  "detail": {
    "instance-id": "i-abcd3333",
    "state": "stopped"
  }
}

sourceは「イベントの送信元」を表します。AWSサービスのイベントの場合は、aws.ec2のようなサービス名が入ります。
detail-typeは「イベントの種類」を表します。

detailは「イベントの内容」を表し、sourceとdetail-typeの組み合わせによりスキーマが決まります。

上記のイベントサンプルをもとに、必要な設定を進めていきます。

Lambda用のIAMロールを作成

  • 信頼されるエンティティ: Lambda
  • ポリシー: (AWSLambdaBasicExecutionRole)

Lambda関数を作成

  • オプション: 一から作成
  • ランタイム: Python3.8
  • アーキテクチャ: x86_64
  • ロール: 作成したロール

Lambdaコードを記述

import boto3

def lambda_handler(event, context):
    
    time = event["time"]
    instance_id = event["detail"]["instance-id"]
    state = event["detail"]["state"]
    
    
    message = f"該当EC2インスタンスの状態変化を検知しました。問題がないか確認お願いします\n発生日時: {time}\nインスタンス: {instance_id}\n状態: {state}"
    
    print(message)
    
    return True

先程確認したサンプルイベントを参考に、イベントから必要なデータを取り出すようにコードを記述します。

今回必要なのは、「時間」「インスタンスID」「インスタンスの状態」です。
CloudWatch Logsでテスト結果を確認するため、Printをします。

Lambda関数のテストを実施

Lambdaでテストイベントを作成して、作成したLambda関数が正常に実行されるかを確認します。

テストイベントのJSONには、先ほどEventBridgeの画面で確認したサンプルイベントを貼ります。

ではLambda関数をテストしてみましょう。
テストボタンクリック後に、CloudWatch Logsを確認します。

CloudWatchのページで、ロググループ(※今回作成したLambdaの名前のロググループが作成されています)⇒ログストリーム(※最新のログストリーム)⇒ログイベントを確認します。

サンプルイベントから正常にデータを取り出すことができました。

2. EventBridgeルールの作成

EventBridgeのルールを作成します。
ルールとは、イベントバスで受信したイベントのうち「どのイベントをターゲットに送信するかを定義する」規則です。

  • 名前: ※任意の名前

  • イベントバス: default

  • ルールタイプ: イベントパターンを持つルールタイプ

  • イベントソース: AWS イベントまたは EventBridge パートナーイベント

  • イベントパターン: イベントパターンのフォーム

フォームに従って入力を行います。
イベントタイプ:EC2 Instance State-change Notification
特定の状態: "shutting-down","terminated","stopped"
インスタンスID: ※事前に立ち上げたEC2のうち、1台のインスタンスのIDを指定

フォームに入力を行うと、自動でJSONが作成されます。

{
  "source": ["aws.ec2"],
  "detail-type": ["EC2 Instance State-change Notification"],
  "detail": {
    "state": ["shutting-down", "terminated", "stopped"],
    "instance-id": ["xxxxxxxxxxxxxxxxxx"]
  }
}

入力が終わったら、次の画面へ進みます。

  • ターゲットタイプ: AWSのサービス
  • ターゲットを選択: Lambda関数
  • 機能: ※作成したLambda関数

ターゲットは、イベントの送信先となり、イベントを処理するものです。今回はLambda関数を指定しています。

以上の設定でEventBridgeルールを作成します。

EC2をダウンさせる

事前に起動させていたEC2インスタンスを2台停止します。

インスタンス2台が停止したことを確認したら、CloudWatch Logsを確認します。

Lambdaが実行されたことが分かります。
EventBridgeでEC2インスタンスの状態変更を検知できています。

3. Slackで自動通知を行う

Webhook URLを取得

Lambda関数の処理結果を、Slackのチャンネルに通知します。
Slack側の設定方法は、以前に書いた「GuardDutyの検知結果をSlackに自動通知する」を参照ください。

https://zenn.dev/megazone_japan/articles/8bc406a059ae3e

2の「Slack通知を実装」と同じ方法で、Webhook URLを取得します。

Lambdaコードを変更

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

def lambda_handler(event, context):
    
    time = event["time"]
    instance_id = event["detail"]["instance-id"]
    state = event["detail"]["state"]
    
    url = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    message = f"該当EC2インスタンスの状態変化を検知しました。問題がないか確認お願いします\n発生日時: {time}\nインスタンス: {instance_id}\n状態: {state}"
    
    msg = {
        "channel": "#xxxxxxxxxxxxxxxxx",
        "username": "",
        "text": message,
        "icon_emoji": ""
    }
    
    encoded_msg = json.dumps(msg).encode('utf-8')
    resp = http.request('POST',url, body=encoded_msg)
    
    return True

Webhook URLとSlackチャンネル名は、自身の環境のものに置き換えます。

EC2をダウンさせる

検証のため、EC2 2台を起動状態にします。

準備ができたら、EC2を停止させましょう。


すぐにSlackに通知が届きました。

さいごに

このようにイベントの自動検知/他のAWSサービスとの連携が簡単にできました。
また今後の記事でEventBridgeについて掘り下げていければと思います。

御覧いただき ありがとうございました!

MEGAZONE株式会社 Tech Blog

Discussion