S3にファイルが作成されたときにSystems Manager Run Commandを実行するLambda関数を作成してみた

2023.06.29

S3にファイルが作成されたときにEC2にコピーするLambda関数を作成したのでブログに残します。

作成したLambda関数

以下のコードを作成しました。

from datetime import datetime
import boto3
import os

ssm = boto3.client('ssm')

instance_id = os.environ['instance_id']
now = datetime.now()
file_name = 'file_name' + now.strftime('%Y%m%d%H%M%S')

def lambda_handler(event, context):
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key']
    response = ssm.send_command(
        InstanceIds=[instance_id],
        DocumentName='AWS-RunShellScript',
        Parameters={
            'commands': [
                f'aws s3 cp s3://{bucket}/{key} /home/ec2-user/{file_name}'
            ],
            'executionTimeout': ['3600'],
        }
    )

こちらはS3にファイルが作成されるとAWS Systems Manager Run CommandでAWS-RunShellScriptドキュメントを実行します。
AWS-RunShellScriptはSystems Managerのマネージドノードに対してコマンドを実行することができるものになります。
今回は「aws s3 cp」を実行してS3で作成されたファイルをec2-userのホームディレクトリにfile_nameyyyymmddhhmmssというファイル名で作成します。
send_command

デプロイ

Lambdaの作成はAWS SAMを使用しました。
コマンドを実行する環境はCloudShellを使用しています。
今回Pythonの3.9を使用しているので以下のコマンドを実行してPython 3.9が使用できるようにします。

sudo yum install gcc openssl-devel bzip2-devel libffi-devel -y
wget https://www.python.org/ftp/python/3.9.17/Python-3.9.17.tgz
tar xzf Python-3.9.17.tgz
cd Python-3.9.17
./configure --enable-optimizations --prefix=$HOME/.local
sudo make altinstall
cd ..
sudo rm -rf Python-3.9.17 Python-3.9.17.tgz

作成したtemplate.yamlは以下になります。
今回は既にSystems ManagerにマネージドノードのEC2が追加されている前提で進めます。
マネージドノードについてはこちらのドキュメントをご確認ください。
Lambda関数以外にもS3とIAMロールも一緒に作成をしています。

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: SSM RunCommand

Parameters:
  instanceid:
    Type: String

Resources:
  FunctionIamPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "logs:CreateLogGroup"
            Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
          - Effect: Allow
            Action:
              - "logs:CreateLogStream"
              - "logs:PutLogEvents"
            Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/function-ssm-runcommand-${AWS::Region}:*"
          - Effect: Allow
            Action:
              - "ssm:SendCommand"
            Resource: "*"
      ManagedPolicyName: !Sub "policy-lambda-ssm-runcommand-${AWS::Region}"

  FunctionIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action: "sts:AssumeRole"
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
      ManagedPolicyArns:
        - !Ref FunctionIamPolicy
      RoleName: !Sub "role-lambda-ssm-runcommand-${AWS::Region}"
      Tags:
        - Key: Name
          Value: !Sub "role-lambda-ssm-runcommand-${AWS::Region}"

  S3:
    Type: AWS::S3::Bucket
    DependsOn: Function
    Properties:
      BucketName: !Sub s3-${AWS::AccountId}
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      NotificationConfiguration:
        LambdaConfigurations:
          - Event: "s3:ObjectCreated:*"
            Function: !GetAtt Function.Arn

  Function:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: function/
      Environment:
        Variables:
          instance_id: !Ref instanceid
      Events:
        S3:
          Properties:
            Bucket: !Ref S3
            Events: s3:ObjectCreated:*
          Type: S3
      FunctionName: !Sub function-ssm-runcommand-${AWS::Region}
      Handler: lambda_function.lambda_handler
      Role: !GetAtt FunctionIamRole.Arn
      Runtime: python3.9
      Timeout: 900

Lambda関数のコードとtemplate.yamlは私のGitHubリポジトリに保管しています。
今回はそこからクローンしてデプロイする手順を記載します。
以下のコマンドを実行します。

git clone https://github.com/Kobayashi-Riku0226/lambda_repository.git
cd lambda_repository/ssm_runcommand
sam build
sam deploy --stack-name スタック名 --s3-bucket CloudFormationテンプレートを格納するS3バケット --capabilities CAPABILITY_NAMED_IAM --parameter-overrides instanceid=ファイルをコピーするEC2インスタンス ID

動作確認

動作確認は上記のデプロイで作成されたS3バケットにファイルをputします。
以下のコマンドをCloudShellで実行してください。

echo "test file" > test.txt
aws s3 cp ./test.txt s3://S3バケット名

実行後、ファイルをコピーするEC2インスタンスにSSHやSession Managerで接続してec2-userのホームディレクトリを見るとファイルが作成されていることが確認できます。

ls -la /home/ec2-user/
total 20
drwx------. 3 ec2-user ec2-user 136 Jun 29 08:02 .
drwxr-xr-x. 4 root     root      38 Jun 29 06:54 ..
-rw-r--r--. 1 ec2-user ec2-user  18 Jan 28 22:29 .bash_logout
-rw-r--r--. 1 ec2-user ec2-user 141 Jan 28 22:29 .bash_profile
-rw-r--r--. 1 ec2-user ec2-user 492 Jan 28 22:29 .bashrc
drwx------. 2 ec2-user ec2-user  29 Jun 28 14:22 .ssh
-rw-r--r--. 1 root     root      10 Jun 29 08:02 file_name20230629080257

さいごに

今回はS3にファイルが作成されたときにEC2にファイルをコピーするLambda関数を作成してみました。
設定自体はシンプルなのでS3のイベント起因でコマンドを実行したいみたいな時の参考になればと思います。