CloudFormationカスタムリソースでDynamoDBを初期セットアップを実行する
CloudFormationでDynamoDB作成時に、DBの初期化として、テストレコードの追加を併せて実行することを考えます。
今回はCloudFormationカスタムリソースを使用して、DynamoDBテーブルの初期セットアップを実施します。
構築する環境
CloudFormationでDynamoDBテーブルを作成します。
合わせてLambda関数も作成します。
この関数がCloudFormationスタック作成時に自動的に実行されるように、CloudFormationカスタムリソースに関数を関連づけます。
この関数の働きは、DynamoDBテーブルを初期化することです。
初期化処理は、S3バケットに配置されているJSONデータを読み込み、DynamoDBテーブルにアイテムとして登録する処理を行います。
関数のランタイム環境はPython3.8です。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/105
テンプレートファイルのポイント解説
DynamoDBテーブル
Resources:
Table:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: Artist
AttributeType: S
- AttributeName: SongTitle
AttributeType: S
BillingMode: PROVISIONED
KeySchema:
- AttributeName: Artist
KeyType: HASH
- AttributeName: SongTitle
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: !Ref ReadCapacityUnits
WriteCapacityUnits: !Ref WriteCapacityUnits
TableClass: STANDARD
TableName: !Sub "${Prefix}-Music"
Code language: YAML (yaml)
特別な設定は不要です。
以下のAWS公式ページの設定をCloudFormationテンプレートに書き出しました。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/getting-started-step-1.html
DynamoDBに関する基本的な事項に関しては、以下のページもご確認ください。
Lambda関数
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
JSON_S3_BUCKET: !Ref JsonS3Bucket
JSON_S3_KEY: !Ref JsonS3Key
TABLE_NAME: !Ref Table
Code:
ZipFile: |
import boto3
import cfnresponse
import json
import os
JSON_S3_BUCKET = os.environ['JSON_S3_BUCKET']
JSON_S3_KEY = os.environ['JSON_S3_KEY']
TABLE_NAME = os.environ['TABLE_NAME']
CREATE = 'Create'
response_data = {}
s3_client = boto3.client('s3')
dynamodb_client = boto3.client('dynamodb')
def lambda_handler(event, context):
try:
if event['RequestType'] == CREATE:
s3_response = s3_client.get_object(
Bucket=JSON_S3_BUCKET,
Key=JSON_S3_KEY)
body = s3_response['Body'].read()
json_data = json.loads(body.decode('utf-8'))
print(json_data)
for item in json_data:
dynamodb_response = dynamodb_client.put_item(
TableName=TABLE_NAME,
Item=item
)
print(dynamodb_response)
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
FunctionName: !Sub "${Prefix}-function"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Timeout: !Ref Timeout
Code language: YAML (yaml)
Lambda関数で実行するコードをインライン表記で定義します。
詳細につきましては、以下のページをご確認ください。
cfnresponseモジュールを使用して、関数をLambda-backedカスタムリソースとして実装します。
詳細につきましては、以下のページをご確認ください。
Variablesプロパティで環境変数を定義することができます。
環境変数は関数内から参照することができます。
今回は初期化処理用のデータを格納しているS3バケット名や、ファイル名を環境変数として渡します。
実行するコードの内容ですが、以下の通りです。
- os.environにアクセスして、CloudFormationテンプレートで定義した環境変数を取得する。
- Boto3でS3用クライアントオブジェクトを作成して、S3バケットから初期化用のJSONファイルを取得する。
- Boto3でDynamoDBクライアントオブジェクトを作成して、サンプルデータをテーブルに保存する。
- cfnresponseモジュールのsend関数を使用して、カスタムリソースが正常に完了した旨のメッセージをCloudFormationに通知する。
S3バケットから読み込むJSONファイルの内容は以下の通りです。
[
{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}, "AlbumTitle": {"S": "Somewhat Famous"}, "Awards": {"N": "1"}},
{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Howdy"}, "AlbumTitle": {"S": "Somewhat Famous"}, "Awards": {"N": "2"}},
{"Artist": {"S": "Acme Band"}, "SongTitle": {"S": "Happy Day"}, "AlbumTitle": {"S": "Songs About Life"}, "Awards": {"N": "10"}},
{"Artist": {"S": "Acme Band"}, "SongTitle": {"S": "PartiQL Rocks"}, "AlbumTitle": {"S": "Another Album Title"}, "Awards": {"N": "8"}}
]
Code language: JSON / JSON with Comments (json)
このデータもAWS公式ページで紹介されているデータです。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/getting-started-step-2.html
関数用のIAMロールは以下の通りです。
Resources:
FunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: !Sub "${Prefix}-S3GetObjectPolicy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource:
- !Sub "arn:aws:s3:::${JsonS3Bucket}/*"
- PolicyName: !Sub "${Prefix}-DynamodbPutItemPolicy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- dynamodb:PutItem
Resource:
- !Sub "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${Table}"
Code language: YAML (yaml)
AWS管理ポリシーであるAWSLambdaBasicExecutionRoleに加えて、インラインポリシーで2つの権限を与えます。
1つ目はS3バケットからオブジェクトを取得する権限です。
これはサンプルデータであるJSONファイルを取得するために必要な権限です。
2つ目はDynamoDBにアイテムを保存する権限です。
JSONファイル内のデータをDynamoDBテーブルに書き込むために必要な権限です。
CloudFormationカスタムリソース
続いてCloudFormationカスタムリソース本体を確認します。
Resources:
CustomResource:
Type: Custom::CustomResource
Properties:
ServiceToken: !Ref FunctionArn
Code language: YAML (yaml)
カスタムリソースに、先述のLambda関数を関連付けます。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- DynamoDBテーブル:fa-105-Music
- Lambda関数:fa-105-function
AWS Management Consoleから各リソースを確認します。
まずCloudFormationカスタムリソースおよびLambda関数を確認します。
確かにLambda関数とカスタムリソース本体が正常に作成されていることがわかります。
つまりCloudFormationスタック作成時に、自動的にLambda関数が作成・実行されたことになります。
次にDynamoDBを確認します。
CloudFormationテンプレートで指定した通りに、DynamoDBテーブルが作成されています。
動作確認
CloudFormationカスタムリソースの動作状況を確認します。
CloudFormationカスタムリソースに紐づけたLambda関数の実行ログを確認します。
S3バケットに設置されているJSONデータを読み込んで、1行ずつDynamoDBテーブルに保存しています。
最後にCloudFormationにメッセージを通知しています。
DynamoDBテーブルの内容を確認します。
確かにテーブルにアイテムが保存されています。
以上のことから、CloudFormationスタック作成時に、CloudFormationカスタムリソースによって、これに紐づくLambda関数が自動的に実行されて、DynamoDBテーブルにアイテムが保存されたことが確認できました。
まとめ
CloudFormationカスタムリソースを使用して、DynamoDBテーブルの初期セットアップを実施する方法を確認しました。