はじめに
YAMLでCloudFormationテンプレートを作成しました。API GatewayとLambdaとDynamoDBです。
DynamoDBのテーブルをCloudFormationでデプロイする(YAML)
Lambda関数をCloudFormationでデプロイする(YAML)。コードベタ書きとS3からのダウンロード
API GatewayをCloudFormationでデプロイしてLambda関数を実行(YAML)
やること
今回やることは大きくわけて3つです。
- LambdaスタックからLambda関数名をエクスポート
- API Gatewayスタックで、Lambda関数名をインポート
- DynamoDBスタック、Lambdaスタック、API Gatewayスタックを一括でデプロイ
Lambdaスタックから関数名をエクスポート
YAMLコード
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for Lambda"
Parameters:
NameOfFunction:
Type: String
Description: "Name of Lambda Function"
Resources:
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Ref NameOfFunction
Description: "Function that put items to DynamoDB"
Runtime: "python3.9"
MemorySize: "128"
Timeout: "5"
Role: "arn:aws:iam::738452829225:role/service-role/putDynamoFunc-role-l6trp0uq"
Handler: index.lambda_handler
Architectures: [ "x86_64" ]
EphemeralStorage:
Size: "512"
Code:
S3Bucket: "bucket-atsushi-00"
S3Key: "python/package.zip"
### 追記 ###
Outputs:
NameOfLambdaFunc:
Description: "Name of Lambda Function"
Value: !Ref LambdaFunction
Export:
Name: "ExportFunctionName"
コード説明
「### 追記 ###」の下が、前回の記事から追記した箇所になります。
Outputs:
「Outputs」を使うことにより、テンプレート内で取得した値を別のスタックに出力できます。
そして別のスタックから、その値をインポートすることができます。
「Value」を先に説明すると、エクスポートする値です。ここでは「LambdaFunction」という論理IDを指定しています。結果として、Lambda関数の名前が出力されます。どのような値が出力されるかは、都度確認する必要があると思います。
「Export」の中に「Name」を記述して、どのような名前でエクスポートするか指定します。ここで指定したものはインポート時に指定するものになります。
ここでは「ExportFunctionName」にしました。この名前でAPI Gatewayスタックからインポートします。
API GatewayスタックでLambda関数名をインポート
YAMLコード
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for API Gateway"
Parameters:
NameOfAPIGateway:
Type: String
Description: "Name of API Gateway"
Resources:
ApiGwRestAPI:
Type: AWS::ApiGateway::RestApi
Properties:
Description: "This is the Description of ApiGwRestAPI"
Name: !Ref NameOfAPIGateway
EndpointConfiguration:
Types:
- REGIONAL
ApiGwDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
Description: "This is the Description of ApiGwDeployment"
RestApiId: !Ref ApiGwRestAPI
DependsOn:
- ApiGwResource
- ApiGwMethod
ApiGwStage:
Type: AWS::ApiGateway::Stage
Properties:
RestApiId: !Ref ApiGwRestAPI
DeploymentId: !Ref ApiGwDeployment
StageName: "v1"
ApiGwResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt ApiGwRestAPI.RootResourceId
PathPart: "testPath"
RestApiId: !Ref ApiGwRestAPI
ApiGwMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: "POST"
ResourceId: !Ref ApiGwResource
RestApiId: !Ref ApiGwRestAPI
AuthorizationType: NONE
Integration:
Type: "AWS"
IntegrationHttpMethod: "POST"
# 変更前→ Uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:putDynamoFunc/invocations"
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FuncName}/invocations"
- FuncName: {'Fn::ImportValue': !Sub 'ExportFunctionName'}
IntegrationResponses:
- StatusCode: "200"
MethodResponses:
- StatusCode: "200"
ResponseModels:
application/json: Empty
DependsOn: "LambdaPermission"
LambdaPermission:
Type: "AWS::Lambda::Permission"
Properties:
# 変更前→ FunctionName: "putDynamoFunc"
FunctionName: !ImportValue "ExportFunctionName"
Action: "lambda:InvokeFunction"
Principal: "apigateway.amazonaws.com"
コード解説
コメントアウトした箇所のすぐ下が変更した箇所です。
Integration
Uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:putDynamoFunc/invocations"
これについては、Fn::Subに記載されている以下のかたちで文字列を完成させています。
!Sub
- String
- Var1Name: Var1Value
Var2Name: Var2Value
変更箇所を抜粋すると、
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FuncName}/invocations"
- FuncName: {'Fn::ImportValue': !Sub 'Stack-Lambda-FunctionName'}
AWSリージョンは「${AWS::Region}」で取得し、AWSアカウントの番号は「${AWS::AccountId}」で取得して組み込んでいます。
そして、Lambdaスタックからインポートした関数名は、「${FuncName}」という変数で組み込んでいます。
肝心な「${FuncName}」の値は、「{‘Fn::ImportValue’: !Sub ‘Stack-Lambda-FunctionName’}」で取得しています。
ImportValue関数を使って、エクスポートされたName(LambdaスタックのNameで指定した値)を指定して取得することができます。
LambdaPermission
「LambdaPermission」の変更箇所はもう少しシンプルです。
FunctionName: "putDynamoFunc"
を
!ImportValue "ExportFunctionName"
に変更しました。
「putDynamoFunc」という関数名をベタ書きしてましたが、「!ImportValue “ExportFunctionName”」とすることで、ImportValue関数でLambdaスタックから関数名をインポートしています。
一括デプロイの準備
S3へテンプレートをアップロード
先ほどのLambdaスタック(Lambda.yml)とAPI Gatewayスタック(APIGateway.yml)、そしてDynamoDBスタック(DynamoDB.yml)をS3へアップロードします。
ちなみにDynamoDBスタックのテンプレート(DynamoDB.yml)は、前回の記事から変更していないです。念のため下記に記載しておきます。
(補足)DynamoDBのYAMLコード
---
AWSTemplateFormatVersion: 2010-09-09
Description: "Code for DynamoDB"
Parameters:
TableName:
Type: String
Description: "Name of DynamoDB Table"
Resources:
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: "Email"
AttributeType: "S"
- AttributeName: "Name"
AttributeType: "S"
- AttributeName: "Gender"
AttributeType: "S"
- AttributeName: "Inquiry"
AttributeType: "S"
KeySchema:
- AttributeName: "Email"
KeyType: "HASH"
- AttributeName: "Name"
KeyType: "RANGE"
ProvisionedThroughput:
ReadCapacityUnits: "3"
WriteCapacityUnits: "3"
GlobalSecondaryIndexes:
- IndexName: "GSI-Name"
KeySchema:
- AttributeName: "Name"
KeyType: "HASH"
- AttributeName: "Inquiry"
KeyType: "RANGE"
Projection:
NonKeyAttributes:
- "Email"
ProjectionType: "INCLUDE"
ProvisionedThroughput:
ReadCapacityUnits: "1"
WriteCapacityUnits: "1"
- IndexName: "GSI-Gender"
KeySchema:
- AttributeName: "Gender"
KeyType: "HASH"
- AttributeName: "Inquiry"
KeyType: "RANGE"
Projection:
NonKeyAttributes:
- "Email"
ProjectionType: "INCLUDE"
ProvisionedThroughput:
ReadCapacityUnits: "1"
WriteCapacityUnits: "1"
TableName: !Ref TableName
一括デプロイ
YAMLコード
これらの3つのテンプレートを実行するYAMLコードが下記です。ここでは「All.yml」としました。
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for DynamoDB、Lambda Function、API Gateway"
Parameters:
TbName:
Type: String
Description: "Name of DynamoDB Table"
FuncName:
Type: String
Description: "Name of Lambda Function"
ApiName:
Type: String
Description: "Name of API Gateway"
Resources:
StackDynamoDB:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: "https://bucket-atsushi-00.s3.ap-northeast-1.amazonaws.com/CloudFormation-Templates/DynamoDB.yml"
Parameters:
TableName: !Ref TbName
StackLambdaFunc:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: "https://bucket-atsushi-00.s3.ap-northeast-1.amazonaws.com/CloudFormation-Templates/Lambda.yml"
Parameters:
NameOfFunction: !Ref FuncName
DependsOn:
- StackDynamoDB
StackAPIGateway:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: "https://bucket-atsushi-00.s3.ap-northeast-1.amazonaws.com/CloudFormation-Templates/APIGateway.yml"
Parameters:
NameOfAPIGateway: !Ref ApiName
DependsOn:
- StackDynamoDB
- StackLambdaFunc
コード解説
Parameters
スタックを実行(作成)するときにユーザーが指定する値です。
DynamoDB.ymlではテーブル名、Lambda.ymlでは関数名、APIGateway.ymlではAPI Gatewayの名前を入力するようにテンプレートを作っています。ですので「All.yml」でデプロイ実行時にすべて手入力させるように、Parametersプロパティで記述しています。
AWS::CloudFormation::Stack
「AWS::CloudFormation::Stack」を使うことで、テンプレートからテンプレートを呼び出せます。つまり、「All.yml」から「DynamoDB.yml」と「Lambda.yml」と「APIGateway.yml」を呼び出せます。
「TemplateURL」に各テンプレートが保存されているS3のURLを記述しています。
「Parameters」には、呼び出すテンプレートに渡したい値を記述します。
- 「DymanoDB.yml」にはテーブル名(「All.yml」の中の”TbName”変数)を渡しています。
- 「Lambda.yml」には関数名(「All.yml」の中の”FuncName”変数)を渡しています。
- 「APIGateway.yml」にはAPI Gatewayの名前(「All.yml」の中”ApiName”変数)を渡しています。
DependsOn
読み込まれるテンプレートの順番は、「DynamoDB.yml」→「Lambda.yml」→「APIGateway.yml」となります。
なので、LambdaスタックはDynamoDBスタックに依存しています。そして、API Gatewayスタックは、DynamoDBスタックとLambdaスタックに依存しています。
一括デプロイ実行
一括デプロイするコマンドは以下のとおりです。
aws cloudformation create-stack \
--template-body file:///Users/atsushi/Document/AWS/CloudFormation/All.yml \
--stack-name Stack-All \
--parameters ParameterKey="TbName",ParameterValue="inquiry-table" \
ParameterKey="FuncName",ParameterValue="putDynamoFunc" \
ParameterKey="ApiName",ParameterValue="apiInvokeLambda"
実行時の画面ショットはこんな感じです。呼び出されるスタックには自動的にスタック名が付けられています。
テスト
一括デプロイで作成されたAPI Gatewayから、ペイロードを与えてテスト実行してみます。
以下のようなペイロードを指定してテストしました。200ステータスが返ってきています。
一括デプロイで作成されたDynamoDBを見てみると、テーブルに値が入っています。
最後に
ネストされたスタックの実行をするために、「AWS::CloudFormation::Stack」をコード上で使うこと。
テンプレートから値をエクスポートするには、「Outputs」プロパティを使用すること。
テンプレートから値をインポートするには「!ImportValue」関数を使うこと。
あと、YAMLコード内で値を参照するときに、「!Sub」を使うことを少しやりました。
最後までお読みいただき、ありがとうございました。
参考サイト
CloudFormation テンプレートが肥大化したので分割する方法を教えてください
コメント