S3に格納されたファイルを検知し、EC2上のシェルを自動実行する


S3に格納されたファイルを検知して、EC2上のシェルを自動実行する(&SNSにて、結果をメールに送信する)

(やった手順)
1.(EC2上)シェルの作成
2.(EC2上)シェルの動作確認
3.(AWS)S3バケットの作成
4.(AWS)Lambdaの作成 & IAMの設定
5.SNSの設定(トピックの作成 & サブスクリプションの作成)
6.動作確認

(省略していること)
・EC2へのsshの接続手順(teratermなどで、接続済みの状態で記載してます)
・EC2のインスタンス名などは、知っている状態で記載しています

1.(EC2上)シェルの作成【test.sh】

(※今回は環境の都合上、「hulft」ユーザで作成しています。)
(※合わせる必要がある箇所は、※表記していきます)

(【test.sh】の中身)

#!/bin/bash
# 動作確認

# Lambdaから引数を取得
VALUE=${1}
echo "Lambdaから取得した値は【"${VALUE}"】" # Lambdaから値を取得する前提で記載

echo "実行ユーザ確認"
whoami

# ディレクトリ移動
cd /***/***/***/***/work/

echo "現在パス確認"
DIR_VALUE=$(pwd)
echo "${DIR_VALUE}"

echo "実行前確認"
ls -l
echo ""

# ファイル作成
touch ${DIR_VALUE}/${VALUE}".txt"

echo "実行後確認"
ls -l

2.(EC2上)シェルの動作確認【test.sh】

引数を渡して、「〇〇.txt」ファイルが作成されるのを確認しました

3.(AWS)S3バケットの作成

普通に作成しました。
一応リージョンは「ap-northeast-1」で統一
S3バケットの下に「lambda-fin」のサブフォルダを作成
(ここの配下に、完了ファイルを格納予定です)

4.(AWS)Lambdaの作成 & IAMの設定

こちらも普通に作成

※S3のトリガーを設定します(格納されたタイミングなので、「ObjectCreateByPut」)

※タイムアウト時間は、処理によって設定します(今回は3分)

※アクセスする実行ロールに、ポリシー
「AmazonSSMFullAccess」(SSMを用いて、Lambdaからシェル実行を行う)
「AmazonSNSFullAccess」(SNSを用いて、シェル実行後、完了メールを送信する)
を追加します

(IAM設定画面)

(※SNSの設定は、後述)

(Lambdaの中身)

import boto3
import logging
import time
import json

# ログ設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
ssm = boto3.client('ssm')
sns = boto3.client('sns')

# 対象インスタンスを定義
instance = ["i-***************"]

# 実行するコマンドを定義(作成したシェルのパス&引数)
# (※シェルの実行ユーザと合わせる必要があります)
cmd_to_test = "su hulft -c \"bash /**/**/**/**/work/test.sh 9999\""

# 結果通知用TOPICを定義
# ここのarnは、後述するSNSのarnを転記
ERROR_TOPIC = 'arn:aws:sns:ap-northeast-1:*********:*********'
 INFO_TOPIC = 'arn:aws:sns:ap-northeast-1:*********:*********'

def lambda_handler(event, context):
    
    # S3に格納されたファイルを確認
    input_bucket = event['Records'][0]['s3']['bucket']['name']
    input_key = event['Records'][0]['s3']['object']['key']
    print("バケット名:" +  input_bucket , " ファイル名:" + input_key)
    
    try:
        # デプロイコマンドを実行
        res = ssm.send_command(
            InstanceIds = instance,
            DocumentName = "AWS-RunShellScript",
            Comment = 'Executing test command...', 
            TimeoutSeconds = 100, 
            Parameters = {
                "commands": [
                    cmd_to_test
                ]
            }
        )
        command_id = res['Command']['CommandId'] # CommandIdを取得

        # 処理完了まで5秒待つ
        time.sleep(5)

        # ステータスを取得
        list_invocations = ssm.list_command_invocations(
            CommandId = command_id,
            Details = True
            )
        deploy_res = list_invocations['CommandInvocations'][0]['Status']

        # 実行結果が失敗していれば通知の上、処理を中断
        if (deploy_res!='Success'):
            # メール通知
            res = sns.publish(
                TopicArn = ERROR_TOPIC,
                Message = 'テスト結果:'+ deploy_res + 'のため、テスト失敗',
                Subject = '【エラー】テスト失敗'
            )
            return False
        # メール通知
        res = sns.publish(
            TopicArn = INFO_TOPIC,
            Message = 'テスト成功',
            Subject = 'テスト成功'
        )
        return True

    except Exception as e:
        logger.error(e)
        raise e

5.SNSの設定(トピックの作成 & サブスクリプションの作成)

トピックの作成
(※ここのARNを、Lambdaに記載する)

トピックに紐づくサブスクリプションの作成

6.動作確認

これでいよいよ、
「S3にファイルを格納」したタイミングでLambdaが実行

Lambda処理内にて、SSMを利用して「send_command」実行
(send_commandの中身は、「bash 〇〇.sh (引数) キック」)

Ec2上のシェルがキックされ、結果によって、SNS経由してメール受領
となります。

(0) EC2上に「〇〇.txtがないこと」を確認

(1) S3に(任意の)ファイルをアップロード

(2)CloudWatchで動いているのを確認

(3)EC2上に「〇〇.txtができたこと」を確認

(4)メール受信していることを確認