API GatewayのCloudWatchLogsの設定をIaCで行う

Pocket

こんにちは、DX推進室の安齋です。

今回はAWSサーバーレスリソース構築でハマった・工夫した箇所の紹介の第2回目となります。

第2回はAPI GatewayのCloudWatch Logsの設定をSAMとCloudFormationで行った方法のTipsとなります。

同方法を調べた際、日本語の検索結果が無かったので、同じように困った人がいた時のために書いておこうと思います。

今回のテーマ

serverless-2-1

上記構成図の赤枠内の管理者用API GatewayのアクセスログをCloudWatch Logsに流し込みます。全てIaCで。

最初はこれでイケると思った

SAMドキュメントにはAPI Gatewayのアクセスログ出力先とAPI Gateway用のIAMロールを用意する記述がありましたが、同IAMロールをAPI Gatewayにアタッチする方法がありませんでした。

調べた結果、英語サイトに解決策が載っており、AWS::ApiGateway::Account を使用することでアタッチができました。

しかし、それだけではアクセスログは出力されませんでした。

SAMテンプレート

以下、API GatewayのCloudWatch Logsに関する記述です。

Resources:
#===============================
# API Gateway CloudWatchLogs IAM Role
#===============================
# ★★★ココからポイント★★★ #
  ApiGwAccountConfig:
    Type: AWS::ApiGateway::Account
    Properties:
      CloudWatchRoleArn: !ImportValue APIGatewayRoleArn
# ★★★ココまでポイント★★★ #

#===============================
# API Gateway for Admin
#===============================
  AdminApiResource:
    Type: AWS::Serverless::Api
    Properties:
      AccessLogSetting:
        DestinationArn: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/apigateway/rest/${ServiceName}-${Env}-admin

...省略(第1回に記述済み)

CloudFormationテンプレート

今回は、API Gateway用のIAMロール、API GatewayのCloudWatch Logs、CloudWatch Logs用のKMSの箇所を抜粋しています。

Resources:
#===============================
# API Gateway IAM Role
#===============================
  APIGatewayRole:
    Type: AWS::IAM::Role
    UpdateReplacePolicy: Retain
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
      RoleName: !Sub ${ServiceName}-${Env}-apigateway-role
      Policies:
        - PolicyName: !Sub ${ServiceName}-${Env}-apigateway-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: CloudWatchLogs
                Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:DescribeLogGroups
                  - logs:DescribeLogStreams
                  - logs:PutLogEvents
                  - logs:GetLogEvents
                  - logs:FilterLogEvents
                Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/*

#===============================
# API Gateway for Admin Log
#===============================
  APIGatewayForAdminLogGroup:
    Type: AWS::Logs::LogGroup
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      LogGroupName: !Sub /apigateway/rest/${ServiceName}-${Env}-admin
      KmsKeyId: !GetAtt LogEncryptionKey.Arn
      RetentionInDays: 30

#===============================
# Log KMS Key
#===============================
  LogEncryptionKey:
    Type: AWS::KMS::Key
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      EnableKeyRotation: true
      KeyPolicy:
        Version: '2012-10-17'
        Id: key-default-1
        Statement:
        - Sid: Allow access for Key Administrators
          Effect: Allow
          Principal:
            AWS:
              - !Sub arn:aws:iam::${AWS::AccountId}:role/Administrator
          Action:
            - kms:Create*
            - kms:Describe*
            - kms:Enable*
            - kms:List*
            - kms:Put*
            - kms:Update*
            - kms:Revoke*
            - kms:Disable*
            - kms:Get*
            - kms:Delete*
            - kms:TagResource
            - kms:UntagResource
            - kms:ScheduleKeyDeletion
            - kms:CancelKeyDeletion
          Resource: '*'
        - Sid: Allow use of the key
          Effect: Allow
          Principal:
            AWS:
              - !Sub arn:aws:iam::${AWS::AccountId}:role/Administrator
          Action:
            - kms:DescribeKey
            - kms:Encrypt
            - kms:Decrypt
            - kms:ReEncrypt*
            - kms:GenerateDataKey*
          Resource: '*'
        - Sid: Allow use of the key from CloudWatchLogs
          Effect: Allow
          Principal:
            Service: !Sub logs.${AWS::Region}.amazonaws.com
          Action:
            - kms:DescribeKey
            - kms:Encrypt
            - kms:Decrypt
            - kms:ReEncrypt*
            - kms:GenerateDataKey*
          Resource: '*'
          Condition:
            ArnLike:
              kms:EncryptionContext:aws:logs:arn:
                - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/apigateway/*

#==============================
# Outputs
#==============================
Outputs:
#===============================
# API Gateway IAM Role
#===============================
  APIGatewayRoleArn:
    Value: !GetAtt APIGatewayRole.Arn
    Export:
      Name: APIGatewayRoleArn

参考資料

  • Necessary for API Gateway Logging
    • https://gist.github.com/villasv/4f5b62a772abe2c06525356f80299048

これで解決!

SAMドキュメントのAPI GatewayのCloudWatch Logsの設定を読むと、Format が必須であり、最低限 $context.requestId が必要でした。Format 必須要否は「条件付き」だったので、デフォルトのフォーマットがあり、カスタマイズしたい場合に Format を使用するのだと浅く読んでいました。

SAMテンプレート

#===============================
# API Gateway for Admin
#===============================
  AdminApiResource:
    Type: AWS::Serverless::Api
    Properties:
      AccessLogSetting:
        DestinationArn: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/apigateway/rest/${ServiceName}-${Env}-admin
# ★★★ココから追記★★★ #
        Format: '$context.requestId $context.identity.sourceIp $context.identity.caller  $context.identity.user [$context.requestTime] "$context.httpMethod $context.resourcePath $context.protocol" $context.status $context.responseLength'
# ★★★ココまで追記★★★ #

...省略(第1回に記述済み)

めでたしめでたし

無事にAPI GatewayのアクセスログをCloudWatch Logsに出力することができました。API GatewayにIAMロールを紐づける設定がSAMやCloudFormationのAPI Gateway V2に無かったことで、半ば全IaCを諦めていたのですが、無事に方法を見つけることができました。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です