【AWS】Athena で CloudTrail のログ調査をできるようにする手順

CloudTrail のイベント履歴は、過去90日分の管理イベントを表示・検索・ダウンロードできる機能です。これはアカウント作成時から自動で利用できますが、90日を超えた調査には向きません。

一方で、CloudTrail の証跡を作成しておくと、イベントを S3 バケットへ配信・保存できます。90日超の調査は、この S3 に保存されたログを Athena で検索する必要があります。

手順の全体像

手順の全体像は以下になります。

CloudTrail Trail
  ↓
S3 バケットにログ保存
  ↓
Athena テーブル作成
  ↓
SQLで検索

重要なのは、事故が起きてからAthenaテーブルを作るのではなく、事前に作っておくことです。

手順1:CloudTrail が S3 に保存されているか確認する

CloudTrail の証跡が S3 にログを出している必要があります。

AWSコンソールの場合

CloudTrail
↓
証跡
↓
対象のTrail
↓
S3 バケット名を確認

確認するポイントは以下です。

・S3BucketName
・S3KeyPrefix
・IsOrganizationTrail
・IsMultiRegionTrail

手順2:S3上のCloudTrailログパスを確認する

S3 にログがあるか確認します。通常のアカウント証跡なら、以下のようなパスになります。

s3://<bucket>/AWSLogs/<account-id>/CloudTrail/<region>/<yyyy>/<mm>/<dd>/
【例】
s3://aws-cloudtrail-log-aggregation-xxxx/AWSLogs/123456789012/CloudTrail/ap-northeast-1/2026/05/16/

組織証跡の場合は、以下のように Organization ID が入ることがあります。

s3://<bucket>/AWSLogs/<organization-id>/<account-id>/CloudTrail/<region>/<yyyy>/<mm>/<dd>/
【例】
s3://aws-cloudtrail-log-aggregation-xxxx/AWSLogs/o-xxxxxxxxxx/123456789012/CloudTrail/ap-northeast-1/2026/05/16/

手順3:Athena のクエリ結果保存先を設定する

Athena はクエリ結果を S3 に保存します。未設定だとクエリ実行時にエラーになります。

AWS コンソールの場合

Athena
↓
Query editor
↓
Settings
↓
Manage
↓
Query result location

ログ用S3バケットと同じでも動きますが、分けて管理しても良いです。

手順4:Athena データベースを作成する

Athena のクエリエディタで実行します。

CREATE DATABASE IF NOT EXISTS cloudtrail_logs;

その後、利用DBを切り替えます。

USE cloudtrail_logs;

手順5:Athena テーブルを作成する

ここが一番重要です。CloudTrail ログは日付・リージョン・アカウントIDでパスが分かれているので、Athena では パーティション を使うのが基本です。

さらに、CloudTrail はパス構造が決まっているため、Partition Projection を使うと便利です。Partition Projection を使うと、新しい日付のパーティションを ALTER TABLE ADD PARTITION で毎回追加しなくて済みます。

パターンA:通常のアカウント証跡の場合

S3 パスが以下の場合です。

s3://<bucket>/AWSLogs/<account-id>/CloudTrail/<region>/<yyyy>/<mm>/<dd>/

Athena テーブル例です。

CREATE EXTERNAL TABLE IF NOT EXISTS cloudtrail_logs.cloudtrail_partitioned (
  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,
  readOnly string,
  resources array<struct<
    arn:string,
    accountId:string,
    type:string
  >>,
  eventType string,
  apiVersion string,
  recipientAccountId string,
  serviceEventDetails string,
  sharedEventID string,
  vpcEndpointId string
)
PARTITIONED BY (
  account_id string,
  region string,
  year string,
  month string,
  day 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://<cloudtrail-log-bucket>/AWSLogs/'
TBLPROPERTIES (
  'projection.enabled' = 'true',

  'projection.account_id.type' = 'enum',
  'projection.account_id.values' = '123456789012,234567890123',

  'projection.region.type' = 'enum',
  'projection.region.values' = 'ap-northeast-1,us-east-1,ap-southeast-1',

  'projection.year.type' = 'integer',
  'projection.year.range' = '2023,2030',

  'projection.month.type' = 'integer',
  'projection.month.range' = '1,12',
  'projection.month.digits' = '2',

  'projection.day.type' = 'integer',
  'projection.day.range' = '1,31',
  'projection.day.digits' = '2',

  'storage.location.template' = 's3://<cloudtrail-log-bucket>/AWSLogs/${account_id}/CloudTrail/${region}/${year}/${month}/${day}'
);

置き換える場所はここです。

<cloudtrail-log-bucket>
123456789012,234567890123
ap-northeast-1,us-east-1,ap-southeast-1

パターンB:Organizations 組織証跡の場合

S3 パスがこれの場合です。

s3://<bucket>/AWSLogs/<organization-id>/<account-id>/CloudTrail/<region>/<yyyy>/<mm>/<dd>/

その場合、最後の storage.location.template が変わります。

'storage.location.template' = 's3://<cloudtrail-log-bucket>/AWSLogs/o-xxxxxxxxxx/${account_id}/CloudTrail/${region}/${year}/${month}/${day}'

組織証跡は、管理アカウントで作成すると組織内アカウントのイベントを同じS3バケットへ集約できます。メンバーアカウント側では組織証跡を表示できますが、変更・削除はできません。


手順6:まず1日分だけ検索テストする

テーブル作成後、いきなり広範囲検索しない方がいいです。まず1アカウント、1リージョン、1日で試します。

SELECT
  eventTime,
  eventSource,
  eventName,
  userIdentity.arn,
  sourceIPAddress,
  awsRegion
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND region = 'ap-northeast-1'
  AND year = '2026'
  AND month = '05'
  AND day = '16'
ORDER BY eventTime DESC
LIMIT 50;

結果が返ればOKです。


手順7:よく使う調査SQLを保存する

DB を運用しているとよく使用する調査 SQL が定まってきます。その SQL を保存します。

1. 誰がログインしたか確認する

SELECT
  eventTime,
  userIdentity.type,
  userIdentity.arn,
  sourceIPAddress,
  userAgent,
  responseElements,
  additionalEventData
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND region = 'us-east-1'
  AND year = '2026'
  AND month = '05'
  AND eventName = 'ConsoleLogin'
ORDER BY eventTime DESC;

ConsoleLogin はグローバル系イベントとして us-east-1 側で見ることが多いです。


2. IAM変更を調べる

SELECT
  eventTime,
  eventName,
  userIdentity.arn,
  sourceIPAddress,
  requestParameters,
  errorCode,
  errorMessage
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND region = 'us-east-1'
  AND year = '2026'
  AND month = '05'
  AND eventSource = 'iam.amazonaws.com'
  AND eventName IN (
    'CreateUser',
    'DeleteUser',
    'CreateRole',
    'DeleteRole',
    'AttachUserPolicy',
    'DetachUserPolicy',
    'AttachRolePolicy',
    'DetachRolePolicy',
    'PutUserPolicy',
    'PutRolePolicy',
    'UpdateAssumeRolePolicy',
    'CreateAccessKey',
    'DeleteAccessKey'
  )
ORDER BY eventTime DESC;

IAM はグローバルサービスなので、まず us-east-1 を見るのが実務上わかりやすいです。


3. セキュリティグループ変更を調べる

SELECT
  eventTime,
  eventName,
  userIdentity.arn,
  sourceIPAddress,
  requestParameters,
  errorCode
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND region = 'ap-northeast-1'
  AND year = '2026'
  AND month = '05'
  AND eventSource = 'ec2.amazonaws.com'
  AND eventName IN (
    'AuthorizeSecurityGroupIngress',
    'AuthorizeSecurityGroupEgress',
    'RevokeSecurityGroupIngress',
    'RevokeSecurityGroupEgress',
    'ModifySecurityGroupRules',
    'CreateSecurityGroup',
    'DeleteSecurityGroup'
  )
ORDER BY eventTime DESC;

4. ルートユーザー利用を調べる

SELECT
  eventTime,
  eventSource,
  eventName,
  userIdentity.type,
  userIdentity.arn,
  sourceIPAddress,
  userAgent
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND year = '2026'
  AND month = '05'
  AND userIdentity.type = 'Root'
ORDER BY eventTime DESC;

5. エラーになったAPIを調べる

SELECT
  eventTime,
  eventSource,
  eventName,
  userIdentity.arn,
  sourceIPAddress,
  errorCode,
  errorMessage
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND year = '2026'
  AND month = '05'
  AND errorCode IS NOT NULL
ORDER BY eventTime DESC
LIMIT 200;

6. 特定IPからの操作を調べる

SELECT
  eventTime,
  eventSource,
  eventName,
  userIdentity.arn,
  sourceIPAddress,
  awsRegion,
  requestParameters
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE year = '2026'
  AND month = '05'
  AND sourceIPAddress = 'xxx.xxx.xxx.xxx'
ORDER BY eventTime DESC
LIMIT 200;

この場合、アカウントやリージョンを絞らないと広範囲検索になるので、できれば以下も入れた方がいいです。

AND account_id = '123456789012'
AND region = 'ap-northeast-1'

7. 特定ユーザー・ロールの操作を調べる

SELECT
  eventTime,
  eventSource,
  eventName,
  userIdentity.arn,
  sourceIPAddress,
  awsRegion,
  requestParameters
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND year = '2026'
  AND month = '05'
  AND userIdentity.arn LIKE '%AdminRole%'
ORDER BY eventTime DESC
LIMIT 300;

手順8:コスト・速度対策

Athena はスキャンしたデータ量に応じて課金されます。
なので、CloudTrail 調査では必ず以下を絞ります。

account_id
region
year
month
day
eventSource
eventName

以下は悪い例です。

SELECT *
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE eventName = 'CreateUser';

これは全期間・全アカウント・全リージョンを見に行く可能性があり、遅く高くなります。

良い例です。

SELECT
  eventTime,
  eventName,
  userIdentity.arn,
  sourceIPAddress,
  requestParameters
FROM cloudtrail_logs.cloudtrail_partitioned
WHERE account_id = '123456789012'
  AND region = 'us-east-1'
  AND year = '2026'
  AND month = '05'
  AND day BETWEEN '01' AND '16'
  AND eventSource = 'iam.amazonaws.com'
  AND eventName = 'CreateUser';

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

AlphaOmega Captcha Classica  –  Enter Security Code
     
 

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください