これは「「はじめに」の Advent Calendar 2021」1日目の記事です。
TL;DR
- 基本的に変数はLocal Valueで作る
- Variableはモジュールの引数として定義し、モジュールを再利用する場合に使う
- 1ディレクトリで複数のStateやワークスペースを切り替える際は、Variableで環境名を設定、Local Valueを通してresourceから利用する。
Variableとは
公式ドキュメント: https://www.terraform.io/docs/language/values/variables.html
× Variable
○ Input Variable
- Local Valueがまだなかった時、内部の変数と外から注入される引数としての両方が責務だった。
が、内部の変数はLocal Valueを使うようになった。 - Input Variableはその名の通り、入力用の変数として内部で利用しないことを公式でも推奨している。
- Input Variableは実行時にファイル・環境変数・オプション・対話的入力など、様々な注入方法を利用して- 外部から値を設定・上書きできる。
- Terraform CloudだとVariableごとにWorkspaceに値を設定していく必要がある。
Input Variableの問題点
- 他のVariableを参照して新しいVariableを定義できない。
- 関数を利用できない。
- モジュールではなくワークスペースの作成に利用する場合、渡された値により作成されるリソースが変わるため、思った通りの値にならない。
Local Valueとは
公式ドキュメント: https://www.terraform.io/docs/language/values/locals.html
- スコープがモジュール内に閉じた変数。
- 他のLocal Valueを参照してあたらしいLocal Valueを定義できる。
- 関数や式展開を利用できる。
- 外部から値を設定・上書きできない。
使い方のおさらい
- 変数定義
locals {
name = "Foo"
resource_count = 1
# 配列
cidr_list = ["10.1.0.0/16", "10.2.0.0/16"]
# Map
}
# locals内に定義した変数名が衝突しなければ何度でも定義可能
locals {
key_values = {
key = "Bar"
value = "Baz"
}
}
- 関数・他のLocal Valueの利用
locals {
name = "foo"
another_name = upper(local.name)
count = 1
}
locals {
# Localのスコープはモジュール全体なので、他のLocalブロックから参照可能
another_count = count + 1
}
- リソースからは
local.${名前}
で利用
locals {
name_prefix = "foo-"
}
resource "aws_instance" "foo" {
...
tags {
Name = "${local.name_prefix}-web"
}
}
Local ValueとInput Variableの強みを両方活かす方法
- Local Valueは外部から値を設定できない。
- とはいえ、環境ごとに変数セットをごっそり切り替えたい場合もある。
- Input Variableで多くの変数を切り替えると、変数間の依存や関数が使えないため、重複記述が多くなる。
- Input VariableとLocal Valueを組み合わせて利用することでお互いのメリットを活かしデメリットを解消する。
具体的には・・・
ワークスペースの場合以下のように、環境名のみをVariableで渡し、環境ごとの変数セットをLocal Valueを定義して利用する。
variable "env" {}
locals {
_env = {
devlopment = {
name_prefix = "dev"
service = "web"
cidr = "10.1.0.0/16"
ec2_web_name = "${local.name_prefix}-${local.service}"
domain = "dev.example.com"
},
production = {
name_prefix = "prod"
service = "web"
cidr = "10.2.0.0/16"
ec2_web_name = "${local.name_prefix}-${local.service}"
domain = "prod.example.com"
},
}
env = local._env[var.env]
}
resource "aws_ec2_instance" "web" {
...
tags {
Name = "local.env.ec2_web_name
}
}
- resourceからは、
local.env.*
を利用することでenv
ごとの値を参照できる。 - envに不正な値が設定され予期せぬリソースが作成されるのを抑制できる。上記なら
env=stage
を入力されても参照先がないのでエラーになる。 - 1ソースコードで複数のワークスペースや複数Stateを作る場合には、上記の方法は重宝する。
- モジュールの場合でも基本的な考え方は一緒で、例えばEC2の作成をモジュール化する場合、SecurityGroupのルール、InstanceTypeのパターンなどをパターン化して、
A
B
C
セットのどれを作るか、みたいな形で選ばせたいときは、不正な値の抑制ができる。 - 例えば、基本セットとして
Publicに公開するAPI
かPrivate向けのAPI
かでSecurityGroupの内容を変えるとか。
variable "accessibility" {}
locals {
_acc = {
public = {
},
private = {
}
}
acc = local._acc
}
...(略)
- また、受け取る値は
Input Variable
で定義するしかないが、resource
はすべてLocal Valueを参照することで、関数が必要だったり変数同士の文字列結合や計算する必要がある場合も見通しよく記述できる。
なぜ書いたのか
- 2021年になっても「変数はVariablesで定義しよう」みたいな記事やブログが検索上位に散見されるので、1人でも多くの人にLocal Valueの利用を促進させるため。たとえ似たような記事が多くとも誰かの役に立つはず・・・