API Gateway+LambdaなCloudFormationテンプレートを作る

CloudFormationテンプレートは少し苦手意識があるのですが、会社の勉強会で取り上げようと思ったので頑張って作りました。

Lambda関数

まずLambda関数を作ります。勉強会用のサンプルなのでとても適当です。APIからAPIを呼んでるだけの意味のないコードですが、中身が本質ではないので許してください。

ちなみにnode-fetchは外部パッケージなのでnpm installが必要です。

const fetch = require('node-fetch');

exports.handler = async (event) => {
  console.log(event);
  
  const pathParams = event.pathParameters;
  const isbn = pathParams.isbn;

  const url = "http://api.openbd.jp/v1/get?isbn=" + isbn;

  const res = await fetch(url);
  const data = await res.json();
  const response = {
    statusCode: 200,
    body: JSON.stringify(data),
  };
  return response;
};

これを、Lambda関数ページの「アクション」⇒「関数のエクスポート」でZipファイル化します。そして、Zipファイルを適当なS3バケットにアップロードします。

これでLambdaの準備は完了です。

CloudFormationテンプレートファイルを作る

テンプレートファイルの完成形は以下になります。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  FunctionName:
    Type: String
    Description: "Please enter function name"
Resources:
  # Lambda
  Lambda:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code: 
        S3Bucket: "********"
        S3Key: !Sub "********.zip"
      Description: "Lambda function to get informations of books"
      FunctionName: !Sub ${FunctionName}
      Handler: index.handler
      Runtime: nodejs16.x
      MemorySize: 128
      Timeout: 10
      Role: !Sub arn:aws:iam::${AWS::AccountId}:role/lambda-basic-policy
  # API Gateway
  ApiGW:
    Type: 'AWS::ApiGateway::RestApi'
    Properties:
      Name: "openbd-api"
  ApiBooksResource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
      RestApiId: !Ref ApiGW
      ParentId: !GetAtt ApiGW.RootResourceId
      PathPart: 'books'
    DependsOn:
      - ApiGW
      - Lambda
  ApiIsbnResource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
      RestApiId: !Ref ApiGW
      ParentId: !Ref ApiBooksResource
      PathPart: '{isbn}'
    DependsOn:
      - ApiBooksResource
  LambdaPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName: !Sub ${FunctionName} 
      Action: 'lambda:InvokeFunction'
      Principal: 'apigateway.amazonaws.com'
    DependsOn:
      - ApiIsbnResource
  ResourceMethod:
    Type: 'AWS::ApiGateway::Method'
    Properties:
      RestApiId: !Ref ApiGW
      ResourceId: !Ref ApiIsbnResource
      AuthorizationType: 'None'
      HttpMethod: 'GET'
      Integration:
        Type: 'AWS_PROXY'
        IntegrationHttpMethod: 'POST'
        Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}/invocations'
    DependsOn: 'LambdaPermission'

どういうテンプレートかというと、S3に置いてあるZipファイルを元にLambda関数を作成し、API Gatewayに接続するということをやっています。

***になっている部分は、適当に自分の環境に置き換えてください。

いくつかポイントがあります。

①Lambdaのロール

LambdaにIAMロールのARNが設定してありますが、これはあらかじめ作っておきます。ロールの内容はAWSLambdaBasicExecutionRoleがついていればOKです。

②API Gatewayのリソースパス

今回、APIをhttps://***/books/{isbn}というURLにしました。そのため、まずAPI本体を作り、次にbooksというリソースを作り、その下に{isbn}というリソースを作るといった構成になっています。

最後にメソッドを作っていますが、{isbn}に対してGETメソッドを定義しています。

③依存関係

Lambdaよりも先にAPI Gatewayがデプロイされてしまうと、接続先のLambdaがないので404エラーとなります。そこで、DependsOnパラメータによってデプロイの順番を制御しています。

まとめ

CDKと比べてCloudFormationのテンプレートは難しいです。もう幾度となくエラーを出しまくって試行錯誤の末にようやく完成できました。

今後もIaCはCDKをメインに使っていくと思いますが、CloudFormationも知識として知っておかなければならないと思うので、良い機会でした。

コメントする

メールアドレスが公開されることはありません。