使用 Cloudformation 在 S3 存储桶中创建文件夹

create folder inside S3 bucket using Cloudformation

我可以使用 cloudformation 创建 S3 存储桶,但我想在 S3 存储桶中创建一个文件夹..like

<mybucket>--><myfolder>

请告诉我用于在存储桶内创建文件夹的模板...两者应同时创建...

我正在使用 AWS lambda,如下所示

stackname = 'myStack'
client = boto3.client('cloudformation')
response = client.create_stack(
    StackName= (stackname),
    TemplateURL= 'https://s3.amazonaws.com/<myS3bucket>/<myfolder>/nestedstack.json',
    Parameters=<params>
)

使用 AWS CloudFormation 模板无法做到这一点。

需要说明的是,文件夹实际上并不存在于 Amazon S3 中。相反,对象的路径被添加到对象的名称 (key) 之前。

因此,存储在名为 foo 的文件夹中的文件 bar.txt 实际上存储的密钥为:foo/bar.txt

您也可以将文件复制到一个不存在的文件夹中,该文件夹将自动创建(实际上并非如此,因为该文件夹本身并不存在)。但是管理控制台会提供这样一个文件夹的外观,路径会提示存放在这样一个文件夹中。

底线:无需预先创建文件夹。就像它已经存在一样使用它。

AWS 不提供官方 CloudFormation 资源来在 S3 存储桶中创建对象。但是,您可以创建一个 Lambda-backed Custom Resource to perform this function using the AWS SDK, and in fact the gilt/cloudformation-helpers GitHub 存储库,它提供了一个现成的自定义资源来执行此操作。

与任何自定义资源设置一样有点冗长,因为您需要首先部署 Lambda 函数和 IAM 权限,然后将其作为堆栈模板中的自定义资源引用。

首先,将 Lambda::Function 和关联的 IAM::Role 资源添加到您的堆栈模板:

"S3PutObjectFunctionRole": {
  "Type": "AWS::IAM::Role",
  "Properties": {
    "AssumeRolePolicyDocument": {
      "Version" : "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": [ "lambda.amazonaws.com" ]
          },
          "Action": [ "sts:AssumeRole" ]
        }
      ]
    },
    "ManagedPolicyArns": [
      { "Ref": "RoleBasePolicy" }
    ],
    "Policies": [
      {
        "PolicyName": "S3Writer",
        "PolicyDocument": {
          "Version" : "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "s3:DeleteObject",
                "s3:ListBucket",
                "s3:PutObject"
              ],
              "Resource": "*"
            }
          ]
        }
      }
    ]
  }
},
"S3PutObjectFunction": {
  "Type": "AWS::Lambda::Function",
  "Properties": {
    "Code": {
      "S3Bucket": "com.gilt.public.backoffice",
      "S3Key": "lambda_functions/cloudformation-helpers.zip"
    },
    "Description": "Used to put objects into S3.",
    "Handler": "aws/s3.putObject",
    "Role": {"Fn::GetAtt" : [ "S3PutObjectFunctionRole", "Arn" ] },
    "Runtime": "nodejs",
    "Timeout": 30
  },
  "DependsOn": [
    "S3PutObjectFunctionRole"
  ]
},

然后您可以使用 Lambda 函数作为自定义资源来创建您的 S3 对象:

"MyFolder": {
  "Type": "Custom::S3PutObject",
  "Properties": {
    "ServiceToken": { "Fn::GetAtt" : ["S3PutObjectFunction", "Arn"] },
    "Bucket": "mybucket",
    "Key": "myfolder/"
  }
},

除了 BucketKey 之外,您还可以通过添加 Body 参数来使用相同的自定义资源编写基于字符串的 S3 对象(请参阅 docs).

我们无法(至少目前)无法在 s3 存储桶中创建子文件夹。

您可以尝试使用以下命令:

 aws s3 mb s3://yavdhesh-bucket/inside-folder

然后尝试使用命令列出存储桶中的所有文件夹:

aws s3 ls s3://yavdhesh-bucket

你会发现子文件夹没有创建。

只有一种方法可以创建子文件夹,即 creating/copying 在不存在的子文件夹或子目录(相对于存储桶)中创建一个文件

例如,

aws s3 cp demo.txt s3://yavdhesh-bucket/inside-folder/

现在,如果您列出子文件夹中的文件,它应该可以工作。

 aws s3 ls s3://yavdhesh-bucket/inside-folder/

它应该列出该子文件夹中存在的所有文件。

希望对您有所帮助。

我最终得到了一个小 python 脚本。它应该是 运行 手动,但它会自动同步。它适用于不想创建 Lambda 支持的自定义资源的懒人。

import subprocess
import json

STACK_NAME = ...
S3_RESOURCE = <name of your s3 resource, as in CloudFormation template file>
LOCAL_DIR = <path of your local dir>

res = subprocess.run(
    ['aws', 'cloudformation', 'describe-stack-resource', '--stack-name', STACK_NAME, '--logical-resource-id', S3_RESOURCE],
    capture_output=True,
)
out = res.stdout.decode('utf-8')
resource_details = json.loads(out)
resource_id = resource_details['StackResourceDetail']['PhysicalResourceId']

res = subprocess.run(
    ['aws', 's3', 'sync', LOCAL_DIR, f's3://{resource_id}/', '--acl', 'public-read']
)