Help us understand the problem. What is going on with this article?

[JAWS-UG CLI] Lambda:#11 S3-Get-Object

More than 5 years have passed since last update.

前提条件

Lambdaへの権限

  • Lambdaに対してフル権限があること。
  • S3に対してフル権限があること。

CloudWatchへの権限 (任意)

CloudWatchに対してRead権限があること。

AWS CLI

以下のバージョンで動作確認済

  • AWS CLI 1.9.15
コマンド
aws --version
aws-cli/1.9.15 Python/2.7.5 Darwin/13.4.0 botocore/1.3.15

IAM Role

''ロールが存在すること。

変数の設定
IAM_ROLE_NAME='lambdaExecution'
コマンド
aws iam get-role \
  --role-name ${IAM_ROLE_NAME}
結果(例)
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Effect": "Allow",
                    "Sid": ""
                }
            ]
        },
        "RoleId": "AROAXXXXXXXXXXXX",
        "CreateDate": "2016-01-04T06:05:53Z",
        "RoleName": "lambdaExecution",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaExecution"
    }
}

存在しない場合は、以下の手順で作成します。

0. 準備

0.1. 変数の確認

プロファイルが想定のものになっていることを確認します。

変数の確認
aws configure list
結果(例)
            Name                    Value             Type    Location
            ----                    -----             ----    --------
         profile       administrator-prjz-mbp13        env    AWS_DEFAULT_PROFILE
      access_key     ****************XXXX shared-credentials-file
      secret_key     ****************XXXX shared-credentials-file
          region                ap-northeast-1        env    AWS_DEFAULT_REGION

プロファイルが想定のものになっていることを確認します。

1. 事前作業

1.1. バケットの作成 (S3)

変数の設定
ORG_NAME=<組織名>
変数の設定
S3_BUCKET_NAME="${ORG_NAME}-$(date +%Y%m%d)-mybucket" \
  && echo ${S3_BUCKET_NAME}

同名のバケットが存在しないことを確認します。(存在する場合は、別の名前に変更してください。)

コマンド
aws s3 ls ${S3_BUCKET_NAME}
結果(例)
A client error (NoSuchBucket) occurred when calling the ListObjects operation: The specified bucket does not exist
コマンド
aws s3 mb s3://${S3_BUCKET_NAME}
結果(例)
make_bucket: s3://example-mybucket-20160104/

1.2. 画像のアップロード (S3)

コマンド
curl https://dzpp79ucibp5a.cloudfront.net/groups_logos/3485_normal_1405560765_jawsug-cli-logo.png > HappyFace.jpg
コマンド
aws s3 cp HappyFace.jpg s3://${S3_BUCKET_NAME}/
結果(例)
upload: ./jawsug-cli-logo.png to s3://example-mybucket-20160104/HappyFace.jpg
コマンド
aws s3 ls s3://${S3_BUCKET_NAME}/
結果(例)
2016-01-04 19:35:49      35720 HappyFace.jpg

2. Lambda関数デプロイパッケージを作成する

2.1. フォルダ作成

  1. フォルダ(examplefolder)を作成します。
コマンド
mkdir -p ~/tmp/examplefolder \
  && cd ~/tmp/examplefolder

2.2. コード

変数の設定
LAMBDA_FUNC_NAME='s3_get_object'

テキストエディターを開き、次のコードをコピーします。

s3_get_object.js
console.log('Loading function');

var aws = require('aws-sdk');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });

exports.handler = function(event, context) {
          //console.log('Received event:', JSON.stringify(event, null, 2));

          // Get the object from the event and show its content type
          var bucket = event.Records[0].s3.bucket.name;
          var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
          var params = {
              Bucket: bucket,
              Key: key
          };
          s3.getObject(params, function(err, data) {
              if (err) {
                  console.log(err);
                  var message = "Error getting object " + key + " from bucket " + bucket +
                      ". Make sure they exist and your bucket is in the same region as this function.";
                  console.log(message);
                  context.fail(message);
              } else {
                  console.log('CONTENT TYPE:', data.ContentType);
                  context.succeed(data.ContentType);
              }
          });
};

2.3. 圧縮

フォルダーそのものではなく、フォルダーの内容を zip 圧縮します。

これが Lambda 関数デプロイパッケージです。

コマンド
zip ${LAMBDA_FUNC_NAME}.zip ${LAMBDA_FUNC_NAME}.js
結果(例)
      adding: s3_get_object.js (deflated 50%)

3. デプロイパッケージをアップロードしてテストする

3.1. Lambda関数を作成する

変数の設定
IAM_ROLE_NAME='lambdaExecution'
コマンド
aws iam get-role \
  --role-name ${IAM_ROLE_NAME}
結果(例)
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Effect": "Allow",
                    "Sid": ""
                }
            ]
        },
        "RoleId": "AROAXXXXXXXXXXXXXXXXX",
        "CreateDate": "2016-01-04T06:05:53Z",
        "RoleName": "lambdaExecution",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaExecution"
    }
}
変数の設定
IAM_ROLE_ARN=$( \
        aws iam get-role \
          --role-name ${IAM_ROLE_NAME} \
          --query 'Role.Arn' \
          --output text \
) \
        && echo ${IAM_ROLE_ARN}
コマンド
cd ~/tmp/examplefolder/ \
  && ls s3_get_object.zip
変数の確認
cat << ETX

        IAM_ROLE_ARN:    ${IAM_ROLE_ARN}
        LAMBDA_FUNC_NAME ${LAMBDA_FUNC_NAME}

ETX
コマンド
aws lambda create-function \
        --function-name ${LAMBDA_FUNC_NAME} \
        --zip-file fileb://${LAMBDA_FUNC_NAME}.zip \
        --role ${IAM_ROLE_ARN} \
        --handler ${LAMBDA_FUNC_NAME}.handler \
        --timeout 30 \
        --runtime nodejs \
        --memory-size 1024
結果(例)
{
        "CodeSha256": "mIeNn3X046rKDb412oBVpLbm+sFIQU8CvQTyJ9Kr79s=",
        "FunctionName": "s3_get_object",
        "CodeSize": 1480,
        "MemorySize": 1024,
        "FunctionArn": "arn:aws:lambda:us-west-2:019764038158:function:s3_get_object",
        "Version": "$LATEST",
        "Role": "arn:aws:iam::019764038158:role/lambdaExecution",
        "Timeout": 10,
        "LastModified": "2016-01-04T01:34:25.994+0000",
        "Handler": "s3_get_object.handler",
        "Runtime": "nodejs",
        "Description": ""
}

3.2. Lambda関数をテストする(手動で呼び出す)

変数の設定
FILE_DATA_LAMBDA="${LAMBDA_FUNC_NAME}-data.json" \
        && echo ${FILE_DATA_LAMBDA}
変数の確認
cat << ETX

        FILE_DATA_LAMBDA: ${FILE_DATA_LAMBDA}
        S3_BUCKET_NAME:   ${S3_BUCKET_NAME}

ETX
コマンド
cat << EOF > ${FILE_DATA_LAMBDA}
{
        "Records": [
          {
            "eventVersion": "2.0",
            "eventTime": "1970-01-01T00:00:00.000Z",
            "requestParameters": {
              "sourceIPAddress": "127.0.0.1"
            },
            "s3": {
              "configurationId": "testConfigRule",
              "object": {
                "eTag": "0123456789abcdef0123456789abcdef",
                "sequencer": "0A1B2C3D4E5F678901",
                "key": "HappyFace.jpg",
                "size": 35720
              },
              "bucket": {
                "arn": "arn:aws:s3:::${S3_BUCKET_NAME}",
                "name": "${S3_BUCKET_NAME}",
                "ownerIdentity": {
                  "principalId": "EXAMPLE"
                }
              },
              "s3SchemaVersion": "1.0"
            },
            "responseElements": {
              "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH",
              "x-amz-request-id": "EXAMPLE123456789"
            },
            "awsRegion": "us-west-2",
            "eventName": "ObjectCreated:Put",
            "userIdentity": {
              "principalId": "EXAMPLE"
            },
            "eventSource": "aws:s3"
          }
        ]
}
EOF

cat ${FILE_DATA_LAMBDA}

jsonlint -q ${FILE_DATA_LAMBDA}
  1. 次のLambda CLI invokeコマンドを実行して関数を呼び出します。
コマンド
aws lambda invoke \
        --invocation-type Event \
        --function-name ${LAMBDA_FUNC_NAME} \
        --payload file://${FILE_DATA_LAMBDA} \
        --log-type Tail \
        outputfile.txt
変数の設定
LOG_GROUP_NAME=$( \
        aws logs describe-log-groups \
          --query 'logGroups[*].[logGroupName]' \
          --output text
)

LOG_STREAM_NAMES=$( \
        aws logs describe-log-streams \
          --log-group-name "${LOG_GROUP_NAME}" \
          --query 'logStreams[*].logStreamName' \
          --output text
)
コマンド
echo LOG_STREAM_NAMES="'${LOG_STREAM_NAMES}'"

for i in ${LOG_STREAM_NAMES}; do
        aws logs get-log-events \
          --log-group-name "${LOG_GROUP_NAME}" \
          --log-stream-name "$i" \
          --query 'events[*].message' \
          --output text
done | less

4. Lambda関数アクセスポリシーにアクセス権限追加

変数の設定
AWS_ID=$( \
          aws iam get-role \
            --role-name ${IAM_ROLE_NAME} \
            --query 'Role.Arn' \
            --output text | \
            sed 's/^.*:://' | \
            sed 's/:.*$//' \
) \
          && echo ${AWS_ID}
変数の確認
cat << EOF

        LAMBDA_FUNC_NAME: ${LAMBDA_FUNC_NAME}
        S3_BUCKET_NAME:   ${S3_BUCKET_NAME}
        AWS_ID:           ${AWS_ID}

EOF
コマンド
aws lambda add-permission \
        --function-name ${LAMBDA_FUNC_NAME} \
        --statement-id some-unique-id \
        --action "lambda:InvokeFunction" \
        --principal s3.amazonaws.com \
        --source-arn arn:aws:s3:::${S3_BUCKET_NAME} \
        --source-account ${AWS_ID}
結果(例)
{
        "Statement": "{\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"XXXXXXXXXXXX\"},\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:s3:::opelabprj22-20160104-mybucket\"}},\"Action\":[\"lambda:InvokeFunction\"],\"Resource\":\"arn:aws:lambda:us-west-2:XXXXXXXXXXXX:function:CreateThumbnail\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"s3.amazonaws.com\"},\"Sid\":\"some-unique-id\"}"
}
コマンド
aws lambda get-policy \
  --function-name ${LAMBDA_FUNC_NAME}
結果(例)
{
        "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"XXXXXXXXXXXX\"},\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:s3:::opelabprj22-20160104-mybucket\"}},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:XXXXXXXXXXXX:function:CreateThumbnail\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"s3.amazonaws.com\"},\"Sid\":\"some-unique-id\"}],\"Id\":\"default\"}"
}

5. S3バケットの設定

5.1. 通知の設定

通知設定をS3バケットに追加するときは、以下のセクションでこれが必要になります。

コマンド
LAMBDA_FUNC_ARN=$( \
        aws lambda get-function-configuration \
          --function-name ${LAMBDA_FUNC_NAME} \
          --query 'FunctionArn' \
          --output text \
) \
        && echo ${LAMBDA_FUNC_ARN}
結果
arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:CreateThumbnail
変数の設定
FILE_S3_NOTIF="${S3_BUCKET_NAME}-notif.json"
コマンド
cat << EOF > ${FILE_S3_NOTIF}
{
        "LambdaFunctionConfigurations": [
          {
            "LambdaFunctionArn": "${LAMBDA_FUNC_ARN}",
            "Events": [
              "s3:ObjectCreated:*"
            ]
          }
        ]
}
EOF

jsonlint -q ${FILE_S3_NOTIF}
コマンド
aws s3api put-bucket-notification-configuration \
        --bucket ${S3_BUCKET_NAME} \
        --notification-configuration file://${FILE_S3_NOTIF}
コマンド
aws s3api get-bucket-notification-configuration \
        --bucket ${S3_BUCKET_NAME}
結果
      {
        "LambdaFunctionConfigurations": [
          {
              "LambdaFunctionArn": "arn:aws:lambda:us-west-2:019764038158:function:s3getobj",
              "Id": "s3getobj",
              "Events": [
                  "s3:ObjectCreated:*"
              ]
          }
        ]
      }

5.2. 画像のアップロード (S3)

コマンド
curl https://dzpp79ucibp5a.cloudfront.net/groups_logos/3485_normal_1405560765_jawsug-cli-logo.png > jawsug-cli-logo.png
コマンド
aws s3 cp jawsug-cli-logo.png s3://${S3_BUCKET_NAME}/
結果(例)
      upload: ./jawsug-cli-logo.png to s3://example-mybucket-20160104/jawsug-cli-logo.png
コマンド
aws s3 ls s3://${S3_BUCKET_NAME}/
結果(例)
      2016-01-04 19:35:49      35720 jawsug-cli-logo.png

5.3. ログ確認

変数の設定
LOG_GROUP_NAME=$( \
        aws logs describe-log-groups \
          --query 'logGroups[*].[logGroupName]' \
          --output text
)

LOG_STREAM_NAMES=$( \
        aws logs describe-log-streams \
          --log-group-name "${LOG_GROUP_NAME}" \
          --query 'logStreams[*].logStreamName' \
          --output text
)
コマンド
echo LOG_STREAM_NAMES="'${LOG_STREAM_NAMES}'"

for i in ${LOG_STREAM_NAMES}; do
        aws logs get-log-events \
          --log-group-name "${LOG_GROUP_NAME}" \
          --log-stream-name "$i" \
          --query 'events[*].message' \
          --output text
done | less

完了

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away