0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS CDKでAPI Gatewayのアクセスログの送信先にFirehoseストリームを設定する

Last updated at Posted at 2025-03-07

はじめに

今回は AWS CDKを使い以下を行ったため、そのソースコードのご紹介です。

  • API GatewayのアクセスログをS3へ保存するFirehoseストリームの作成
  • 上記Firehoseストリームを送信先とするAPI Gateway APIの作成

言語はTypeScirptになります。

前回投稿内容と照らし合わせながらご紹介していきます。

目次

  1. 必要となるAWSリソース🔨
  2. IAMロールの作成
  3. S3バケットの作成
  4. Firehoseストリームの作成
  5. API Gateway APIの作成
  6. まとめ📝
  7. 参考文献🌟

必要となるAWSリソース🔨

今回のゴールである、API Gatewayのアクセスログの送信先にFirehoseストリームを設定を行う上で、必須となるAWSリソースは以下です。

  • IAMロール:Firehoseストリームが必要とする許可を設定
  • S3バケット:Firehoseストリームが配信するログの記録先
  • Firehoseストリーム:API Gateway アクセスログの送信先
  • API Gateway API:アクセスログの送信先としてFirehoseストリームを設定

IAMロールの作成

まずは、IAMロールの作成です。
Firehoseストリームのサービスアクセスで設定するロールになります。

  // Create Role
  const firehoseDeliveryRole = new Role(scope, `AmazonApigatewayMyApiFirehoseDeliveryRole`, {
    roleName: `AmazonApigatewayMyApiFirehoseDeliveryRole`,
    assumedBy: new ServicePrincipal('firehose.amazonaws.com'),
    inlinePolicies: {
      ecsServicePolicy: new PolicyDocument({
        statements: [
          new PolicyStatement({
            actions: [
              's3:AbortMultipartUpload',
              's3:GetBucketLocation',
              's3:GetObject',
              's3:ListBucket',
              's3:ListBucketMultipartUploads',
              's3:PutObject',
            ],
            resources: [`arn:aws:s3:::amazon-apigateway-my-api*`],
          }),
          new PolicyStatement({
            actions: ['lambda:InvokeFunction'],
            resources: [`arn:aws:lambda:${scope.region}:${scope.account}:function:FirehoseTransformFunction`],
          }),
        ],
      }),
    },
  });

S3バケットの作成

続いて、S3バケットの作成です。

  // Create S3
  const logS3 = new Bucket(scope, `AmazonApigatewayMyApiLogS3`, {
    accessControl: BucketAccessControl.PRIVATE,
    blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
    bucketName: `amazon-apigateway-my-api-log-ap-northeast-1`,
    encryption: BucketEncryption.S3_MANAGED,
    removalPolicy: RemovalPolicy.DESTROY,
  });
  cdk.Tags.of(logS3).add('Name', `amazon-apigateway-my-api-log-ap-northeast-1`);
  cdk.Tags.of(logS3).add('Role', 'S3');

Firehoseストリームの作成

続いて、Firehoseストリームの作成です。
Firehoseストリームの設定では、以下3つの内容を指します。

レコードを編集、変換、および転換はCloudWatchLogsへのログ記録で必要な設定で、CDKだとprocessingConfigurationを指しますが、今回は直接Firehoseストリーム > S3となるため定義なしとしています。

  // Create KinesisDeliveryStream
  const logDeliveryStream = new CfnDeliveryStream(scope, `AmazonApigatewayMyApiLogStream`, {
    deliveryStreamName: `amazon-apigateway-my-api-log-stream`,
    deliveryStreamType: 'DirectPut',
    extendedS3DestinationConfiguration: {
      bucketArn: logS3.bucketArn,
      bufferingHints: {
        intervalInSeconds: 900,
        sizeInMBs: 50,
      },
      compressionFormat: 'UNCOMPRESSED',
      prefix: 'apigateway/',
      roleArn: firehoseDeliveryRole.roleArn,
    },
  });

API Gateway APIの作成

最後に、API Gateway APIの作成です。
MyApiはAPI名です。
accessLogDestination: new FirehoseLogDestination()で、アクセスログの送信先として上記で作成したFirehoseストリーム(logDeliveryStream)を設定しています。

// Create ApiGateway Api
const myApi = new RestApi(this, 'MyApi', {
  restApiName: 'myApi',
  defaultCorsPreflightOptions: {
    allowOrigins: Cors.ALL_METHODS,
        allowMethods: ['GET', 'OPTIONS'],
        statusCode: 204,
      },
      deployOptions: {
        stageName: this.apiGatewayStage,
        accessLogDestination: new FirehoseLogDestination(logDeliveryStream),
        accessLogFormat: AccessLogFormat.jsonWithStandardFields(),
        metricsEnabled: true,
        loggingLevel: MethodLoggingLevel.INFO,
        dataTraceEnabled: true,
        tracingEnabled: true,
  },
});

まとめ📝

これまでの処理をまとめると以下のような感じです。
可読性向上のために以下を行いましたが、ファイル分割しても良いかもです( ^ω^ )

  • Firehoseストリームの作成処理をメソッド化
  • ログ名(logName)・ゾーン名(zoneName)は引数渡しで共通化
import { RemovalPolicy } from 'aws-cdk-lib';
import * as cdk from 'aws-cdk-lib';
import {
  AccessLogFormat,
  FirehoseLogDestination,
  MethodLoggingLevel,
  RestApi,
} from 'aws-cdk-lib/aws-apigateway';
import { CfnDeliveryStream } from 'aws-cdk-lib/aws-kinesisfirehose';
import { PolicyDocument, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { BlockPublicAccess, Bucket, BucketAccessControl, BucketEncryption } from 'aws-cdk-lib/aws-s3';

// ApiGateway アクセスログ配信用Firehoseストリーム作成
const ApiGWFirehoseStream: CfnDeliveryStream = createApiGWAccessLogResource(this, 'amazon-apigateway-my-api', zoneName);

// Create ApiGateway Api
const myApi = new RestApi(this, 'MyApi', {
  restApiName: 'myApi',
  defaultCorsPreflightOptions: {
    allowOrigins: Cors.ALL_METHODS,
        allowMethods: ['GET', 'OPTIONS'],
        statusCode: 204,
      },
      deployOptions: {
        stageName: this.apiGatewayStage,
        accessLogDestination: new FirehoseLogDestination(ApiGWFirehoseStream),
        accessLogFormat: AccessLogFormat.jsonWithStandardFields(),
        metricsEnabled: true,
        loggingLevel: MethodLoggingLevel.INFO,
        dataTraceEnabled: true,
        tracingEnabled: true,
  },
});

/**
 * ApiGatewayのアクセスログをS3へ保存するためのFirehoseストリームを作成
 *
 * @remarks
 *
 * @param scope - スコープ
 * @param logName - ログ名
 * @param zoneName - DNSZoneName
 * @returns Firehoseストリーム
 */
export function createApiGWAccessLogResource(scope: cdk.Stack, logName: string, zoneName: string): CfnDeliveryStream {
  // Snake形からCamel形に変換
  const logNameCamel = snakeToCamel(logName);

  // Create Role
  const firehoseDeliveryRole = new Role(scope, `${logNameCamel}FirehoseDeliveryRole`, {
    roleName: `${logNameCamel}FirehoseDeliveryRole`,
    assumedBy: new ServicePrincipal('firehose.amazonaws.com'),
    inlinePolicies: {
      ecsServicePolicy: new PolicyDocument({
        statements: [
          new PolicyStatement({
            actions: [
              's3:AbortMultipartUpload',
              's3:GetBucketLocation',
              's3:GetObject',
              's3:ListBucket',
              's3:ListBucketMultipartUploads',
              's3:PutObject',
            ],
            resources: [`arn:aws:s3:::${logName}*`],
          }),
          new PolicyStatement({
            actions: ['lambda:InvokeFunction'],
            resources: [`arn:aws:lambda:${scope.region}:${scope.account}:function:FirehoseTransformFunction`],
          }),
        ],
      }),
    },
  });

  // Create S3
  const logS3 = new Bucket(scope, `${logNameCamel}LogS3`, {
    accessControl: BucketAccessControl.PRIVATE,
    blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
    bucketName: `${logName}-log-${zoneName}`,
    encryption: BucketEncryption.S3_MANAGED,
    removalPolicy: RemovalPolicy.DESTROY,
  });
  cdk.Tags.of(logS3).add('Name', `${logName}-log-${zoneName}`);
  cdk.Tags.of(logS3).add('Role', 'S3');

  // Create KinesisDeliveryStream
  const logDeliveryStream = new CfnDeliveryStream(scope, `${logNameCamel}LogStream`, {
    deliveryStreamName: `${logName}-log-stream`,
    deliveryStreamType: 'DirectPut',
    extendedS3DestinationConfiguration: {
      bucketArn: logS3.bucketArn,
      bufferingHints: {
        intervalInSeconds: 900,
        sizeInMBs: 50,
      },
      compressionFormat: 'UNCOMPRESSED',
      prefix: 'apigateway/',
      roleArn: firehoseDeliveryRole.roleArn,
    },
  });

  return logDeliveryStream;
}

/**
 * Snake > Camel形に変換する
 * @remarks
 * 例:`amazon-apigateway-my-api`は`AmazonApigatewayMyApi`に変換される
 * @param str - 変換したい文字列
 *
 * @returns 変換後結果
 */
export function snakeToCamel(str: string): string {
  str = str.replace(/([-_]\w)/g, (g) => g[1].toUpperCase());
  return str.charAt(0).toUpperCase() + str.slice(1);
}

参考文献🌟

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?