API Getewayの実行ログ保持期間をCDKで設定する方法 <Custom Resources と Lambda関数を使用する場合>

2024.04.21

はじめに

CDKでAPI Gateway の実行ログを有効化すると、デプロイ時にCDKにより自動でロググループが作成されます。
しかしAPI Gatewayのクラスは実行ログの保持期間を設定するプロパティを持っておらずこのままでは設定できません。

このような設定項目についてデプロイ時に設定を行いたい場合、Custom Resources と Lambda関数を使用して設定できる方法が有りましたので紹介します。

実行ログの出力を有効化した場合の挙動

以下のようにCDKでAPI Gatewayを作成すると、実行ログ出力が有効化されます。

./lib/api-stack.ts

import {
  aws_lambda,
  aws_lambda_nodejs,
  aws_apigateway,
} from 'aws-cdk-lib';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class ApiStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);

    const testFunc = new aws_lambda_nodejs.NodejsFunction(this, 'testFunc', {
      functionName: 'testFunc',
      runtime: aws_lambda.Runtime.NODEJS_20_X,
      architecture: aws_lambda.Architecture.ARM_64,
      entry:
        './src/lambda/handler.ts',
    });

    const restApi = new aws_apigateway.LambdaRestApi(this, 'RestApi', {
      handler: testFunc,
      deployOptions: {
        stageName: 'v1',
        tracingEnabled: true,
        dataTraceEnabled: true, // この部分で実行ログの出力を有効化
        loggingLevel: aws_apigateway.MethodLoggingLevel.ERROR, // 出力する実行ログのログレベルを設定
      }
    });
  }
}

LambdaRestApiクラスのdataTraceEnabledをtrueにすることで、API Gatewayの実行ログ出力が有効化されます。しかし保持期間を設定するプロパティは存在しません。

このCDKスタックをデプロイすると、API Gatewayの実行ログのロググループの作成まで自動で行われ、以下のロググループがが作成されます。
当然、保持期間がCDKで設定できていないので、保持の項目が失効しないになってます。

このように自動でロググループが作成されてしまい、且つ保持期間の設定プロパティが無いため、他の手段を用いて保持期間を設定する必要があります。

Custom Resources と Lambda関数を使用して実行ログの保持期間を設定する方法

それでは設定項目がCDKに存在しない実行ログの保持期間を、Custom Resources と Lambda関数を使用して設定する方法を紹介します。

Custom Resources と Lambda関数のそれぞれの役割は以下の通りです。

Custom Resources

Custom Resources は通常のCloudFormationで定義されているリソースタイプでは対応できないような処理を行うことができます。
そしてonCreateというパラメーターを使用する事で、処理をデプロイ時に実行させることが可能となります。

このonCreateは今回、デプロイ時にLambda関数を実行させるために使用します。

Lambda関数

Lambda関数はCustom Resourcesで実行する処理を記述します。
Lambda関数内にはロググループの保持期間を設定する処理を記述します。

Custom Resourcesを追加したCDKスタック

CDKスタックにCustom Resourcesを追加した全体のコードが以下です。

./lib/api-stack.ts

import {
  aws_lambda,
  aws_lambda_nodejs,
  aws_apigateway,
} from 'aws-cdk-lib';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class ApiStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);

    const testFunc = new aws_lambda_nodejs.NodejsFunction(this, 'testFunc', {
      functionName: 'testFunc',
      runtime: aws_lambda.Runtime.NODEJS_20_X,
      architecture: aws_lambda.Architecture.ARM_64,
      entry:
        './src/lambda/handler.ts',
    });

    const restApi = new aws_apigateway.LambdaRestApi(this, 'RestApi', {
      handler: testFunc,
      deployOptions: {
        stageName: 'v1',
        tracingEnabled: true,
        dataTraceEnabled: true, // この部分で実行ログの出力を有効化
        loggingLevel: aws_apigateway.MethodLoggingLevel.ERROR, // 出力する実行ログのログレベルを設定
      }
    });

    // ロググループの保持期間を設定するLambdaの作成
    const addLogGroupRetentionFunction = new aws_lambda_nodejs.NodejsFunction(
      this,
      'AddLogGroupRetentionFunction',
      {
        functionName: 'add-log-group-retention-function',
        runtime: aws_lambda.Runtime.NODEJS_20_X,
        architecture: aws_lambda.Architecture.ARM_64,
        entry:
          '../data-api/src/src/lambda/handlers/api-gateway/add-log-group-retention-func.mts',
        environment: {
          LOG_GROUP_NAME: `API-Gateway-Execution-Logs_${restApi.restApiId}/${restApi.deploymentStage.stageName}`,
        },
      }
    );

    // 上記Lambdaに追加するIAMポリシーの作成(PutRetentionPolicyを許可)
    const addLogGroupRetentionFunctionRole = new aws_iam.PolicyStatement({
      actions: ['logs:PutRetentionPolicy'],
      resources: [
        `arn:aws:logs:${Stack.of(this).region}:${Stack.of(this).account}:log-group:API-Gateway-Execution-Logs_${restApi.restApiId}/${restApi.deploymentStage.stageName}:*`,
      ],
      effect: aws_iam.Effect.ALLOW,
    });
    addLogGroupRetentionFunction.addToRolePolicy(
      addLogGroupRetentionFunctionRole
    );


    // デプロイ時にLambdaを実行するためのカスタムリソースの作成
    const customResource = new custom_resources.AwsCustomResource(
      this,
      'CustomResource',
      {
        policy: custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
          resources: custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
        }),
        onCreate: {
          service: 'Lambda',
          action: 'invoke',
          parameters: {
            FunctionName: addLogGroupRetentionFunction.functionName,
          },
          physicalResourceId: custom_resources.PhysicalResourceId.of(
            addLogGroupRetentionFunction.functionName
          ),
        },
      }
    );
    addLogGroupRetentionFunction.grantInvoke(customResource.grantPrincipal);
  }
}

実行ロググロープの保持期間を設定するLambda関数の作成

// ロググループの保持期間を設定するLambdaの作成
const addLogGroupRetentionFunction = new aws_lambda_nodejs.NodejsFunction(
  this,
  'AddLogGroupRetentionFunction',
  {
    functionName: 'add-log-group-retention-function',
    runtime: aws_lambda.Runtime.NODEJS_20_X,
    architecture: aws_lambda.Architecture.ARM_64,
    entry:
      '../data-api/src/src/lambda/handlers/api-gateway/add-log-group-retention-func.mts',
    environment: {
      LOG_GROUP_NAME: `API-Gateway-Execution-Logs_${restApi.restApiId}/${restApi.deploymentStage.stageName}`,
    },
  }
);

環境変数にはロググループの名前を設定します。

実行ログのグループ名はプレフィックスが固定で、API-Gateway-Execution-Logs_<APIのID>/<APIのデプロイステージ名> という形式となります。

ログ保持期間を設定する権限を付与

// 上記Lambdaに追加するIAMポリシーの作成(PutRetentionPolicyを許可)
const addLogGroupRetentionFunctionRole = new aws_iam.PolicyStatement({
  actions: ['logs:PutRetentionPolicy'],
  resources: [
    `arn:aws:logs:${Stack.of(this).region}:${Stack.of(this).account}:log-group:API-Gateway-Execution-Logs_${restApi.restApiId}/${restApi.deploymentStage.stageName}:*`,
  ],
  effect: aws_iam.Effect.ALLOW,
});
addLogGroupRetentionFunction.addToRolePolicy(
  addLogGroupRetentionFunctionRole
);

Lambda関数にロググループの保持期間を設定する権限を付与します。
許可するアクションはlogs:PutRetentionPolicyです。
resource の指定には、ロググループのARNを指定します。
ARNの末尾に:*を付ける必要がありますのでご注意下さい。

Custom Resourcesの作成

// デプロイ時にLambdaを実行するためのカスタムリソースの作成
const customResource = new custom_resources.AwsCustomResource(
  this,
  'CustomResource',
  {
    policy: custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
      resources: custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
    }),
    onCreate: {
      service: 'Lambda',
      action: 'invoke',
      parameters: {
        FunctionName: addLogGroupRetentionFunction.functionName,
      },
      physicalResourceId: custom_resources.PhysicalResourceId.of(
        addLogGroupRetentionFunction.functionName
      ),
    },
  }
);
addLogGroupRetentionFunction.grantInvoke(customResource.grantPrincipal);

Custom Resourcesを作成し、デプロイ時にLambda関数を実行するように設定します。
onCreate配下のparametersは以下のように設定します。

  • service: Lambda
    • ここにはどのサービスを使うかを設定するため、Lambdaと記載します。
  • action: invoke
    • ここにはLambdaのどのアクションを行うか記載します。今回Lambda関数を実行するのでinvokeと記載します。
  • parameters: 配下のFunctionNameにLambda関数の名前を指定します。
  • physicalResourceId: 作成するCustom Resourcesの物理IDを指定します。ここは一位の一意の値を指定します。今回は関数名にしています。

最後に以下のコードでCustom ResourcesのリソースにLambda関数の実行権限を付与します。

addLogGroupRetentionFunction.grantInvoke(customResource.grantPrincipal);

リソースのデプロイ順番について補足

デプロイの挙動を確認したところ、Custom Resourcesはどのリソースよりも後に作られてました。このため、実行ログのロググループより先に作られて保持期間が設定できない、という事は発生しません。

実行ログに保持期間を設定するLambda関数

関数自体のコードは以下となります。

./src/lambda/set-retention-policy.ts

import {
  CloudWatchLogsClient,
  PutRetentionPolicyCommand,
} from '@aws-sdk/client-cloudwatch-logs';
const region = process.env.AWS_REGION as string;
const logGroupName = process.env.LOG_GROUP_NAME as string;

const s3Client = new CloudWatchLogsClient({ region: region });

export const handler = async (): Promise<void> => {
  try {
    const putRetentionPolicyCommand = new PutRetentionPolicyCommand({
      logGroupName: logGroupName,
      retentionInDays: 180, // 180日間のログを保持
    });
    await s3Client.send(putRetentionPolicyCommand);
  } catch (error) {
    console.log(
      'addRetentionHandlerUnknownError',
      JSON.stringify(error, undefined, 2)
    );
  }
};

SDKの@aws-sdk/client-cloudwatch-logsを使用します。SDKを使用して、API呼び出しを行ってロググループの保持期間を設定します。
呼び出しているAPIはPutRetentionPolicyです。
PutRetentionPolicyCommandクラス内のretentionInDaysで保持期間を設定します。

デプロイ実行

Custom ResourcesとLambda関数を追加したCDKをデプロイしますと、想定通りに既に作成作成されていた実行ロググループに保持期間が設定されました。

最後に

今回はCDKのCustom Resources と Lambda関数を使用してAPI Gatewayの実行ログの保持期間を設定する方法を紹介しました。
API Gatewayの実行ログのようにCDKで設定できない設定項目をデプロイ時に設定したい場合、今回のようにCustom Resourcesで設定できますので、同じような場面で今後も役に立ちそうです。