新機能のS3 AnnotationsをCRUDし、Annotation Table + Athenaで横断検索してみた
はじめに
2026年6月16日、Amazon S3の新機能「S3 Annotations」が発表されました。S3オブジェクトに対して最大1000個、各最大1MBのカスタムメタデータ(annotation)を付与できる機能です。
従来のS3メタデータとの比較を以下に示します。
| 項目 | user-defined metadata | object tags | S3 Annotations |
|---|---|---|---|
| 上限数 | — | 10個 | 1000個 |
| サイズ上限 | リクエストヘッダー内 2KB | Key 最大128文字、Value 最大256文字 | 各1MB |
| 形式 | Key-Value(ASCII) | Key-Value | 任意(JSON、テキスト等) |
| 更新 | オブジェクト再PUT必須 | 個別更新可 | 個別更新可 |
| ライフサイクル連携 | — | ✅ | — |
| アクセス制御連携 | — | ✅(条件キー) | — |
| 横断検索(Athena) | — | S3 Inventory経由 | Annotation Table |
タグはライフサイクルルールやIAM条件キーとの連携に使われる仕組みであり、Annotationsはこれらの上位互換ではありません。
本記事では、boto3およびAWS CLIでAnnotationのCRUD操作を確認し、Annotation Tableを有効化してAthenaによる横断検索まで実施します。
検証環境
| 項目 | 値 |
|---|---|
| リージョン | ap-northeast-1 |
| バケット種別 | 汎用 S3 バケット(バージョニング無効) |
| Python | 3.14(rc1) |
| boto3 | 1.43.31(2026-06-16 リリース) |
| botocore | 1.43.31 |
| AWS CLI | v2.35.6(2026-06-17 リリース) |
| Athena | エンジンバージョン3 |
本記事の操作はboto3およびAWS CLIで実施しています。
Annotation CRUD 操作(boto3)
boto3 1.43.31で利用可能なannotation関連メソッド:
import boto3
s3 = boto3.client('s3', region_name='ap-northeast-1')
[m for m in dir(s3) if 'annot' in m.lower()]
# ['delete_object_annotation', 'get_object_annotation', 'list_object_annotations',
# 'put_object_annotation', 'update_bucket_metadata_annotation_table_configuration']
以下、バケットにテスト用オブジェクト test-object.txt を配置した状態で操作します。
import json
BUCKET = "my-annotation-demo-bucket"
KEY = "test-object.txt"
PutObjectAnnotation(JSON)
annotation_json = json.dumps({
"project": "annotation-test",
"owner": "demo-user",
"created": "2026-06-17"
})
resp = s3.put_object_annotation(
Bucket=BUCKET,
Key=KEY,
AnnotationName='test-metadata',
AnnotationPayload=annotation_json.encode()
)
{
'ETag': '"1fa459dad748f9fcc3be1e3dcc50ea82"',
'Key': 'test-object.txt',
'AnnotationName': 'test-metadata',
'ResponseMetadata': {
'RequestId': 'XXXXXXXXXXXX',
'HostId': 'XXXXXXXXXXXX',
'HTTPStatusCode': 200
}
}
ResponseMetadataは以降省略します。
PutObjectAnnotation(Plain Text)
resp = s3.put_object_annotation(
Bucket=BUCKET,
Key=KEY,
AnnotationName='ai-summary',
AnnotationPayload=b'AI-generated summary: A test file for demonstrating S3 Annotations.'
)
{
'ETag': '"403c26f2a55cdc54cf931b03be006b75"',
'AnnotationName': 'ai-summary'
}
ListObjectAnnotations
resp = s3.list_object_annotations(Bucket=BUCKET, Key=KEY)
{
'AnnotationCount': 2,
'Annotations': [
{
'AnnotationName': 'ai-summary',
'Size': 67,
'ETag': '"403c26f2a55cdc54cf931b03be006b75"',
'LastModified': datetime(2026, 6, 17, 1, 37, 36, tzinfo=tzutc()),
'ChecksumAlgorithm': ['CRC32']
},
{
'AnnotationName': 'test-metadata',
'Size': 78,
'ETag': '"1fa459dad748f9fcc3be1e3dcc50ea82"',
'LastModified': datetime(2026, 6, 17, 1, 37, 36, tzinfo=tzutc()),
'ChecksumAlgorithm': ['CRC32']
}
]
}
Listで返るETagはオブジェクト本体のETagではなく、各annotationに対して返される値です。
GetObjectAnnotation
resp = s3.get_object_annotation(
Bucket=BUCKET,
Key=KEY,
AnnotationName='test-metadata'
)
body = resp['AnnotationPayload'].read().decode()
# body
'{"project": "annotation-test", "owner": "demo-user", "created": "2026-06-17"}'
# resp(AnnotationPayload 以外)
{
'ETag': '"1fa459dad748f9fcc3be1e3dcc50ea82"',
'ContentLength': 78
}
AnnotationPayload は StreamingBody 型で、.read() で本文を取得します。
DeleteObjectAnnotation
resp = s3.delete_object_annotation(
Bucket=BUCKET,
Key=KEY,
AnnotationName='ai-summary'
)
{} # HTTPStatusCode: 204
削除後にListを再取得して確認します。
resp = s3.list_object_annotations(Bucket=BUCKET, Key=KEY)
{
'AnnotationCount': 1,
'Annotations': [
{
'AnnotationName': 'test-metadata',
'Size': 78,
'ETag': '"1fa459dad748f9fcc3be1e3dcc50ea82"',
'LastModified': datetime(2026, 6, 17, 1, 37, 36, tzinfo=tzutc()),
'ChecksumAlgorithm': ['CRC32']
}
]
}
ai-summary が消え、test-metadata のみが残っていることを確認できました。
AWS CLI での操作(v2.35.6)
AWS CLI v2.35.6(2026-06-17リリース)でannotation操作コマンドが追加されました。boto3との主な違いを含めて紹介します。
PutObjectAnnotation
--annotation-payload はstreaming blobで、ファイルパスを直接指定します。file:// や fileb:// プレフィックスは使用できません。
echo -n '{"source":"cli","version":"2.35.6"}' > /tmp/payload.txt
aws s3api put-object-annotation \
--bucket my-annotation-demo-bucket \
--key videos/sample.mp4 \
--annotation-name "cli-test" \
--annotation-payload /tmp/payload.txt \
--region ap-northeast-1
{
"ETag": "\"39ce0435575e8e057d4a919c727ffe0a\"",
"ChecksumCRC64NVME": "SvqIamuCqI0=",
"ChecksumType": "FULL_OBJECT",
"ServerSideEncryption": "AES256",
"Key": "videos/sample.mp4",
"AnnotationName": "cli-test"
}
GetObjectAnnotation
ペイロードの出力先は位置引数で指定します(s3api get-object と同じパターン)。
aws s3api get-object-annotation \
--bucket my-annotation-demo-bucket \
--key videos/sample.mp4 \
--annotation-name "cli-test" \
--region ap-northeast-1 \
/tmp/output.txt
cat /tmp/output.txt
# {"source":"cli","version":"2.35.6"}
ListObjectAnnotations / DeleteObjectAnnotation
# List
aws s3api list-object-annotations \
--bucket my-annotation-demo-bucket \
--key videos/sample.mp4 \
--region ap-northeast-1
# Delete
aws s3api delete-object-annotation \
--bucket my-annotation-demo-bucket \
--key videos/sample.mp4 \
--annotation-name "cli-test" \
--region ap-northeast-1
boto3 との違い
| 項目 | boto3 | AWS CLI(v2.35.6) |
|---|---|---|
| ペイロード指定 | AnnotationPayload=bytes |
--annotation-payload <filepath>(file:// 不可) |
| ペイロード取得 | StreamingBody.read() |
位置引数で出力先ファイル指定 |
| チェックサム | 本検証ではCRC32 | 本検証ではCRC64NVME |
| コピー時のannotation | — | --copy-props all で s3 cp/mv/sync 時にコピー |
--copy-props all でのコピー
v2.35.6で追加された --copy-props all オプションにより、S3間コピー時にannotation・メタデータ・タグをまとめてコピーできます。
aws s3 cp s3://my-annotation-demo-bucket/videos/sample.mp4 \
s3://my-annotation-demo-bucket/videos/sample-copy.mp4 \
--copy-props all \
--region ap-northeast-1
Annotation Table で横断検索(Athena)
個別オブジェクトのannotationをAPIで取得するだけでなく、バケット全体のannotationをSQLで横断検索できます。S3 MetadataのAnnotation Tableを有効化し、Athenaから参照する手順を確認しました。
S3 Metadata との関係
Annotation Tableは、2024年のre:Inventで発表された「S3 Metadata」基盤の拡張です。S3 Metadataでは、オブジェクトの作成・削除イベントを記録するJournal Tableと、オブジェクト一覧のスナップショットであるInventory Tableが提供されていました。今回のS3 Annotationsリリースに合わせて、annotationペイロードを横断検索するためのAnnotation Tableが追加されています。
いずれも同じ MetadataConfiguration で設定し、S3 Tables(Apache Iceberg)上に格納される点は共通です。
従来構成との比較
これまでS3オブジェクトにメタデータを持たせて横断検索したい場合、外部データベース(DynamoDB等)に保存する構成が一般的でした。DevelopersIOでも以下のような構成が紹介されています。
これらの構成とAnnotationsを比較すると、以下のようになります。
| 観点 | 従来構成(S3 + Lambda + DynamoDB) | S3 Annotations |
|---|---|---|
| メタデータ保存先 | DynamoDB テーブル | S3 オブジェクト自体に付随 |
| 同期の仕組み | EventBridge → Lambda / Step Functions | 外部DBへの同期処理は不要(Annotation Table への反映は非同期) |
| 追加コンポーネント | Lambda, DynamoDB, EventBridge 等 | 自前の同期処理・外部DBは不要 |
| 横断検索 | DynamoDB Query / Scan / GSI | Annotation Table + Athena |
| レイテンシ | DynamoDB: ミリ秒単位 | Athena: 秒単位(バッチ向き) |
| コスト構造 | Lambda 実行 + DynamoDB RCU/WCU | S3 API リクエスト + Athena スキャン |
Annotationsにより、メタデータ管理のための構成を大幅に簡素化できます。一方、ミリ秒単位の低レイテンシな参照が必要な場合は従来構成が適切です。
ここからの検証では、横断検索の有用性を示すため複数オブジェクトへannotationを追加しています。対象はvideos/sample.mp4、videos/another.mp4、docs/report.pdfの3つです。
IAM ロール作成
S3 Metadataがannotation情報をAnnotation Tableへ反映する際に使用するサービスロールを作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "metadata.s3.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
権限ポリシー:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObjectAnnotation",
"s3:GetObjectVersionAnnotation",
"s3:ListBucket",
"s3:ListBucketVersions"
],
"Resource": [
"arn:aws:s3:::my-annotation-demo-bucket",
"arn:aws:s3:::my-annotation-demo-bucket/*"
]
}
]
}
Metadata Configuration 有効化
s3.create_bucket_metadata_configuration(
Bucket=BUCKET,
MetadataConfiguration={
'JournalTableConfiguration': {
'RecordExpiration': {'Expiration': 'DISABLED'}
},
'InventoryTableConfiguration': {
'ConfigurationState': 'DISABLED'
},
'AnnotationTableConfiguration': {
'ConfigurationState': 'ENABLED',
'Role': 'arn:aws:iam::123456789012:role/S3MetadataAnnotationRole'
}
}
)
本記事の検証時点ではboto3で実行しましたが、AWS CLI v2.35.6でも実行可能であることを確認しました。
バックフィルと ACTIVE 確認
今回の検証では、作成直後の TableStatus は BACKFILLING でした。既存のannotationをテーブルに反映する処理が走ります。
resp = s3.get_bucket_metadata_configuration(Bucket=BUCKET)
config = resp['GetBucketMetadataConfigurationResult']['MetadataConfigurationResult']
print(config['AnnotationTableConfigurationResult']['TableStatus'])
BACKFILLING
今回の検証では、3オブジェクト・3 annotationの小規模環境でMetadata Configuration作成から約25分で ACTIVE になりました。
Glue Data Catalog に federated catalog を作成
AthenaからAnnotation Tableを参照するには、Glue Data CatalogにS3 Tables用のfederated catalogを作成します。
import boto3
glue = boto3.client('glue', region_name='ap-northeast-1')
glue.create_catalog(
Name='s3tablescatalog',
CatalogInput={
'FederatedCatalog': {
'Identifier': 'arn:aws:s3tables:ap-northeast-1:123456789012:bucket/*',
'ConnectionName': 'aws:s3tables'
},
'CreateDatabaseDefaultPermissions': [
{
'Principal': {'DataLakePrincipalIdentifier': 'IAM_ALLOWED_PRINCIPALS'},
'Permissions': ['ALL']
}
],
'CreateTableDefaultPermissions': [
{
'Principal': {'DataLakePrincipalIdentifier': 'IAM_ALLOWED_PRINCIPALS'},
'Permissions': ['ALL']
}
]
}
)
Annotation Table のスキーマ
ACTIVEになったAnnotation TableをAthenaで確認すると、以下のカラム構成でした。
| カラム | 説明 |
|---|---|
| bucket | バケット名 |
| object_key | オブジェクトキー |
| object_version_id | バージョンID(非バージョニング環境ではNULL) |
| name | Annotation 名 |
| last_modified_date | Annotation 最終更新日時 |
| size | Annotation サイズ(バイト) |
| e_tag | Annotation ETag |
| checksum_algorithm | チェックサムアルゴリズム |
| text_value | Annotation ペイロード(テキスト) |
本検証で作成したJSON / テキスト形式のannotationは、text_value カラムに文字列として格納されていました。JSON文字列として保存したannotationであれば、Athenaの json_extract_scalar で内部フィールドを抽出できます。
Athena クエリ
テーブルパスは "s3tablescatalog/aws-s3"."b_<バケット名>"."annotation" です。
全件取得
SELECT object_key, name, text_value
FROM "s3tablescatalog/aws-s3"."b_my-annotation-demo-bucket"."annotation"
LIMIT 10;
| object_key | name | text_value |
|---|---|---|
| videos/sample.mp4 | mediainfo | {"codec":"H.265","resolution":"3840x2160","audio_tracks":12} |
| videos/another.mp4 | mediainfo | {"codec":"H.264","resolution":"1920x1080","audio_tracks":2} |
| docs/report.pdf | classification | {"category":"finance","sensitivity":"internal"} |
3件のannotationがすべて格納されていることを確認しました。
JSON フィールドでフィルタ
JSON形式のannotationに対して json_extract_scalar で条件指定できます。annotation名で絞ってからJSON抽出を行います。
SELECT object_key, name, text_value
FROM "s3tablescatalog/aws-s3"."b_my-annotation-demo-bucket"."annotation"
WHERE name = 'mediainfo'
AND CAST(json_extract_scalar(text_value, '$.audio_tracks') AS INTEGER) > 8;
| object_key | name | text_value |
|---|---|---|
| videos/sample.mp4 | mediainfo | {"codec":"H.265","resolution":"3840x2160","audio_tracks":12} |
audio_tracks > 8 の条件で正しく1件のみヒットしました。S3オブジェクトに付与したannotationの中身を、SQLで横断検索できることを確認できました。
まとめ
S3 Annotationsをboto3 / AWS CLIで操作し、AnnotationのPut / Get / List / Deleteと、Annotation Table + Athenaによる横断検索を確認しました。今回利用したboto3 1.43.31 / botocore 1.43.31およびAWS CLI v2.35.6では、本記事で扱ったAnnotationのCRUD操作を実行できました。
S3 Annotationsは、S3オブジェクトに対して従来のuser-defined metadataやobject tagsより大きな情報を付与できる仕組みです。今回の検証では、JSON文字列やテキストをannotationとして保存し、個別APIやCLIで取得できることを確認しました。
また、Annotation Tableを有効化することで、保存したannotationをAthenaからSQLで参照できました。JSON文字列として保存したannotationについては、json_extract_scalar を使って内部フィールドを条件にした検索も可能でした。
従来、S3オブジェクトに付随するメタデータを横断検索したい場合は、LambdaやDynamoDBなどを組み合わせて別途管理する構成が必要になることがありました。S3 AnnotationsとAnnotation Tableを利用することで、ユースケースによっては自前の同期基盤や外部DBを用意せずに、メタデータの付与からAthenaによる横断検索までを構成できます。
一方で、Annotation Tableへの反映は非同期です。今回の小規模な検証環境でも、Metadata Configuration作成から ACTIVE になるまで約25分かかりました。ミリ秒単位の低レイテンシな参照や、GSIなどを使った検索が必要な場合は、DynamoDBなどの従来構成が適している場面もあります。
AWS CLI v2.35.6ではannotation操作コマンドが追加され、Put / Get / List / DeleteをCLIからも実行できました。SDKだけでなくCLIからも扱えるようになったことで、検証やスクリプト化もしやすくなっています。








