模块中 Terraform 提供程序/变量共享

37

有没有一种方式可以将项目中定义的所有模块的提供者进行抽象化。

例如,我有这个项目

├── modules
│   ├── RDS
│   └── VPC
└── stacks
    ├── production
    │   └── main.tf
    └── staging
        └── main.tf

它能够正常工作......问题在于模块的定义。

├── RDS
│   ├── README.md
│   ├── main.tf
│   ├── providers.tf
│   └── variables.tf
└── VPC
    ├── README.md
    ├── main.tf
    ├── providers.tf
    └── variables.tf

这两个模块中的提供者完全相同

# providers.tf
provider "aws" {
  region = "${var.region}"
  version = "~> 1.26"
}

每个模块中的变量都不同,但它们都有region变量。

Translated:

Each module has different variables, but they all have the region variable.

# variables.tf
variable "region" {
  default     = "eu-central-1"
  description = "AWS region."
}
# other module dependent variables...

有没有一种方法可以在模块级别上定义这些信息位,以便最终我得到类似于以下内容的东西

├── modules
│   ├── providers.tf  <<< include the *shared* provider definition block
│   ├── variables.tf  <<< include the *shared* region vaiable definition block
│   ├── RDS
│   │   ├── README.md
│   │   ├── main.tf
│   │   └── variables.tf
│   └── VPC
│       ├── README.md
│       ├── main.tf
│       └── variables.tf

最后一件事,模块定义大多数时候都有一个资源属性(从terraform注册中心拉取一个模块……因此我不知道是否可行同时继承来自注册中心的源和基础模块)


1
你应该使用工作区/分支来代替将环境放置在目录中。这将解决你的问题并遵循最佳实践。https://www.terraform.io/docs/enterprise/workspaces/repo-structure.html - Matt Schuchard
1
我选择了最简单的解决方案,将providers.tf文件建立符号链接... 我计划很快测试terragrunt并看看效果如何... 目前符号链接运行良好... 至于工作区分支模型,它有点复杂(而且团队不会从中受益,因为我们都是terraform的新手),当我们尝试修复某些需要在许多分支中应用的东西时,我们只会陷入分支中。 - a14m
@MattSchuchard 那个terraform链接提供了三个选项,其中一个是为每个环境使用一个分支。对于基于git的典型存储库,以这种方式使用分支与最基本的git指导相违背。 - GaTechThomas
将提供程序添加到模块中似乎不是一个好主意。在父脚本中包含提供程序,并声明模块块,然后“继承”您已经设置的提供程序。此外,它们已经正式支持共享。看起来他们只是在争论默认的-var-file位置。通过使用-var-file并将其指向常见变量,保持DRY。添加此-var-file只是一个小不便。 - Rick O'Shea
4个回答

18

目前无法实现这个功能。 之前在以下github议题中曾经讨论过同样的话题:

TL;DR
模块之间共享变量违反了terraform核心的明确性原则。

解决方法
一个解决方法是将*shared*文件放置在父目录中,并使用符号链接将它们添加到模块中。


1
这是有问题的 - 我尝试了这个但得到了一个额外的资源。简而言之,我将sgA.tf从moduleA链接到moduleB中,然后创建了sgA'... 这不是我期望的结果(我只想在moduleB中使用sgA)。 - Antonio Gomez Alvarado
13
Terraform开发者忽视了软件开发中的一个核心概念:DRY(Don't Repeat Yourself)。很遗憾。 - jcollum
只需使用-var-file=xxx.vars即可保持DRY。您还可以使用带有TF_VARS-前缀的环境变量,尽管这种方法很笨拙且容易出错。讨论似乎围绕着默认的-var-file,因此维护者已经在促进变量共享方面做出了努力。 - Rick O'Shea

9
您可以通过传递一个提供者别名来将提供者参数从模块中抽象出来。这样,您就可以创建一个不需要引用Region之类细节的模块,然后在调用时传递这些细节。
对于您的用例,您可以在堆栈文件夹中定义别名提供者(最好在文件中定义并为每个堆栈文件夹创建符号链接)。
# stacks/{staging,production}/providers.tf
provider "aws" {
  alias  = "us-east-1"
  region = "us-east-1"
}

provider "aws" {
  alias   = "us-east-2"
  region  = "us-east-2"
}

当您调用模块时,请传递您想要使用的提供程序别名(这假定模块仅使用特定提供程序类型中的1个):

# stacks/{staging,production}/main.tf
module "VPC-us-east-1" {
  source = "../../modules/VPC"

  providers = {
    aws      = "aws.us-east-1"
  }
}

module "VPC-us-east-2" {
  source = "../../modules/VPC"

  providers = {
    aws      = "aws.us-east-2"
  }
}

模块内的提供者能使用插值吗?即providers = { aws = "${var.my_region}" } - tavor999

8
如果你了解terragrunt,这将不是问题。

Terragrunt是Terraform的薄包装器,提供了额外的工具来处理多个Terraform模块。

它专为你目前遇到的问题而设计。
account
 └ _global
 └ region
    └ _global
    └ environment
       └ resource

快速入门

查看 terragrunt-infrastructure-modules-exampleterragrunt-infrastructure-live-example 仓库,获取演示这些功能的完整示例代码。

您可以使用 prod/terraform.tfvarsprod/account.tfvars 来设置全局变量,或将 tfvars 文件放在 _global 文件夹下。


0

目前,Hashicorp配置语言没有全局变量功能。

variables用于定义模块接口,或者像环境变量(内联ENV)一样使用。因此,将变量用作全局共享不是最佳方法。因为每次定义一些模块时,您总是需要考虑在上层模块中使用了哪些变量。你可能会以这种方式结束:first_module_s3_bucket_namesecond_module_s3_bucket_name

但是,locals更有意义地与子模块共享。因为locals应该帮助避免重复相同的值。

如果您感兴趣,Hashicorp正在进行新的功能请求讨论。

https://github.com/hashicorp/terraform/issues/25431


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