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