首页 > 解决方案 > 将主体附加到存储桶策略文档而不是覆盖它

问题描述

我正在开发一个包含多个环境的 SPA:dev、preprod、prod

每个 env 都有一个对应的 CloudFront 分配和存储桶网站。

我们还有一个带有用户手册的静态网站,在行为 /documentation/* 上提供

此静态网站存储在单独的存储桶中

所有环境共享相同的文档,因此所有环境只有一个存储桶。

该项目是一个公司门户,因此不应公开访问用户文档。

为了实现这一点,我们使用了 OAI,因此只能通过CloudFront 访问存储桶(lambda@edge 确保用户拥有有效的令牌,否则将他重定向,因此文档是私有的)。

当我使用 dev 部署时一切都很好

terraform workspace select dev
terraform apply -var-file=dev.tfvars

但是当我尝试在 preprod 上部署时

terraform workspace select preprod
terraform apply -var-file=preprod.tfvars

Terraform 以这种方式更改 OAI ID

  # module.s3.aws_s3_bucket_policy.documentation_policy will be updated in-place
  ~ resource "aws_s3_bucket_policy" "documentation_policy" {
        bucket = "my-bucket"
      ~ policy = jsonencode(
          ~ {
              ~ Statement = [
                  ~ {
                        Action    = "s3:GetObject"
                        Effect    = "Allow"
                      ~ Principal = {
                          ~ AWS = "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3U64NEVQ9IQHH" -> "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3ORU58OAALJAP"
                        }
                        Resource  = "arn:aws:s3:::my-bucket/*"
                        Sid       = ""
                    },
                ]
                Version   = "2012-10-17"
            }
        )
    }

而我希望校长以这种方式添加:

  # module.s3.aws_s3_bucket_policy.documentation_policy will be updated in-place
  ~ resource "aws_s3_bucket_policy" "documentation_policy" {
        bucket = "my-bucket"
      ~ policy = jsonencode(
          ~ {
              Statement = [
                  {
                        Action    = "s3:GetObject"
                        Effect    = "Allow"
                        Principal = {
                          AWS = "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3U64NEVQ9IQHH"
                        }
                        Resource  = "arn:aws:s3:::my-bucket/*"
                        Sid       = ""
                    },
                  + {
                  +     Action    = "s3:GetObject"
                  +     Effect    = "Allow"
                  +     Principal = {
                  +       AWS = "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3ORU58OAALJAP"
                  +     }
                  +     Resource  = "arn:aws:s3:::my-bucket/*"
                  +     Sid       = ""
                  + },
                ]
                Version   = "2012-10-17"
            }
        )
    }

有没有办法使用 terraform 0.13.5 来实现这一点

有关信息,这是我documentation-bucket.tf在创建后在每个工作区中导入的

resource "aws_s3_bucket" "documentation" {
  bucket = var.documentation_bucket

  tags = {
    BillingProject = var.billing_project
    Environment    = var.env
    Terraform      = "Yes"
  }

  logging {
    target_bucket = var.website_logs_bucket
    target_prefix = "s3-access-logs/${var.documentation_bucket}/"
  }

  lifecycle {
    prevent_destroy = true
  }
}

data "aws_iam_policy_document" "documentation" {
  statement {
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.documentation.arn}/*"]

    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn]
    }
  }
}

resource "aws_s3_bucket_policy" "documentation_policy" {
  bucket = aws_s3_bucket.documentation.id
  policy = data.aws_iam_policy_document.documentation.json
}

最好的祝福

标签: amazon-web-servicesterraform

解决方案


假设:

根据您所说的,您似乎在不同的状态文件中管理相同的资源(基于“[...] 我在创建后在每个工作区中导入的假设”)

你这样做基本上造成了脑裂的情况。

假设二:您正在部署一个 S3 存储桶,并且多个 CloudFront 分配访问这个存储桶都在同一个 AWS 账户中。

回答:

虽然这样做完全没问题,但这不是它应该设置的方式。单个资源应仅由单个 terraform 状态(工作区)管理,否则您将看到这种预期但不希望出现的不稳定状态行为。

我建议在单个工作区配置中管理 S3 存储桶,甚至创建一个名为“共享”的新工作区。

在此工作区中,您可以使用terraform_remote_state 数据源导入其他工作区的状态并构建一个策略,包括从其他状态中提取的所有 OAI。当然,您可以在不创建新的共享工作区的情况下这样做。

我希望这会有所帮助,虽然它可能不是预期的解决方案 - 也许我的假设是错误的。

最后的话:

在环境之间共享资源不被认为是好的做法,因为当您停用环境时数据很可能会保留,并且管理访问可能会变得复杂且不安全。

更好地保持环境版本尽可能接近,例如12 因素应用程序的 Dev/Prod Parity,但尽量不要共享资源。如果您觉得需要共享资源,请花一些时间,再次挑战您的架构。


推荐阅读