将多个文件上传到 Terraform 中的多个 S3 存储桶
Posted
技术标签:
【中文标题】将多个文件上传到 Terraform 中的多个 S3 存储桶【英文标题】:Upload multiple files to multiple S3 buckets in Terraform 【发布时间】:2021-12-06 17:25:27 【问题描述】:我对 terraform 很陌生。我的要求是将对象上传到现有的 s3 存储桶。我想将一个或多个对象从我的源上传到一个或多个仅使用一种资源的存储桶。使用count
和count.index
我可以创建不同数量的资源。但是,这样做会阻止我使用fileset
,这有助于递归上传文件夹中的所有内容。
基本代码如下所示。这是用于将多个文件上传到单个存储桶,但我想修改以将多个文件上传到不同的存储桶。;
variable "source_file_path"
type = list(string)
description = "Path from where objects are to be uploaded"
variable "bucket_name"
type = list(string)
description = "Name or ARN of the bucket to put the file in"
variable "data_folder"
type = list(string)
description = "Object path inside the bucket"
resource "aws_s3_bucket_object" "upload_object"
for_each = fileset(var.source_file_path, "*")
bucket = var.bucket_name
key = "$var.data_folder$each.value"
source = "$var.source_file_path$each.value"
我创建了一个具有以下值的 vars.tfvars 文件;
source_file_path = ["source1","source2"]
bucket_name = ["bucket1","bucket2"]
data_folder = ["path1","path2"]
所以,我需要的是 terraform,以便能够通过在存储桶内创建 path1 将所有文件从 source1 上传到 bucket1 s3 存储桶。对于 source2、bucket2 和 path2 也是如此。
这是可以在 terraform 中完成的吗?
【问题讨论】:
查看setproduct 以构建数据结构,您可以为此传递给for_each
。
此外,如果源和存储桶之间存在 1:1 的关系,则使用单独的列表没有多大意义。使用map
。
【参考方案1】:
从您的问题描述看来,描述您想要创建的内容的更直观的数据结构将是对象映射,其中键是存储桶名称,值描述该存储桶的设置:
variable "buckets"
type = map(object(
source_file_path = string
key_prefix = string
))
在您的 .tfvars
文件中定义存储桶时,现在将显示为具有复杂类型的单个定义:
buckets =
bucket1 =
source_file_path = "source1"
key_prefix = "path1"
bucket2 =
source_file_path = "source2"
key_prefix = "path2"
这个数据结构每个bucket都有一个元素,所以适合直接用for_each
作为描述bucket的资源:
resource "aws_s3_bucket" "example"
for_each = each.buckets
bucket = each.key
# ...
有一个预先存在的官方模块hashicorp/dir/template
,它已经封装了在目录前缀下查找文件的工作,根据文件名后缀为每个文件分配一个Content-Type,并可选地呈现模板。 (如果不需要,可以忽略模板功能,让目录只包含静态文件。)
每个存储桶都需要该模块的一个实例,因为每个存储桶都有自己的目录和一组文件,因此我们可以使用for_each
chaining 告诉 Terraform 该模块的每个实例都与一个相关桶:
module "bucket_files"
for_each = aws_s3_bucket.example
base_dir = var.buckets[each.key].source_file_path
模块文档显示how to map the result of the module to S3 bucket objects,但该示例仅适用于模块的单个实例。在您的情况下,我们需要一个额外的步骤来将其转换为跨所有存储桶的单个文件集合,which we can do using flatten
:
locals
bucket_files_flat = flatten([
for bucket_name, files_module in module.bucket_files : [
for file_key, file in files_module.files :
bucket_name = bucket_name
local_key = file_key
remote_key = "$var.buckets[each.key].key_prefix$file_key"
source_path = file.source_path
content = file.content
content_type = file.content_type
etag = file.digests.md5
]
])
resource "aws_s3_bucket_object" "example"
for_each =
for bf in local.bucket_files_flat :
"s3://$bf.bucket_name/$bf.remote_key" => bf
# Now the rest of this is basically the same as
# the hashicorp/dir/template S3 example, but using
# the local.bucket_files_flat structure instead
# of the module result directly.
bucket = each.value.bucket_name
key = each.value.remote_key
content_type = each.value.content_type
# The template_files module guarantees that only one of these two attributes
# will be set for each file, depending on whether it is an in-memory template
# rendering result or a static file on disk.
source = each.value.source_path
content = each.value.content
# Unless the bucket has encryption enabled, the ETag of each object is an
# MD5 hash of that object.
etag = each.value.etag
Terraform 需要为aws_s3_bucket_object.example
的每个实例提供一个唯一的跟踪密钥,因此我只是随意地决定在这里使用s3://
URI 约定,因为我希望习惯于使用 S3 的人会熟悉它。这意味着资源块将声明具有如下地址的实例:
aws_s3_bucket_object.example["s3://bucket1/path1example.txt"]
aws_s3_bucket_object.example["s3://bucket2/path2other_example.txt"]
由于这些对象由它们在 S3 中的最终位置唯一标识,因此 Terraform 会将对文件的更改理解为就地更新,但对位置的任何更改都视为同时删除现有对象并添加新对象。
(我复制了这样一个事实,即您的示例只是将路径前缀与文件名连接起来而没有任何中间分隔符,这就是为什么它在上面显示为 path1example.txt
而不是 path1/example.txt
。如果你想要斜杠,你可以将其添加到在local.bucket_files_flat
中定义remote_key
的表达式中。)
【讨论】:
感谢您的帮助。我正在尝试使用此解决方案。到目前为止,您建议的方法正在提供帮助,但是由于我是 terraform 的新手,所以我需要了解更多。一旦我能够完全实施,我将放弃更新。以上是关于将多个文件上传到 Terraform 中的多个 S3 存储桶的主要内容,如果未能解决你的问题,请参考以下文章
使用 Terraform 将文件上传到 AWS Secrets Manager
具有自动缩放组的多个模板文件和使用 Terraform 的启动配置
如何将 terraform 文件(main.tf)拆分为多个文件(无模块)?