Terraformを使ってAWSやGCP等のクラウドインフラストラクチャをコードで管理(IaC=Infrastructure as Code)していきます。
第2回となる前回はTerraformを使用するうえで理解しておくべき基本的な知識について解説しました。
第3回となる今回は、Terraformのコードを記述する言語(DSL)であるHCL2について、基本を解説します。
HCL2とは
TerraformはHCL2(HashiCorp Configuration Language2)で記述します。
HCL2の構文はJSONに似ていますが、JSONはデータ構造を表すことに特化しているのに対し、HCL2では以下のようなプログラミング言語としての各種機能が備わっています。
- 変数定義、繰り返し処理、算術/論理演算、条件処理(三項演算子)などが使える
- コメントが書ける (#)
- キーとバリューの紐付けはJSONの”.”ではなく”=”で行う
- ヒアドキュメントが使える
ブロック
HCL2ではブロックと呼ぶ単位でコードを記述していきます。
以下はリソースブロックの例です。
resource "aws_instance" "tftest" {
ami = "ami-08c84d37db8aafe00"
instance_type = "t3.nano"
}
ブロックの種類
ブロックには以下のような種類があります。
種類 | 説明 |
---|---|
locals | 外部から変更できないローカル変数を定義する |
variable | 外部から変更可能なグローバル変数を定義する |
terraform | Terraformの設定を定義する |
provider | 使用するプロバイダを定義する |
data | Terraformで管理していない外部リソースを取り込む |
resource | Terraformで管理対象となるリソースを記述する |
output | 外部から参照できるようにする値を定義する |
今回は、この中で変数を定義するブロックである locals と variable について解説します。
変数ブロックの種類 – localsとvariable –
locals
localsブロックではローカル変数(プライベート変数)を定義します。
Terraform内でのローカル関数となるため、外部から参照や変更はできません。
localsブロックの基本的な構文
loals {
<変数名> = <値>
<変数名> = <値>
}
localsブロック内で定義した変数は ${local.変数名} で値を参照します。
使用例
loals {
project = "project1"
env = "dev"
}
resource <RESOURCE_TYPE> <RESOURCE_NAME} {
...
tags = {
Name = "${local.project}-${local.env}"
}
}
上記の例では、 project と env という2つのローカル変数を定義し、それぞれに値を代入しています。
そして、続くresourceブロック内でそれぞれを参照しています。
この場合、Name には ”project1-dev” が定義されます。
variable
variableブロックでは外部から変更可能なグローバル変数を定義します。
外部から参照と変更が可能であるため、例えばGitBashなどのターミナルからterraformコマンドを実行する際にオプションとして渡した値で、variableで定義した変数を上書きするといったことが可能です。
variableブロックの基本的な構文
variable <変数名> {
type = <変数の型>
default = <デフォルト値>
description = <変数の説明>
}
variableブロックで定義した変数は ${var.変数名} で値を参照します。
使用例
variable "project" {
type = string
default = "project1"
description = "管理対象のプロジェクト"
}
variable "env" {
type = string
default = "dev"
description = "管理対象の環境 Prod or Dev"
}
resource <RESOURCE_TYPE> <RESOURCE_NAME} {
...
tags = {
Name = "${var.project}-${var.env}"
}
}
上記の例では、 project と env という2つの変数(グローバル変数)を定義し、それぞれに型(この場合 string(文字列)型)とデフォルト値を設定しています。
そして、続くresourceブロック内でそれぞれを参照しています。
各変数の値が上書きされていなければデフォルト値が使用されます。
この場合、Name には ”project1-dev” が定義されます。
データ型
HCL2では以下のデータ型が使用できます。
型 | 説明 |
---|---|
string | Unicode文字列 |
number | 数値(整数、少数どちらも格納可) |
bool | ブール値(trueまたはfalse) |
list | 特定の型の値で構成される配列 |
map | 文字列をキーとした配列 他言語でいうところの連想配列やディクショナリ |
set | 重複がなくユニークな値のみで構成される配列 |
tuple | カラムの型が決められたlist |
object | キーバリュー型の構造体 キーごとにバリューの型を指定したmap |
string, number, bool
基本となるデータ型として、string(文字列), number(数値), bool(ブール値) の3種類があります。
使用例
variable "message" {
type = string
default = "Hello World"
}
variable "max_count" {
type = number
default = 10
}
variable "is_enable" {
type = bool
default = true
}
list
list型は配列です。複数の変数を繋げたリスト構造を持ちます。
使用例
variable "lst_sample" {
type = list(string)
default = ["suzuki", "yamada", "tanaka"]
}
list型の各要素として格納するデータの型を宣言します。上記例ではstring型となります。
tuple型の変数の値は ${var.変数名[index]} で値を参照します。
他の多くの言語と同様に、最初の要素のインデックスが0で、以降1,2,3…と続きます。
username = ${var.lst_sample[0]}
map
map型はキーバリュータイプの配列で、文字列をキーとしてデータを格納することが可能です。
使用例
variable "map_sample" {
type = map(string)
default = {
"suzuki" = "admin"
"yamada" = "user"
"tanaka" = "guest"
}
}
上記の例では、 値(バリュー)の型を string と定義しています。
object型の変数の値は ${var.変数名.キー} で値を参照します。
role = ${var.map_sample.suzuki}
set
set型はlistと同じ配列ですが、格納するデータの重複が自動で排除され、ユニークな値のみが格納されます。
使用例
variable "set_sample" {
type = set(string)
default = ["suzuki", "yamada", "suzuki", "yamada"]
}
上記例では”suzuki”と”yamada”が重複していますが、実際に格納されている値は以下のようになります。
default = ["suzuki", "yamada"]
tuple
tuple型はカラム毎に格納するデータの型が決められた配列です。
配列(list型)では全てのカラムが同じ型となりますが、tuple型はカラム毎に型を変えることが可能です。
使用例
variable "tpl_sample" {
type = tuple([string, number])
default = ["suzuki", 41]
}
tuple型の変数の値は ${var.変数名[index]} で値を参照します。
username = ${var.tpl_sample[0]}
age = ${var.tpl_sample[1]}
object
object型はキーバリュータイプの構造体で、キー毎に型を定義することが可能です。
使用例
variable "obj_sample" {
type = object({
name = string
age = number
})
default = {
name = "suzuki"
age = 41
}
}
上記の例では、 “name”をキーとして格納する値の型を string 、”age”をキーとして格納する値の型を number と定義しています。
object型の変数の値は ${var.変数名.キー} で値を参照します。
username = ${var.obj_sample.name}
変数の上書き
variableブロックで定義した変数は外部からの上書きが可能です。
上書きする方法としては以下の3つがあります。
- 環境変数
- 変数ファイル
- コマンド引数
それぞれの方法と使いどころを見ていきましょう。
環境変数による上書き
terraformを実行する環境にて、予め環境変数に変数の情報をセットしておくことにより、variableの変数を上書きすることが可能です。
ファイルには記述したくない情報(鍵やパスワード等)を変数にセットする必要がある場合や、terraformコマンドの実行環境(prod/dev/stgなど)によって変数の内容を変えたい場合はこの方法が便利です。
このとき、環境変数名は
TF_VAR_<変数名>
というフォーマットである必要があります。
例
ソースコード
variable "message" {
type = string
default = "Hello World"
}
環境変数をセットしてterraform実行
export TF_VAR_message=”Goodbye World”
terraform apply
ちなみに、bash(GitBash含む)では以下のように実行しても同じ結果となります。
TF_VAR_message=”Goodbye World” terraform apply
ただし、以下の2つのパターンでは、コード編集後に再度applyした場合の結果は異なります。
TF_VAR_message=”Goodbye World” terraform apply
# コードを編集して再度apply
terraform apply
export TF_VAR_message=”Goodbye World”
terraform apply
# コードを編集して再度apply
terraform apply
このあたりの話はLinuxシェルの仕組みの話になってくるのでこの場での説明はしませんが、気になる方は『Linux 環境変数 シェル変数』あたりのキーワードで調べてみてください。
変数ファイルによる上書き
変数の情報を記載したファイルを用意しておき読み込ませることにより、variableの変数を上書きします。
変数ファイルはコードを構成するファイルの一部としてGitなどで管理できますので、変更箇所を管理したい場合や、ロジックとデータを分離してコードを管理したい場合に有効な方法です。
このとき、変数ファイルの名前は
<任意のファイル名>.tfvars
である必要があります。
変数ファイルの記述例(variables.tfvars)
message = ”Goodbye World”
terraform実行
terraform apply -var-file=variables.tfvars
このとき、ファイル名を『terraform.tfvars』にしている場合は、上記のようにコマンド実行時のオプションとしてファイル名を指定しなくても自動的に読み込まれます。
常に使用する変数の定義を terraform.tfvars に記述しておき、実行時に値を切り替えたい変数を個別の変数ファイル(例:prod.tfvers、dev.tfvers)に記述してコマンド実行時に指定するといった使い方がよいでしょう。
コマンド引数による上書き
terraformコマンド実行時にコマンド引数として指定することにより、variableの変数を上書きします。
テストやデバッグなどの目的でコマンド実行時に一時的に変数を変更したい場合に使用する事が多い方法です。
実行例
terraform apply -var message=”Goodbye World”
上書き順序について
変数を上書きする3つの方法には適用順序があります。以下の順に適用され、最後に適用されたものが実際の処理に反映されます。
- 環境変数
- 変数ファイル
- コマンド引数
例えば上記3つ全ての方法を用いて同じ変数の値を指定した場合、コマンド引数で指定した値が使用されます。
おわりに
今回は、HCL2の変数について解説しました。
次回はブロックについて解説していきます。