DynamoDB StreamをLambda(Python)で処理する時の共通事前処理を考える

AWSリソース同士の連携でLambdaをつかうのは便利だけどeventが毎度複雑でたいへん。
今回はDynamoDB StreamをPythonで拾うとき、主要な処理以外をなんとか簡潔に書けるようにしたいチャレンジ。

DynamoDB ストリーム と AWS Lambda のトリガー – Amazon DynamoDB

共通処理の仕様

このあたりの処理を使いまわせば楽になりそう。

  • 対応する関数(lambda含む)を登録して、レコードごとに実行したい

    • 呼ばれる側はコンテキスト決め打ちでよいので、分岐とか不要
  • Itemのデータを取り出しやすくする
    • ついでにDynamoおなじみの型付きでくるので、PythonのDictに変換する
  • (Option) 例外処理

で、エントリーポイントは、こんな風に書けたらよいかなと。

handler(予定)
def lambda_handler(event, context):
    ds = DynamoStreamDispatcher(event)
    ds.on_insert.append(lambda rec: print(rec.event_name)) # lambda OK
    ds.on_remove.append(after_remove1)
    ds.on_remove.append(after_remove2) #複数の処理 OK
    ds.on_modify.append(after_modify)


    ds.dispatch()
    return True

ハンドラを登録してdispatchする感じ。

ざっくり作ってみる

とりあえず当初にきめたlambda_handlerに書きたい内容を実装してみた。

lambda_function.py
from __future__ import print_function

import json
from boto3.dynamodb.types import TypeDeserializer
deser = TypeDeserializer()

print('Loading function')


class DeRecord:
    ## Itemをデシリアライズしたもの
    def __init__(self, rec):
        self.event_name = rec['eventName']
        self.old = self._desi(rec['dynamodb'].get('OldImage'))
        self.new = self._desi(rec['dynamodb'].get('NewImage'))

    def _desi(self, image):
        d = {}
        if image:
            for key in image:
                d[key] = deser.deserialize(image[key])
        return d


class DynamoStreamDispatcher:
    def __init__(self, event):
        self.on_insert = []
        self.on_remove = []
        self.on_modify = []
        self.records   = []
        for r in event['Records']:
            # レコードはdictに加工しちゃう。
            self.records.append(DeRecord(r))

        self.raw = event

    def dispatch(self):
        """
        synced dispatcher
        """
        results = []
        for r in self.records:
            try:
                for runner in getattr(self, 'on_' + r.event_name.lower()):
                    results.append(runner(r))
            except AttributeError:
                print("Unknown event " + r.event_name)
                continue

        return results


## ここから、個別Lambda用処理の関数サンプル。引数は加工済みのレコード
def after_remove1(rec):
    print("deleted")
    return None

def after_remove2(rec):
    print(rec.old)
    return None


def after_modify(rec):
    print("key updated...")
    print(rec.old['Message'])
    print(rec.new['Message'])
    return None


def lambda_handler(event, context):
    ds = DynamoStreamDispatcher(event)
    ds.on_insert.append(lambda rec: print(rec.event_name))
    ds.on_remove.append(after_remove1)
    ds.on_remove.append(after_remove2)
    ds.on_modify.append(after_modify)

    ds.dispatch()
    return True

Sample event templateからDynamoDB Update(※末尾に付属)を流してみる。

START RequestId: 6ed79996-0ecc-11e7-8985-db0ca21254c3 Version: $LATEST
INSERT
key updated...
New item!
This item has changed
deleted
{u'Message': u'This item has changed', u'Id': Decimal('101')}
END RequestId: 6ed79996-0ecc-11e7-8985-db0ca21254c3

登録した関数がそれぞれ実行されてOK。

ほぼ自分用ライブラリだけど、PyPIに似たようなのがなければ登録して使いまわそうかな。
あとは差分とかをうまいこと格納したいね。


付録: DynamoDB Updateのサンプルイベント

{
  "Records": [
    {
      "eventID": "1",
      "eventVersion": "1.0",
      "dynamodb": {
        "Keys": {
          "Id": {
            "N": "101"
          }
        },
        "NewImage": {
          "Message": {
            "S": "New item!"
          },
          "Id": {
            "N": "101"
          }
        },
        "StreamViewType": "NEW_AND_OLD_IMAGES",
        "SequenceNumber": "111",
        "SizeBytes": 26
      },
      "awsRegion": "us-west-2",
      "eventName": "INSERT",
      "eventSourceARN": "arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
      "eventSource": "aws:dynamodb"
    },
    {
      "eventID": "2",
      "eventVersion": "1.0",
      "dynamodb": {
        "OldImage": {
          "Message": {
            "S": "New item!"
          },
          "Id": {
            "N": "101"
          }
        },
        "SequenceNumber": "222",
        "Keys": {
          "Id": {
            "N": "101"
          }
        },
        "SizeBytes": 59,
        "NewImage": {
          "Message": {
            "S": "This item has changed"
          },
          "Id": {
            "N": "101"
          }
        },
        "StreamViewType": "NEW_AND_OLD_IMAGES"
      },
      "awsRegion": "us-west-2",
      "eventName": "MODIFY",
      "eventSourceARN": "arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
      "eventSource": "aws:dynamodb"
    },
    {
      "eventID": "3",
      "eventVersion": "1.0",
      "dynamodb": {
        "Keys": {
          "Id": {
            "N": "101"
          }
        },
        "SizeBytes": 38,
        "SequenceNumber": "333",
        "OldImage": {
          "Message": {
            "S": "This item has changed"
          },
          "Id": {
            "N": "101"
          }
        },
        "StreamViewType": "NEW_AND_OLD_IMAGES"
      },
      "awsRegion": "us-west-2",
      "eventName": "REMOVE",
      "eventSourceARN": "arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
      "eventSource": "aws:dynamodb"
    }
  ]
}

参考:

続きを読む

[JAWS-UG CLI] StepFunctions #7 ステートマシンの実行 (RetryStateMachine)

前提条件

StepFunctionsへの権限

StepFunctionsに対してフル権限があること。

AWS CLIのバージョン

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

  • AWS CLI 1.11.63
コマンド
aws --version

結果(例):

  aws-cli/1.11.63 Python/2.7.10 Darwin/15.6.0 botocore/1.5.26

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        DIR_CONF:            (0.3) ${DIR_CONF}
        STEPF_STATEM_ARN:    (0.4) ${STEPF_STATEM_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) stepfunctionsas_full-prjZ-mbp13
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  DIR_CONF:            (0.3) conf-stepfunctions
  STEPF_STATEM_ARN:    (0.4) arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:RetryStateMachine

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials \
       | grep '\[' \
       | sed 's/\[//g' | sed 's/\]//g'

結果(例):

  iamFull-prjz-mbpr13
  stepfunctionsas_full-prjZ-mbp13
変数の設定
export AWS_DEFAULT_PROFILE='stepfunctionsas_full-prjZ-mbp13'

0.2. リージョンの指定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. 設定ファイル用ディレクトリの指定

変数の設定
DIR_CONF='conf-stepfunctions'
コマンド
mkdir -p ${DIR_CONF}

0.4. ステートマシンの指定

変数の設定
STEPF_STATEM_NAME='RetryStateMachine'
コマンド
STEPF_STATEM_ARN=$( \
        aws stepfunctions list-state-machines \
          --query "stateMachines[?name == \`${STEPF_STATEM_NAME}\`]".stateMachineArn \
          --output text \
) \
        && echo ${STEPF_STATEM_ARN}

結果(例):

  arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:RetryStateMachine

1. 事前作業

1.1. 実行名の決定

変数の設定
STEPF_EXEC_NAME='retry-1'

1.2. 入力の指定

変数の設定
FILE_INPUT="${DIR_CONF}/stepfunctions-exec-${STEPF_EXEC_NAME}.json" \
        && echo ${FILE_INPUT}
コマンド
cat << EOF > ${FILE_INPUT}
{
        "Comment" : "AWS Step Functions"
}
EOF

cat ${FILE_INPUT}

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

コマンド
jsonlint -q ${FILE_INPUT}

エラーが出力されなければOKです。

2. 実行

変数の確認
cat << ETX

        STEPF_STATEM_ARN: ${STEPF_STATEM_ARN}
        STEPF_EXEC_NAME:  ${STEPF_EXEC_NAME}
        FILE_INPUT:       ${FILE_INPUT}

ETX
コマンド
aws stepfunctions start-execution \
        --state-machine-arn ${STEPF_STATEM_ARN} \
        --name ${STEPF_EXEC_NAME} \
        --input file://${FILE_INPUT}

結果(例):

  {
    "startDate": 1489733645.48,
    "executionArn": "arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:RetryStateMachine:retry-1"
  }

3. 事後作業

コマンド
STEPF_EXEC_ARN=$( \
        aws stepfunctions list-executions \
          --state-machine-arn ${STEPF_STATEM_ARN} \
          --query "executions[?name == \`${STEPF_EXEC_NAME}\`].executionArn" \
          --output text \
) \
        && echo ${STEPF_EXEC_ARN}

結果(例):

  arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:RetryStateMachine:retry-1
コマンド
STEPF_EXEC_STATUS=$( \
        aws stepfunctions describe-execution \
          --execution-arn ${STEPF_EXEC_ARN} \
          --query 'status' \
          --output text \
) \
        && echo ${STEPF_EXEC_STATUS}

結果(例):

  SUCCEEDED

完了

アウトプットの確認

コマンド
STEPF_EXEC_OUTPUT=$( \
        aws stepfunctions describe-execution \
          --execution-arn ${STEPF_EXEC_ARN} \
          --query 'output' \
          --output text \
) \
        && echo ${STEPF_EXEC_OUTPUT}

結果(例):

  "This is a fallback from a custom Lambda function exception"

実行ヒストリの確認

コマンド
aws stepfunctions get-execution-history \
        --execution-arn ${STEPF_EXEC_ARN}

結果(例):

  {
    "events": [
      {
          "timestamp": 1489887775.672,
          "executionStartedEventDetails": {
              "input": "{n  "Comment" : "AWS Step Functions"n}n",
              "roleArn": "arn:aws:iam::XXXXXXXXXXXX:role/states-lambda-role"
          },
          "type": "ExecutionStarted",
          "id": 1,
          "previousEventId": 0
      },
      {
          "timestamp": 1489887775.732,
          "type": "TaskStateEntered",
          "id": 2,
          "stateEnteredEventDetails": {
              "input": "{n  "Comment" : "AWS Step Functions"n}n",
              "name": "CreateAccount"
          },
          "previousEventId": 0
      },
      {
          "timestamp": 1489887775.732,
          "lambdaFunctionScheduledEventDetails": {
              "input": "{n  "Comment" : "AWS Step Functions"n}n",
              "resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:FailFunction"
          },
          "type": "LambdaFunctionScheduled",
          "id": 3,
          "previousEventId": 2
      },
      {
          "timestamp": 1489887776.548,
          "type": "LambdaFunctionStarted",
          "id": 4,
          "previousEventId": 3
      },
      {
          "lambdaFunctionFailedEventDetails": {
              "cause": "{"errorMessage":"Account is in use!","errorType":"AccountAlreadyExistsError","stackTrace":["exports.handler (/var/task/FailFunction.js:6:42)"]}",
              "error": "AccountAlreadyExistsError"
          },
          "timestamp": 1489887776.96,
          "type": "LambdaFunctionFailed",
          "id": 5,
          "previousEventId": 4
      },
      {
          "timestamp": 1489887776.96,
          "stateExitedEventDetails": {
              "output": "{"Error":"AccountAlreadyExistsError","Cause":"{\"errorMessage\":\"Account is in use!\",\"errorType\":\"AccountAlreadyExistsError\",\"stackTrace\":[\"exports.handler (/var/task/FailFunction.js:6:42)\"]}"}",
              "name": "CreateAccount"
          },
          "type": "TaskStateExited",
          "id": 6,
          "previousEventId": 5
      },
      {
          "timestamp": 1489887776.988,
          "type": "PassStateEntered",
          "id": 7,
          "stateEnteredEventDetails": {
              "input": "{"Error":"AccountAlreadyExistsError","Cause":"{\"errorMessage\":\"Account is in use!\",\"errorType\":\"AccountAlreadyExistsError\",\"stackTrace\":[\"exports.handler (/var/task/FailFunction.js:6:42)\"]}"}",
              "name": "CustomErrorFallback"
          },
          "previousEventId": 6
      },
      {
          "timestamp": 1489887776.988,
          "stateExitedEventDetails": {
              "output": ""This is a fallback from a custom Lambda function exception"",
              "name": "CustomErrorFallback"
          },
          "type": "PassStateExited",
          "id": 8,
          "previousEventId": 7
      },
      {
          "executionSucceededEventDetails": {
              "output": ""This is a fallback from a custom Lambda function exception""
          },
          "timestamp": 1489887776.988,
          "type": "ExecutionSucceeded",
          "id": 9,
          "previousEventId": 8
      }
    ]
  }

続きを読む

[JAWS-UG CLI] StepFunctions #6 ステートマシンの作成 (RetryStateMachine)

前提条件

StepFunctionsへの権限

StepFunctionsに対してフル権限があること。

AWS CLIのバージョン

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

  • AWS CLI 1.11.63
コマンド
aws --version

結果(例):

  aws-cli/1.11.63 Python/2.7.10 Darwin/15.6.0 botocore/1.5.26

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        DIR_CONF:            (0.3) ${DIR_CONF}
        IAM_ROLE_ARN:        (0.4) ${IAM_ROLE_ARN}
        LAMBDA_FUNC_ARN:     (0.5) ${LAMBDA_FUNC_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) stepfunctionsas_full-prjZ-mbp13
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  DIR_CONF:            (0.3) conf-stepfunctions
  IAM_ROLE_ARN:        (0.4) arn:aws:iam::XXXXXXXXXXXX:role/states-lambda-role
  LAMBDA_FUNC_ARN:     (0.5) arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:FailFunction

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials \
       | grep '\[' \
       | sed 's/\[//g' | sed 's/\]//g'

結果(例):

  iamFull-prjz-mbpr13
  stepfunctionsas_full-prjZ-mbp13
変数の設定
export AWS_DEFAULT_PROFILE='stepfunctionsas_full-prjZ-mbp13'

0.2. リージョンの指定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. 設定ファイル用ディレクトリの指定

変数の設定
DIR_CONF='conf-stepfunctions'
コマンド
mkdir -p ${DIR_CONF}

0.4. IAMロールの指定

IAMロールを指定します。

変数の設定
IAM_ROLE_NAME='states-lambda-role'

ARNを取得します。

コマンド
IAM_ROLE_ARN=$( \
        aws iam get-role \
          --role-name ${IAM_ROLE_NAME} \
          --query 'Role.Arn' \
          --output text \
) \
        && echo ${IAM_ROLE_ARN}

結果(例):

  arn:aws:iam::XXXXXXXXXXXX:role/states-lambda-role

0.5. Lambda関数の指定

Lambda関数を指定します。

変数の設定
LAMBDA_FUNC_NAME='FailFunction'

ARNを取得します。

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

結果(例):

  arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:FailFunction

1. 事前作業

1.1. ステートマシン名の決定

変数の設定
STEPF_STATEM_NAME='RetryStateMachine'

同名のステートマシンが存在しないことを確認します。

コマンド
aws stepfunctions list-state-machines \
        --query "stateMachines[?name == \`${STEPF_STATEM_NAME}\`]"

結果(例):

  []

1.2. ステートマシン定義ファイルの作成

変数の設定
FILE_INPUT="${STEPF_STATEM_NAME}.json" \
        && echo ${FILE_INPUT}
コマンド
cat << EOF > ${FILE_INPUT}
{
         "Comment": "A Catch example of the Amazon States Language using an AWS Lambda Function",
         "StartAt": "CreateAccount",
         "States": {
            "CreateAccount": {
               "Type": "Task",
               "Resource": "${LAMBDA_FUNC_ARN}",
               "Catch": [ {
                  "ErrorEquals": ["AccountAlreadyExistsError"],
                  "Next": "CustomErrorFallback"
               }, {
                  "ErrorEquals": ["States.TaskFailed"],
                  "Next": "ReservedTypeFallback"
               }, {
                  "ErrorEquals": ["States.ALL"],
                  "Next": "CatchAllFallback"
               } ],
              "End": true
            },
            "CustomErrorFallback": {
               "Type": "Pass",
               "Result": "This is a fallback from a custom Lambda function exception",
               "End": true
            },
            "ReservedTypeFallback": {
               "Type": "Pass",
               "Result": "This is a fallback from a reserved error code",
               "End": true
            },
            "CatchAllFallback": {
               "Type": "Pass",
               "Result": "This is a fallback from any error code",
               "End": true
            }
         }
}
EOF

cat ${FILE_INPUT}

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

コマンド
jsonlint -q ${FILE_INPUT}

エラーが出力されなければOKです。

2. ステートマシンの作成

変数の確認
cat << ETX

        STEPF_STATEM_NAME: ${STEPF_STATEM_NAME}
        FILE_INPUT:        ${FILE_INPUT}
        IAM_ROLE_ARN:      ${IAM_ROLE_ARN}

ETX
コマンド
aws stepfunctions create-state-machine \
        --name ${STEPF_STATEM_NAME} \
        --definition file://${FILE_INPUT} \
        --role-arn ${IAM_ROLE_ARN}

結果():

      {
        "creationDate": 1489740033.593,
        "stateMachineArn": "arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:RetryStateMachine"
      }

3. 事後作業

3.1. ARNの取得

コマンド
STEPF_STATEM_ARN=$( \
        aws stepfunctions list-state-machines \
          --query "stateMachines[?name == \`${STEPF_STATEM_NAME}\`]".stateMachineArn \
          --output text \
) \
        && echo ${STEPF_STATEM_ARN}

結果(例):

  arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:RetryStateMachine

3.2. ステートマシンのステータス確認

コマンド
STEPF_STATEM_STATUS=$( \
        aws stepfunctions describe-state-machine \
          --state-machine-arn ${STEPF_STATEM_ARN} \
          --query "status" \
          --output text \
) \
        && echo ${STEPF_STATEM_STATUS}

結果(例):

  ACTIVE

3.3. ステートマシンの確認

コマンド
aws stepfunctions describe-state-machine \
        --state-machine-arn ${STEPF_STATEM_ARN}

結果(例):

  (yet)

完了

続きを読む

[JAWS-UG CLI] Lambda #25 StepFunctions用関数 (FailFunction)

前提条件

Lambdaへの権限

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

AWS CLI

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

  • AWS CLI 1.10.60
コマンド
aws --version

結果(例):

  aws-cli/1.11.34 Python/2.7.10 Darwin/15.6.0 botocore/1.4.91

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

IAM Role

‘lambdaBasicExecution’ロールが存在すること。

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        IAM_ROLE_ARN:        (0.3) ${IAM_ROLE_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) lambdaFull-prjz-mbp13
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  IAM_ROLE_ARN:        (0.3) arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials \
       | grep '\[' \
       | sed 's/\[//g' | sed 's/\]//g'

結果(例):

  iamFull-prjz-mbpr13
  lambdaFull-prjz-mbp13
変数の設定
export AWS_DEFAULT_PROFILE='lambdaFull-prjz-mbp13'

0.2. リージョンの指定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. IAMロールの指定

変数の設定
IAM_ROLE_NAME='lambdaBasicExecution'
コマンド
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": "2017-03-19T01:23:45Z",
        "RoleName": "lambdaBasicExecution",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution"
      }
  }
コマンド
IAM_ROLE_ARN=$( \
        aws iam get-role \
          --role-name ${IAM_ROLE_NAME} \
          --query 'Role.Arn' \
          --output text \
) \
        && echo ${IAM_ROLE_ARN}

結果(例):

  arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution

1. 事前作業

1.1. Lambda関数名の決定

変数の設定
LAMBDA_FUNC_NAME='FailFunction'

同名のLambda関数の不存在確認

コマンド
aws lambda get-function \
        --function-name ${LAMBDA_FUNC_NAME}

結果(例):

  A client error (ResourceNotFoundException) occurred when calling the GetFunction operation: Function not found: arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:FailFunction

1.2. Lambda関数

変数の設定
FILE_LAMBDA_FUNC='FailFunction.js'
コマンド
cat << EOF > ${FILE_LAMBDA_FUNC}
exports.handler = function(event, context, callback) {
   function AccountAlreadyExistsError(message) {
       this.name = "AccountAlreadyExistsError";
       this.message = message;
   }
   AccountAlreadyExistsError.prototype = new Error();

   const error = new AccountAlreadyExistsError("Account is in use!");
   callback(error);
};
EOF
  cat ${FILE_LAMBDA_FUNC}

結果(例):

  exports.handler = function(event, context, callback) {
     function AccountAlreadyExistsError(message) {
         this.name = "AccountAlreadyExistsError";
         this.message = message;
     }
     AccountAlreadyExistsError.prototype = new Error();

     const error = new AccountAlreadyExistsError("Account is in use!");
     callback(error);
  };
コマンド
zip ${LAMBDA_FUNC_NAME}.zip ${FILE_LAMBDA_FUNC}

結果:

  adding: FailFunction.js (deflated 43%)

2. Lambda関数の作成

変数の設定
LAMBDA_FUNC_DESC='Always generate an error.'
LAMBDA_RUNTIME='nodejs4.3'
LAMBDA_HANDLER="${LAMBDA_FUNC_NAME}.handler"
FILE_LAMBDA_ZIP="${LAMBDA_FUNC_NAME}.zip"
変数の確認
cat << ETX

        LAMBDA_FUNC_NAME:  ${LAMBDA_FUNC_NAME}
        LAMBDA_FUNC_DESC: "${LAMBDA_FUNC_DESC}"
        LAMBDA_RUNTIME:    ${LAMBDA_RUNTIME}
        FILE_LAMBDA_ZIP    ${FILE_LAMBDA_ZIP}
        IAM_ROLE_ARN:      ${IAM_ROLE_ARN}
        LAMBDA_HANDLER:    ${LAMBDA_HANDLER}

ETX
コマンド
aws lambda create-function \
        --function-name ${LAMBDA_FUNC_NAME} \
        --description "${LAMBDA_FUNC_DESC}" \
        --zip-file fileb://${FILE_LAMBDA_ZIP} \
        --runtime ${LAMBDA_RUNTIME} \
        --role ${IAM_ROLE_ARN} \
        --handler ${LAMBDA_HANDLER}

結果(例):

  {
    "CodeSha256": "uW+ZzaP1iDzaUfhFUyed0CdaOcSxbcsFd7yJXjHbWX8=",
    "FunctionName": "FailFunction",
    "CodeSize": 353,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:FailFunction",
    "Version": "$LATEST",
    "Role": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution",
    "Timeout": 3,
    "LastModified": "2017-03-19T01:23:45.678+0000",
    "Handler": "FailFunction.handler",
    "Runtime": "nodejs4.3",
    "Description": "Always generate an error."
  }
コマンド
aws lambda get-function \
        --function-name ${LAMBDA_FUNC_NAME}

結果(例):

  {
    "Code": {
      "RepositoryType": "S3",
      "Location": "https://awslambda-ap-ne-1-tasks.s3-ap-northeast-1.amazonaws.com/snapshots/XXXXXXXXXXXX/FailFunction-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?x-amz-security-token=AQxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&AWSAccessKeyId=ASIAXXXXXXXXXXXXXXXX&Expires=xxxxxxxxxx&Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  },
  "Configuration": {
      "Version": "$LATEST",
      "CodeSha256": "uW+ZzaP1iDzaUfhFUyed0CdaOcSxbcsFd7yJXjHbWX8=",
      "FunctionName": "FailFunction",
      "MemorySize": 128,
      "CodeSize": 353,
      "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:FailFunction",
      "Handler": "FailFunction.handler",
      "Role": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution",
      "Timeout": 3,
      "LastModified": "2017-03-19T01:23:45.678+0000",
      "Runtime": "nodejs4.3",
      "Description": "Always generate an error."
    }
  }
コマンド
aws lambda get-function-configuration \
        --function-name ${LAMBDA_FUNC_NAME}

結果(例):

  {
    "CodeSha256": "uW+ZzaP1iDzaUfhFUyed0CdaOcSxbcsFd7yJXjHbWX8=",
    "FunctionName": "FailFunction",
    "CodeSize": 353,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:FailFunction",
    "Version": "$LATEST",
    "Role": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution",
    "Timeout": 3,
    "LastModified": "2017-03-19T01:23:45.678+0000",
    "Handler": "FailFunction.handler",
    "Runtime": "nodejs4.3",
    "Description": "Always generate an error."
  }

3. Lambda関数の動作確認

3.1. サンプルデータの作成

変数の設定
FILE_INPUT="${LAMBDA_FUNC_NAME}-data.json" \
          && echo ${FILE_INPUT}

サンプルデータ:

  cat << EOF > ${FILE_INPUT}
  {
    "key3": "value3",
    "key2": "value2",
    "key1": "value1"
  }
  EOF

  cat ${FILE_INPUT}

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

コマンド
jsonlint -q ${FILE_INPUT}

エラーが出力されなければOKです。

3.2. lambda関数の手動実行

変数の設定
FILE_OUTPUT_LAMBDA="${LAMBDA_FUNC_NAME}-out.txt"
FILE_LOG_LAMBDA="${LAMBDA_FUNC_NAME}-$(date +%Y%m%d%H%M%S).log"
変数の確認
cat << ETX

        LAMBDA_FUNC_NAME:   ${LAMBDA_FUNC_NAME}
        FILE_INPUT:         ${FILE_INPUT}
        FILE_OUTPUT_LAMBDA: ${FILE_OUTPUT_LAMBDA}
        FILE_LOG_LAMBDA:    ${FILE_LOG_LAMBDA}

ETX
コマンド
aws lambda invoke \
        --function-name ${LAMBDA_FUNC_NAME} \
        --log-type Tail \
        --payload file://${FILE_INPUT} \
        ${FILE_OUTPUT_LAMBDA} \
        > ${FILE_LOG_LAMBDA}
コマンド
cat ${FILE_LOG_LAMBDA} \
        | jp.py 'StatusCode'

結果(例):

  200

3.3. lambda関数の実行結果の確認

コマンド
cat ${FILE_OUTPUT_LAMBDA}

結果:

  {"errorMessage":"Account is in use!","errorType":"AccountAlreadyExistsError","stackTrace":["exports.handler (/var/task/FailFunction.js:6:42)"]}

3.4. lambda関数のログの確認

コマンド
cat ${FILE_LOG_LAMBDA} \
        | jp.py 'LogResult' \
        | sed 's/\"//g' \
        | base64 --decode

結果(例):

  START RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Version: $LATEST
  2017-03-19T01:23:45.678Z     xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx    {"errorMessage":"Account is in use!","errorType":"AccountAlreadyExistsError","stackTrace":["exports.handler (/var/task/FailFunction.js:6:42)"]}
  REPORT RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx      Duration: 41.14 ms      Billed Duration: 100 ms         Memory Size: 128 MB        Max Memory Used: 10 MB

完了

続きを読む

[JAWS-UG CLI] StepFunctions #5 ステートマシンの実行 (LambdaStateMachine)

前提条件

StepFunctionsへの権限

StepFunctionsに対してフル権限があること。

AWS CLIのバージョン

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

  • AWS CLI 1.11.57
コマンド
aws --version

結果(例):

  aws-cli/1.11.34 Python/2.7.10 Darwin/15.6.0 botocore/1.4.91

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        DIR_CONF:            (0.3) ${DIR_CONF}
        STEPF_STATEM_ARN:    (0.4) ${STEPF_STATEM_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) stepfunctionsas_full-prjZ-mbp13
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  DIR_CONF:            (0.3) conf-stepfunctions
  STEPF_STATEM_ARN:    (0.4) arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:LambdaStateMachine

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials \
       | grep '\[' \
       | sed 's/\[//g' | sed 's/\]//g'

結果(例):

  iamFull-prjz-mbpr13
  stepfunctionsas_full-prjZ-mbp13
変数の設定
export AWS_DEFAULT_PROFILE='stepfunctionsas_full-prjZ-mbp13'

0.2. リージョンの指定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. 設定ファイル用ディレクトリの指定

変数の設定
DIR_CONF='conf-stepfunctions'
コマンド
mkdir -p ${DIR_CONF}

0.4. ステートマシンの指定

変数の設定
STEPF_STATEM_NAME='LambdaStateMachine'
コマンド
STEPF_STATEM_ARN=$( \
        aws stepfunctions list-state-machines \
          --query "stateMachines[?name == \`${STEPF_STATEM_NAME}\`]".stateMachineArn \
          --output text \
) \
        && echo ${STEPF_STATEM_ARN}

結果(例):

  arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:LambdaStateMachine

1. 事前作業

1.1. 実行名の決定

変数の設定
STEPF_EXEC_NAME='lambda-1'

1.2. 入力の指定

変数の設定
FILE_INPUT="${DIR_CONF}/stepfunctions-exec-${STEPF_EXEC_NAME}.json" \
        && echo ${FILE_INPUT}
コマンド
cat << EOF > ${FILE_INPUT}
{
        "who" : "AWS Step Functions"
}
EOF

cat ${FILE_INPUT}

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

コマンド
jsonlint -q ${FILE_INPUT}

エラーが出力されなければOKです。

2. 実行

変数の確認
cat << ETX

        STEPF_STATEM_ARN: ${STEPF_STATEM_ARN}
        STEPF_EXEC_NAME:  ${STEPF_EXEC_NAME}
        FILE_INPUT:       ${FILE_INPUT}

ETX
コマンド
aws stepfunctions start-execution \
        --state-machine-arn ${STEPF_STATEM_ARN} \
        --name ${STEPF_EXEC_NAME} \
        --input file://${FILE_INPUT}

結果(例):

  {
    "startDate": 1489733645.48,
    "executionArn": "arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:LambdaStateMachine:lambda-1"
  }

3. 事後作業

コマンド
STEPF_EXEC_ARN=$( \
        aws stepfunctions list-executions \
          --state-machine-arn ${STEPF_STATEM_ARN} \
          --query "executions[?name == \`${STEPF_EXEC_NAME}\`].executionArn" \
          --output text \
) \
        && echo ${STEPF_EXEC_ARN}

結果(例):

  arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:LambdaStateMachine:lambda-1
コマンド
STEPF_EXEC_STATUS=$( \
        aws stepfunctions describe-execution \
          --execution-arn ${STEPF_EXEC_ARN} \
          --query 'status' \
          --output text \
) \
        && echo ${STEPF_EXEC_STATUS}

結果(例):

  SUCCEEDED

完了

アウトプットの確認

コマンド
STEPF_EXEC_OUTPUT=$( \
        aws stepfunctions describe-execution \
          --execution-arn ${STEPF_EXEC_ARN} \
          --query 'output' \
          --output text \
) \
        && echo ${STEPF_EXEC_OUTPUT}

結果(例):

  "Hello, AWS Step Functions!"

実行ヒストリの確認

コマンド
aws stepfunctions get-execution-history \
        --execution-arn ${STEPF_EXEC_ARN}

結果(例):

  {
    "events": [
      {
          "timestamp": 1489839841.46,
          "executionStartedEventDetails": {
              "input": "{\n  \"who\" : \"AWS Step Functions\"\n}\n",
              "roleArn": "arn:aws:iam::XXXXXXXXXXXX:role/StepFunctionsRole-ap-northeast-1"
          },
          "type": "ExecutionStarted",
          "id": 1,
          "previousEventId": 0
      },
      {
          "timestamp": 1489839841.507,
          "type": "TaskStateEntered",
          "id": 2,
          "stateEnteredEventDetails": {
              "input": "{\n  \"who\" : \"AWS Step Functions\"\n}\n",
              "name": "HelloWorld"
          },
          "previousEventId": 0
      },
      {
          "timestamp": 1489839841.507,
          "lambdaFunctionScheduledEventDetails": {
              "input": "{\n  \"who\" : \"AWS Step Functions\"\n}\n",
              "resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction"
          },
          "type": "LambdaFunctionScheduled",
          "id": 3,
          "previousEventId": 2
      },
      {
          "timestamp": 1489839842.291,
          "type": "LambdaFunctionStarted",
          "id": 4,
          "previousEventId": 3
      },
      {
          "lambdaFunctionSucceededEventDetails": {
              "output": "\"Hello, AWS Step Functions!\""
          },
          "timestamp": 1489839842.587,
          "type": "LambdaFunctionSucceeded",
          "id": 5,
          "previousEventId": 4
      },
      {
          "timestamp": 1489839842.587,
          "stateExitedEventDetails": {
              "output": "\"Hello, AWS Step Functions!\"",
              "name": "HelloWorld"
          },
          "type": "TaskStateExited",
          "id": 6,
          "previousEventId": 5
      },
      {
          "executionSucceededEventDetails": {
              "output": "\"Hello, AWS Step Functions!\""
          },
          "timestamp": 1489839842.587,
          "type": "ExecutionSucceeded",
          "id": 7,
          "previousEventId": 6
      }
    ]
  }

続きを読む

[JAWS-UG CLI] StepFunctions #4 ステートマシンの作成 (LambdaStateMachine)

前提条件

StepFunctionsへの権限

StepFunctionsに対してフル権限があること。

AWS CLIのバージョン

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

  • AWS CLI 1.11.57
コマンド
aws --version

結果(例):

  aws-cli/1.11.34 Python/2.7.10 Darwin/15.6.0 botocore/1.4.91

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        IAM_ROLE_ARN:        (0.3) ${IAM_ROLE_ARN}
        LAMBDA_FUNC_ARN:     (0.4) ${LAMBDA_FUNC_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) stepfunctionsas_full-prjZ-mbp13
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  IAM_ROLE_ARN:        (0.3) arn:aws:iam::XXXXXXXXXXXX:role/StepFunctionsRole-ap-northeast-1
  LAMBDA_FUNC_ARN:     (0.4) arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. リージョンの指定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.2. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials \
       | grep '\[' \
       | sed 's/\[//g' | sed 's/\]//g'

結果(例):

  iamFull-prjz-mbpr13
  stepfunctionsas_full-prjZ-mbp13
変数の設定
export AWS_DEFAULT_PROFILE='stepfunctionsas_full-prjZ-mbp13'

0.3. IAMロールの指定

IAMロールを指定します。

変数の設定
IAM_ROLE_NAME="StepFunctionsRole-${AWS_DEFAULT_REGION}"

ARNを取得します。

コマンド
IAM_ROLE_ARN=$( \
        aws iam get-role \
          --role-name ${IAM_ROLE_NAME} \
          --query 'Role.Arn' \
          --output text \
) \
        && echo ${IAM_ROLE_ARN}

結果(例):

  arn:aws:iam::XXXXXXXXXXXX:role/StepFunctionsRole-ap-northeast-1

0.4. Lambda関数の指定

Lambda関数を指定します。

変数の設定
LAMBDA_FUNC_NAME='HelloFunction'

ARNを取得します。

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

結果(例):

  arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction

1. 事前作業

1.1. ステートマシン名の決定

変数の設定
STEPF_STATEM_NAME='LambdaStateMachine'

同名のステートマシンが存在しないことを確認します。

コマンド
aws stepfunctions list-state-machines \
        --query "stateMachines[?name == \`${STEPF_STATEM_NAME}\`]"

結果(例):

  []

1.2. ステートマシン定義ファイルの作成

変数の設定
FILE_INPUT="${STEPF_STATEM_NAME}.json" \
        && echo ${FILE_INPUT}
変数の確認
cat << ETX

       FILE_INPUT:      ${FILE_INPUT}
       LAMBDA_FUNC_ARN: ${LAMBDA_FUNC_ARN}

ETX
コマンド
cat << EOF > ${FILE_INPUT}
{
        "Comment": "A Hello World example of the Amazon States Language using an AWS Lambda Function",
        "StartAt": "HelloWorld",
        "States": {
          "HelloWorld": {
            "Type": "Task",
            "Resource": "${LAMBDA_FUNC_ARN}",
            "End": true
          }
        }
}
EOF

      cat ${FILE_INPUT}

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

コマンド
jsonlint -q ${FILE_INPUT}

エラーが出力されなければOKです。

2. ステートマシンの作成

変数の確認
cat << ETX

        STEPF_STATEM_NAME: ${STEPF_STATEM_NAME}
        FILE_INPUT:        ${FILE_INPUT}
        IAM_ROLE_ARN:      ${IAM_ROLE_ARN}

ETX
コマンド
aws stepfunctions create-state-machine \
        --name ${STEPF_STATEM_NAME} \
        --definition file://${FILE_INPUT} \
        --role-arn ${IAM_ROLE_ARN}

結果(例):

  {
    "creationDate": 1489740033.593,
    "stateMachineArn": "arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:LambdaStateMachine"
  }

3. 事後作業

3.1. ARNの取得

コマンド
STEPF_STATEM_ARN=$( \
        aws stepfunctions list-state-machines \
          --query "stateMachines[?name == \`${STEPF_STATEM_NAME}\`]".stateMachineArn \
          --output text \
) \
        && echo ${STEPF_STATEM_ARN}

結果(例):

  arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:LambdaStateMachine

3.2. ステートマシンのステータス確認

コマンド
STEPF_STATEM_STATUS=$( \
        aws stepfunctions describe-state-machine \
          --state-machine-arn ${STEPF_STATEM_ARN} \
          --query "status" \
          --output text \
) \
        && echo ${STEPF_STATEM_STATUS}

結果(例):

  ACTIVE

3.3. ステートマシンの確認

コマンド
aws stepfunctions describe-state-machine \
        --state-machine-arn ${STEPF_STATEM_ARN}

結果(例):

  {
    "status": "ACTIVE",
    "definition": "{\n  \"Comment\": \"A Hello World example of the Amazon States Language using an AWS Lambda Function\",\n  \"StartAt\": \"HelloWorld\",\n  \"States\": {\n    \"HelloWorld\": {\n      \"Type\": \"Task\",\n      \"Resource\": \"arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction\",\n      \"End\": true\n    }\n  }\n}\n",
    "name": "LambdaStateMachine",
    "roleArn": "arn:aws:iam::XXXXXXXXXXXX:role/StepFunctionsRole-ap-northeast-1",
    "stateMachineArn": "arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:LambdaStateMachine",
    "creationDate": 1489838417.466
  }

完了

続きを読む

[JAWS-UG CLI] IAM #75 IAMロールのポリシー追加 (AWSLambdaRole / AWS)

まず最初に、 今回実行するLambda関数に必要な権限を付与するためのIAMロールを作成していきます。

Lambda関数を実行する上で、最低限必要な権限となります。

前提条件

IAMへの権限

IAMに対してフル権限があること。

AWS CLI

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

  • AWS CLI 1.10.60
コマンド
aws --version

結果(例):

  aws-cli/1.11.34 Python/2.7.10 Darwin/15.6.0 botocore/1.4.91

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

0.1. 変数の確認

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

変数の確認
aws configure list

結果(例):

        Name                    Value             Type    Location
        ----                    -----             ----    --------
     profile       iamFull-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

今回は、IAMでの作業となるため、リージョンはどこになっていても影響あり
ません。

0.2. IAMロール名の指定

ポリシーをアタッチするIAMロールを指定します。

変数の設定
IAM_ROLE_NAME="StepFunctionsRole-${AWS_DEFAULT_REGION}" \
  && echo ${IAM_ROLE_NAME}

IAMロールの内容を確認しましょう。

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

結果(例):

  {
      "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Principal": {
                        "Service": "states.${AWS_DEFAULT_REGION}.amazonaws.com"
                    },
                    "Effect": "Allow",
                    "Sid": ""
                }
            ]
        },
        "RoleId": "AROAXXXXXXXXXXXXXXXXX",
        "CreateDate": "2017-03-18T01:23:45Z",
        "RoleName": "StepFunctionsRole-ap-northeast-1",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/StepFunctionsRole-ap-northeast-1"
      }
  }

1. 事前作業

次に、このIAMロールにIAMポリシーを適用して、このIAMロールを利用するこ
とで得られる権限を決定します。

1.1. IAMロールに適用するIAMポリシーの決定

今回のStepFunctionsを実行する上で必要となる権限については、すでにAWS管
理ポリシーで”AWSLambdaRole’という名前で提供されています。

まず、’Lambda’というキーワードでAWS管理ポリシーから検索してみましょう

変数の設定
SEARCH_KEYWORD='Lambda'
コマンド
aws iam list-policies \
        --scope AWS \
        --max-items 1000 \
        --query "Policies[?contains(PolicyName, \`${SEARCH_KEYWORD}\`)].PolicyName"

結果(例):

  [
    "AWSLambdaFullAccess",
    "AWSLambdaDynamoDBExecutionRole",
    "AWSLambdaExecute",
    "AWSLambdaKinesisExecutionRole",
    "AWSLambdaReadOnlyAccess",
    "AWSLambdaBasicExecutionRole",
    "AWSLambdaInvocation-DynamoDB",
    "AWSLambdaVPCAccessExecutionRole",
    "AWSLambdaRole",
    "AWSLambdaENIManagementAccess"
  ]

利用するIAMポリシーを決めます。

変数の設定
IAM_POLICY_NAME='AWSLambdaRole'

次に、ポリシーを特定するためのAWSリソース識別名(ARN)を取得します。

変数の設定
IAM_POLICY_ARN=$( \
        aws iam list-policies \
          --max-items 1000 \
          --query "Policies[?PolicyName==\`${IAM_POLICY_NAME}\`].Arn" \
          --output text \
) \
        && echo "${IAM_POLICY_ARN}"

結果(例):

  arn:aws:iam::aws:policy/service-role/AWSLambdaRole

ポリシーの内容を見るために、そのポリシーの最新バージョン名を取得します

コマンド
IAM_POLICY_VERSION=$( \
        aws iam list-policies \
          --max-items 1000 \
          --query "Policies[?PolicyName==\`${IAM_POLICY_NAME}\`].DefaultVersionId" \
          --output text \
) \
        && echo ${IAM_POLICY_VERSION}

結果(例)

  v1

ポリシーの最新バージョンの内容を見てみましょう。

コマンド
aws iam get-policy-version \
        --policy-arn ${IAM_POLICY_ARN} \
        --version-id ${IAM_POLICY_VERSION}

結果(例):

  {
      "PolicyVersion": {
          "CreateDate": "2015-02-06T18:41:28Z", 
          "VersionId": "v1", 
          "Document": {
              "Version": "2012-10-17", 
              "Statement": [
                  {
                      "Action": [
                          "lambda:InvokeFunction"
                      ], 
                      "Resource": [
                          "*"
                      ], 
                      "Effect": "Allow"
                  }
              ]
          }, 
          "IsDefaultVersion": true
      }
  }

以下の権限を許可されていることがわかります。

  • lambda:InvokeFunction

これは、Lambda関数を実行するために必要な権限です。

1.2. IAMポリシーの確認

IAMロールに適用されているIAMポリシーを確認してみましょう。

コマンド
aws iam list-attached-role-policies \
        --role-name ${IAM_ROLE_NAME}

結果:

  {
      "AttachedPolicies": [], 
  }

作成直後のIAMロールには、適用されているIAMポリシーはありません。

2. IAMポリシーの適用

IAMロールにIAMポリシーを適用します。

変数の確認
cat << ETX

        IAM_ROLE_NAME:  ${IAM_ROLE_NAME}
        IAM_POLICY_ARN: ${IAM_POLICY_ARN}

ETX
コマンド
aws iam attach-role-policy \
        --role-name ${IAM_ROLE_NAME} \
        --policy-arn ${IAM_POLICY_ARN}

結果:

  (戻り値なし)

3. 事後作業

ポリシーの確認

IAMロールに適用されているIAMポリシーを再度確認してみましょう。

コマンド
aws iam list-attached-role-policies \
        --role-name ${IAM_ROLE_NAME}

結果(例):

  {
    "AttachedPolicies": [
      {
          "PolicyName": "AWSLambdaRole",
          "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaRole"
      }
    ]
  }

完了

以上で、今回実行するStepFunctionsに必要なIAMロールの作成は完了です。

続きを読む

[JAWS-UG CLI] Lambda #24 StepFunctions用関数 (Hello World)

前提条件

Lambdaへの権限

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

AWS CLI

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

  • AWS CLI 1.10.63
コマンド
aws --version

結果(例):

  aws-cli/1.11.63 Python/2.7.10 Darwin/15.6.0 botocore/1.5.26

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

IAM Role

‘lambdaBasicExecution’ロールが存在すること。

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:  (0.2) ${AWS_DEFAULT_REGION}
        IAM_ROLE_ARN:        (0.3) ${IAM_ROLE_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) lambdaFull-prjz-mbp13
  AWS_DEFAULT_REGION:  (0.2) ap-northeast-1
  IAM_ROLE_ARN:        (0.3) arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials \
       | grep '\[' \
       | sed 's/\[//g' | sed 's/\]//g'

結果(例):

  iamFull-prjz-mbpr13
  lambdaFull-prjz-mbp13
変数の設定
export AWS_DEFAULT_PROFILE='lambdaFull-prjz-mbp13'

0.2. リージョンの指定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. IAMロールの指定

変数の設定
IAM_ROLE_NAME='lambdaBasicExecution'
コマンド
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": "2017-03-18T01:23:45Z",
        "RoleName": "lambdaBasicExecution",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution"
      }
  }
コマンド
IAM_ROLE_ARN=$( \
        aws iam get-role \
          --role-name ${IAM_ROLE_NAME} \
          --query 'Role.Arn' \
          --output text \
) \
        && echo ${IAM_ROLE_ARN}

結果(例):

  arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution

1. 事前作業

1.1. Lambda関数名の決定

変数の設定
LAMBDA_FUNC_NAME='HelloFunction'

同名のLambda関数の不存在確認

コマンド
aws lambda get-function \
        --function-name ${LAMBDA_FUNC_NAME}

結果(例):

  A client error (ResourceNotFoundException) occurred when calling the GetFunction operation: Function not found: arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction

1.2. Lambda関数

変数の設定
FILE_LAMBDA_FUNC='HelloFunction.js'
コマンド
cat << EOF > ${FILE_LAMBDA_FUNC}
exports.handler = (event, context, callback) => {
    callback(null, "Hello, " + event.who + "!");
};
EOF

cat ${FILE_LAMBDA_FUNC}

結果(例):

  exports.handler = (event, context, callback) => {
      callback(null, "Hello, " + event.who + "!");
  };
コマンド
zip ${LAMBDA_FUNC_NAME}.zip ${FILE_LAMBDA_FUNC}

結果:

  adding: HelloFunction.js (deflated 43%)

2. Lambda関数の作成

変数の設定
LAMBDA_FUNC_DESC='Say Hello to someone'
LAMBDA_RUNTIME='nodejs4.3'
LAMBDA_HANDLER="${LAMBDA_FUNC_NAME}.handler"
FILE_LAMBDA_ZIP="${LAMBDA_FUNC_NAME}.zip"
変数の確認
cat << ETX

        LAMBDA_FUNC_NAME:  ${LAMBDA_FUNC_NAME}
        LAMBDA_FUNC_DESC: "${LAMBDA_FUNC_DESC}"
        LAMBDA_RUNTIME:    ${LAMBDA_RUNTIME}
        FILE_LAMBDA_ZIP    ${FILE_LAMBDA_ZIP}
        IAM_ROLE_ARN:      ${IAM_ROLE_ARN}
        LAMBDA_HANDLER:    ${LAMBDA_HANDLER}

ETX
コマンド
aws lambda create-function \
        --function-name ${LAMBDA_FUNC_NAME} \
        --description "${LAMBDA_FUNC_DESC}" \
        --zip-file fileb://${FILE_LAMBDA_ZIP} \
        --runtime ${LAMBDA_RUNTIME} \
        --role ${IAM_ROLE_ARN} \
        --handler ${LAMBDA_HANDLER}

結果(例):

  {
    "CodeSha256": "aNNHoAV+4ERLB3aO2i72HiCZzsDDNF/22V4HSO+7Gk0=",
    "FunctionName": "HelloFunction",
    "CodeSize": 270,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction",
    "Version": "$LATEST",
    "Role": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution",
    "Timeout": 3,
    "LastModified": "2017-03-18T01:23:45.678+0000",
    "Handler": "HelloFunction.handler",
    "Runtime": "nodejs4.3",
    "Description": "Say Hello to someone"
  }
コマンド
aws lambda get-function \
        --function-name ${LAMBDA_FUNC_NAME}

結果(例):

  {
    "Code": {
      "RepositoryType": "S3",
      "Location": "https://awslambda-ap-ne-1-tasks.s3-ap-northeast-1.amazonaws.com/snapshots/XXXXXXXXXXXX/HelloFunction-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?x-amz-security-token=AQxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&AWSAccessKeyId=ASIAXXXXXXXXXXXXXXXX&Expires=xxxxxxxxxx&Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  },
  "Configuration": {
      "Version": "$LATEST",
      "CodeSha256": "aNNHoAV+4ERLB3aO2i72HiCZzsDDNF/22V4HSO+7Gk0=",
      "FunctionName": "HelloFunction",
      "MemorySize": 128,
      "CodeSize": 270,
      "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction",
      "Handler": "HelloFunction.handler",
      "Role": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution",
      "Timeout": 3,
      "LastModified": "2017-03-18T01:23:45.678+0000",
      "Runtime": "nodejs4.3",
      "Description": "Say Hello to someone"
    }
  }
コマンド
aws lambda get-function-configuration \
        --function-name ${LAMBDA_FUNC_NAME}

結果(例):

  {
    "CodeSha256": "aNNHoAV+4ERLB3aO2i72HiCZzsDDNF/22V4HSO+7Gk0=",
    "FunctionName": "HelloFunction",
    "CodeSize": 270,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:HelloFunction",
    "Version": "$LATEST",
    "Role": "arn:aws:iam::XXXXXXXXXXXX:role/lambdaBasicExecution",
    "Timeout": 3,
    "LastModified": "2017-03-18T01:23:45.678+0000",
    "Handler": "HelloFunction.handler",
    "Runtime": "nodejs4.3",
    "Description": "Say Hello to someone"
  }

3. Lambda関数の動作確認

3.1. サンプルデータの作成

変数の設定
FILE_INPUT="${LAMBDA_FUNC_NAME}-data.json" \
          && echo ${FILE_INPUT}
サンプルデータ
cat << EOF > ${FILE_INPUT}
{
        "who": "AWS Step Functions"
}
EOF

cat ${FILE_INPUT}

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

コマンド
jsonlint -q ${FILE_INPUT}

エラーが出力されなければOKです。

3.2. lambda関数の手動実行

変数の設定
FILE_OUTPUT_LAMBDA="${LAMBDA_FUNC_NAME}-out.txt"
FILE_LOG_LAMBDA="${LAMBDA_FUNC_NAME}-$(date +%Y%m%d%H%M%S).log"
変数の確認
cat << ETX

        LAMBDA_FUNC_NAME:   ${LAMBDA_FUNC_NAME}
        FILE_INPUT:         ${FILE_INPUT}
        FILE_OUTPUT_LAMBDA: ${FILE_OUTPUT_LAMBDA}
        FILE_LOG_LAMBDA:    ${FILE_LOG_LAMBDA}

ETX
コマンド
aws lambda invoke \
        --function-name ${LAMBDA_FUNC_NAME} \
        --log-type Tail \
        --payload file://${FILE_INPUT} \
        ${FILE_OUTPUT_LAMBDA} \
        > ${FILE_LOG_LAMBDA}
コマンド
cat ${FILE_LOG_LAMBDA} \
        | jp.py 'StatusCode'

結果(例):

  200

3.3. lambda関数の実行結果の確認

コマンド
cat ${FILE_OUTPUT_LAMBDA}

結果:

  "Hello, AWS Step Functions!"

3.4. lambda関数のログの確認

コマンド
cat ${FILE_LOG_LAMBDA} \
        | jp.py 'LogResult' \
        | sed 's/\"//g' \
        | base64 --decode

結果(例):

  START RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Version: $LATEST
  END RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  REPORT RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx      Duration: 2.08 ms       Billed Duration: 100 ms         Memory Size: 128 MB        Max Memory Used: 11 MB

完了

続きを読む