[アップデート] Amazon CloudWatch の GetMetricData イベントが CloudTrail データイベントで取得出来るようになりました

2024.06.08

いわさです。

3 ~ 4 日前に、Datadog からの CloudWatch GetMetricData API の呼び出し部分のコスト最適化を図りたくて、まずは呼び出し状況の棚卸しをしようとしたんですよ。次の記事です。

上記記事を見て頂くとわかるのですが、呼び出し状況の確認に CloudWatch メトリクスの Usage CallCount を使って GetMetricData API の呼び出し回数をカウントしています。
これ、実際には Datadog 以外からの呼び出しも含まれており厳密な数値を取ることは出来ません。

なぜこのような形にしていたかというと、API の呼び出し状況を分析したかったので本来であれば CloudTrail を使いたいところだったのですが上記記事執筆時点では CloudTrail では CloudWatch GetMetricData API の呼び出し記録はされなかったためです。

GetMetricData が CloudTrail で記録出来るようになった

昨日のアップデートで CloudTral に記録出来るようになったとアナウンスがありました。
タイムリーすぎて「CloudTrail 開発チームは DevelopersIO をウォッチしているのかな?」など思いましたがこのアップデートはかなりありがたいです。

GetMetricData に加えて GetMetricWidgetImage API も記録対象のようですね。
GetMetricData API の呼び出し状況を分析して最適化を図ることも出来ますし、あるいはコンプライアンス的な観点から記録が求められる際にも対応することが出来ます。

なおこのイベントは標準では記録されず、CloudTrail データイベントとして記録されます。
したがって別途追加料金が発生するのと、オプトイン操作が必要という点に注意してください。
標準の管理イベントとデータイベントの違いを知りたいという方は以下を参照してください。

有効化してみた

では早速有効化してみましょう。
冒頭の記事で使った AWS + Datadog の統合環境で確認してみます。

CloudTrail 証跡を編集しデータイベントを有効化します。
高度なイベントセレクターでデータイベントタイプに「CloudWatch metric」を選択出来るようになっていますので、こちらを選択します。

今回は CloudTrail から CloudWatch Logs へ出力しています。
CloudTrail のデータイベントは、CloudTrail コンソールのイベント履歴画面から確認することが出来ないので、手軽に検証したい時は CloudWatch Logs への出力オプションを使うと分析が捗ります。データカタログなど構築済みであれば Athena でも良いのですけども。

CloudWatch Logs の Live Tail 機能で少し待機していると...来ました!
Datadog 統合用に用意した IAM ロールを使って呼び出しされているイベントを発見しましたよ。こいつが Datadog からの呼び出しイベントです。

適当に抽出したイベント1件のレコードを以下に引用します。
大阪リージョンを対象とした GetMetricData が実行されています。
metricDataQueries細かくリストで指定されているんだなぁとか色々わかりますね。

{
    "eventVersion": "1.09",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "HOGEHOGEHOGEHOGE:DatadogAWSIntegration",
        "arn": "arn:aws:sts::123456789012:assumed-role/DatadogIntegrationRole/DatadogAWSIntegration",
        "accountId": "123456789012",
        "accessKeyId": "HOGEHOGEHOGEHOGE",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "HOGEHOGEHOGEHOGE",
                "arn": "arn:aws:iam::123456789012:role/DatadogIntegrationRole",
                "accountId": "123456789012",
                "userName": "DatadogIntegrationRole"
            },
            "attributes": {
                "creationDate": "2024-06-07T20:42:01Z",
                "mfaAuthenticated": "false"
            }
        }
    },
    "eventTime": "2024-06-07T21:07:23Z",
    "eventSource": "monitoring.amazonaws.com",
    "eventName": "GetMetricData",
    "awsRegion": "ap-northeast-3",
    "sourceIPAddress": "43.206.163.137",
    "userAgent": "python-urllib3/1.26.18",
    "requestParameters": {
        "metricDataQueries": [
            {
                "id": "m1",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ConsumedCapacity",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Average"
                }
            },
            {
                "id": "m2",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ConsumedCapacity",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Sum"
                }
            },
            {
                "id": "m3",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ProvisionedBucketSize",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Average"
                }
            },
            {
                "id": "m4",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ProvisionedBucketSize",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Sum"
                }
            },
            {
                "id": "m5",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ProvisionedRefillRate",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Average"
                }
            },
            {
                "id": "m6",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ProvisionedRefillRate",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Sum"
                }
            },
            {
                "id": "m7",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ThrottledEvents",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Average"
                }
            },
            {
                "id": "m8",
                "metricStat": {
                    "metric": {
                        "namespace": "AWS/States",
                        "metricName": "ThrottledEvents",
                        "dimensions": [
                            {
                                "name": "APIName",
                                "value": "ListStateMachines"
                            }
                        ]
                    },
                    "period": 60,
                    "stat": "Sum"
                }
            }
        ],
        "startTime": "Jun 7, 2024, 8:31:40 PM",
        "endTime": "Jun 7, 2024, 9:07:00 PM"
    },
    "responseElements": null,
    "requestID": "8c4227fe-d9c8-4965-ac9f-d1b51d4a3aaf",
    "eventID": "7d50b99a-53de-49aa-a47b-d74ff2b91fa4",
    "readOnly": true,
    "resources": [
        {
            "type": "AWS::CloudWatch::Metric"
        }
    ],
    "eventType": "AwsApiCall",
    "managementEvent": false,
    "recipientAccountId": "123456789012",
    "eventCategory": "Data",
    "tlsDetails": {
        "tlsVersion": "TLSv1.3",
        "cipherSuite": "TLS_AES_128_GCM_SHA256",
        "clientProvidedHostHeader": "monitoring.ap-northeast-3.amazonaws.com"
    }
}

また、しばらく Live Tail で待機していると、だいたい 9 ~ 10 分間隔くらいで呼び出しされていることもわかりました。
Datadog サポート経由でポーリング間隔を変えてもらった時などにも、本当に変わっているのか確認するのに使えそうですね。

なお、上記では検証中に Datadog 側の対象リージョンを追加したりしているせいか us-east-1 へのポーリングも発生していました。

さいごに

本日は Amazon CloudWatch の GetMetricData イベントが CloudTrail データイベントで取得出来るようになったので確認してみました。

今回のアップデートによって、より詳細な情報を取得することが出来るようになりました。
GetMetricData API の高騰で分析が必要な環境であれば、例えば Datadog でいう対象リージョンやリソースを見直すとか、そういった際のヒントに使ったり。
あるいは CloudWatch 連携周りおトラブルシューティングなどにも活用出来そうです。いいぞ。

データイベントで追加料金が発生するという点だけ気をつけましょう!