---
title: "Terraform moduleでトグル | grasys blog"
url: "https://blog.grasys.io/post/eric/tftoggle/"
description: "こんにちは、エリックです。今回はTerraformの話にします。 Terraformでworkspaceを使う時に、dev/stg/prodなどの環境を統一するのがベストだけど、たまにコストや機能により「ここにはこれは要らないな」などの場合がある。その時、各環境に有無トグルをどうやって設定するか、どうやって依存などを…"
---

# Terraform moduleでトグル

-   ![](/_astro/noicon.CTHOhNiB_1HMs6A.webp)[eric](/authors/eric/)
-   公開日：2020年9月30日
-   カテゴリー：[Tech](/categories/tech/)
-   タグ：[#Terraform](/tags/terraform/)

![Terraform moduleでトグル](/_astro/og-image.DDSiu2Ny_XHpDd.webp)

こんにちは、エリックです。今回はTerraformの話にします。

Terraformでworkspaceを使う時に、dev/stg/prodなどの環境を統一するのがベストだけど、たまにコストや機能により「ここにはこれは要らないな」などの場合がある。その時、各環境に有無トグルをどうやって設定するか、どうやって依存などを解決するか軽く語ろう。

例として、foo/bar/bazというGKEクラスタのnode poolがあり、stg環境にbazが不要だとする。

-   dev: foo, bar, baz
-   prod: foo, bar, baz
-   stg: foo, bar

各node poolはこんな風：

## `main.tf:`

Plain textcontent\_copy

```
module node-pool-baz {
  source          = ".../container_node_pool/google"
  version         = "1.0.0"
  cluster         = module.cluster.cluster
  node_pool       = var.node_pool_baz
  node            = var.node_baz
  service_account = module.sa-baz.sa
}
```

まず、node poolのモジュールに、有無のトグルを追加：

### `.terraform/modules/node-pool-baz/main.tf:`

Plain textcontent\_copy

```
resource google_container_node_pool default {
+ count      = var.node_pool.enabled ? 1 : 0
  [...]
```

### `.terraform/modules/node-pool-baz/variables.tf:`

Plain textcontent\_copy

```
variable node_pool {
  type = object({
+   enabled    = bool,
    [...]
  })
}
```

見れば分かると思うが、その`count`行は三項演算子で、boolの`enabled`との変数で、構築するかどうか決める。 trueなら、１つ構築、falseなら０構築（構築しない）。`count`はTerraformで、どのリソースにでも使える。

環境（workspace）のtfvarsファイルはこんな風に：

##### `stg.tfvars`:

Plain textcontent\_copy

```
node_pool_foo = {
  enabled     = true,
  [...]
}
node_pool_bar = {
  enabled     = true,
  [...]
}
node_pool_baz = {
  enabled     = false,
  [...]
}
```

ここまでは大丈夫だけど、各node poolに、サービスアカウントも依存するから、 そのmoduleにもenabled追加しよう（そうしないと、enabled:falseの場合にでもサービスアカウントが作成されちゃう）。

まず、サービスアカウントのmoduleにその`enabled`変数を追加して、リスースのところに以上の三項演算子を：

#### `.terraform/modules/sa-baz/variables.tf`:

Plain textcontent\_copy

```
+variable enabled {
+  type    = bool
+  default = true
+}
```

##### `.terraform/modules/sa-baz/main.tf`:

Plain textcontent\_copy

```
resource google_service_account default {
   [...]
+  count        = var.enabled ? 1 : 0
 }

 resource google_project_iam_member default {
   [...]
- for_each = toset(var.roles)
+  for_each = var.enable ? toset(var.roles) : []

   project = var.project
   role    = each.key
   member  = format("serviceAccount:%s", google_service_account.default.email)
 }
```

あと、呼び出し側にもenabledを追加(混乱・重複しないためnode poolのenabledを利用して)。

##### `main.tf`:

Plain textcontent\_copy

```
 module sa-baz {
[...]
   roles        = var.sa_baz_roles
+  enabled      = var.node_pool_baz.enabled
 }
```

OK. 実行してみよう！

Plain textcontent\_copy

```
Error: Missing resource instance key

  on .terraform/modules/sa-baz/main.tf line 15, in resource "google_project_iam_member" "default":
  15:   member  = format("serviceAccount:%s", google_service_account.default.email)

Because google_service_account.default has "count" set, its attributes must be
accessed on specific instances.

For example, to correlate with indices of a referring resource, use:
    google_service_account.default[count.index]
```

なるほど。count追加すると、変数が配列になり、indexで示さないと。 もうちょい修正：

##### `.terraform/modules/sa-baz/main.tf`:

Plain textcontent\_copy

```
- member  = format("serviceAccount:%s", google_service_account.default.email)
+  member  = format("serviceAccount:%s", google_service_account.default[0].email)
```

で、もう一回実行してみる：

Plain textcontent\_copy

```
Error: Unsupported attribute

  on .terraform/modules/node-pool-baz/main.tf line 13, in resource "google_container_node_pool" "default":
  13:     service_account = var.service_account.email
    |----------------
    | var.service_account is tuple with 1 element

This value does not have any attributes.
```

なるほど。配列になったけど、使用してるnode-poolのところが単独の変数を期待してる。どこを修正すれば良いかは、モジュールの出力のところを見てみよう。

##### `.terraform/modules/sa-baz/output.tf`:

Plain textcontent\_copy

```
output sa {
  value = google_service_account.default
}
```

ああ。では、こっちで配列の`[0]`にすれば、配列じゃなくて普通の変数が出てくるわけですね。

Plain textcontent\_copy

```
  value = google_service_account.default[0]
```

もう一回実行：

Plain textcontent\_copy

```
Error: Invalid index

  on .terraform/modules/sa-baz/output.tf line 2, in output "sa":
   2:   value = google_service_account.default[0]
    |----------------
    | google_service_account.default is empty tuple

The given key does not identify an element in this collection value.
```

あ〜、enabled: falseのところは、サービスアカウントがないから配列の`[0]`もないね。 じゃあ、これはどう？

Plain textcontent\_copy

```
   value = length(google_service_account.default) > 0 ? google_service_account.default[0] : ""
```

やってみよう：

Plain textcontent\_copy

```
Error: Error in function call

  on .terraform/modules/sa-baz/output.tf line 2, in output "sa":
   2:   value = length(google_service_account.default > 0) ? google_service_account.default[0] : ""

Call to function "length" failed: argument must be a string, a collection
type, or a structural type.
```

ダメだな。配列がないならlengthも使えない。ちょいググったら、こんな形が出てきた：

Plain textcontent\_copy

```
  value = try(google_service_account.default[0], null)
```

saの配列`[0]`の要素を返してみて、それがなければ`null`を。

Plain textcontent\_copy

```
No changes. Infrastructure is up-to-date.
```

これで解決〜！

…とはいえ、moduleで色んな書き方があるし、0.13から呼び出しの方からでも`for_each`や`count`が使えるようになったらしい。とゆーところで、 0.13でどうやって扱うか、良い方法考えてみるのは宿題！w

〜終〜

## この記事を書いた人

[![](/_astro/noicon.CTHOhNiB_Z24aAgM.webp)](/authors/eric/)

### [eric](/authors/eric/)

ericのプロフィールと執筆記事をご覧いただけます。

[プロフィールと記事一覧](/authors/eric/)