Lambda 関数 + SES を利用して EC2 インスタンスのスクリーンショットをメールアドレスに送信してみた

Lambda 関数 + SES を利用して EC2 インスタンスのスクリーンショットをメールアドレスに送信してみた

Clock Icon2025.05.03

はじめに

テクニカルサポートの 片方 です。
今回は Lambda 関数 + SES を利用して EC2 インスタンスのスクリーンショットをメールアドレスに送信してみました。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/troubleshoot-unreachable-instance.html#instance-console-screenshot

インスタンスに接続できない場合はインスタンスのスクリーンショットをキャプチャして、それをイメージとして表示することができます。このイメージにより、インスタンスのステータスについて可視化されるため、迅速にトラブルシューティングすることができます。
インスタンスの実行中またはクラッシュ後にスクリーンショットを生成できます。イメージは JPG 形式で生成され、100 KB 未満です。スクリーンショットにはデータ転送コストがかかりません。

※ EC2 インスタンスのスクリーンショット例
01
02

やってみた

SES については検証済みの E メールアドレスがあることを前提に進めさせていただきます。

0001

以下の順番で実装します。

  • 実行ロール作成
  • Lambda 関数作成

実行ロール作成

※ 信頼関係

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

※ アタッチするポリシー例

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:GetConsoleScreenshot",
            "Resource": "arn:aws:ec2:*:*:instance/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}

適宜修正してください。

Lambda 関数

Python 3.13 で作成しました。
実行ロールでは、既存のロールを使用するを選択し、先ほど作成したロールを指定します。

import boto3
import base64
import os
import email.mime.multipart
import email.mime.text
import email.mime.application
from datetime import datetime

def lambda_handler(event, context):
    # 環境変数から設定を取得
    instance_id = os.environ.get('INSTANCE_ID')
    recipient_email = os.environ.get('RECIPIENT_EMAIL')
    sender_email = os.environ.get('SENDER_EMAIL')
    aws_region = os.environ.get('AWS_REGION')

    # イベントからパラメータを上書き(オプション)
    if 'instance_id' in event:
        instance_id = event['instance_id']
    if 'email' in event:
        recipient_email = event['email']

    # 必須パラメータの検証
    if not instance_id:
        return {
            'statusCode': 400,
            'body': 'Missing required environment variable: INSTANCE_ID'
        }

    if not recipient_email:
        return {
            'statusCode': 400,
            'body': 'Missing required environment variable: RECIPIENT_EMAIL'
        }

    if not sender_email:
        return {
            'statusCode': 400,
            'body': 'Missing required environment variable: SENDER_EMAIL'
        }

    try:
        # EC2クライアントを作成
        ec2_client = boto3.client('ec2')

        # スクリーンショットを取得
        response = ec2_client.get_console_screenshot(
            InstanceId=instance_id,
            WakeUp=True  # コンソールをウェイクアップ
        )

        # Base64エンコードされた画像をデコード
        image_data = base64.b64decode(response['ImageData'])

        # 現在の日時を取得してファイル名に使用
        timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
        filename = f"ec2-screenshot-{instance_id}-{timestamp}.png"

        # SESクライアントを作成
        ses_client = boto3.client('ses', region_name=aws_region)

        # メールの作成
        msg = email.mime.multipart.MIMEMultipart()
        msg['Subject'] = f'EC2 Instance {instance_id} Screenshot - {timestamp}'
        msg['From'] = sender_email
        msg['To'] = recipient_email

        # メール本文
        body = email.mime.text.MIMEText(
            f'EC2インスタンス {instance_id} のスクリーンショットです。\n'
            f'取得日時: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n\n'
            '自動送信メールです。'
        )
        msg.attach(body)

        # スクリーンショットを添付
        attachment = email.mime.application.MIMEApplication(image_data)
        attachment.add_header('Content-Disposition', 'attachment', filename=filename)
        msg.attach(attachment)

        # メールを送信
        ses_client.send_raw_email(
            Source=sender_email,
            Destinations=[recipient_email],
            RawMessage={'Data': msg.as_string()}
        )

        print(f"スクリーンショットを {recipient_email} に送信しました")

        return {
            'statusCode': 200,
            'body': f'Screenshot of instance {instance_id} sent to {recipient_email}'
        }

    except Exception as e:
        print(f"エラーが発生しました: {str(e)}")
        return {
            'statusCode': 500,
            'body': f'Error: {str(e)}'
        }

環境変数

Lambda 関数の「設定」タブから「環境変数」を選択し、以下の変数を設定します

▽ キー:値

  • INSTANCE_ID :i-xxxxxxxxxxxxxx (対象の EC2 インスタンス ID)
  • RECIPIENT_EMAIL :your-email@example.com (受信者のメールアドレス)
  • SENDER_EMAIL :sender@example.com (SES で検証済みの送信元メールアドレス)

003

タイムアウト設定

デフォルトのタイムアウト値(3秒)では短すぎるため、延長します。
タイムアウトを「60秒」に設定しました。

004

これで実施は終了です。お疲れ様でした!

検証してみた

作成した Lambda 関数をテストします。

005
006

Lambda 関数側では成功してます。
では EC2 インスタンスのスクリーンショットが設定しているメールアドレスへ添付されているか確認します。

結果、問題なく添付されてました。成功です!
007

なお、Windows OS でも成功しました。
008

まとめ

本ブログが誰かの参考になれば幸いです。

参考資料

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.