AWS Athena:跨帐户写入CTAS查询结果

时间:2019-06-25 16:56:48

标签: amazon-web-services amazon-s3 permissions acl amazon-athena

我的帐户A 中有大量的历史数据集。该数据集为csv格式,并按year/month/day/hour/进行了分区。我的目标是通过额外的标准化步骤和额外的分区级别(例如year/month/day/hour/product/,并将其写回到processed/“目录”下的帐户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查询语句发送到Athena。我知道limitations of CTAS queries,例如可以在同一查询中最多写入100个分区,CTAS查询结果的位置必须为空/唯一。因此,我当时处理一个原始分区,并且考虑到这些限制,正在动态生成CTAS查询的内容。

由于我正在使用帐户B 执行这些CTAS查询,但是这些查询的结果应写入帐户A拥有的S3存储桶 中。我获得了以下权限,这些权限是在帐户A的存储桶策略级别上指定的。

{
    "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的方法。一种方法是在帐户B的S3存储桶中输出CTAS查询结果,然后将这些文件复制到帐户A的存储桶(original source

aws s3 cp s3://source_awsexamplebucket/ s3://destination_awsexamplebucket/ --acl bucket-owner-full-control --recursive

另一种方法是用(original source)递归更新acl

aws s3 ls s3://bucket/path/ --recursive | awk '{cmd="aws s3api put-object-acl --acl bucket-owner-full-control --bucket bucket --key "$4; system(cmd)}'

但是这两个选项将需要向S3发出额外的GETPUT请求,因此需要更多的钱来支付AWS。但更重要的是,在CTAS查询成功后,我使用创建的表中的分区更新了账户A 的AWS Glue表(目标表)。这样,帐户A 中的IAM用户可以立即开始查询转换后的数据。这是我如何更新destination_table的一般思路

response = glue_client.get_partitions(
    CatalogId="__ACCOUNT_B_ID__",
    DatabaseName="some_database_in_account_B",
    TableName="ctas_table"
)

for partition in response["Partitions"]:
    for key in ["DatabaseName", "TableName", "CreationTime"]:
        partition.pop(key)

glue_client.batch_create_partition(
    CatalogId="__ACCOUNT_A_ID__",
    DatabaseName="some_database_in_account_A",
    TableName="destination_table",
    PartitionInputList=response["Partitions"]
)

我这样做是代替MSCK REPAIR TABLE destination_table,因为后者出于某种原因会花费很长时间。如您所见,如果我选择使用aws s3 cp,则在复制有关分区的元信息时也需要考虑到这一点

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

更新2019-06-25:

刚刚发现similar post,但似乎他们使用了IAM角色,这不是我的选择

更新2019-06-27

我发现:1)无法在CTAS查询中更改ACL。相反,可以使用新所有权将S3对象复制到自身上(由于John RotensteinTheo的注释)。

更新2019-06-30

回顾一下。我从account B运行CTAS查询,但结果保存在account A拥有的存储桶中。这就是CTAS查询“标头”的样子:

CREATE TABLE some_database_in_account_B.ctas_table
WITH (
  format = 'PARQUET',
  external_location = 's3://__destination_bucket_in_Account_A__/__CTAS_prefix__/',
  partitioned_by = ARRAY['year', 'month', 'day', 'hour', 'product']
) AS (
    ...
    ...
)

由于我使用boto3提交CTAS查询,并且我知道__destination_bucket_in_Account_A____CTAS_prefix__在一起,因此我可以直接更改其ACL,而无需使用aws cp复制文件成功执行CTAS查询后,在同一个python脚本中运行。

s3_resource = aws_session.resource('s3')
destination_bucket = s3_resource.Bucket(name="__destination_bucket_in_Account_A__")

for obj in destination_bucket.objects.filter(Prefix="__CTAS_prefix__"):
    object_acl = s3_resource.ObjectAcl(destination_bucket.name, obj.key)
    object_acl.put(
        ACL='bucket-owner-full-control'
    )

注意,由于我需要提交数量超过AWS Athena限制的CTAS查询,因此我已经实现了自动提交新查询并执行一些其他操作的逻辑,例如更新目标胶水表并记录。因此,包括这些代码行是很简单的。

2 个答案:

答案 0 :(得分:0)

我建议您执行复制。

“其他GET和PUT请求”将是次要的:

  • GET是每1,000个请求$ 0.0004
  • PUT为每1000个请求0.005 USD

或者,您从帐户B运行aws s3 cp --recursive命令以更改所有权(是,还需要进行其他更改,例如将元数据设置为复制命令)来将文件复制到自身(是!)。 。这与您对put-object-acl提出的建议类似。

答案 1 :(得分:0)

当前,唯一做到这一点的唯一方法是在帐户A中使用IAM角色,并使用允许帐户B承担角色的信任策略。您提到这对您的情况是不可能的,这是不幸的。当前无法通过其他方式进行操作的原因是,Athena不会使用“ bucket-owner-full-control”选项来写入文件,因此帐户A将永远不会完全拥有由帐户B中的角色发起的操作创建的任何文件

由于您在目标存储桶中被授予的策略允许所有操作,因此您可以做的一件事是在CTAS操作完成后运行一项任务,该任务列出了创建的对象,并使用以下命令将每个对象复制到自身(相同的源密钥和目标密钥) “存储桶拥有者完全控制” ACL选项。像这样复制对象是更改S3对象的存储和ACL属性的常用方法。正如您所说,这会产生额外的费用,但是与CTAS费用以及与将来查询数据有关的费用相比,这些费用微不足道。

真正的缺点是必须编写一些内容以在CTAS操作之后运行并进行协调。我建议您看一下Step Functions来做到这一点,您可以制作出很好的工作流程来自动化Athena,并且运行成本很小。我的应用程序使用Step Functions,Lambda和Athena并花费几分钱(实际上我使用IAM角色来进行跨帐户工作),或多或少地完全按照您的意图进行操作。