CloudFront DistributionからAPI Gateway – Lambda を呼び出す構築ハンズオン


この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

はじめに

IAM認証や、ACM,Route53などの検証の際に、自分でよく利用する構築をCloudFormation化していなかったと思い着手してみました。
まずは背骨となる、 CloudFront から APIGateway – Lambdaを呼び出す構築部分のCFn化を行なっていきたいと思います。

構成図


ハンズオン

構築の流れ

1.Lambda作成

2.APIGateway作成

3.CloudFront作成

上記の順番で構築を行なっていきます。
最終的には、CloudFrontのディストリビューションドメインから、Lambdaを呼び出せるまでを行なっていきます。


1.Lambda作成

特別な設定はせずstatusCode200を返す、Lambdaを構築します。
AWSTemplateFormatVersion: '2010-09-09'
Description:
  Lambda Create
# ------------------------------------------------------------#
#  Metadata
# ------------------------------------------------------------#
Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Lambda Configuration"
        Parameters:
        - FunctionName
        - Description
        - Handler
        - MemorySize
        - Runtime
        - Timeout

# ------------------------------------------------------------#
#  InputParameters
# ------------------------------------------------------------#
Parameters:
  FunctionName:
    Type: String
    Default: "cfn-lmd-inamura"
  Description:
    Type: String
    Default: "cfn-lmd-inamura"
  Handler:
    Type: String
    Default: "index.lambda_handler"
  MemorySize:
    Type: String
    Default: "128"
  Runtime:
    Type: String
    Default: "python3.9"
  Timeout:
    Type: String
    Default: "10"

# ------------------------------------------------------------#
#  Resources
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
#  Lambda
# ------------------------------------------------------------#
  Lambda:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: |
          import json

          def lambda_handler(event, context):
              # TODO implement
              print(event)
              return {
                  'statusCode': 200,
                  'body': json.dumps('Hello CloudFront!')
              }

      Description: !Ref Description
      FunctionName: !Ref FunctionName
      Handler: !Ref Handler 
      MemorySize: !Ref MemorySize
      Runtime: !Ref Runtime
      Timeout: !Ref Timeout
      Role: !GetAtt LambdaRole.Arn

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${FunctionName}-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Principal:
              Service: lambda.amazonaws.com
      Policies:
        - PolicyName: !Sub "${FunctionName}-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "logs:CreateLogGroup"
                Resource: !Sub "arn:${AWS::Partition}:logs:*:*:*"

# ------------------------------------------------------------#
# Output Parameters
#------------------------------------------------------------#          
Outputs:
  LambdaArn:
    Value: !GetAtt Lambda.Arn
    Export:
      Name: !Sub "${FunctionName}-arn"
  LambdaName:
    Value: !Ref FunctionName
    Export:
      Name: !Sub "${FunctionName}-name"

2.APIGateway作成

2.1 APIGateway作成

次はAPIGatewayを構築します。
順次構築できるようにするため、それぞれのリソースにDependsOnを設定しています。
1.で構築したLambdaに対して"AWS::Lambda::Permission"でリソースベースポリシーの追加を行なっています。
AWSTemplateFormatVersion: '2010-09-09'
Description:
  APIGateway Create
# ------------------------------------------------------------#
#  Metadata
# ------------------------------------------------------------#
Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "APIGateway Configuration"
        Parameters:
        - Name
        - FunctionName
        - StageName
# ------------------------------------------------------------#
#  InputParameters
# ------------------------------------------------------------#
Parameters:
  Name:
    Type: String
    Default: "cfn-agw-inamura"
  FunctionName:
    Type: String
    Default: "cfn-lmd-inamura"
  StageName:
    Type: String
    Default: "dev"
# ------------------------------------------------------------#
#  Resources
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
#  APIGateway
# ------------------------------------------------------------#
  Api:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Name: !Ref Name
      EndpointConfiguration:
        Types:
          - REGIONAL

  Resource:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      RestApiId: !Ref Api
      ParentId: !GetAtt Api.RootResourceId
      PathPart: !Ref FunctionName
    DependsOn: Api

  LambdaPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      FunctionName: !Ref FunctionName
      Action: "lambda:InvokeFunction"
      Principal: "apigateway.amazonaws.com"
    DependsOn: Resource 

  ResourceMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      RestApiId: !Ref Api
      ResourceId: !Ref Resource
      AuthorizationType: None
      HttpMethod: ANY
      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

  Deployment:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref Api
    DependsOn: ResourceMethod

  Stage:
    Type: AWS::ApiGateway::Stage
    Properties:
      StageName: !Ref StageName
      Description: dev stage
      RestApiId: !Ref Api
      DeploymentId: !Ref Deployment
    DependsOn: Deployment

# ------------------------------------------------------------#
# Output Parameters
#------------------------------------------------------------#          
Outputs:
  Endpoint:
    Value: !Sub 'https://${Api}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${StageName}/${FunctionName}'
    Export:
      Name: Endpoint
  OriginDomain:
    Value: !Sub '${Api}.execute-api.${AWS::Region}.${AWS::URLSuffix}'   
    Export:
      Name: OriginDomain
  StageName:
    Value: !Sub '${StageName}'
    Export:
      Name: StageName

2.2.構築されたAPIGatewayのエンドポイントを押下する

CloudFormation > スタック > 2.1.で構築したスタック > タブ『出力』 に移動する
下記画面のように Endpoint にエンドポイントが出力されているので、URIを押下する

URI押下後、下記のような画面が表示されることを確認する

※自分だけの現象か、上記構築後Lambdaを確認してもトリガーにはAPIGatewayが表示されませんでした

3.CloudFront作成

2.で構築したAPIGatewayをオリジンにしたCloudFrontを構築していきます。
CachePolicyIdはマネージドポリシーを利用しているので、AWS公式:Using the managed cache policiesからId番号を確認しました。

AWSTemplateFormatVersion: '2010-09-09'
Description:
  CloudFront Create
# ------------------------------------------------------------#
#  Resources
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
#  CloudFront
# ------------------------------------------------------------#
  Distribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Comment: "Create CloudFront with CloudFormation"
        PriceClass: PrinceClass_All
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
          Compress: true
          TargetOriginId: !ImportValue cfn-lmd-inamura-name
          ViewerProtocolPolicy: https-only
        Enabled: true
        HttpVersion: http2and3
        Origins:
          - Id: !ImportValue cfn-lmd-inamura-name
            DomainName: !ImportValue OriginDomain
            CustomOriginConfig:
              HTTPSPort: '443'
              OriginProtocolPolicy: https-only
            OriginPath: !Sub
              - /${StageName}/${FunctionName}
              - StageName: {'Fn::ImportValue': StageName}
                FunctionName: {'Fn::ImportValue': cfn-lmd-inamura-name}
        PriceClass: PriceClass_All
# ------------------------------------------------------------#
# Output Parameters
#------------------------------------------------------------#          
Outputs:
  DomainName:
    Value: !GetAtt Distribution.DomainName
    Export:
      Name: cln-domainname-inamura

挙動の確認

①構築されたCloudFrontからドメイン名を確認する

②ブラウザにドメインを入力して、画面が表示されるか確認をする


さいごに

CloudFrontからS3(OAI)の静的構築におけるハンズオンは多く見つかりましたが、あまりAPIGateway-Lambdaが見当たりませんでしたが、公式と睨めっこをしながら無事に構築が出来ました。
簡単に検証する環境もつくることが出来るようになったので、次はIAM認証でACMなどの構成や、Cognitoでの認証などを検証していきたいです。
今年も残り少ないですが、時間を見つけては検証して自分の作れるものを増やしていきたいと思います!
Last modified: 2022-12-11

Author