CloudFormationを利用してlambdaとAPI Gatewayの連携をさらっとまとめてみました
今回のハマりポイントはlambdaPermissionの設定ぐらいでしょうか。
さくっと出来ちゃうのでCloudFormationはホント便利ですね!
・deploy用にS3バケットを用意
・swaggerファイルとlambdaパッケージをアップロード
・CFn実行
という手順で進めていきます
対象者
・環境構築を楽にしたい人
・サーバーレスなAPIに興味がある人
deploy用のS3バケットを作成
swaggerファイルとlambdaファイルをアップロードする先として
S3バケットとバケットポリシーを定義します
deployXXXBucket.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: This CloudFormation template to create S3 Bucket
Parameters:
DeployBucket:
Description: deploy workspace
Type: String
Resources:
#==============================
# lambda,swaggerファイルをdeploy時に参照するバケット
#==============================
DeployS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref DeployBucket
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
DeployS3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref DeployS3Bucket
PolicyDocument:
Id: DeployS3BucketPolicy
Version: '2012-10-17'
Statement:
- Sid: XXXS3Acl
Effect: Allow
Principal: {
Service: "cloudformation.amazonaws.com"
}
Action:
- "s3:GetObject"
Resource:
- !Sub "arn:aws:s3:::${DeployBucket}/*"
定義したymlファイルを使ってstackを作成します
deployXXXBucket.sh
#!/bin/bash
set -Ceu
BASE_DIR=$(cd $(dirname $0); pwd)
REGION="ap-northeast-1"
CFN_INIT_TEMPLATE_FILE="deployXXXBucket.yml"
STACK_NAME_DEPLOY_BUCKET="apiXXXDeployS3Bucket"
DEPLOY_BUCKET="api-xxx-deploy-bucket"
/usr/bin/aws cloudformation create-stack \
--template-body file://${BASE_DIR}/${CFN_INIT_TEMPLATE_FILE} \
--stack-name ${STACK_NAME_DEPLOY_BUCKET} \
--region ${REGION} \
--parameters ParameterKey=DeployBucket,ParameterValue=${DEPLOY_BUCKET}
lambdaを作成
index.js
exports.handler = async (event, context) => {
context.done(null, new Response(200, JSON.stringify({data: "hello world"})));
};
class Response{
constructor(statusCode, body){
this.statusCode = statusCode;
this.body = body;
this.headers = {"Access-Control-Allow-Origin" : "*"};
}
}
swagger.ymlを作成
swagger.yml
swagger: "2.0"
info:
version: "2019-03-26T11:14:17Z"
title:
Fn::Sub: ${ApiGwName}
host: "api-xxx.hoge.me"
schemes:
- "https"
paths:
/:
get:
x-amazon-apigateway-integration:
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiXXXFunction.Arn}/invocations
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
produces:
- "application/json"
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
headers:
Access-Control-Allow-Origin:
type: "string"
definitions:
Empty:
type: "object"
title: "Empty Schema"
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal: "*"
Action: "execute-api:Invoke"
Resource:
Fn::Sub: "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}*"
CFnテンプレートを作成します
template.cfn.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: This CloudFormation template to create ApiXXX
Parameters:
DeployBucket:
Description: to use swagger file, lambda package at build
Type: String
GwStage:
Description: API Gateway deploy stage
Type: String
AllowedValues:
- v1
ApiXXXGwRole:
Description: API Gateway role name
Type: String
LambdaName:
Description: lambda function name
Type: String
LambdaRoleName:
Description: lambda function role
Type: String
Resources:
#==============================
# apigateway
#==============================
ApiXXX:
Properties:
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: !Sub "s3://${DeployBucket}/swagger.yml"
StageName:
Fn::Sub: ${GwStage}
Type: AWS::Serverless::Api
Role:
Fn::GetAtt:
- ApiXXXApiGwRole
- Arn
DependsOn:
- ApiXXXApiGwRole
- ApiXXXFunction
#==============================
# lambda(SAM管理)
#==============================
ApiXXXFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Ref LambdaName
Handler: index.handler
Runtime: nodejs10.x
CodeUri: src/function
MemorySize: 128
Timeout: 60
Role:
Fn::GetAtt:
- LambdaExecutionRole
- Arn
ApiXXXApiGwRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref ApiXXXGwRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref LambdaRoleName
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
LambdaPermissionxxxx:
Type: "AWS::Lambda::Permission"
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref ApiXXXFunction
Principal: apigateway.amazonaws.com
定義したymlファイルを使って変更セットを作成します
deployXXX.sh
#!/bin/bash
set -Ceu
BASE_DIR=$(cd $(dirname $0); pwd)
REGION="ap-northeast-1"
SWAGGER_FILE="swagger.yml"
DEPLOY_BUCKET="api-xxx-deploy-bucket"
CFN_TEMPLATE_FILE="template.cfn.yml"
CFN_TEMPLATE_OUTPUT_FILE="output-template.cfn.yml"
STACK_NAME="apiXXXDeploy"
/usr/bin/aws s3 cp ./${SWAGGER_FILE} s3://${DEPLOY_BUCKET}/
/usr/bin/aws cloudformation package \
--template-file ${CFN_TEMPLATE_FILE} \
--output-template-file ${CFN_TEMPLATE_OUTPUT_FILE} \
--s3-bucket ${DEPLOY_BUCKET}
GwStage="v1"
DeployBucket=$DEPLOY_BUCKET
ApiGwName="apiXXX"
ApiXXXGwRole="apiXXXGwRole"
LambdaName="apiXXXLambda"
LambdaRoleName="apiXXXLambdaRole"
/usr/bin/aws cloudformation deploy \
--template-file ${BASE_DIR}/${CFN_TEMPLATE_OUTPUT_FILE} \
--stack-name ${STACK_NAME} \
--region ${REGION} \
--parameter-overrides GwStage=${GwStage} DeployBucket=${DeployBucket} ApiGwName=${ApiGwName} ApiXXXGwRole=${ApiXXXGwRole} LambdaName=${LambdaName} LambdaRoleName=${LambdaRoleName} \
--capabilities CAPABILITY_NAMED_IAM
CFn変更セットを実行
ビルドを実行します
変更セットにステータスがCREAE_IN_PROGRESSで作成されている様子が確認できますね
ステータスがCREATE_COMPLETEになると、
変更セット詳細ページから作成リソースを確認できます
実行すると用意したリソースが作成されています。簡単ですね