问题描述
在创建 Terraform 模板的过程中,有一个部分涉及创建 VPC。用户可以选择指定参数 create-vpc
。这个参数将传递给模块 terraform-aws-vpc,该模块将基于此参数创建 VPC,如下所示:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
create_vpc = var.create-vpc
# ... 省略其他配置
}
现在,我正试图使用一个 data
源来与我的模板的其余部分共享 VPC,如下所示:
data "aws_vpc" "myvpc" {
id = "${module.vpc.vpc_id}"
}
如果 VPC 尚未创建,我该如何更新上述代码,以使用变量 vpc-id
替代模块的输出?理想情况下,我想实现类似如下的操作:
data "aws_vpc" "myvpc" {
count = var.vpc-id != "" ? 1 : 0
id = "${module.vpc.vpc_id}"
}
data "aws_vpc" "myvpc" {
count = var.vpc-id != "" ? 0 : 1
id = var.vpc-id
}
但由于资源名称需要唯一,上述方法无法实现。
解决方案
请注意以下操作可能会因版本差异而有所不同,修改前请做好备份。
方案1:使用局部变量控制 VPC 创建
首先,我们需要声明一个用于自己模块的 vpc_id
变量,其余部分将使用它来做出决策:
variable "vpc_id" {
type = string
default = null # 可选,没有默认值
}
然后,我们可以使用这个变量来决定是否创建 VPC。我们可以使用局部变量来达到这个目的:
locals {
create_vpc = var.vpc_id != null ? true : false
}
现在,我们可以在模块和 data
资源中使用这个值,以便我们将要么创建新的 VPC,要么检索现有 VPC 的数据:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
create_vpc = local.create_vpc
# ... 省略其他配置
}
data "aws_vpc" "selected" {
count = local.create_vpc ? 0 : 1
id = var.vpc_id
}
接下来,我们可以创建另一个局部变量,根据需要从这两个位置派生其他与 VPC 相关的值,例如 id
和 cidr_block
:
locals {
vpc = local.create_vpc ? {
id = module.vpc.vpc_id
cidr_block = module.vpc.vpc_cidr_block
} : {
id = data.aws_vpc.selected.id
cidr_block = data.aws_vpc.selected.cidr_block
}
}
现在,无论这个模块是创建自己的 VPC 还是使用已有的 VPC,你都可以在配置的其他地方使用 local.vpc.id
和 local.vpc.cidr_block
来获取适当的值。
方案2:使用模块组合(Module Composition)
使用模块组合的方法可能适合你,特别是当你构建一个旨在由另一个 Terraform 模块调用的共享模块时。
另一种方法是,你的模块永远不创建自己的 VPC,始终期望有一个 VPC 被提供给它。这是“依赖反转”原则的一个例子。在这种情况下,你的模块专注于解决新问题,而与创建 VPC 的问题解耦。调用模块可以根据适用情况决定如何获取 VPC 信息。
在你的模块中,你可以声明一个名为 vpc
的输入变量,其结构与我们在上一个方法中创建的 local.vpc
相同:
variable "vpc" {
type = object({
id = string
cidr_block = string
})
# 这次 VPC 是必需的。调用方决定如何获得它。
}
然后,你的模块中的表达式将以 var.vpc.id
而不是前一种方法中的 local.vpc.id
获取 VPC id。
调用模块现在可以在几种不同的方式中选择为该变量提供值:
使用 terraform-aws-modules/vpc/aws 模块:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
# ... 其他配置
}
module "your_module" {
source = "./modules/your-module"
vpc = {
id = module.vpc.vpc_id
cidr_block = module.vpc.vpc_cidr_block
}
}
直接使用 aws_vpc 资源类型:
resource "aws_vpc" "example" {
# ... 其他配置
}
module "your_module" {
source = "./modules/your-module"
vpc = aws_vpc.example
}
使用 aws_vpc 数据源查找现有的 VPC:
data "aws_vpc" "example" {
tags = {
Environment = "STAGE"
}
}
module "your_module" {
source = "./modules/your-module"
vpc = data.aws_vpc.example
}
换句话说,这使得你的新模块专注于解决它尝试解决的任何新问题,而与创建 VPC 的问题解耦。调用模块可以根据适当情况决定如何获得 VPC 信息