33
16

More than 1 year has passed since last update.

[terraform] VariableとLocal Valueの使い所

Last updated at Posted at 2021-12-01

これは「「はじめに」の 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に公開するAPIPrivate向けのAPI かでSecurityGroupの内容を変えるとか。
variable "accessibility" {}

locals {
  _acc = {
    public = {
    },
    private = {
    }
  }
  acc = local._acc
}
...(略)
  • また、受け取る値は Input Variable で定義するしかないが、resource はすべてLocal Valueを参照することで、関数が必要だったり変数同士の文字列結合や計算する必要がある場合も見通しよく記述できる。

なぜ書いたのか

  • 2021年になっても「変数はVariablesで定義しよう」みたいな記事やブログが検索上位に散見されるので、1人でも多くの人にLocal Valueの利用を促進させるため。たとえ似たような記事が多くとも誰かの役に立つはず・・・
33
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
16