😸

【Beginner】AthenaでCloudTrailログ用テーブルの手動パーティショニングとクエリを実行してみた

2022/08/19に公開約9,100字

1. はじめに

 こんにちわ、Mitsuoです。
今回はCloudTrailのログをAthenaでクエリ実行してみました。
ただし、CloudTrailコンソールを使わずに手動でパーティションニング分割を行います。


2. CloudTrailコンソールでのAthenaテーブル作成について

 CloudTrailとAthenaはサービスが統合されています。CloudTrailのコンソールから簡単なオペレーションでテーブルを作成し、クエリすることが出来ます。きめ細かく言うと、CloudTrailのイベント履歴の画面右上にある「Athenaテーブルを作成する」を押すとクエリの情報元になるバケット名を指定するだけでDDL(data definition language)のSQL文を用意してくれます。

以下が作成されるSQL文となり、TABLE_NAMECOMMENTの[S3_BUCKET_URL]に選択したバケット名が、LOCATIONの[S3_BUCKET_URL]には同様のバケットのARN名(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>>>,
    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
)
COMMENT 'CloudTrail table for [S3_BUCKET_NAME] bucket'
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION '[S3_BUCKET_URL]'
TBLPROPERTIES ('classification'='cloudtrail');

AWS公式もYOUTUBEに紹介動画をあげているので、ニーズは高いのでしょう。
参考:Amazon Athena を使用して AWS CloudTrail ログを検索する方法を教えてください。

上記の動画を見てAthenaを使えば、お手軽にクエリ実行が出来るのは事実ですが、以下の様な注意点があります。

  • CloudTrailコンソールで作成するテーブルはDDLを変更することが出来ない。バケットの選択のみ可能
  • ロケーションで指定したARNの配下にあるデータ全てがスキャン対象になる

全てがスキャン対象になる事は悪くないのでは?と聞こえるかもしれませんが、Athenaの料金モデルはスキャンしたデータに対して課金されるため、本来必要としていないデータまでスキャン対象に含めるとコスト効率が悪くなります。

その為、CloudTrailコンソールを利用する=必ずしも正解という訳でないと思っています。


3. 「手動」とは?

記事の冒頭でも述べた手動とは「AthenaのクエリエディタでDDL(data definition language)のSQLを実行すること」です。
また、その際にデータベースのパーティショニングを行います。
パーティショニングとは、その名前の通り、データベースにおけるテーブル内のデータを複数に分割して保持する機能を指します。

このように、必要最低限の範囲にデータを分割しスキャン件数を減らすことが出来ます。


4. 実際に試してみる

では、実際に試してみます。

以降の手順は公式ドキュメントを参考にして実施します。
また、CloudTrailの履歴設定、ログ保管元のS3バケットの作成は事前に完了しているものとします。
加えてワークグループの設定はこの手順では考慮しません。

Creating the table for CloudTrail logs in Athena using manual partitioning

  1. はじめに、コンソールのトップ画面から「Athena」を押下し「クエリエディタ」の画面に移ります。

image

image

  1. 「設定」タブにある「クエリの結果と暗号化の設定」の画面右側にある「管理」を押下します。

image

  1. クエリ実行結果を格納するS3バケット内の場所、バケット所有者、クエリ結果を暗号化などを設定します

image

バケットの場所は、s3://バケット名/オブジェクト(ディレクトリ)名/です。
バケット所有者は、バケットを作成したAWSアカウントIDを選択してください。
クエリ結果の暗号化は、有効化する場合はSSE-S3CSE-KMSSSE-KMSのいすれかから選択します。

  1. 次に、エディタタグ内でデータソースとデータベースを指定します。
    データカタログはデフォルトのAwsDataCatalog、データベースはdefalutを選択します。

image


データカタログとデータベースについて

データカタログとは、Glueのリソースでありメタデータを整理するためのシステムです。
AWS Glue Data Catalogの中にデータカタログと言うリソースが存在するイメージで考えています。
そのAWS Glue Data Catalogと言うのが、Apache Hive MetastoreというOSSと互換性のあるメタデータを管理する為のリポジトリになります。

以下の記事が全体像を把握するために非常に分かりやすかったので、ご参考ください。
参考:[小ネタ] Athena のテーブル, データカタログなどの概念がわからなかったのでまとめた

外部に独自でMetastoreを保持している場合は、AWSがLambdaで用視しているコネクタを用いて、他のデータカタログを選択するケースがあるようです。

次にデータベースですが、テーブルを格納するためのリソースになります。
ざっくりな言い方になりますが、検証活用であればdefalutを選択するで良いと思います。
ただし、本番環境で利用する場合や、利用ルールが厳格なアカウントだとワークグループやデータベースを分離して活用するのが一般的なのではないでしょうか。闇雲にdefalt内にテーブルを作っていると他に利用する方の迷惑になるでしょう。

なお、データカタログとそこに含まれるデータ群を合わせて「データソース」といいます。


説明が長くなりましたが、次の手順に移ります。

  1. 以下のSQLコマンドを修正します。
項目 修正方法
${TableName} 作成するテーブル名
${BacketName} CloudTrailのログが格納されているS3バケット名
${Account_ID} AWSアカウント名
CREATE EXTERNAL TABLE ${TableName} (
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>>>,
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
)
PARTITIONED BY (region string, year string, month string, day string)
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://${BacketName}/AWSLogs/${Account_ID}/CloudTrail/';

上記のルールの通り差し替えても良いですし、CloudTrailコンソールからDDLをコピーして、PARTITIONEDの行だけ追加する方法でも良いです。都合のいい方を選択してください。

補足:パーティションについて

PARTITIONEDは、どのようにパーティショニングを行うかを定義しています。
サンプルでは、リージョン、年、月、日でパーティションを分けていますが、月全体でスキャンを掛けたい場合は、
PARTITIONED BY (region string, year string, month string)の様に指定します。

この後の手順は、カラムがHive標準かどうかで決まります。
Hive標準と言うのは、col1=val1/col2=val2のような物です。
CloudTrailの場合は、Hive標準では無いため、ALTER TABLE ADD PARTITIONをパーティション数実行する必要があります。


  1. SQLコマンドを実行するとテーブルが作成されます。
    パーティショニングがされている事も確認出来ます。

クエリが成功した事を確認します。

image

テーブル名は、cloudtrail_logs_tableにしました。

image

パーティーショニングされている事を確認出来ます。

image


  1. クエリを実行するために以下のSQLコマンドを修正の上、実行します。これによりパーティーションを読み込みます。
項目 修正方法
${Region} リージョン名
例: 東京リージョンの場合 ap-northeast-1
${year} 年を入力 例: 2019年の場合 2019
${month} 月を入力 例: 2月の場合 02
${day} 日を入力 例: 1日の場合 01
${BacketName} CloudTrailのログが格納されているS3バケット名
${Account_ID} AWSアカウント名
ALTER TABLE table_name ADD 
   PARTITION (region='${Region}',
              year='${year}',
              month='${month}',
              day='${day}')
   LOCATION 's3://${BacketName}/AWSLogs/${Account_ID}/CloudTrail/${Region}/${year}/${month}/${day}/'

image

  1. クエリを実行します。ここではパーティーション指定したリージョンと日付のログを参照します。
    ここでは、2022年8月19日分のデータのみ抽出します。
SELECT *
FROM cloudtrail_logs_table 
WHERE region='ap-northeast-1' AND year='2022' AND month='08' AND day='19'

image

結果が確認出来ました!

5. まとめ

 お恥ずかしながらAthenaを初めて使ったのですが、この様に手順を模してみるだけでも、S3を直接確認しにいく煩わしさから解放され、便利さを感じる事が出来る素晴らしいサービスだと思いました。
基本的なSQLの知識さえあれば、ある程度応用して使えると思うので、僕の様にまだ使った事が無い方は是非とも試しに使ってみてください。

このブログが誰かの役に立てば嬉しいです!土日はゆっくりお休みください。Mitsuoでした!

6. 参考資料

Amazon Athena を使用して AWS CloudTrail ログを検索する方法を教えてください。
Creating the table for CloudTrail logs in Athena using manual partitioning
[小ネタ] Athena のテーブル, データカタログなどの概念がわからなかったのでまとめた

Discussion

ログインするとコメントできます