Terraform 1.3でoptional()によるobject attributes(variable)へのデフォルト値設定が楽になる!
tl;dr;
- Terraform 1.3 で
optional()modifier によるデフォルト値のセットがサポートされる予定 - 現在 Terraform 1.3 Alpha リリースで試す事が可能、フィードバック募集中
- 英語書くのしんどい場合は私に DM 投げてもらっても大丈夫です!
variable "with_optional_attribute" {
type = object({
a = string # a required attribute
b = optional(string) # an optional attribute
c = optional(number, 127) # an optional attribute with default value
})
}
これまでの方法 (おさらい)
これまで Terraform v0.14, v0.15 で、それぞれ optional() modifier と defaults() function が実験的機能 (experimental support) として追加されてきました。
optional() modifier (v0.14~, experimental)
v0.14 で追加された optional() modifier を使うと、object type として定義されている variable のうち、任意の attribute を optional としてマークすることができます。
variable "with_optional_attribute" {
type = object({
a = string # a required attribute
b = optional(string) # an optional attribute
})
}
この場合、b attribute は optional のため、指定がない場合は Terraform が自動的に null をセットします。これによって module 側で必要に応じて fallback 処理を行うことができますが、あまり理想的とは言えません。
variable の中では default を明示的に指定することもできます。
variable "with_optional_attribute" {
type = object({
a = string # a required attribute
b = optional(string) # an optional attribute
})
default = {
a = "required"
b = "optional"
}
}
しかし上記のコードで得られる挙動は、a attribute のみに値をセットして呼び出した場合、default で定義されている object そのものが override されるため、結局 type の定義に fallback し、b には optional ではなく null がセットされます。
反対に、b attribute のみに値をセットして呼び出した場合、default で定義されている object そのものが override されるため、今度は required として定義されている a attribute がセットされていない、というエラーになってしまいます。
$ terraform plan -var='with_optional_attribute={"a"="foo"}'
Changes to Outputs:
+ with_optional_attribute = {
+ a = "foo"
+ b = null
}
$ terraform plan -var='with_optional_attribute={"b"="foo"}'
╷
│ Error: Invalid value for input variable
│
│ The argument -var="with_optional_attribute=..." does not contain a valid value for variable "with_optional_attribute":
│ attribute "a" is required.
╵
╷
│ Error: Incorrect variable type
│
│ on main.tf line 5:
│ 5: variable "with_optional_attribute" {
│
│ The resolved value of variable "with_optional_attribute" is not appropriate: attribute "a" is required.
こと object type に関しては、残念ながらああり使い勝手はよくありません。
例えば、ネストされた他の要素を含む object type の variable の場合、特定の1要素だけ値をカスタマイズして、他はデフォルト値のままで呼び出したい、というニーズを上手く満たすことができません。
defaults() function (v0.15~, experimental)
そこで v0.15 では、1つの案として defaults() function が追加されました。
これを使うと、variable 中で optional() としてマークした任意の object attribute が null であった場合に、指定したデフォルト値をセットすることができます。
terraform {
experiments = [module_variable_optional_attrs]
}
variable "with_optional_attribute" {
type = object({
attr_1 = string
attr_2 = optional(bool) # an optional attribute
attr_3 = map(
object({
nested_attr_1 = number
nested_attr_2 = optional(string) # an optional attribute
})
)
})
}
locals {
with_optional_attribute = defaults(var.with_optional_attribute, {
# If "attr_2" isn't set then it will default to true
attr_2 = true
# If _any_ of the map elements omit "nested_attr_2"
# then this default will be used instead.
attr_3 = {
nested_attr_2 = "default value"
}
})
}
output "with_optional_attribute" {
value = local.with_optional_attribute
}
上記は例ですが、このように別途 locals を用意し、その中で defaults() function を使い、optional() でマークされている attribute に対して、null であった時のデフォルト値を与えています。
with_optional_attribute = {
attr_1 = "required value"
attr_3 = {
"foo" = {
nested_attr_1 = 100
nested_attr_2 = "custom value"
}
"bar" = {
nested_attr_1 = 200
}
}
}
そこに対して上記のような input variable を与えると、output は下記のようになります。
Changes to Outputs:
+ with_optional_attribute = {
+ attr_1 = "required value"
+ attr_2 = true
+ attr_3 = {
+ "bar" = {
+ nested_attr_1 = 200
+ nested_attr_2 = "default value"
}
+ "foo" = {
+ nested_attr_1 = 100
+ nested_attr_2 = "custom value"
}
}
}
このように、attr_2 や nested_attr_2 が正しく定義したデフォルト値をとっている事が分かります。
しかし、ご覧いただいた通り、定義そのものが冗長になってしまいますし、あまり直感的ではありません。力技感があって、積極的に自分のモジュールで使いたいかと言われると、うーん… となるかもしれません。
新機能: optional() でデフォルト値を一緒にセットできるようになる
この新機能を使うと、先程の defaults() による例は、下記のようにシンプルに書くことができます。
terraform {
experiments = [module_variable_optional_attrs]
}
variable "with_optional_attribute" {
type = object({
attr_1 = string
# an optional attribute with default value
attr_2 = optional(bool, true)
attr_3 = map(
object({
nested_attr_1 = number
# an optional attribute with default value
nested_attr_2 = optional(string, "default value")
})
)
})
}
output "with_optional_attribute" {
value = var.with_optional_attribute
}
これで先程の例と全く同じ output が得られるはずです。よさそう!
variable type が object のコレクションであるような、もう少し複雑な場合でも同様に対応できます。
この例では、attr_2 にネストされた object が含まれますが、そのネストされた object の attributes に対しても optional() 使ってデフォルト値を定義することができます。
terraform {
experiments = [module_variable_optional_attrs]
}
variable "with_optional_attribute" {
type = list(
object({
attr_1 = bool
attr_2 = optional(object({
nested_attr_1 = optional(number, 1)
nested_attr_2 = optional(string, "default value")
}), {})
})
)
}
output "with_optional_attribute" {
value = var.with_optional_attribute
}
with_optional_attribute = [
{
attr_1 = true
},
{
attr_1 = false
attr_2 = {
nested_attr_1 = 100
}
}
]
Changes to Outputs:
+ with_optional_attribute = [
+ {
+ attr_1 = true
+ attr_2 = {
+ nested_attr_1 = 1
+ nested_attr_2 = "default value"
}
},
+ {
+ attr_1 = false
+ attr_2 = {
+ nested_attr_1 = 100
+ nested_attr_2 = "default value"
}
},
]
Discussion