概要
Route53にはDNSクエリログというのがあり、それをCloudWatch Logsに保存する仕組みをCloudFormationで作成するのは実は大変でした。
それが少し簡単になったため、今回CloudFormationで作成する方法を書いてみました。
やること
Route53のDNSクエリログをCloudWatch Logsに保存する仕組みを、CloudFormationで構築します。
ポイント
同じような仕組みとして、ECSイベントやRDSイベントをCloudWatch Logsに転送する仕組みをCloudFormationで作成する方法を記事にしています。
タイトルだけだとRoute53とECS,RDSで関連性が薄いようにも見えますが、共通点が一緒なのです。
その共通点というのが、「リソースポリシー」です。
リソースポリシーの説明も以下の記事で記載しています。
ポイントなのが、CloudWatch Logsのロググループにリソースポリシーを与えてあげないと、DNSクエリログが転送されないのです。
そして、そのリソースポリシーが今まではCloudFormation未対応だったのが、ついに対応されるようになったのです。
コード
※GitHubにもあります。
AWSTemplateFormatVersion: "2010-09-09" Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Domain" Parameters: - Domain - Label: default: "LogName" Parameters: - LogName Parameters: Domain: Type: String LogName: Type: String Resources: Route53HostedZone: Type: AWS::Route53::HostedZone DependsOn: ResourcePolicyForLogs Properties: HostedZoneConfig: Comment: !Ref Domain Name: !Ref Domain QueryLoggingConfig: CloudWatchLogsLogGroupArn: !GetAtt DNSQueryLogGroup.Arn DNSQueryLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/route53/${LogName}" ResourcePolicyForLogs: Type: AWS::Logs::ResourcePolicy Properties: PolicyName: "DNSQueryLog-ToLogs-ResourcePolicy" PolicyDocument: !Sub - | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "route53.amazonaws.com" }, "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "${CloudWatchLogsLogGroupArn}" } ] } - CloudWatchLogsLogGroupArn: !GetAtt DNSQueryLogGroup.Arn
解説
AWS::Route53::HostedZone
Route53のホストゾーン作成になります。
ここで大事なのはQueryLoggingConfig
で、CloudWatchLogsLogGroupArn
にログのARNを指定します。
Route53HostedZone: Type: AWS::Route53::HostedZone DependsOn: ResourcePolicyForLogs Properties: HostedZoneConfig: Comment: !Ref Domain Name: !Ref Domain QueryLoggingConfig: CloudWatchLogsLogGroupArn: !GetAtt DNSQueryLogGroup.Arn
また、Route53HostedZone
はDNSQueryLogGroup
のARNを参照しているため依存関係にあり、ロググループが作られてからホストゾーン作成が始まります。
ところが、Route53HostedZone
のリソース定義にリソースポリシーであるResourcePolicyForLogs
への参照はなく依存関係がないため、ホストゾーンとリソースポリシーはどちらが先に作られるかわかりません。
しかし、ホストゾーンでクエリログ設定をするロググループにはリソースポリシーが付与されていないといけないため、DependsOn: ResourcePolicyForLogs
とすることで、明示的に依存関係を持たせて、リソースポリシーが作られてからホストゾーンが作成されるようにしています。
(なくても成功しました、、、)
Route53HostedZone: Type: AWS::Route53::HostedZone DependsOn: ResourcePolicyForLogs
AWS::Logs::ResourcePolicy
AWS::Logs::ResourcePolicy
によってリソースポリシーを作成しています。
ResourcePolicyForLogs: Type: AWS::Logs::ResourcePolicy Properties: PolicyName: "DNSQueryLog-ToLogs-ResourcePolicy" PolicyDocument: !Sub - | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "route53.amazonaws.com" }, "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "${CloudWatchLogsLogGroupArn}" } ] } - CloudWatchLogsLogGroupArn: !GetAtt DNSQueryLogGroup.Arn
このリソースポリシーのPrincipalには、Serviceとしてroute53.amazonaws.com
を指定する必要があります。
そして、お決まりのログ転送に必要なアクション(logs:CreateLogStream
, logs:PutLogEvents
)を記述し、Resourceには転送先ログのARNを指定します。
このResource指定には、組み込み関数SubのList形式の記法により、変数で指定しています。
Resourceはワイルドカードの指定も可能です。
またリソースポリシーはJSON形式での記述になりますが、yamlでJSONを扱う時、よく1行の文字列にして使用しているケースを見かけることがあります。
しかし、上記のように「|」を使うことで、そのままJSONの形式でyamlに記述することが可能になります。
注意
本題とは関係ないのですが、上記CloudFormationスタックは、バージニアリージョン(us-east-1)での実行を忘れないようにしてください。
Route53のDNSクエリログに指定するロググループはus-east-1での作成が必須であるからです。
たとえば東京リージョンなどで実行しようとすると以下のようなエラーメッセージに遭遇します。
Resource handler returned message: "The ARN for the CloudWatch Logs log group is invalid. (省略)
CloudFormationの公式ドキュメントなどにも載っています。
最後に
CloudFormationでCloudWatch Logsのリソースポリシーを、カスタムリソースを使わずに作成できるようになったのを数ヶ月遅れで知ったため、このような記事を書いてみました。