Athena Federated Query 経由で DynamoDB のデータを QuickSight で可視化するハンズオンでつまづいた部分の補足
コーヒーが好きな emi です。
DynamoDB のデータを QuickSight で可視化・分析したい要件があり、改めて試してみました。
以下のハンズオンを参考にしましたが、サービスのアップデートによりところどころ変更がありましたので、特に変更点やつまづいた部分に特化して本ブログに残しておきます。
構成図イメージ
①⇒②⇒③ の順番で試していきます。
①
Lambda の Python バージョン
2021 年のハンズオンをお借りしているため、Python のバージョンが古い 3.7 でした。CloudFormation の JSON(CSVToDynamotransaction.json)内で Python のバージョンを 3.11 にするとそのまま問題なく動きました。
"Runtime": "python3.7",
↓
"Runtime": "python3.11",
②
Athena のエンジンバージョン
現在の Athena のエンジンバージョンは v3 です。「クエリエンジンをアップグレード」 で 「手動」 を選択する場合は v3 を選択します。が、検証の場合は 「クエリエンジンをアップグレード」 の部分は 「自動」 で良いと思います。
また、本番運用の場合はエンジンバージョンのアップグレードを自動で実施するか手動で実施するかプロジェクト運用を加味してご判断ください。
Athena DynamoDB コネクタ が Glue 接続推奨に
Athena Federated Query(フェデレーテッドクエリ)は、Lambda 関数や Glue 接続を使って、様々なデータソースに保存されているデータにまたがる SQL を実行することが可能な Athena の機能です。
DynamoDB のデータは Athena から直接クエリすることができないのですが、この Athena フェデレーテッドクエリを使うことによってクエリできるようになります。
フェデレーテッドクエリの準備は Athena の 「データソースとカタログ」 画面からデータソースを作成することで行えます。
「データソースの作成」 から DynamoDB を選び、
データソースの詳細を入力します。
以下ドキュメントを参照すると、Athena で DynamoDB コネクタを作成するには以下 2 種類の方法が記載されています。
- Glue connections (recommended)
- Legacy connections
検索して見つかるのは Legacy connections のやり方が多いのですが、今は Glue connections(AWS Glue 接続)を使うのが推奨と書かれていました。
注記
2024 年 12 月 3 日以降に作成された Athena データソースコネクタは、AWS Glue 接続を使用します。
以下ドキュメントもご参照ください。
また、この時設定している 「Amazon S3 内の流出場所」 とは、Lambda 関数のレスポンスサイズ制限を超えるデータを保存するための領域となります。Spill(溢れる)Bucket と呼ぶものです。以下 re:Post も参照ください。
さて、Lambda 関数に付与する IAM ロールは新しいものを作成することとします。
確認画面では、Lambda 関数、Lambda 実行ロール、Glue 接続など、Athena 以外のリソースが作成されることがかかれています。これに承諾のチェックを付け 「データソースの作成」 をクリックすると、データソースが作成されます。
Athena でデータソースが作成できました。フェデレーテッドクエリの準備は OK です。
Athena データソースの作成とともにできたリソースを確認
CloudFormation テンプレート
Athena Federated Query のための Lambda 関連リソースは裏で CloudFormation によって作られていました。athenafederatedcatalog-athena-xxx
というスタック名でスタックが存在します。
リソースタブを確認すると、
- Lambda 関数
- IAM ポリシー
- IAM ロール
が作成されているのが分かります。
テンプレートは以下のようになっていました。
athenafederatedcatalog-athena-q37hj06v
Transform: AWS::Serverless-2016-10-31
Parameters:
LambdaFunctionName:
Description: This is the name of the lambda function that will be created. This
name must satisfy the pattern ^[a-z0-9-_]{1,64}$
Type: String
AllowedPattern: ^[a-z0-9-_]{1,64}$
SpillBucket:
Description: The name of the bucket where this function can spill data.
Type: String
GlueConnection:
Description: Name of glue connection storing connection details for Federated
Data source.
Type: String
LambdaRoleArn:
Description: (Optional) A custom role to be used by the Connector lambda
Type: String
Default: ''
KmsKeyId:
Description: (Optional) By default any data that is spilled to S3 is encrypted
using AES-GCM and a randomly generated key. Setting a KMS Key ID allows your
Lambda function to use KMS for key generation for a stronger source of encryption
keys.
Type: String
Default: ''
Conditions:
HasKmsKeyId:
Fn::Not:
- Fn::Equals:
- Ref: KmsKeyId
- ''
NotHasLambdaRole:
Fn::Equals:
- Ref: LambdaRoleArn
- ''
CreateKmsPolicy:
Fn::And:
- Condition: HasKmsKeyId
- Condition: NotHasLambdaRole
IsRegionBAH:
Fn::Equals:
- Ref: AWS::Region
- me-south-1
IsRegionHKG:
Fn::Equals:
- Ref: AWS::Region
- ap-east-1
Resources:
ConnectorConfig:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
glue_connection:
Ref: GlueConnection
FunctionName:
Ref: LambdaFunctionName
PackageType: Image
ImageUri:
Fn::Sub:
- ${Account}.dkr.ecr.${AWS::Region}.amazonaws.com/athena-federation-repository-dynamodb:2025.14.4
- Account:
Fn::If:
- IsRegionBAH
- '084828588479'
- Fn::If:
- IsRegionHKG
- 183295418215
- 292517598671
Description: Enables Amazon Athena to communicate with DynamoDB, making your
tables accessible via SQL
Timeout: 900
MemorySize: 3008
Role:
Fn::If:
- NotHasLambdaRole
- Fn::GetAtt:
- FunctionRole
- Arn
- Ref: LambdaRoleArn
FunctionRole:
Condition: NotHasLambdaRole
Type: AWS::IAM::Role
Properties:
ManagedPolicyArns:
- Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
FunctionExecutionPolicy:
Condition: NotHasLambdaRole
Type: AWS::IAM::Policy
Properties:
PolicyName: FunctionExecutionPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- dynamodb:DescribeTable
- dynamodb:ListSchemas
- dynamodb:ListTables
- dynamodb:Query
- dynamodb:Scan
- dynamodb:PartiQLSelect
- glue:GetTableVersions
- glue:GetPartitions
- glue:GetTables
- glue:GetTableVersion
- glue:GetDatabases
- glue:GetTable
- glue:GetPartition
- glue:GetDatabase
- athena:GetQueryExecution
Effect: Allow
Resource: '*'
- Action:
- s3:GetObject
- s3:ListBucket
- s3:GetBucketLocation
- s3:GetObjectVersion
- s3:PutObject
- s3:PutObjectAcl
- s3:GetLifecycleConfiguration
- s3:PutLifecycleConfiguration
- s3:DeleteObject
Effect: Allow
Resource:
- Fn::Sub:
- arn:${AWS::Partition}:s3:::${bucketName}
- bucketName:
Ref: SpillBucket
- Fn::Sub:
- arn:${AWS::Partition}:s3:::${bucketName}/*
- bucketName:
Ref: SpillBucket
- Action:
- glue:GetConnection
Effect: Allow
Resource:
- Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:connection/${GlueConnection}
- Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:catalog
Roles:
- Ref: FunctionRole
FunctionKmsPolicy:
Condition: CreateKmsPolicy
Type: AWS::IAM::Policy
Properties:
PolicyName: FunctionKmsPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- kms:GenerateRandom
Effect: Allow
Resource: '*'
- Action:
- kms:GenerateDataKey
Effect: Allow
Resource:
Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${KmsKeyId}
Roles:
- Ref: FunctionRole
Lambda
Lambda 関数は athenafederatedcatalog_athena_xxx
という名前で作成されました。関数はコードで表示されず、イメージ URL が表示されています。
コードプレビューは利用できません
関数コードはコンテナイメージとしてデプロイされています。IDE はコードを表示できません。
- イメージ URL:
292517598671.dkr.ecr.ap-northeast-1.amazonaws.com/athena-federation-repository-dynamodb:2025.14.4
この ECR リポジトリは自身の AWS アカウントではありません。利用ユーザー側からはこの AWS アカウントの ECR リポジトリのアプリケーションを見に行っていると読み取れます。
AWS アカウント ID で Web 検索すると以下の Serverless Application Repository が確認できました。
Lambda コンソールに戻ります。アプリケーションのリンクをクリックすると、以下の概要が表示されます。下部のリソースを見ると、Lambda 関数(ConnectorConfig)、IAM ポリシー(FunctionExecutionPolicy)、IAM ロール(FunctionRole)が表示されています。ConnectorConfig をクリックすると Lambda 関数のコンソールに戻ります。
FunctionRole をクリックして IAM ロールを確認します。IAM ロールは athenafederatedcatalog-athena-xxx-FunctionRole-xxx
という名前で作成されていました。
IAM ロールには
- AWSLambdaBasicExecutionRole(AWS 管理ポリシー)
- FunctionExecutionPolicy(カスタマーインラインポリシー)
が付与されていました。
FunctionExecutionPolicy
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:DescribeTable",
"dynamodb:ListSchemas",
"dynamodb:ListTables",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:PartiQLSelect",
"glue:GetTableVersions",
"glue:GetPartitions",
"glue:GetTables",
"glue:GetTableVersion",
"glue:GetDatabases",
"glue:GetTable",
"glue:GetPartition",
"glue:GetDatabase",
"athena:GetQueryExecution"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetLifecycleConfiguration",
"s3:PutLifecycleConfiguration",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::federated-spill-123456789012-ap-northeast-1",
"arn:aws:s3:::federated-spill-123456789012-ap-northeast-1/*"
],
"Effect": "Allow"
},
{
"Action": [
"glue:GetConnection"
],
"Resource": [
"arn:aws:glue:ap-northeast-1:123456789012:connection/athenafederatedcatalog_athena_q37hj06v",
"arn:aws:glue:ap-northeast-1:123456789012:catalog"
],
"Effect": "Allow"
}
]
}
さて、Lambda コンソールに戻ります。Lambda アプリケーションの 「デプロイ」 タブでは以下のように SAM テンプレートが表示されました。
以下 README も必要に応じて参照ください。
Lambda でデプロイされていたアプリケーションは Servless Application Repository から検索することで参照できました。
Glue Connection
Glue コンソールの Data Catalog メニューから Connections をクリックして確認すると、
- Name:
athenafederatedcatalog_athena_xxx
- Status:Ready
- Type:DYNAMODB
という Connection が存在しています。
クリックして詳細を見てみましたが、特に細かな詳細はなさそうです。Description がデータソースの作成時に設定したものですね。
ちなみに Database の default を確認してみましたが、DynamoDB のテーブルがここで確認できるわけではなかったです。
DynamoDB へのフェデレーテッドクエリを実施
Athena コンソールからハンズオン用に作成した 「handson」 ワークグループを選択します。データソースは先ほど作成した Athena-xxx
を選択し、データベースは default を選択します。
テーブルに DynamoDB テーブルが表示されるので、
SELECT * FROM "作成したデータソース"."default"."transaction" ;
とクエリを実行すると、 10 万行データが取得できました。
さて、気になるのは複数表示された DynamoDB テーブルです。私の AWS アカウントの東京リージョンに存在する DynamoDB テーブルが全部見えています。
これを特定の DynamoDB テーブルのみ表示させるようにできるかどうかは解決方法が見つけられていません。また、テーブル名までは見えてしまうがテーブルの内容はクエリで取得させないようにする、と言った方法は論理的には可能と思いますが、未検証です。
他に気になった点として、一部 DynamoDB テーブルのカラムがすべて取得できていない(列数が足りない)場合がありました。以下は GenU アプリケーションで一部の列にデータが入っていないカラムが表示できていない例です。
AWS Glueでのデータベースとテーブルのセットアップ を見ると以下の記載がありました。
コネクタに組み込まれたスキーマ推論機能には制限があるため、メタデータ用としては AWS Glue の使用が適しています。これを行うには、AWS Glue にデータベースとテーブルが必要です。DynamoDB で使用できるようにするには、そのプロパティを編集する必要があります。
Glue を使用して DynamoDB テーブルからスキーマを推測する方法が記載されています。本記事では試さないのですが、必要に応じて参照ください。
また、Athena Federated Query ではなく Amazon Ion 形式で DynamoDB のデータを S3 バケットにエクスポートし、S3 に対して Athena でクエリするという方法もありました。
③
QuickSight に Athena フェデレーテッドクエリ用 Lambda へのアクセス権限を付与する
QuickSight でデータを可視化する前に QuickSight に Athena と Athena フェデレーテッドクエリ用 Lambda へのアクセス権限を付与しないといけないのですが、手順に従って通常通り 「QuickSight で管理されるロールを使用する (デフォルト)」 を選択した状態で許可しようとすると、Lambda 関数が出てきません。
もちろん、データセットの作成で Athena を選択しカタログでフェデレーテッドクエリ用のカタログを選択すると権限不足でエラーとなります。
エラーの詳細
リージョン:
ap-northeast-1
タイムスタンプ:
1745232499015
requestId:
c76c0018-3e75-4336-b201-6a5ca72c54de
sourceErrorCode:
100071
sourceErrorMessage:
[Simba][AthenaJDBC](100071) An error has been thrown from the AWS Athena client. Failed to invoke lambda function due to com.amazonaws.services.lambda.model.AWSLambdaException: User: arn:aws:sts::123456789012:assumed-role/aws-quicksight-service-role-v0/QuickSight-RoleSession-1745232498676 is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:ap-northeast-1:123456789012:function:athenafederatedcatalog_athena_q37hj06v because no identity-based policy allows the lambda:InvokeFunction action (Service: AWSLambda; Status Code: 403; Error Code: AccessDeniedException; Request ID: fde400ef-adf4-42e1-b426-97224a42888b; Proxy: null) [Execution ID not available]
sourceErrorState:
HY000
sourceException:
java.sql.SQLException
sourceType:
ATHENA
「QuickSight で管理されるロールを使用する (デフォルト)」 を選択した際に使われる aws-quicksight-service-role-v0
というロールはユーザー側で直接編集してはいけないことになっています。「セキュリティとアクセス許可」 画面からの操作ではなく、IAM コンソールや CLI などで直接編集すると、以下ブログのようにエラーが発生します。
問題切り分けのため、QuickSight のロールをデフォルトの aws-quicksight-service-role-v0
ではなく、ユーザー作成のカスタムロールにして Lambda 権限を付与して試してみます。
以下 IAM ロール 「quicksight-admin-role」 作成しました。
付与する IAM ポリシーは 「QuickSight で管理されるロールを使用する (デフォルト)」 を選択した際に使われる aws-quicksight-service-role-v0
を参考にしました。Lambda に関する許可を QuickSight に付与するため、以下の IAM ポリシーを手動で新規作成し付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:athenafederatedcatalog_athena_q37hj06v"
}
]
}
IAM ロールを QuickSight に設定します。QuickSight の管理画面で 「セキュリティとアクセス許可」 を開き 「管理」 をクリックします。
「既存のロールを使用する」 で先ほど作成した IAM ロールを選択して保存します。
設定できると以下のような表示になります。
以下 Community の Q&A も参照ください。
QuickSight データセット作成
付与した IAM ロールの権限で Athena をデータソースとするデータセットを作成できるか試します。「新しいデータセット」 から Athena を選択し、データソース名と Athena ワークグループを選択してデータソースを作成します。
先ほどと違い、今度はカタログとデータベースとテーブルにアクセスできました!カタログは DynamoDB のものを選択し、データベースは default を選択します。S3 のテーブルと DynamoDB のテーブルを JOIN するので、「カスタム SQL を使用」 をクリックします。
カスタム SQL 名:transaction-master-join
とし、以下のようにクエリを記載します。カタログ名やデータベース名はご自身で設定したものに合わせてください。
SELECT
t."Transaction ID",
m."Sales Channel",
m."Item Type",
date(date_parse("Order Date",'%c/%e/%y')) "Order Date",
date(date_parse("Ship Date",'%c/%e/%y')) "Ship Date",
CAST (t."Units Sold" as INT) "Units Sold",
t."Region",
t."Country",
t."Order Priority",
m."Unit Price",
m."Unit Cost",
m."Unit Price" * CAST (t."Units Sold" as INT) "Total Revenue",
m."Unit Cost" * CAST (t."Units Sold" as INT) "Total Cost",
(m."Unit Price" - m."Unit Cost") * CAST (t."Units Sold" as INT) "Total Profit"
FROM
"AwsDataCatalog"."mydb"."master" m,
"Athena-q37hj06v"."default"."transaction" t
WHERE m.id = t."Item ID" and m."Sales Channel" = t."Sales Channel"
SQL を入力したら、「データの編集/プレビュー」 をクリックします。
データのプレビューができる画面に遷移します。「適用」 をクリックすると、画面下半分に S3 のテーブルと DynamoDB のテーブルが JOIN されたクエリ結果が表示されます。
クエリモードは SPICE にしておきます。直接クエリだと、グラフを更新するたびに Athena や S3 のデータを取りに行って JOIN するクエリが流れて料金も時間もかかります。そのため SPICE という QuickSight のインメモリ領域にデータを読み込んでおき、SPICE のデータを参照してグラフを図示するようにします。
クエリ結果が確認できたら、「更新して視覚化」 をクリックして分析画面を作成します。
分析の作成(可視化)
ここまでくれば後は問題なくほぼ手順通りに進みました。少し画面がアップデートされて変わっているので、ご参考にキャプチャをいくつか紹介します。
今回は新規シートはタイル形式のまま進めます。
「Total Cost」 「Total Profit」 「Total Revenue」 の表示方法を 「通貨」 に変えます。
ビジュアルを一つ作成してみます。タイルを選択した状態でビジュアルから 「垂直積み上げ棒グラフ」 を選択します。
X 軸などは以下のように設定します。
ASIA の棒を右クリックして 「Country までドリルダウン」 します。
アジアの国ごとに棒グラフが表示できました。
あとはコントロールなどの設定を手順通りに進めます。
分析が作成できました。
試した後は後片付けを忘れずに実施してください。
その他
DynamoDB に格納されたデータを Athena フェデレーテッドクエリで取得する際、フルスキャンされてしまうのかどうかは後ほど調査したいと思っています。
パフォーマンス や、以下のブログが参考になりそうです。
DynamoDB に対する一部のデータ操作に関するイベントは CloudTrail の証跡でデータイベントを有効にするか、DynamoDB Streams で取得できそうです。
また、DynamoDB は自動でスケールしていくので大量データをすべてフルスキャンする場合はスキャン料金がかかってしまいます。別途バッチ処理などで S3 へダンプする仕組みを作って実際は S3 のデータを可視化する…といった仕組みの方が効率的で取り扱いしやすい可能性もあるので、システムの要件や状況に応じて直接 DynamoDB データを可視化するのかどうかは検討の余地がありそうです。
おわりに
本記事への質問やご要望については画面下部のお問い合わせ「DevelopersIO について」からご連絡ください。記事に関してお問い合わせいただけます。
参考
昔のもの