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

前提条件
========


Lambdaへの権限
--------------

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


CloudWatchへの権限 (任意)
------------------------

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


AWS CLI
-------

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

* AWS CLI 1.9.15

```bash:コマンド:
aws --version
```

```結果(例):
aws-cli/1.9.15 Python/2.7.5 Darwin/13.4.0 botocore/1.3.15
```

IAM Role
--------

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

```bash:変数の設定:
IAM_ROLE_NAME='lambdaExecution'
```

```bash:コマンド:
aws iam get-role \
  --role-name ${IAM_ROLE_NAME}
```

```json:結果(例):
{
    "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"
    }
}
```

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

* http://qiita.com/tcsh/items/02c9aa296bf46c2130c3


0. 準備
=======


0.1. 変数の確認
---------------

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

```bash:変数の確認:
aws configure list
```

```text:結果(例):
            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)

```bash:変数の設定:
ORG_NAME=<組織名>
```

```bash:変数の設定:
S3_BUCKET_NAME="${ORG_NAME}-$(date +%Y%m%d)-mybucket" \
  && echo ${S3_BUCKET_NAME}
```

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

```bash:コマンド:
aws s3 ls ${S3_BUCKET_NAME}
```

```text:結果(例):
A client error (NoSuchBucket) occurred when calling the ListObjects operation: The specified bucket does not exist
```

```bash:コマンド:
aws s3 mb s3://${S3_BUCKET_NAME}
```

```text:結果(例):
make_bucket: s3://example-mybucket-20160104/
```


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

```bash:コマンド:
curl https://dzpp79ucibp5a.cloudfront.net/groups_logos/3485_normal_1405560765_jawsug-cli-logo.png > HappyFace.jpg
```

```bash:コマンド:
aws s3 cp HappyFace.jpg s3://${S3_BUCKET_NAME}/
```

```text:結果(例):
upload: ./jawsug-cli-logo.png to s3://example-mybucket-20160104/HappyFace.jpg
```

```bash:コマンド:
aws s3 ls s3://${S3_BUCKET_NAME}/
```

```text:結果(例):
2016-01-04 19:35:49      35720 HappyFace.jpg
```




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

## 2.1. フォルダ作成

1. フォルダ(examplefolder)を作成します。

```bash:コマンド:
mkdir -p ~/tmp/examplefolder \
  && cd ~/tmp/examplefolder
```


## 2.2. コード

```bash:変数の設定:
LAMBDA_FUNC_NAME='s3_get_object'
```

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

```js: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 関数デプロイパッケージです。

```bash:コマンド:
zip ${LAMBDA_FUNC_NAME}.zip ${LAMBDA_FUNC_NAME}.js
```

```text:結果(例):
      adding: s3_get_object.js (deflated 50%)
```



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


## 3.1. Lambda関数を作成する

```bash:変数の設定
IAM_ROLE_NAME='lambdaExecution'
```

```bash:コマンド
aws iam get-role \
  --role-name ${IAM_ROLE_NAME}
```

```json:結果(例):
{
    "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"
    }
}
```

```bash:変数の設定:
IAM_ROLE_ARN=$( \
        aws iam get-role \
          --role-name ${IAM_ROLE_NAME} \
          --query 'Role.Arn' \
          --output text \
) \
        && echo ${IAM_ROLE_ARN}
```

```bash:コマンド:
cd ~/tmp/examplefolder/ \
  && ls s3_get_object.zip
```

```bash:変数の確認:
cat << ETX

        IAM_ROLE_ARN:    ${IAM_ROLE_ARN}
        LAMBDA_FUNC_NAME ${LAMBDA_FUNC_NAME}

ETX
```

```bash:コマンド:
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
```

```json:結果(例):
{
        "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関数をテストする(手動で呼び出す)

```bash:変数の設定:
FILE_DATA_LAMBDA="${LAMBDA_FUNC_NAME}-data.json" \
        && echo ${FILE_DATA_LAMBDA}
```

```bash:変数の確認:
cat << ETX

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

ETX
```

```bash:コマンド:
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}
```

2. 次のLambda CLI invokeコマンドを実行して関数を呼び出します。

```bash:コマンド:
aws lambda invoke \
        --invocation-type Event \
        --function-name ${LAMBDA_FUNC_NAME} \
        --payload file://${FILE_DATA_LAMBDA} \
        --log-type Tail \
        outputfile.txt
```

```bash:変数の設定:
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
)
```

```bash:コマンド:
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関数アクセスポリシーにアクセス権限追加

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

```bash:変数の確認:
cat << EOF

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

EOF
```

```bash:コマンド:
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}
```

```json:結果(例):
{
        "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\"}"
}
```

```bash:コマンド:
aws lambda get-policy \
  --function-name ${LAMBDA_FUNC_NAME}
```

```text:結果(例):
{
        "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バケットに追加するときは、以下のセクションでこれが必要になります。

```bash:コマンド:
LAMBDA_FUNC_ARN=$( \
        aws lambda get-function-configuration \
          --function-name ${LAMBDA_FUNC_NAME} \
          --query 'FunctionArn' \
          --output text \
) \
        && echo ${LAMBDA_FUNC_ARN}
```

```text:結果:
arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:CreateThumbnail
```

```bash:変数の設定:
FILE_S3_NOTIF="${S3_BUCKET_NAME}-notif.json"
```

```bash:コマンド:
cat << EOF > ${FILE_S3_NOTIF}
{
        "LambdaFunctionConfigurations": [
          {
            "LambdaFunctionArn": "${LAMBDA_FUNC_ARN}",
            "Events": [
              "s3:ObjectCreated:*"
            ]
          }
        ]
}
EOF

jsonlint -q ${FILE_S3_NOTIF}
```

```bash:コマンド:
aws s3api put-bucket-notification-configuration \
        --bucket ${S3_BUCKET_NAME} \
        --notification-configuration file://${FILE_S3_NOTIF}
```

```bash:コマンド:
aws s3api get-bucket-notification-configuration \
        --bucket ${S3_BUCKET_NAME}
```

```json:結果:
      {
        "LambdaFunctionConfigurations": [
          {
              "LambdaFunctionArn": "arn:aws:lambda:us-west-2:019764038158:function:s3getobj",
              "Id": "s3getobj",
              "Events": [
                  "s3:ObjectCreated:*"
              ]
          }
        ]
      }
```


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

```bash:コマンド:
curl https://dzpp79ucibp5a.cloudfront.net/groups_logos/3485_normal_1405560765_jawsug-cli-logo.png > jawsug-cli-logo.png
```

```bash:コマンド:
aws s3 cp jawsug-cli-logo.png s3://${S3_BUCKET_NAME}/
```

```text:結果(例):
      upload: ./jawsug-cli-logo.png to s3://example-mybucket-20160104/jawsug-cli-logo.png
```

```bash:コマンド:
aws s3 ls s3://${S3_BUCKET_NAME}/
```

```text:結果(例):
      2016-01-04 19:35:49      35720 jawsug-cli-logo.png
```


## 5.3. ログ確認

```bash:変数の設定:
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
)
```

```bash:コマンド:
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
```


完了
====