Partition Projectionで 安心CloudTrailログ分析

# はじめに
MEGAZONE 株式会社 のテック陣「MEGAZONEのゆかいな仲間たち」がおくる、Megazone Japan Advent Calendar 2023 の11日目のエントリーです。

阿河です。
みなさん、CloudTrailは使っていますか??

CloudTrailはアカウントのイベントのモニタリングをして、証跡としてS3バケットに配信できます。蓄積したログはAthenaを利用して分析することも可能です。

ただし年月が経過するとCloudTrailのログは倍増していき、Ahtenaのクエリ費用も増加します。
本記事では、コストを気にしつつ、安全にCloudTrailログに対する分析を行う方法をご紹介いたします。

# 概要
1. Athenaで分析をする際の問題点
2. Partition Projection
3. 検証

# 1. Athenaで分析をする際の問題点

### 証跡について

https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/cloudtrail-getting-started.html 

AWS CloudTrail は、AWS アカウントの運用とリスクの監査、ガバナンス、コンプライアンスを行えるように支援する AWS のサービスです。
ユーザー、ロール、または AWS のサービスによって実行されたアクションは、CloudTrail にイベントとして記録されます。

**要はAWS環境で発生するアクティビティのうち、「誰が」「いつ」「何を」したか記録を追跡することができます。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697264/e0890730-b03a-963a-2dd8-58f1e21c3c0b.png)

CloudTrail コンソールの[イベント履歴]からAWSリージョンで記録された過去90日間のAPIアクティビティ(管理イベント) を確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697264/c941849a-b6e2-d932-4568-0f9be7726434.png)


またAWSアカウント内のイベントを継続的に記録するには証跡を作成して、ログファイルをS3バケットに配信できます。

この機能により、組織はアカウント内で発生したアクティビティを把握することができます。

### Athenaを利用した分析

蓄積したCloudTrailログの分析方法の一つとして、AWS Athenaが利用できます。

Amazon Athenaは標準的なSQLを使用して、Amazon S3内のデータを直接分析することを容易にするインタラクティブなクエリサービスです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697264/b5268886-f9e0-7227-1039-7b36040a6105.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697264/7d93d6f7-769f-5a0d-5628-7b9a72959726.png)


CloudTrailのコンソールページから、テーブル作成のためのクエリを発行できます。

```
CREATE EXTERNAL TABLE [TABLE_NAME] (
    eventVersion STRING,
    userIdentity STRUCT<
        type: STRING,
        principalId: STRING,
        arn: STRING,
        accountId: STRING,
        invokedBy: STRING,
        accessKeyId: STRING,
        userName: STRING,
        sessionContext: STRUCT<
            attributes: STRUCT<
                mfaAuthenticated: STRING,
                creationDate: STRING>,
            sessionIssuer: STRUCT<
                type: STRING,
                principalId: STRING,
                arn: STRING,
                accountId: STRING,
                username: STRING>,
            ec2RoleDelivery: STRING,
            webIdFederationData: MAP<STRING,STRING>>>,
    eventTime STRING,
    eventSource STRING,
    eventName STRING,
    awsRegion STRING,
    sourceIpAddress STRING,
    userAgent STRING,
    errorCode STRING,
    errorMessage STRING,
    requestParameters STRING,
    responseElements STRING,
    additionalEventData STRING,
    requestId STRING,
    eventId STRING,
    resources ARRAY<STRUCT<
        arn: STRING,
        accountId: STRING,
        type: STRING>>,
    eventType STRING,
    apiVersion STRING,
    readOnly STRING,
    recipientAccountId STRING,
    serviceEventDetails STRING,
    sharedEventID STRING,
    vpcEndpointId STRING,
    tlsDetails STRUCT<
        tlsVersion: STRING,
        cipherSuite: STRING,
        clientProvidedHostHeader: STRING>
)
COMMENT 'CloudTrail table for [S3_BUCKET_NAME] bucket'
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION '[S3_BUCKET_URL]'
TBLPROPERTIES ('classification'='cloudtrail');
```

シンプルにAthenaで分析をかけたい場合は、上記のように自動生成で環境を用意することが可能です。

https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html

作成する各列の名前とデータ型は、CloudTrailレコードの内容に対応しています。


実際にAthenaでクエリを実行してみましょう。

```
SELECT 
*
FROM "default"."[※テーブル名]"
WHERE eventName='CreatePresignedNotebookInstanceUrl';
```

※
Source: AwsDataCatalog
Database: default

今回はdefaultデータベース配下のテーブル(先程作成したテーブル)にクエリをかけています。
私はよくSageMakerノートブックインスタンスを使っているのですが、Jupyter サーバーへの接続に用いる「CreatePresignedNotebookInstanceUrl」API記録を探すとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697264/5223419b-5b25-4be9-9ea1-e5dd11da5742.png)

結果としては、211件の結果が返ってきました。
そしてデータスキャン量を見ると、90MBを超えています。

特にパーティション分割しているわけではないので、全スキャンされています。

https://aws.amazon.com/jp/athena/pricing/

Athenaでは、クエリ単位でスキャンされたバイト数に対して課金がされます。
よってクエリの際にスキャンデータ量が多ければ、それだけ課金額が高くなります。

こちらを指定した日付の範囲内で、実行されたAPIを調べられるようにします。


# 2. Partition Projection

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/partition-projection.html

Athena では、高度にパーティションされたテーブルのクエリ処理を高速化し、パーティション管理を自動化するためにPartition Projectionを使用できます。

Partition Projectionでは、Athena は AWS Glue のテーブルに直接設定したテーブルプロパティを使用してパーティション値と場所を計算します。

クエリおよび基盤となるデータの特定の特性によっては、Partition Projectionによって、パーティションメタデータの取得時に制限されているクエリのクエリランタイムが大幅に短縮されます。

この機能を使えばパーティション管理を自動化できます。
(手動でもできなくはないのですが、少し手間がかかります)

# 3. 検証

### テーブルの作成

```
CREATE EXTERNAL TABLE [※テーブル名] (
    eventVersion STRING,
    userIdentity STRUCT<
        type: STRING,
        principalId: STRING,
        arn: STRING,
        accountId: STRING,
        invokedBy: STRING,
        accessKeyId: STRING,
        userName: STRING,
        sessionContext: STRUCT<
            attributes: STRUCT<
                mfaAuthenticated: STRING,
                creationDate: STRING>,
            sessionIssuer: STRUCT<
                type: STRING,
                principalId: STRING,
                arn: STRING,
                accountId: STRING,
                username: STRING>,
            ec2RoleDelivery: STRING,
            webIdFederationData: MAP<STRING,STRING>>>,
    eventTime STRING,
    eventSource STRING,
    eventName STRING,
    awsRegion STRING,
    sourceIpAddress STRING,
    userAgent STRING,
    errorCode STRING,
    errorMessage STRING,
    requestParameters STRING,
    responseElements STRING,
    additionalEventData STRING,
    requestId STRING,
    eventId STRING,
    resources ARRAY<STRUCT<
        arn: STRING,
        accountId: STRING,
        type: STRING>>,
    eventType STRING,
    apiVersion STRING,
    readOnly STRING,
    recipientAccountId STRING,
    serviceEventDetails STRING,
    sharedEventID STRING,
    vpcEndpointId STRING,
    tlsDetails STRUCT<
        tlsVersion: STRING,
        cipherSuite: STRING,
        clientProvidedHostHeader: STRING>
)
COMMENT 'CloudTrail table for [※バケット名]'
PARTITIONED BY (date STRING)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://[※バケット名]/AWSLogs/[※アカウント番号]/CloudTrail/'
TBLPROPERTIES (
  'projection.enabled'='true', 
  'projection.date.type'='date', 
  'projection.date.range' = '2023/11/01,NOW',
  'projection.date.format' = 'yyyy/MM/dd',
  'projection.date.interval' = '1',
  'projection.date.interval.unit' = 'DAYS',
  'storage.location.template'='s3://[※バケット名]/AWSLogs/[※アカウント番号]/CloudTrail/[※リージョン]/${date}/',
  'classification'='cloudtrail'
  )
```

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/create-table.html

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/alter-table-set-tblproperties.html

CREATE TABLEの追加ポイントとしては「PARTITIONED BY」と「TBLPROPERTIES」です。


### クエリの実行

ではクエリを実行してみます。

```
SELECT 
*
FROM "default"."[※テーブル名]"
WHERE eventName='CreatePresignedNotebookInstanceUrl'
and date='2023/11/05';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697264/b392e4e2-ecbd-34e8-d934-f0a30ff3d40a.png)

2023/11/05の日付を指定して、スキャンします。
データスキャン量が抑制されています。

また都度手動でパーティションを設定する手間も発生していません。

```
SELECT 
*
FROM "default"."[※テーブル名]"
WHERE eventName='CreatePresignedNotebookInstanceUrl'
and date between '2023/11/05' and '2023/11/07';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697264/828ef41a-3a3d-1f33-ccd8-bae0c5982574.png)

上記のクエリは、11/5~11/7の日付範囲で検索をかけた例です。
全スキャンするパターンと比べて、スキャン量が抑えられています。

# さいごに

CloudTrailは重要なサービスでありつつも、扱い方がいまいちわからず、証跡を有効化して終わりというケースもあるのではと思います(私もAWSを触り始めた当初はそうでした)
今回の記事がそういった方の参考になれば幸いです。

御覧いただき ありがとうございました。