Amazon web services AWS Athena:CTAS查询结果的跨账户写入

Amazon web services AWS Athena:CTAS查询结果的跨账户写入,amazon-web-services,amazon-s3,permissions,acl,amazon-athena,Amazon Web Services,Amazon S3,Permissions,Acl,Amazon Athena,我有一个帐户A中的大型历史数据集。此数据集采用csv格式,并按年/月/日/小时/进行分区。我的目标是将这些数据转换为拼花地板,并附加标准化步骤和额外的分区级别,例如年/月/日/小时/产品/,然后将其写回已处理/目录下帐户A的同一存储桶中。所以“目录”树看起来像 S3_bucket_Account_A dataset | ├── raw │   ├── year=2017 | │   ├── month=01 | | │   ├── day=01 | │   | | ├─

我有一个帐户A中的大型历史数据集。此数据集采用csv格式,并按
年/月/日/小时/
进行分区。我的目标是将这些数据转换为拼花地板,并附加标准化步骤和额外的分区级别,例如
年/月/日/小时/产品/
,然后将其写回
已处理/
目录下帐户A的同一存储桶中。所以“目录”树看起来像

S3_bucket_Account_A

dataset
|
├── raw
│   ├── year=2017
|   │   ├── month=01
|   |   │   ├── day=01
|   │   |   |   ├── hour=00
|   │   |   |   └── hour=01
|                                 
├── processed
│   ├── year=2017
|   │   ├── month=01
|   |   │   ├── day=01
|   |   |   │   ├── hour=00
|   |   │   |   |   ├── product=A
|   |   │   |   |   └── product=B
|   |   |   │   ├── hour=01
|   |   │   |   |   ├── product=A
|   |   │   |   |   └── product=B
为了做到这一点,我将使用boto3 API向雅典娜发送CTAS查询语句。我知道,例如,可以在同一查询中写入多达100个分区,CTAS查询结果的位置必须为空/唯一。因此,我一次处理一个原始分区,并在考虑这些限制的情况下动态生成CTAS查询的内容

由于我使用帐户B执行这些CTAS查询,但这些查询的结果应写入帐户A所拥有的S3 bucket。我已被授予以下权限,这些权限是在帐户A的Bucket策略级别指定的

{
    "Effect": "Allow",
    "Principal": {
        "AWS": "__ARN_OF_ACCOUNT_B__"
    },
    "Action": [
        "s3:*"
    ],
    "Resource": [
        "arn:aws:s3:::dataset",
        "arn:aws:s3:::dataset/*"
    ]
}
问题是帐户A(存储桶所有者)无法访问由于帐户B的Athena执行CTAS查询而写入的文件

据我所知,有一个选项是A帐户为我创建IAM角色,然后我将像A帐户一样执行此任务。但不幸的是,此选项是不可能的

我已经找到了如何转移S3对象的所有权/更改ACL的方法。一种方法是将CTAS查询结果输出到帐户B的S3 bucket中,然后将这些文件复制到帐户A的bucket中()

aws s3 cp s3://source\u awsexamplebucket/s3://destination\u awsexamplebucket/--acl bucket owner完全控制--递归
另一种方法是使用类似()

aws s3 ls s3://bucket/path/--recursive | awk'{cmd=“aws s3api放置对象acl--acl bucket owner完全控制--bucket bucket--key“$4;系统(cmd)}” 但这两个选项将需要额外的
GET
PUT
请求到S3,因此需要更多的钱来支付AWS。但更重要的是,在CTAS查询成功后,我使用创建的表中的分区更新帐户A的AWS Glue表(目标表)。这样,帐户A中的IAM用户可以立即开始查询转换后的数据。下面是我如何更新目的地表的一般想法

response=glue\u client.get\u分区(
CatalogId=“\uuuuu账户\u B\u ID\uuuuuu”,
DatabaseName=“帐户中的某些数据库”,
TableName=“ctas\U表”
)
对于响应中的分区[“分区”]:
对于输入[“DatabaseName”、“TableName”、“CreationTime”]:
partition.pop(键)
粘合\u客户端。批处理\u创建\u分区(
CatalogId=“\uuuuu账户\uu ID\uuuuuu”,
DatabaseName=“帐户中的某些数据库”,
TableName=“目的地表”,
PartitionInputList=响应[“分区”]
)
我这样做,而不是
MSCK REPAIR TABLE destination\u TABLE
,因为后者由于某种原因需要很长时间。如您所见,如果我选择使用
awss3cp
,我在复制分区的元信息时也需要考虑到这一点

因此,我真正的问题是如何在由另一个帐户执行的CTAS查询中授予bucket所有者完全控制权?

更新2019-06-25: 刚刚发现,但他们似乎使用IAM角色,这不是我的案例的选项

更新2019-06-27 我发现:1)不可能在CTAS查询中更改ACL。相反,S3对象可以用新的所有权在自身上复制(感谢和的注释)

更新2019-06-30 只是简单地回顾一下。我从
帐户B
运行CTAS查询,但结果保存在
帐户a
拥有的存储桶中。这是CTAS查询“标题”的外观:

CREATE TABLE some_database_in_account_B.ctas_TABLE
与(
格式='拼花',
外部位置='s3://\UU目的地\U存储桶\U账户中\U A\UU/\UU CTAS\U前缀\UU/',
分区单位=数组['year','month','day','hour','product']
)作为(
...
...
)
由于我使用
boto3
提交CTAS查询,并且我知道
\uuu账户中的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu前缀
一起提交,因此在成功执行CTAS查询后,我可以

s3\u resource=aws\u session.resource('s3'))
destination\u bucket=s3\u resource.bucket(name=“\u destination\u bucket\u in\u Account\u A\u”)
对于目标_bucket.objects.filter(Prefix=“uu CTAS_Prefix_uu”)中的obj:
object_acl=s3_resource.ObjectAcl(destination_bucket.name,obj.key)
对象_acl.put(
ACL='bucket-owner-full-control'
)

注意,由于我需要提交大量超出AWS Athena限制的CTAS查询,我已经实现了自动提交新查询并执行一些附加操作的逻辑,例如更新目标粘合表和日志记录。因此,包含这些代码行非常简单。

我建议您执行复制

“额外的获取和放置请求”将是次要的:

  • GET是每1000个请求0.0004美元
  • PUT为每1000个请求0.005美元

或者,您可以从帐户B运行
aws s3 cp--recursive
命令,将文件复制到它们自己(是!),同时更改所有权(它还需要另一个更改,例如将元数据设置为可接受的复制命令)。这类似于您对
put object acl

的建议。目前,唯一干净地做到这一点的方法是在帐户A中使用IAM角色,并使用允许帐户B承担该角色的信任策略