如何使用 Terraform 仅根据环境加载某些文件?
How can I load certain files based only on environment with Terraform?
对于 运行 我的 terraform 脚本,我 terraform apply -var 'ENV=dev' -var-file="dev.tfvars"
在 dev.tfvars
中,我想加载其他具有仅用于开发环境的变量的文件。
我应该可以对 staging.tfvars
等做同样的事情
有什么想法吗?
据我所知,您有 2 个选择:
- 使用 TF 工作区,您可以在环境之间设置小的差异。如 docs 中所述,您将需要更改资源以使用插值序列
terraform.workspace
。我发现这更难维护。
- 创建一个 TF 包装器
包装器将接收环境和 TF 命令,将 link 环境相关的 .tf
文件并调用 terraform
加载 .tfvars
文件。例如,您可以有一个包含以下文件的 env/
目录:
env/
| dev.tf
| staging.tf
| prod.tf
并且您的包装器将 link,在项目根目录中,正确的文件,如下所示:
env.tf -> env/dev.tf
然后它会调用terraform -var-file=".tfvars" -var 'ENV='
通过这种方式,terraform 将读取 env.tf
文件内容并将其与其他 .tf
文件一起加载。
我不会采用这种方法,而是重新设计您的 Terraform 代码库的结构,以便 dev/staging/prod 环境等都在单独的目录中,并且仅在每个目录中的 terraform.tfvars
文件不同。
我认为最好的方法是自由使用模块和符号链接。我在 my answer 中讨论了这个关于 Terraform 多租户的相关问题。
示例布局可能如下所示:
$ tree -a
.
├── dev
│ ├── eu-west-1
│ │ ├── bar
│ │ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ │ └── terraform.tfvars
│ │ └── foo
│ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ ├── foo.tf -> ../../../stacks/foo.tf
│ │ └── terraform.tfvars
│ ├── global
│ │ └── baz
│ │ ├── baz.tf -> ../../../stacks/baz.tf
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── us-east-1
│ ├── bar
│ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ ├── terraform.tfvars
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── foo
│ ├── foo.tf -> ../../../stacks/foo.tf
│ ├── terraform.tfvars
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
├── modules
│ ├── bar
│ │ └── main.tf
│ └── foo
│ ├── main.tf
│ ├── outputs.tf
│ └── vars.tf
├── production
│ ├── eu-west-1
│ │ ├── bar
│ │ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ │ └── terraform.tfvars
│ │ └── foo
│ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ ├── foo.tf -> ../../../stacks/foo.tf
│ │ └── terraform.tfvars
│ ├── global
│ │ └── baz
│ │ ├── baz.tf -> ../../../stacks/baz.tf
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── us-east-1
│ ├── bar
│ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ ├── terraform.tfvars
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── foo
│ ├── foo.tf -> ../../../stacks/foo.tf
│ ├── terraform.tfvars
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
├── providers
│ └── aws
│ ├── eu-west-1.tf
│ └── us-east-1.tf
├── stacks
│ ├── bar.tf
│ ├── baz.tf
│ └── foo.tf
└── staging
├── eu-west-1
│ ├── bar
│ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ └── terraform.tfvars
│ └── foo
│ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ ├── foo.tf -> ../../../stacks/foo.tf
│ └── terraform.tfvars
├── global
│ └── baz
│ ├── baz.tf -> ../../../stacks/baz.tf
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
└── us-east-1
├── bar
│ ├── bar.tf -> ../../../stacks/bar.tf
│ ├── terraform.tfvars
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
└── foo
├── foo.tf -> ../../../stacks/foo.tf
├── terraform.tfvars
└── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
这使您能够在一个地方(stacks
目录)定义 Terraform 代码的确切配置,并且只允许通过每个目录的 terraform.tfvars
文件进行覆盖。在上面的布局中,我们有一个 foo
和一个 bar
模块,我们希望在 2 个不同的 AWS 区域中应用所有 dev/staging/production 以及一些应该在全球应用的不可配置的 Terraform 代码对于每个环境级别(想想 IAM 角色等)。
我们在模块文件夹下定义我们的模块(foo
可能是一个完整的模块,而 bar
可能是来自第三方位置的源模块,例如另一个 Git 仓库或 Terraform's module registry)。然后我们为 stacks
文件夹中的模块提供一个瘦包装器,它看起来像这样:
variable "name" {}
variable "count" {
default = 2
}
module "foo" {
source = "../../../foo"
name = "${var.name}"
count = "${var.count}"
}
然后在每个 ${environment}/${region}/foo/terraform.tfvars
中我们总是定义一个名称(因为它是必需的)并可选地定义一个计数(默认):
name = dev-foo
count = 1
或
name = production-foo
上面的布局也让您一目了然地看到您的所有环境和配置,并且可以简化您使用 Terraform 的任何包装脚本,只需使用目录路径即可设置状态配置。
对于 运行 我的 terraform 脚本,我 terraform apply -var 'ENV=dev' -var-file="dev.tfvars"
在 dev.tfvars
中,我想加载其他具有仅用于开发环境的变量的文件。
我应该可以对 staging.tfvars
等做同样的事情
有什么想法吗?
据我所知,您有 2 个选择:
- 使用 TF 工作区,您可以在环境之间设置小的差异。如 docs 中所述,您将需要更改资源以使用插值序列
terraform.workspace
。我发现这更难维护。 - 创建一个 TF 包装器
包装器将接收环境和 TF 命令,将 link 环境相关的 .tf
文件并调用 terraform
加载 .tfvars
文件。例如,您可以有一个包含以下文件的 env/
目录:
env/
| dev.tf
| staging.tf
| prod.tf
并且您的包装器将 link,在项目根目录中,正确的文件,如下所示:
env.tf -> env/dev.tf
然后它会调用terraform -var-file=".tfvars" -var 'ENV='
通过这种方式,terraform 将读取 env.tf
文件内容并将其与其他 .tf
文件一起加载。
我不会采用这种方法,而是重新设计您的 Terraform 代码库的结构,以便 dev/staging/prod 环境等都在单独的目录中,并且仅在每个目录中的 terraform.tfvars
文件不同。
我认为最好的方法是自由使用模块和符号链接。我在 my answer 中讨论了这个关于 Terraform 多租户的相关问题。
示例布局可能如下所示:
$ tree -a
.
├── dev
│ ├── eu-west-1
│ │ ├── bar
│ │ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ │ └── terraform.tfvars
│ │ └── foo
│ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ ├── foo.tf -> ../../../stacks/foo.tf
│ │ └── terraform.tfvars
│ ├── global
│ │ └── baz
│ │ ├── baz.tf -> ../../../stacks/baz.tf
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── us-east-1
│ ├── bar
│ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ ├── terraform.tfvars
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── foo
│ ├── foo.tf -> ../../../stacks/foo.tf
│ ├── terraform.tfvars
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
├── modules
│ ├── bar
│ │ └── main.tf
│ └── foo
│ ├── main.tf
│ ├── outputs.tf
│ └── vars.tf
├── production
│ ├── eu-west-1
│ │ ├── bar
│ │ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ │ └── terraform.tfvars
│ │ └── foo
│ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ ├── foo.tf -> ../../../stacks/foo.tf
│ │ └── terraform.tfvars
│ ├── global
│ │ └── baz
│ │ ├── baz.tf -> ../../../stacks/baz.tf
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── us-east-1
│ ├── bar
│ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ ├── terraform.tfvars
│ │ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
│ └── foo
│ ├── foo.tf -> ../../../stacks/foo.tf
│ ├── terraform.tfvars
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
├── providers
│ └── aws
│ ├── eu-west-1.tf
│ └── us-east-1.tf
├── stacks
│ ├── bar.tf
│ ├── baz.tf
│ └── foo.tf
└── staging
├── eu-west-1
│ ├── bar
│ │ ├── bar.tf -> ../../../stacks/bar.tf
│ │ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ │ └── terraform.tfvars
│ └── foo
│ ├── eu-west-1.tf -> ../../../providers/aws/eu-west-1.tf
│ ├── foo.tf -> ../../../stacks/foo.tf
│ └── terraform.tfvars
├── global
│ └── baz
│ ├── baz.tf -> ../../../stacks/baz.tf
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
└── us-east-1
├── bar
│ ├── bar.tf -> ../../../stacks/bar.tf
│ ├── terraform.tfvars
│ └── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
└── foo
├── foo.tf -> ../../../stacks/foo.tf
├── terraform.tfvars
└── us-east-1.tf -> ../../../providers/aws/us-east-1.tf
这使您能够在一个地方(stacks
目录)定义 Terraform 代码的确切配置,并且只允许通过每个目录的 terraform.tfvars
文件进行覆盖。在上面的布局中,我们有一个 foo
和一个 bar
模块,我们希望在 2 个不同的 AWS 区域中应用所有 dev/staging/production 以及一些应该在全球应用的不可配置的 Terraform 代码对于每个环境级别(想想 IAM 角色等)。
我们在模块文件夹下定义我们的模块(foo
可能是一个完整的模块,而 bar
可能是来自第三方位置的源模块,例如另一个 Git 仓库或 Terraform's module registry)。然后我们为 stacks
文件夹中的模块提供一个瘦包装器,它看起来像这样:
variable "name" {}
variable "count" {
default = 2
}
module "foo" {
source = "../../../foo"
name = "${var.name}"
count = "${var.count}"
}
然后在每个 ${environment}/${region}/foo/terraform.tfvars
中我们总是定义一个名称(因为它是必需的)并可选地定义一个计数(默认):
name = dev-foo
count = 1
或
name = production-foo
上面的布局也让您一目了然地看到您的所有环境和配置,并且可以简化您使用 Terraform 的任何包装脚本,只需使用目录路径即可设置状态配置。