Terraform - 每次应用时将文件上传到S3

24

我需要将一个文件夹上传到S3 Bucket。但第一次申请时,它只是上传了。但这里有两个问题:

  1. 上传的版本输出为空。我希望得到像1、2、3这样的版本号。
  2. 当再次运行terraform apply时,它会显示Apply complete! Resources: 0 added, 0 changed, 0 destroyed。我希望每次运行terraform apply都能上传并创建新版本。

我做错了什么?这是我的Terraform配置:

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my_bucket_name"

  versioning {
    enabled = true
  }
}

resource "aws_s3_bucket_object" "file_upload" {
  bucket = "my_bucket"
  key    = "my_bucket_key"
  source = "my_files.zip"
}

output "my_bucket_file_version" {
  value = "${aws_s3_bucket_object.file_upload.version_id}"
}

1
我不确定,但你可以尝试使用这个链接:https://www.terraform.io/docs/providers/archive/d/archive_file.html 来生成zip文件。只需检查一下terraform是否将此新zip文件识别为更改的文件,并使用新版本ID再次上传包含相同内容的新zip文件即可。 - Arpit Agrawal
行为与存储桶未启用版本控制一致。您能检查一下是否真的未启用吗? - Alex Harvey
3个回答

37

当Terraform检测到配置与远程对象属性之间的差异时,才会对远程对象进行更改。就你目前编写的配置而言,配置仅包括文件名,没有包含有关文件内容的任何信息,因此Terraform无法对文件的更改做出反应。

要进行后续更改,有几个选项:

  • 您可以为每个新版本使用不同的本地文件名。
  • 您可以为每个新版本使用不同的远程对象路径。
  • 您可以使用对象ETag让Terraform识别内容何时发生了更改,而不管本地文件名或对象路径如何。

在这种情况下,最后一种方法似乎最符合您的要求。为此,请添加etag参数,并将其设置为文件的MD5哈希值:

resource "aws_s3_bucket_object" "file_upload" {
  bucket = "my_bucket"
  key    = "my_bucket_key"
  source = "${path.module}/my_files.zip"
  etag   = "${filemd5("${path.module}/my_files.zip")}"
}
用这个额外参数,Terraform会检测磁盘上文件的MD5哈希值是否与存储在S3中的哈希值不同,并计划相应地更新该对象。
(我不确定version_id出了什么问题。只要启用了存储桶版本控制,它应该能正常工作。)

在撰写本文时(Terraform 4.52.0),有一个警告提示要将aws_s3_bucket_object替换为aws_s3_object。否则答案是相同的。https://registry.terraform.io/providers/hashicorp/aws/4.52.0/docs/resources/s3_object - Ari

11

现在首选的解决方案是使用source_hash属性。请注意,aws_s3_bucket_object已被aws_s3_object替换。

locals {
  object_source = "${path.module}/my_files.zip"
}

resource "aws_s3_object" "file_upload" {
  bucket      = "my_bucket"
  key         = "my_bucket_key"
  source      = local.object_source
  source_hash = filemd5(local.object_source)
}

请注意,当使用加密时,etag可能会出现问题。


-6

你不应该使用 Terraform 来做这件事。Terraform 应该用于编排和配置基础设施,而不是文件。话虽如此,Terraform 不知道你的文件发生了什么变化。除非你改变它们的名称,否则 Terraform 将不会更新状态。

另外,最好使用 local-exec 来完成这个任务。类似这样:

resource "aws_s3_bucket" "my-bucket" {
# ...

  provisioner "local-exec" {
     command = "aws s3 cp path_to_my_file ${aws_s3_bucket.my-bucket.id}"
  }
}

24
S3经常用于存储在基础架构定义中被引用的部署包,例如Lambda或Kinesis Analytics for Java中的部署包。这种使用S3完全符合“基础架构及其配置”,这就是为什么Terraform为其提供了资源,并且为什么您应该使用Terraform将某些文件上传到S3的原因。 - meustrus
我理解并同意@meustrus的评论,但现在已经弃用了aws_s3_bucket_object,转而使用aws_s3_object,后者仅用于读取文件。我猜TF团队正在与Stargazer对齐。 - Gomsy
他们可能对@Gomsy的S3有正确的看法。在过去的几年中,我遇到了一些问题,即在Terraform允许满足依赖关系之前,S3对象和相关资源已经完全更新。可能存在一些最终一致性问题,阻止S3对象满足Terraform的资源要求。 - meustrus
2
aws_s3_object 不是只读的。文档提供了上传文件的示例。 - sdgfsdh
1
正如官方文档所解释的那样,local-exec 应该作为最后的选择。因此 Martin 的答案更加合理。 - Pedram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接