1.8.1.1. 有条件创建
Terraform被设计成声明式而非命令式,例如没有常见的if
条件语句,后来才加上了count
和for_each
实现的循环语句(但循环的次数必须是在plan阶段就能够确认的,无法根据其他resource的输出动态决定)
有时候我们需要根据某种条件来判断是否创建一个资源。虽然我们无法使用if来完成条件判断,但我们还有count
和for_each
可以帮助我们完成这个目标。
我们以UCloud为例,假如我们正在编写一个旨在被复用的模块,模块的逻辑要创建一台虚拟机,我们的代码可以是这样的:
data ucloud_vpcs "default" {
name_regex = "^Default"
}
data "ucloud_images" "centos" {
name_regex = "^CentOS 7"
}
resource "ucloud_instance" "web" {
availability_zone = "cn-bj2-02"
image_id = data.ucloud_images.centos.images[0].id
instance_type = "n-basic-2"
}
output "uhost_id" {
value = ucloud_instance.web.id
}
非常简单。但是如果我们想进一步,让模块的调用者决定创建的主机是否要搭配一个弹性公网IP该怎么办?
我们可以在上面的代码后面接上这样的代码:
variable "allocate_public_ip" {
description = "Decide whether to allocate a public ip and bind it to the host"
type = bool
default = false
}
resource "ucloud_eip" "public_ip" {
count = var.allocate_public_ip ? 1 : 0
name = "public_ip_for_${ucloud_instance.web.name}"
internet_type = "bgp"
}
resource "ucloud_eip_association" "public_ip_binding" {
count = var.allocate_public_ip ? 1 : 0
eip_id = ucloud_eip.public_ip[0].id
resource_id = ucloud_instance.web.id
}
我们首先创建了名为allocate_public_ip
的输入变量,然后在编写弹性IP相关资源代码的时候都声明了count
参数,值使用了条件表达式,根据allocate_public_ip
这个输入变量的值决定是1
还是0
.这实际上实现了按条件创建资源。
需要注意的是,由于我们使用了count
,所以现在弹性IP相关的资源实际上是多实例资源类型的。我们在ucloud_eip_association.public_ip_binding
中引用ucloud_eip.public
时,还是要加上访问下标。由于ucloud_eip_association.public_ip_binding
与ucloud_eip.public
实际上是同生同死,所以在这里他们之间的引用还比较简单;如果是其他没有声明count
的资源引用它们的话,还要针对allocate_public_ip
为false
时ucloud_eip.public
实际为空做相应处理,比如在output
中:
output "public_ip" {
value = join("", ucloud_eip.public_ip[*].public_ip)
}
使用join
函数就可以在即使没有创建弹性IP时也能返回空字符串。或者我们也可以用条件表达式:
output "public_ip" {
value = length(ucloud_eip.public_ip[*].public_ip) > 0 ? ucloud_eip.public_ip[0].public_ip : ""
}