1.9.8.1. 创建资源的条件依赖另一个资源的输出时怎么办
我们在有条件创建当中介绍了如何可以通过判断用户的输入参数来决定是否要创建某个资源,让我们来看一下这样一个 Module 的例子:
variable "vpc_id" {
type = string
default = null
}
resource "ucloud_vpc" "vpc" {
count = var.vpc_id == null ? 1 : 0
cidr_blocks = ["10.0.0.0/16"]
name = "vpc"
}
resource "ucloud_subnet" "subnet" {
cidr_block = "10.0.0.0/24"
vpc_id = var.vpc_id == null ? ucloud_vpc.vpc[0].id : var.vpc_id
}
我们想在 Module 中创建一个 ucloud_subnet
,用户可以输入一个 vpc_id
配置给它,也可以不输入,这时 Module 会创建一个 ucloud_vpc
来用。
假如我们使用这个模块,不传入 vpc_id
:
module vpc {
source = "./vpc"
}
这段代码生成的 Plan 内容如下:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.vpc.ucloud_subnet.subnet will be created
+ resource "ucloud_subnet" "subnet" {
+ cidr_block = "10.0.0.0/24"
+ create_time = (known after apply)
+ id = (known after apply)
+ name = (known after apply)
+ remark = (known after apply)
+ tag = "Default"
+ vpc_id = (known after apply)
}
# module.vpc.ucloud_vpc.vpc[0] will be created
+ resource "ucloud_vpc" "vpc" {
+ cidr_blocks = [
+ "10.0.0.0/16",
]
+ create_time = (known after apply)
+ id = (known after apply)
+ name = "vpc"
+ network_info = (known after apply)
+ remark = (known after apply)
+ tag = "Default"
+ update_time = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
完全符合预期。假如我们希望由模块的调用者来创建 Vpc 的话:
resource "ucloud_vpc" "vpc" {
cidr_blocks = ["10.0.0.0/16"]
name = "vpc"
}
module vpc {
source = "./vpc"
vpc_id = ucloud_vpc.vpc.id
}
我们执行 terraform plan
的话,会得到这样的结果:
╷
│ Error: Invalid count argument
│
│ on vpc/main.tf line 16, in resource "ucloud_vpc" "vpc":
│ 16: count = var.vpc_id == null ? 1 : 0
│
│ The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this,
│ use the -target argument to first apply only the resources that the count depends on.
╵
Terraform 试图向我们抱怨,我们在 count
参数的表达式里使用了一个必须在 apply
阶段才能知道的值,所以它无法在 plan
阶段就计算出 count
的值。它建议我们先用 terraform apply
命令搭配 -target
参数把 Vpc 先创建出来,消除后续计算 Plan 时尚不知晓的值来解决这个问题。
这当然是一种很麻烦的方法,所以我们在设计 Module 时就要考虑到这种问题。有一种很简单的方法可以解决这个问题:
variable "vpc" {
type = object(
{
id = string
}
)
default = null
}
resource "ucloud_vpc" "vpc" {
count = var.vpc == null ? 1 : 0
cidr_blocks = ["10.0.0.0/16"]
name = "vpc"
}
resource "ucloud_subnet" "subnet" {
cidr_block = "10.0.0.0/24"
vpc_id = var.vpc == null ? ucloud_vpc.vpc[0].id : var.vpc.id
}
我们把用来判断创建条件的输入参数类型改成 object
,调用 Module 的代码就变成了:
Terraform will perform the following actions:
# ucloud_vpc.vpc will be created
+ resource "ucloud_vpc" "vpc" {
+ cidr_blocks = [
+ "10.0.0.0/16",
]
+ create_time = (known after apply)
+ id = (known after apply)
+ name = "vpc"
+ network_info = (known after apply)
+ remark = (known after apply)
+ tag = "Default"
+ update_time = (known after apply)
}
# module.vpc.ucloud_subnet.subnet will be created
+ resource "ucloud_subnet" "subnet" {
+ cidr_block = "10.0.0.0/24"
+ create_time = (known after apply)
+ id = (known after apply)
+ name = (known after apply)
+ remark = (known after apply)
+ tag = "Default"
+ vpc_id = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
成功计算出 Plan。请注意虽然这个 Plan 仍然是创建两个资源,但 ucloud_vpc
资源并不是 Module 创建的。
这个方法的原理就是虽然 var.vpc.id
仍然是一个只有在 apply
阶段才能知道的值,但 var.vpc
本身是一个在 plan
阶段就可以知道的值,直接可以判读它是否为 null
,所以该方法可以绕过这个限制。