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

Posted

技术标签:

【中文标题】模块中的 Terraform 提供程序/变量共享【英文标题】:Terraform provider/variable sharing in modules 【发布时间】:2018-12-15 06:25:50 【问题描述】:

有没有办法为项目中定义的所有模块抽象提供者。

例如,我有这个项目

├── 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变量。

# 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 注册表中提取一个模块......因此我不知道从注册表和基本模块继承源是否可行)

【问题讨论】:

您应该使用工作区/分支,而不是将环境放在目录中。这将解决您的问题并遵循最佳实践。 terraform.io/docs/enterprise/workspaces/repo-structure.html 我使用了最简单的解决方案来对 providers.tf 文件进行符号链接...我计划很快测试 terragrunt 并看看它是如何进行的...现在符号链接工作正常...至于工作区分支模型有点复杂(团队不会从中受益,因为我们都是 terraform 的新手)并且在尝试修复某些东西时只会将自己纠缠在分支中(需要在许多分支中应用) @MattSchuchard terraform 链接提供了三个选项,一个是每个环境使用一个分支。对于典型的基于 git 的存储库,以这种方式使用分支违背了最基本的 git 指导。 向模块添加提供者闻起来是个坏主意。在您的父脚本中包含提供程序并声明模块块,然后将“继承”您已设置的提供程序。此外,他们已经正式支持共享。似乎他们只是在争论默认的 -var-file 位置。使用 -var-file 保持 DRY 并将其指向公共变量。添加这个 -var-file 有点不方便。 【参考方案1】:

目前还无法实现。 之前在github上也有过关于同一主题的讨论,分别在以下几期:

https://github.com/hashicorp/terraform/issues/5480 https://github.com/hashicorp/terraform/issues/4585 https://github.com/hashicorp/terraform/issues/2714 https://github.com/hashicorp/terraform/issues/1742 https://github.com/hashicorp/terraform/issues/1478

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

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

【讨论】:

这是有问题的 - 我尝试了这个但获得了额外的资源。 TLDR 我将 sgA.tf 从 moduleA 符号链接到 moduleB 并创建了 sgA'...不是我所期望的(我只是想在 moduleB 中使用 sgA) 因此 Terraform 开发人员错过了软件开发中的一个核心概念:DRY。跛脚。 只需使用 -var-file=xxx.vars 即可保持 DRY。您也可以使用带有 TF_VARS- 前缀的环境变量,尽管这很笨拙且容易出错。讨论似乎是围绕一个默认的 -var-file 进行的,因此维护人员已经在促进变量的共享。【参考方案2】:

您可以通过传入要使用的提供者别名从模块中抽象出提供者参数。这使您可以在不参考 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"

然后,当您调用模块时,传入您要使用的提供程序别名(假设模块仅使用任何特定提供程序类型中的一种):

# 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"
  

【讨论】:

模块内的提供者可以使用插值吗?即 ``` 提供者 = aws = "$var.my_region" ```【参考方案3】:

如果你知道terragrunt,这完全没有问题。

Terragrunt 是 Terraform 的瘦包装器,它提供了用于处理多个 Terraform 模块的额外工具。

它专为您刚刚遇到的问题而设计。

account
 └ _global
 └ region
    └ _global
    └ environment
       └ resource

快速入门

查看 terragrunt-infrastructure-modules-example 和 terragrunt-infrastructure-live-example 存储库以获取演示这些功能的完整示例代码

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

【讨论】:

【参考方案4】:

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

variables用于定义模块的模块接口或充当类似ENV变量(内联ENV)。因此,这不是将变量用作全局共享的最佳方式。因为每次定义一些模块时,总是需要考虑在上层模块上使用了哪些变量。也许你最终会喜欢:first_module_s3_bucket_namesecond_module_s3_bucket_name

但是locals 更适合共享给子模块。因为,当地人认为有助于避免重复相同的值。

如果您有兴趣,有关于 Hashicorp 功能请求的新讨论。

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

【讨论】:

以上是关于模块中的 Terraform 提供程序/变量共享的主要内容,如果未能解决你的问题,请参考以下文章

在 terraform 模块中显式使用提供程序

使用 Terraform 模块创建资源,其中一些资源由模块共享

Terraform:如何安装多个版本的提供程序插件? [复制]

Terraform azure 不兼容的提供程序版本

Terraform 中的可选变量在值为 null 时不会被忽略

Terraform:传递 AWS 系统管理器参数存储变量时,Terraform AWS 提供商凭证无效