在 Terraform 中将多个 AWS 帐户作为环境处理的最佳方式是什么?

What is the best way to handle multiple AWS accounts as environments in Terraform?

我们希望将我们的每个 Terraform 环境都放在一个单独的 AWS 账户中,这样可以防止意外部署到生产环境中。如何最好地实现这一点?

我们假设一个帐户专用于生产,另一个帐户专用于预生产,并且可能其他沙盒环境也有唯一的帐户,可能基于每个管理员。另一个假设是您在每个特定于您的环境的 AWS 账户中都有一个 S3 存储桶。此外,我们希望您的 AWS 帐户凭证在 ~/.aws/credentials 中进行管理(或者可能具有 IAM 角色)。

Terraform 后端配置

有两种状态。对于主要状态,我们使用 Partial Configuration 的概念。我们不能通过模块或其他方式将变量传递到后端配置中,因为它是在确定之前读取的。

Terraform 配置设置

这意味着我们在声明后端时缺少一些细节,然后将它们作为参数提供给 terraform init。一旦初始化,它就会被设置,直到 .terraform 目录被删除。

terraform {
  backend "s3" {
    encrypt = true
    key     = "name/function/terraform.tfstate"
  }
}

工作流程注意事项

我们只需要改变我们的初始化方式。我们在 terraform init 上使用 -backend-config 参数。这提供了配置的缺失部分。我在我的 ~/.bash_profile 中通过 bash 别名提供所有缺失的部分,就像这样。

alias terrainit='terraform init \
-backend-config "bucket=s3-state-bucket-name" \ 
-backend-config "dynamodb_table=table-name" \
-backend-config "region=region-name"'

意外错误配置结果

如果适当的必需 -backend-config 参数被省略,初始化将提示您输入它们。如果提供不正确,可能会因权限原因导致失败。此外,远程状态必须配置为匹配,否则它也会失败。为了部署到生产环境,必须在识别适当的帐户环境时出现多次错误。

Terraform 远程状态

下一个问题是远程状态也需要改变,无法通过后端配置拉取配置;但是,可以通过变量设置远程状态。

模块设置

为了简化帐户切换,我们设置了一个非常简单的模块,它接受一个变量 aws-account 和 return 远程状态可以使用适当值的一堆输出。我们还可以包含 environment/account 特定的其他内容。该模块是一个简单的 main.tf,其映射变量具有键 aws-account 和特定于该帐户的值。然后我们有一堆输出,像这样简单地查找地图变量。

variable "aws-region" {
  description = "aws region for the environment"
  type = "map"
  default = {
    Production  = "us-west-2"
    PP      = "us-east-2"
  }
}

output "aws-region" {
  description = “The aws region for the account 
  value = "${lookup(var.aws-region, var.aws-account, "invalid AWS account specified")}"
}

Terraform 配置设置

首先,我们必须将 aws-account 传递给模块。这可能会在 main.tf.

的顶部附近
module "environment" {
  source    = "./aws-account"
  aws-account = "${var.aws-account}"
}

然后将变量声明添加到您的 variables.tf

variable "aws-account" {
  description = "The environment name used to identify appropriate AWS account resources used to configure remote states. Pre-Production should be identified by the string PP. Production should be identified by the string Production. Other values may be added for other accounts later."
}

现在我们已经从模块输出了账户特定变量,它们可以像这样用于远程状态声明。

data "terraform_remote_state" "vpc" {
  backend = "s3"
  config {
    key = "name/vpc/terraform.tfstate"
    region = "${module.environment.aws-region}"
    bucket = "${module.environment.s3-state-bucket-name}"
  }
}

工作流程考虑

如果像这样设置后工作流没有任何变化,每当 plan/apply 或类似的时候,将提示用户通过这样的提示提供 aws-account 变量的值执行。提示内容为variables.tf.

中变量的描述
$ terraform plan
var.aws-account
  The environment name used to identify appropriate AWS account
resources used to configure remote states. Pre-Production should be
identified by the string PP. Production should be identified by the
string Production. Other values may be added for other accounts later.

  Enter a value:

您可以像这样在命令行中提供变量来跳过提示

terraform plan -var="aws-account=PP"

意外错误配置结果

如果未指定 aws-account 变量,将请求它。如果提供了 aws-account 模块不知道的无效值,它将 return 错误,包括字符串“指定的无效 AWS 帐户”多次,因为这是查找的默认值。如果正确传递了 aws-account,但它与 terraform init 中标识的值不匹配,它将失败,因为正在使用的 aws 凭据将无法访问所标识的 S3 存储桶。

我们遇到了类似的问题,我们(部分)解决了在 Jenkins 或任何其他 CI 工具中创建管道的问题。

我们有 3 个不同的环境(开发、暂存和生产)。相同的代码,不同的 tfvars,不同的 aws 帐户。

当 terraform 代码合并到 master 时,可以应用于登台,只有当登台为绿色时,才能执行生产。 没有人在 prod 中手动运行 terraform,aws 凭据存储在 CI 工具中。

此设置可以解决您描述的事故,但也可以防止不同的用户应用不同的本地代码。