こんにちは、CSCの平木です!
皆さんはS3に保管されたログのクエリをしていますか?
AWS S3に保存されたログを分析する際に、
- クエリだけしたいなら必要最低限の権限は何か
- 事前にどんな準備をしていれば上記の権限だけ与えれば済むか
を調査する機会があったため本記事では、AthenaとDuckDBそれぞれのケースで、
最小権限と事前に何をしておくと良いのかの手順を整理します。
前提条件
クエリを実行したい人は、もともとSecurityAuditのAWS管理ポリシーが付与されているものとします。
Athenaを利用する場合
事前に準備が必要なこと
例えば担当者などが管理者などへクエリする環境がほしいと言われた場合に実施するものと仮定します。
- Athena ワークグループの作成
- ワークグループは既にチームや部署で作られているものがあれば新規で作成する必要はあまりないです
 
- Athena データベースの作成
- ワークグループ同様必要に応じて作成してください
 
- Athena テーブルの作成
- Lake Formationでクエリしたいユーザーまたはロールに対して権限追加
- クエリを実行したいユーザーまたはロールに必要な権限の追加(Athena/Glue/S3)
(依頼を受けた人が行う)手順
Athena ワークグループの作成
ワークグループを作成することでリソースの分離やワークグループ内でのクエリのデータ量の上限などを設けられるため作成しておくと良いです。
デフォルトで存在する Primary を使用しても問題ないです。
Athenaを設定する(クエリを実行する)リージョンは、クエリを行いたいS3バケットと同じでなくても使用することはできますが、意図しない費用増加に繋がります。
意図してクロスリージョンで利用する場合には問題ないですが、別段クロスリージョンでなくても良い場合は同じリージョンで実行すると良さそうです。
参考例: Athena のリージョンを間違えて17万円を無駄にしてしまった話 #S3 - Qiita
ナビゲーションペインからワークグループを選択します。
「ワークグループの作成」を押します。
任意のワークグループ名を入力し、エンジンタイプを要件に応じて設定します。
今回はAthena SQLにしました。
続いて認証は環境に応じたほうを選択すべきですが、
今回はIAMを選択します。
クエリ結果の設定については、
S3バケットにクエリ結果を保管したい場合はカスタムマネージドを選択すべきですが、
別段クエリのみだけできれば問題なければAthenaマネージドがおすすめです。(個別のS3バケットが不要かつ追加料金がないため)
詳しくはこちらのブログが分かりやすいです。
必要に応じてオプションの設定をして「ワークグループを作成」を押せばワークグループの作成は完了です。
スクショ内ではクエリのデータ量の上限を設定しています。
Athena データベースの作成
エディタで以下SQLを実行します。
CREATE DATABASE <データベース名>;
テーブルの作成
同様にテーブル作成用のSQLをエディタで実行します。
以下はCloudTrailの例です。
CREATE EXTERNAL TABLE cloudtrail_logs_partition_projection (
    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 ${BucketName} bucket'
PARTITIONED BY (region string, date 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://[バケット名]/AWSLogs/[アカウントID]/CloudTrail/'
TBLPROPERTIES (
    'projection.enabled' = 'true',
    'projection.date.type' = 'date',
    'projection.date.range' = 'NOW-1YEARS,NOW',
    'projection.date.format' = 'yyyy/MM/dd',
    'projection.date.interval' = '1',
    'projection.date.interval.unit' = 'DAYS',
    'projection.region.type' = 'enum',
    'projection.region.values'='us-east-1,us-east-2,us-west-1,us-west-2,ap-south-1,ap-northeast-1,ap-northeast-2,ap-northeast-3,ap-southeast-1,ap-southeast-2,ca-central-1,eu-central-1,eu-west-1,eu-west-2,eu-west-3,eu-north-1,sa-east-1',
    'storage.location.template' = 's3://[バケット名]/AWSLogs/[アカウントID]/CloudTrail/${region}/${date}',
    'classification'='cloudtrail',
    'compressionType'='gzip',
    'typeOfData'='file',
    'classification'='cloudtrail'
);
Lake Formation権限の追加
Lake Formationと調べて、CatalogからDefaultのアカウントIDを選択します。
Permissionタブから「Grants」を押します。
Principal typeをPrincipalを選択し、
PrincipalsでIAM users and rolesを選択した上で許可を与えたいユーザーまたはロールを指定します。
LF-Tags or catalog resourcesでは、Name Data Catalog resourcesを選択し、
CatalogではDefaultとなっているアカウントIDを、
Databasesでは先ほど作成したデータベース名を、
Tablesでは先ほど作成したテーブル名を指定します。
Table permissionsではSelectとDescribeにチェックを入れ、
「Grants」を押すと完了です。
権限の追加
クエリを実行したいユーザーまたはロールに以下の権限を追加してください。
<アカウントID>、<ワークグループ名>、<バケット名>は対応するものを置換してください。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AthenaConsoleListAccess",
            "Effect": "Allow",
            "Action": [
                "athena:ListDataCatalogs",
                "athena:ListEngineVersions",
                "athena:ListWorkGroups"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AthenaQueryExecutionAccess",
            "Effect": "Allow",
            "Action": [
                "athena:BatchGetQueryExecution",
                "athena:GetQueryExecution",
                "athena:GetQueryResults",
                "athena:GetQueryRuntimeStatistics",
                "athena:StartQueryExecution"
            ],
            "Resource": "arn:aws:athena:*:<アカウントID>:workgroup/<ワークグループ名>"
        },
        {
            "Sid": "GlueReadOnlyAccess",
            "Effect": "Allow",
            "Action": [
                "glue:BatchGetPartition",
                "glue:GetDatabase",
                "glue:GetDatabases",
                "glue:GetPartition",
                "glue:GetPartitions",
                "glue:GetTable",
                "glue:GetTables"
            ],
            "Resource": [
                "arn:aws:glue:*:<アカウントID>:catalog",
                "arn:aws:glue:*:<アカウントID>:database/*",
                "arn:aws:glue:*:<アカウントID>:table/*/*"
            ]
        },
        {
            "Sid": "LakeFormationAccess",
            "Effect": "Allow",
            "Action": [
                "lakeformation:GetDataAccess"
            ],
            "Resource": "*"
        }
    ]
}
(依頼した人が行う)手順
サンプルクエリ
事前準備してもらったら動作するか確認
SELECT * FROM cloudtrail_logs_partition_projection WHERE region = 'ap-northeast-1' AND date = '2025/07/01' LIMIT 10;
2. DuckDB(CloudShell)を利用する場合
事前に準備が必要なこと
- クエリを実行したいユーザーまたはロールに必要な権限の追加(CloudShell/S3)
(依頼を受けた人が行う)手順
権限の追加
クエリをしたいユーザーまたはロールに以下の権限を追加で付与します。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowCloudShellFullAccess",
            "Effect": "Allow",
            "Action": [
                "cloudshell:*"
            ],
            "Resource": "*"
        },
        {
            "Sid": "DenyCloudShellFileUploadDownload",
            "Effect": "Deny",
            "Action": [
                "cloudshell:GetFileDownloadUrls",
                "cloudshell:GetFileUploadUrls"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowS3ListBucket",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::<バケット名>"
        },
        {
            "Sid": "AllowS3GetObject",
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<バケット名>/*"
        }
    ]
}
(依頼した人が行う)手順
1. CloudShellでDuckDB CLIをインストール
curl -LO https://github.com/duckdb/duckdb/releases/download/v1.3.2/duckdb_cli-linux-amd64.zip
unzip duckdb_cli-linux-amd64.zip
rm duckdb_cli-linux-amd64.zip
2. DuckDBを起動
./duckdb cloudtrail-analysis
D INSTALL httpfs;
D LOAD httpfs;
D CREATE SECRET (
      TYPE S3,
      PROVIDER CREDENTIAL_CHAIN
  );
3. テーブル作成
CloudTrailの例です。
日付などは実際使用するものに読み替えてください。
CREATE TABLE ct_detailed AS
WITH raw_events AS (
    SELECT 
        unnest(Records) AS Event
    FROM 
        read_json(
            's3://(バケット名)/(CloudTrailまでのパス)/CloudTrail/ap-northeast-1/2025/07/01/*.json.gz',
            maximum_depth=2
        )
)
SELECT
    json_extract_string(Event, '$.eventVersion') AS eventVersion,
    json_extract_string(Event, '$.eventTime') AS eventTime,
    json_extract_string(Event, '$.eventSource') AS eventSource,
    json_extract_string(Event, '$.eventName') AS eventName,
    json_extract_string(Event, '$.awsRegion') AS awsRegion,
    json_extract_string(Event, '$.sourceIPAddress') AS sourceIPAddress,
    json_extract_string(Event, '$.userAgent') AS userAgent,
    json_extract_string(Event, '$.userIdentity.type') AS userType,
    json_extract_string(Event, '$.userIdentity.principalId') AS principalId,
    json_extract_string(Event, '$.userIdentity.arn') AS userArn,
    json_extract_string(Event, '$.userIdentity.accountId') AS accountId,
    json_extract_string(Event, '$.userIdentity.accessKeyId') AS accessKeyId,
    json_extract_string(Event, '$.userIdentity.userName') AS userName,
    json_extract_string(Event, '$.userIdentity.sessionContext.attributes.creationDate') AS sessionCreationDate,
    json_extract_string(Event, '$.userIdentity.sessionContext.attributes.mfaAuthenticated') AS mfaAuthenticated,
    json_extract_string(Event, '$.requestID') AS requestID,
    json_extract_string(Event, '$.eventID') AS eventID,
    json_extract_string(Event, '$.readOnly') AS readOnly,
    json_extract_string(Event, '$.eventType') AS eventType,
    json_extract_string(Event, '$.managementEvent') AS managementEvent,
    json_extract_string(Event, '$.recipientAccountId') AS recipientAccountId,
    json_extract_string(Event, '$.eventCategory') AS eventCategory
FROM 
    raw_events;
以上で準備は完了です。
参考
- Amazon Athenaだけを使える権限でAWSマネジメントコンソールを使う方法 - サーバーワークスエンジニアブログ
- CloudTrailのクエリをDuckDBに置き換えてクラウド破産と無縁な分析基盤を作る #AWS - Qiita
まとめ
今回は、AthenaとDuckDBを使用する場合の最小権限と手順を整理してみました。
当然ではありますが、管理者の立場である事前準備を行う人はさらに権限が必要な点にはご注意ください。
ここから別であれやりたいこれやりたいといった要望もあると思いますので、
ぜひカスタムしていただければと思います。
この記事がどなたかの役に立つとうれしいです。













