Amazon QuickSightダッシュボードのアクセス数をAthena/CloudTrailで調べる

AWS CloudTrailのAPIログにAmazon AthenaからSQLを投げてAmazonQuickSightのダッシュボードのアクセス数を復元する方法を紹介します。
2018.12.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Amazon QuickSightは、分析結果をダッシュボードとして公開すると、参照のみ可能なリーダーと分析結果を共有出来ます。

ダッシュボードを公開した以上は、ダッシュボードのアクセス状況が気になるところです。

残念ながら、現在のAmazon QuickSightの管理画面はSPICE使用量などごく一部のメトリクスしか提供されておらず、CloudWatch連携もありません。 一方で、ダッシュボードへのアクセス(GetDashboard API)はAWS CloudTrailに記録されます。

今回は、このAWS CloudTrailのAPIログにAmazon AthenaからSQLを投げてダッシュボードのアクセス数を復元する方法を紹介します。

AWS CloudTrailログクエリー用のAthenaテーブルを作成

AWS CloudTrailのEvent historyページからウィザード形式で作成出来ます。

詳細は次のページを参照ください。

AWS CloudTrail ログのクエリ

GetDashboard APIのログ形式を確認

GetDashboard APIのログ形式を

  • IAMユーザー経由のAuthorロール
  • Readerロール

それぞれでアクセスして確認します。

違いは userIdentity だけです。

IAMユーザー経由のAuthorには、principalIdの属性が存在します。 残りのフィールドは一緒です。

重要なフィールド

今回の目的では以下の3つのフィールドが重要です。

  • 誰がアクセス? : userIdentity
  • どのダッシュボードにアクセス? : serviceEventDetails.eventRequestDetails.dashboardId
  • いつアクセス? : eventTime

Readerロールの場合のログ形式

READER

{
    "eventVersion": "1.05",
    "userIdentity": {
        "arn": "arn:aws:ds:us-east-1:123456789012:user/d-90672e24b8/bf1c0629-5635-407f-9d5f-e67defed5c3a",
        "accountId": "123456789012",
        "userName": "alice@example.com",
        "type": "Directory"
    },
    "eventTime": "2018-12-17T18:24:06Z",
    "eventSource": "quicksight.amazonaws.com",
    "eventName": "GetDashboard",
    "awsRegion": "us-east-1",
    "requestParameters": null,
    "responseElements": null,
    "eventID": "3288f81f-6e7d-4a15-ab6e-f071b3fc0a62",
    "readOnly": true,
    "eventType": "AwsServiceEvent",
    "recipientAccountId": "123456789012",
    "serviceEventDetails": {
        "eventRequestDetails": {
            "dashboardId": "arn:aws:quicksight:us-east-1:123456789012:dashboard/5b1eb242-b3b9-461d-82ac-910a0b5435fe"
        },
        "eventResponseDetails": {
            "dashboardDetails": {
                "dashboardName": "dashboard name",
                "dashboardId": "arn:aws:quicksight:us-east-1:123456789012:dashboard/5b1eb242-b3b9-461d-82ac-910a0b5435fe",
                "analysisIdList": [
                    "arn:aws:quicksight:us-east-1:123456789012:analysis/cc5cc305-0e90-4a4a-8d4b-f9a1be821b3c"
                ],
                "dataSetIdList": [
                    "arn:aws:quicksight:us-east-1:123456789012:dataset/e1115401-2d74-41e7-ab70-728efad7a289"
                ]
            }
        }
    }
}

IAMユーザー経由のAuthorロールの場合のログ形式

userIdentity 以外の出力は Readerロールと同じ為、異なる箇所だけを抜粋します。

IAM user Author

{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "DUMMY-PRINCIPAL-ID",
        "arn": "arn:aws:iam::123456789012:user/cm-jane.doe",
        "accountId": "123456789012",
        "userName": "cm-jane.doe"
    },
    ...
}

Amazon Athena からAWS CloudTrailログに問い合わせる

Amazon AthenaからこのAWS CloudTrailログに問い合わせる上では

  • userIdentity
  • serviceEventDetails

の操作がややこしそうです。

userIdentityの操作

userIdentity は次のように STRUCT 型をしています。

userIdentity STRUCT<
    type: STRING,
    principalId: STRING,
    arn: STRING,
    ...

そのためuserIdentity内の各値にはuserIdentity.principalId というようにドットでアクセスできます。

serviceEventDetailsの操作

serviceEventDetailsSTRING型です。

"serviceEventDetails": {
    "eventRequestDetails": {
        "dashboardId": "arn:aws:quicksight:us-east-1:123456789012:dashboard/5b1eb242-b3b9-461d-82ac-910a0b5435fe"
    },
    "eventResponseDetails": {
        "dashboardDetails": {
            "dashboardName": "dashboard name",
            "dashboardId": "arn:aws:quicksight:us-east-1:123456789012:dashboard/5b1eb242-b3b9-461d-82ac-910a0b5435fe",
            "analysisIdList": [
                "arn:aws:quicksight:us-east-1:123456789012:analysis/cc5cc305-0e90-4a4a-8d4b-f9a1be821b3c"
            ],
            "dataSetIdList": [
                "arn:aws:quicksight:us-east-1:123456789012:dataset/e1115401-2d74-41e7-ab70-728efad7a289"
            ]
        }
    }
}

という形をしているため、json_extract 関数を使い JSONPath 書式でエレメントを抽出してアクセスします。

json_extract のレスポンスは JSON 型であり、JSON型は順序関係が定まっていないため、ORDER BY したいときは cast(json_extract(serviceeventdetails, '$.eventRequestDetails.dashboardId') AS VARCHAR) というように順序関係が定まっている型に CAST します。

目的別SQL例

ダッシュボード別アクセス数を知りたい

アクセスの多いダッシュボード上位5件を表示

SELECT cast(json_extract(serviceeventdetails,
         '$.eventRequestDetails.dashboardId') AS VARCHAR) AS dashboardid , count(*) cnt
FROM cloudtrail_logs_cloudtrail_tbl
WHERE eventsource = 'quicksight.amazonaws.com'
        AND eventname = 'GetDashboard'
        AND eventtime >= '2018-12-01T00:00:00Z'
GROUP BY  1
ORDER BY  1
LIMIT 5

ユーザー別アクセス数を知りたい

アクセスの多いユーザー上位5件を表示

SELECT useridentity.type ,
         useridentity.username ,
         count(*) cnt
FROM cloudtrail_logs_cloudtrail_us_east_1_123456789012
WHERE eventsource = 'quicksight.amazonaws.com'
        AND eventname = 'GetDashboard'
        AND eventtime >= '2018-12-01T00:00:00Z'
GROUP BY  1,2
ORDER BY  3 desc, 1, 2
LIMIT 5

ダッシュボード x ユーザー別アクセス数を知りたい

SELECT cast(json_extract(serviceeventdetails,
         '$.eventRequestDetails.dashboardId') AS VARCHAR) AS dashboardid , useridentity.type , useridentity.username , count(*) cnt
FROM cloudtrail_logs_cloudtrail_us_east_1_123456789012
WHERE eventsource = 'quicksight.amazonaws.com'
        AND eventname = 'GetDashboard'
        AND eventtime >= '2018-12-01T00:00:00Z'
GROUP BY  1,3, 2
ORDER BY  1, 2,3

謝辞

AWS CloudTrailからAmazon QuickSightダッシュボードのアクセス数を集計できることは、AWS re:Invent開催中にAmazon QuickSightのエンジニアから教わりました。

実際にサービス開発に携わっているエンジニアに直接質問できるのは、re:Inventの醍醐味の一つですね。

参考