[アップデート]Amazon Athenaで提供されるデータコネクタの内12種類がAWSマネージドになったのでPostgreSQLに接続してみました
初めに
Amazon AthenaでAWSが提供するデータコネクタのうち以下の12種類のデータソースに対するコネクタがマネージドで管理されるようになりました。
- Amazon DocumentDB
- Amazon DynamoDB
- Amazon OpenSearch
- Amazon Redshift
- Google BigQuery
- MySQL
- Oracle
- PostgreSQL
- SAP HANA
- Snowflake
- SQL Server
- Teradata
AthenaにおけるデータコネクタはLambda関数を仲介してAthenaがAmazon S3以外のデータソースに対してクエリをかけられるようになる機能であり、これを用いることでインターフェースをAthenaに集約し複数のデータソースを横串で分析が可能となっております。
以前のデータコネクタは、上記記事に記載している通りCloudFormationでLambda関数を含むAWSリソースが対象のアカウント上で展開されるものであったため、0ベースでの構築ほどの手間はいらないもののLambda関数のランタイムのアップグレード等作成されるリソースの管理が必要になっていました。
今回のアップデートはこのLambda関数部分がAWS管理になったことでユーザ側でその管理を削減できるアップデートとなります。
2024/12/05のアップデートによるGlueへの集約
今回のアップデートに際して改めてドキュメントを確認したところ、上記に記載している記事の方式は現時点から2方式ほど古い形になるようで、2024/12/05頃のアップデートにてGlue Data Catalogに設定が集約されていたようです(過去のWhat's newも見直してみましたがこれ自体は見当たらず...)。

以下の記事はDynamoDB版の記事になりますがこのData Catalogを利用する方式となっています。
「Athena設定」のセクションを見てみると、この時点ではまだLambda関数自体は利用する方式であったようですが、今回のアップデートでこの方式がさらに拡張されてLambda関数が不要になったようです。
使ってみる
さて、実際に今回のアップデートに対応したサービスの一つであるPostgreSQLに対して接続を行うデータコネクタを作成してみます。
周辺リソースの作成
今回のアップデートに伴いLambda関数自体は作成されなくなりましたがそれ以外のリソースは必要になります。
当時の詳しい構築は以下記事をご参照いただければと思いますが今回もこれらのリソースを作成します。太字部分のリソースは以前は不要でしたが今回の方式の場合に必要になるリソースです。
- RDS本体(および周辺リソース)
- セキュリティグループ(データコネクタ取り付け用)
- S3バケット(スピルバケット)
- DB認証情報シークレット(Secrets Manager)
- データコネクタDB認証情報用
- VPCエンドポイント(Secrets Manager、S3、Glue、KMS、sts)
- コネクタを所属させるサブネットからサービスへの経路確保(NAT Gateway等経路があれば不要)
- https://docs.aws.amazon.com/athena/latest/ug/athena-connectors-vpc-creation.html
- 執筆時点でドキュメント上stsのエンドポイントに関する記載が見当たらなかったのですがテスト接続のエラーでAssumeRoleに失敗してたので作成しています
- Athena(データコネクタ)に割り当てるIAMロール
上記リソースを作成するためのCloudFormationテンプレートは以下のとおりです。
明確な記載が見当たらなかったのですが、執筆時の検証ではPostgreSQL 18.3でDB側のログでログインできてるログは見えるもののその後タイムアウトで処理ができないということがありましたので一旦17系を指定しています(そうしたら動きました)。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Env:
Type: String
Prefix:
Type: String
VpcId:
Type: String
SubnetGroupId:
Type: String
EndpointSubnetId:
Type: List<String>
Resources:
DBParameterGroup:
Type: AWS::RDS::DBParameterGroup
Properties:
Family: postgres17
Description: Enable connection logging
Parameters:
log_connections: "1"
log_disconnections: "1"
DBInstance:
Type: AWS::RDS::DBInstance
Properties:
Engine: postgres
EngineVersion: "17.9"
MasterUsername: postgres
AllocatedStorage: 20
StorageType: gp3
DBInstanceIdentifier: !Sub ${Env}-${Prefix}-db
MasterUserPassword: !Sub "{{resolve:secretsmanager:${RDSMasterUserCredential}:SecretString:password::}}"
AvailabilityZone: !Sub ${AWS::Region}a
DBInstanceClass: db.t3.small
AutoMinorVersionUpgrade: False
EnablePerformanceInsights: True
CACertificateIdentifier: "rds-ca-rsa4096-g1"
DBSubnetGroupName: !Ref SubnetGroupId
DBParameterGroupName: !Ref DBParameterGroup
VPCSecurityGroups:
- !Ref RdsSg
RDSMasterUserCredential:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub ${Env}-${Prefix}-master-credential
GenerateSecretString:
SecretStringTemplate: '{"username": "postgres"}'
GenerateStringKey: "password"
PasswordLength: 16
ExcludeCharacters: '"@/\'
RdsSg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: data-connector-rds-sg
GroupDescription: String
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 5432
ToPort: 5432
SourceSecurityGroupId: !Ref DataConnectorSg
VpcId: !Ref VpcId
SpillBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${Env}-${Prefix}-spill-bucket
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
DataConnectorSg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: data-connector-client-sg
GroupDescription: String
VpcId: !Ref VpcId
SecretsManagerEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.secretsmanager
SubnetIds: !Ref EndpointSubnetId
VpcEndpointType: Interface
VpcId: !Ref VpcId
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref EndpointSg
STSEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.sts
SubnetIds: !Ref EndpointSubnetId
VpcEndpointType: Interface
VpcId: !Ref VpcId
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref EndpointSg
GlueEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.glue
SubnetIds: !Ref EndpointSubnetId
VpcEndpointType: Interface
VpcId: !Ref VpcId
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref EndpointSg
KMSEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.kms
SubnetIds: !Ref EndpointSubnetId
VpcEndpointType: Interface
VpcId: !Ref VpcId
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref EndpointSg
S3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
SubnetIds: !Ref EndpointSubnetId
VpcEndpointType: Interface
VpcId: !Ref VpcId
PrivateDnsEnabled: true
DnsOptions:
PrivateDnsOnlyForInboundResolverEndpoint: "AllResolvers"
SecurityGroupIds:
- !Ref EndpointSg
EndpointSg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: data-connector-ep-sg
GroupDescription: for VPC Endpoint
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
VpcId: !Ref VpcId
AthenaConnectorRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${Env}-${Prefix}-athena-connector-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- glue.amazonaws.com
- lakeformation.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: AthenaConnectorPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- glue:ManagedConnector
- glue:GetConnection
- lakeformation:GetDataAccess
- ec2:CreateNetworkInterface
- ec2:DeleteNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DescribeSubnets
- ec2:DescribeSecurityGroups
- ec2:DescribeVpcs
Resource: "*"
- Effect: Allow
Action:
- secretsmanager:DescribeSecret
- secretsmanager:GetSecretValue
- secretsmanager:PutSecretValue
Resource: !Ref RDSMasterUserCredential
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:DeleteObject
Resource: !Sub arn:aws:s3:::${SpillBucket}/*
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
Resource: !GetAtt SpillBucket.Arn
IAMポリシーは以下のドキュメントと作成時のエラーを元に調整しました。実際に動かしてみて発生したエラーの内容に応じて調整しています。検証中にうまくいかずトライアンドエラーを繰り返していたのでもしかすると一部権限過剰かもしれません(最後の方なぜか時間開けたら解決した...というのがあり)。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"glue:ManagedConnector",
"glue:GetConnection",
"lakeformation:GetDataAccess",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeVpcs"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue"
],
"Resource": "arn:aws:secretsmanager:ap-northeast-1:xxxxx:secret:dev-postgres-connector-master-credential-xxxxx",
"Effect": "Allow"
},
{
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::dev-postgres-connector-spill-bucket-xxxxx/*",
"Effect": "Allow"
},
{
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::dev-postgres-connector-spill-bucket-xxxxx",
"Effect": "Allow"
}
]
}
信頼ポリシーは以下のとおりです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"glue.amazonaws.com",
"lakeformation.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
コネクタの作成
データソースの作成は以前と同じでデータソースとカタログから行います。


パラメーターは以下のように入力します。
(何度かトライしてるので以降の画像でランダム生成名部分が一部乖離している場合がありますのでご注意ください)

以前作成した際のパラメーター入力は以下の形ですのでだいぶ変わってますね。

うまく作成できていればデータカタログの方に表示され、データベースが表示されます。
以下画面上に利用しているLambda関数という項目はなく、Lambda関数側の一覧を見に行っても特に作成された関数はなかったためユーザ管理から外れていることがわかります。


なおENIを作成する権限があるということは...と思いENI一覧を確認してみたところ、インターフェースがLambdaのタイプのENIが作成されていたためAWS管理なだけで実態はLambda関数になってそうです。

検証中トラブルシュートメモ
作成途中でセッションが切れたり、権限不足で作成が落ちたのですがこの場合Athenaのデータソース側に表示されず、かつリソースが残ったままになるので自力で削除しないとダメなようです。
自分が発生したのは以下2点ですがいずれもlakeformation:GetDataAccessの不足が原因でした。
## 初回作成時に発生
Insufficient Lake Formation permission(s) on arn:aws:glue:ap-northeast-1:xxxxx:connection/athena-postgres-test
## ↑でエラーが発生してConnectionのみ削除した場合に発生、CloudTrailから作成はできていたので削除
Resource is already registered
ConnectionsはGlueの以下の画面で、Lake Formation側はaws lakeformation deregister-resourceを用いて登録解除します。

また作成後のエラーとしてLake FormationへのPrincipalが不足することで詳細画面でエラーが出ます。この段階まで行ったら必要なリソースはできているようなので対象のロールへPrincipalを付与するだけでデータコネクタの再作成は不要でした。

その他以下のようなトラブルシュートを実施しています。
- Glueのテスト接続の利用
- https://docs.aws.amazon.com/glue/latest/dg/console-test-connections.html
- PostgreSQL 18.3で繋がらない問題は検出できませんでした(接続自体は問題なく後続の処理がダメだった?)
- PostgreSQL側の接続ログ
- 接続や認証はうまく行っているところまでは確認できたがその後の処理は見えなかったので推定でエンジンをダウングレードしたところ解決
クエリをかけてみる
ここまで来たらあとはAthena上からクエリをかけるだけです!

...エラーが出てしまいましたね。
https://docs.aws.amazon.com/athena/latest/ug/connectors-postgresql.html
このコネクタは、フェデレーションカタログとしてGlueデータカタログに登録できます。カタログ、データベース、テーブル、列、行、タグの各レベルで、Lake Formationで定義されたデータアクセス制御をサポートします。このコネクタはGlue接続を使用して、構成プロパティをGlueに一元管理します。
権限はLake Formationで管理していますがAthenaを実行しているユーザ(ここではマネジメントコンソールにアクセスしているユーザ)に対して権限が不足しているため失敗してそうです。
今回はpg_catalogにSELECTをかけたいだけなので以下のように権限を付与します。

これでうまく行きました。実行時間については裏でLambdaが動いているのか最初は18秒くらいでしたが2回目は9秒ほどとなっていますが、いずれにしろそこそこの処理オーバーヘッドはありそうです。

実は権限不足〜実際に動くまでかなりの時間トラブルシュートを行っていたのですが、結局時間あけて実行したらなぜか動いた...というパターンとなっておりました(夜にRDSを落として、再度翌日起動して検証したら成功)。
マネージド管理のコネクタリソースが裏で維持されている、もしくはすでにコネクションを貼っていたもので等何らか起因で以前の設定値を持ち続けてたという感じかもしれません。
リソースの削除について
今回作成したリソースが不要になった場合、試す限りでは失敗時同様に「データカタログ」「コネクション」「Data lake locations」の3つの削除が必要となりました(トラブルシュートメモ参照)。
なお各種リソースを削除してもAWS管理で動いてると思われるリソースのENIが結構長く残っていました。
結局待ち切れずに強制削除したのですが、これが残っているとデータコネクタに結びつけるセキュリティグループ(今回の場合data-connector-client-sg)が削除できないのでご注意ください。
終わりに
新しくリリースされたマネージドなデータコネクタを利用してRDS上のPostgreSQLに接続してみました。
事前に作成するリソース自体はまだ残っているもののコネクタ自体は直接管理不要になり、2024/12のアップデート(?)で接続管理はGlue、権限管理はLake Formationになったことで接続に関する情報をこの2サービスで集中管理できるのは非常に便利そうです。
一方でよしなにリソースが作られてしまうので必要なリソースの把握は別途自分でする必要があり、Lake Formationの権限管理はあまり使わない方もいると思うので求められるスキルは少し高くなりそうです。
(Lake Formation、自分は今回の検証で初めて触りました)







