S3 Storage Lens のメトリクスデータを Athena Partition Projection でクエリする

# はじめに

S3 Storage Lensの無料メトリクスデータのエクスポートデータをログアーカイブアカウントへ集約し、全アカウント、全リージョンの全バケットのデータをパーティション射影を利用しクエリします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228453/51f7f7b0-8e4e-6e23-451e-6b947d009119.png)

## FYI

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/storage_lens_view_metrics_export.html

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

## 前提条件

- 既にダッシュボードが作成済みであること

## 流れ

- ログアーカイブアカウントにバケットを作成する
- メンバーアカウントからログアーカイブアカウントのバケットへのエクスポートを設定する
- Athena で Partition Projection テーブルを作成

## ログアーカイブアカウントにバケットを作成する

AWS管理コンソールから手動で作成するか、以下のテンプレートを利用しバケットを作成してください。

### 設定内容抜粋

- バケットの作成リージョンは`us-east-1`を利用（変更可、ダッシュボードのリージョンに合わせてください）
- ACLを無効化（変更可）
- デフォルトダッシュボード`default-account-dashboard`からのアクセスを許可（変更可）

### コード

プリフィックスを指定しない場合は`[prefix]`部分を削除してください。

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: S3 Storage Lens Metrics Data Export Bucket

Parameters:
  Prefix:
    Type: String
    Default: "org-log"
  Env:
    Type: String
    Default: "prod"
    AllowedValues:
      - dev
      - prod
  
Conditions:
  IsUSE1: !Equals [ !Ref "AWS::Region", "us-east-1" ]

Resources:
  StorageLensBucket:
    Type: AWS::S3::Bucket
    Condition: IsUSE1
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: !Sub "${Prefix}-${Env}-s3storagelens-${AWS::AccountId}"
      OwnershipControls:
        Rules:
          - ObjectOwnership: "BucketOwnerEnforced"
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
  StorageLensBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Condition: IsUSE1
    DependsOn: StorageLensBucket
    Properties:
      Bucket: !Ref StorageLensBucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: AllowMonitorAccountAccess
            Principal:
              AWS:
                - arn:aws:iam::111111111111:root
            Effect: Allow
            Action:
              - "s3:GetBucketLocation"
              - "s3:GetObject"
              - "s3:ListBucket"
              - "s3:ListBucketMultipartUploads"
              - "s3:ListMultipartUploadParts"
              - "s3:AbortMultipartUpload"
              - "s3:PutObject"
            Resource:
              - !Sub "arn:aws:s3:::${StorageLensBucket}"
              - !Sub "arn:aws:s3:::${StorageLensBucket}/*"
          - Sid: S3StorageLensExamplePolicy
            Principal:
              Service:
                - storage-lens.s3.amazonaws.com
            Effect: Allow
            Action:
              - 's3:PutObject'
            Resource:
              - !Sub "arn:aws:s3:::${StorageLensBucket}/[prefix]/StorageLens/888888888888/*"
              - !Sub "arn:aws:s3:::${StorageLensBucket}/[prefix]/StorageLens/999999999999/*"
            Condition:
              StringEquals:
                s3:x-amz-acl: "bucket-owner-full-control"
                aws:SourceArn: [
                  "arn:aws:s3:us-east-1:888888888888:storage-lens/default-account-dashboard",
                  "arn:aws:s3:us-east-1:999999999999:storage-lens/default-account-dashboard"
                ]
                aws:SourceAccount: [
                  "888888888888",
                  "999999999999"
                ]
```

## メンバーアカウントからログアーカイブアカウントのバケットへのエクスポートを設定する

- メトリクスのエクスポートを有効にする
- 出力形式： Apache Parquet
- 送信先のバケット
    - 別のアカウントを選択し、ログアーカイブアカウントのアカウントIDを指定
    - 送信先に作成したバケットのs3パスを入力する、`s3://bucket/prefix`、`prefix`は任意

## Athena で Partition Projection テーブルを作成

Athena を利用するまでの設定手順は割愛しています。
ログアーカイブアカウントでテーブルを作成してください。

### FYI

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/storage_lens_understanding_metrics_export_schema.html

### コード

以下の項目を環境に合わせて変更しDDLを実行してください。

- \[bucket_name\]: ログアーカイブアカウントへ作成したバケット名
- \[prefix\]: 任意入力、指定しない場合は削除
- \[dashboard_name\]: エクスポート元のダッシュボード名

```sql
CREATE EXTERNAL TABLE `storagelens`(
    `version_number` string, 
    `configuration_id` string, 
    `report_date` string, 
    `aws_account_number` string, 
    `aws_region` string, 
    `storage_class` string, 
    `record_type` string, 
    `record_value` string, 
    `bucket_name` string, 
    `metric_name` string, 
    `metric_value` bigint
)
PARTITIONED BY ( 
    `accountid` string, 
    `dt` date
)
ROW FORMAT SERDE 
    'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
STORED AS INPUTFORMAT 
    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
OUTPUTFORMAT 
    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
    's3://[bucket_name]/[prefix]/StorageLens'
TBLPROPERTIES (
    'classification'='parquet', 
    'projection.accountid.type'='enum', 
    'projection.accountid.values'='888888888888,999999999999,Unknown', 
    'projection.dt.format'='yyyy-MM-dd',
    'projection.dt.interval'='1',
    'projection.dt.interval.unit'='DAYS',
    'projection.dt.range'='NOW-1YEARS,NOW',
    'projection.dt.type'='date',
    'projection.enabled'='true',
    'storage.location.template'='s3://[bucket_name]/[prefix]/StorageLens/${accountid}/[dashboard_name]/V_1/reports/dt=${dt}'
)
```