Amazon API Gateway でアクセスログを有効化してデプロイしようとすると CloudWatch Logs role ARN must be set in account settings to enable logging
というエラーが出る場合がある😇もしかしたら Amazon API Gateway の設定「CloudWatch ログのロール ARN (CloudWatch log role ARN)」が不足している可能性がある.
「CloudWatch ログのロール ARN」は Amazon API Gateway の API 単位の設定ではなく「アカウント/リージョン」単位の設定で,少し珍しいと思う.もしマルチアカウント戦略を採用できてなく,複数のプロダクトで同じアカウントを共有していたりすると,管理が面倒になることもあると思う😇
AWS CDK でエラーに対処する
選択肢 1: aws_apigateway.RestApi
まず aws_apigateway.RestApi
には cloudWatchRole
という設定があって(デフォルトは false
),true
にすると自動的に IAM Role を追加して Amazon API Gateway に設定してくれる👌以下にサンプルコードを載せておく.
しかし後述する理由で使わない方が良いと思う💨
import { Stack, StackProps, aws_apigateway, aws_logs, } from 'aws-cdk-lib' import { Construct } from 'constructs' export class ApiGatewayCloudWatchRoleStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props) const apiAccessLogGroup = new aws_logs.LogGroup(this, 'ApiGatewayAccessLogGroup', { logGroupName: 'sandbox-api-gateway-access-logs', retention: aws_logs.RetentionDays.ONE_MONTH, }) const api = new aws_apigateway.RestApi(this, 'ApiGateway', { restApiName: 'sandbox-api-gateway-cloudwatch-role', deployOptions: { accessLogDestination: new aws_apigateway.LogGroupLogDestination(apiAccessLogGroup), accessLogFormat: aws_apigateway.AccessLogFormat.jsonWithStandardFields(), }, cloudWatchRole: true, }) api.root.addMethod('GET', new aws_apigateway.HttpIntegration('https://dog.ceo/api/breeds/image/random')) } }
実際にデプロイすると arn:aws:iam::000000000000:role/ApiGatewayCloudWatchRoleS-ApiGatewayCloudWatchRoleA-nA8OCNOvnkCS
という IAM Role が設定されていた💡
もし cloudWatchRole
を設定した Amazon API Gateway を2つ以上デプロイする場合に IAM Role が上書きされてしまうという課題がある.
import { Stack, StackProps, aws_apigateway, aws_logs, } from 'aws-cdk-lib' import { Construct } from 'constructs' export class ApiGatewayCloudWatchRoleStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props) const apiAccessLogGroup = new aws_logs.LogGroup(this, 'ApiGatewayAccessLogGroup', { logGroupName: 'sandbox-api-gateway-access-logs', retention: aws_logs.RetentionDays.ONE_MONTH, }) const api = new aws_apigateway.RestApi(this, 'ApiGateway', { restApiName: 'sandbox-api-gateway-cloudwatch-role', deployOptions: { accessLogDestination: new aws_apigateway.LogGroupLogDestination(apiAccessLogGroup), accessLogFormat: aws_apigateway.AccessLogFormat.jsonWithStandardFields(), }, cloudWatchRole: true, }) api.root.addMethod('GET', new aws_apigateway.HttpIntegration('https://dog.ceo/api/breeds/image/random')) const api2 = new aws_apigateway.RestApi(this, 'ApiGateway2', { restApiName: 'sandbox-api-gateway-cloudwatch-role2', deployOptions: { accessLogDestination: new aws_apigateway.LogGroupLogDestination(apiAccessLogGroup), accessLogFormat: aws_apigateway.AccessLogFormat.jsonWithStandardFields(), }, cloudWatchRole: true, }) api2.root.addMethod('GET', new aws_apigateway.HttpIntegration('https://dog.ceo/api/breeds/image/random')) } }
実際にデプロイすると arn:aws:iam::000000000000:role/ApiGatewayCloudWatchRoleS-ApiGateway2CloudWatchRole-gT1C2Om2Nr4Y
という IAM Role に上書きされていた😇 AWS CDK でリソースを管理しているのにデプロイするリソースの順番によって「CloudWatch ログのロール ARN」の値が変わってしまうのは微妙だと思う.
選択肢 2: aws_apigateway.CfnAccount
そこで aws_apigateway.CfnAccount
を使うと「CloudWatch ログのロール ARN」を独立したリソースとして管理できる❗️ドキュメントには以下のように書いてあった.できれば AWS CDK Stack も別にしておくと良いと思う👌
To avoid overwriting other roles, you should only have one AWS::ApiGateway::Account resource per region per account.
以下のように aws_iam.Role
と aws_apigateway.CfnAccount
を組み合わせて実装できる.
import { Duration, Stack, StackProps, aws_apigateway, aws_iam, } from 'aws-cdk-lib' import { Construct } from 'constructs' export class ApiGatewayAccountStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props) const role = new aws_iam.Role(this, 'ApiGatewayLogsRole', { roleName: 'api-gateway-logs-role', assumedBy: new aws_iam.ServicePrincipal('apigateway.amazonaws.com'), managedPolicies: [ aws_iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonAPIGatewayPushToCloudWatchLogs'), ], maxSessionDuration: Duration.hours(1), }) new aws_apigateway.CfnAccount(this, 'ApiGatewayAccount', { cloudWatchRoleArn: role.roleArn, }) } }
実際にデプロイすると arn:aws:iam::000000000000:role/api-gateway-logs-role
という指定した IAM Role を設定できていた👏