tkherox blog

データサイエンスおよびソフトウェア開発、たまに育児についての話を書いています

terraformで if 分を扱ってみたい

はじめに

terraformでモジュールを定義している際にIF文を扱いたいと思った際に調べたことをまとめておきます。

結論から言うとTerraformでは他のプログラミング言語で実装されているIF文と呼ばれる構文は定義されていません。
ただ、どうしてもIF文を使いたいというシーンは実際にはあったりするので、今回はTerraformでIF文ライクな記述をする方法を記載します。 また、追加で補足しておくとTerraformでのIF文による宣言的記述は推奨はされていないため、可能であれば回避するのがベターであるのでその点は理解した上で活用してもらえると良いかと思います。

Terraformとは

まずはTerraformについて簡単に触れておきます。

Terraformとはインフラを宣言的にコードで記述、定義された構造化データに基づいてインフラ構築を自動化できるツールです。一般的にIaCと呼ばれるもの該当します。Terraformを利用するメリットとしては、誰でも同一のインフラ構成を再現できることと、工数削減効果の2つが挙げられます。 この宣言されたコードをGitで共有することで誰しもが同じ環境を構築することができ、インフラ構成もアプリケーションのソースのバージョンと対応して管理していくことができます。

https://www.terraform.io/

加えて、TerraformではProviderと言われるプラグインベースの機能拡張を行うことができるサポート機能が用意されています。この Provider を使うことで様々な外部のインフラリソースを制御することを実現しています。
利用可能な Provider は TerraformRegsitory で確認が可能です。 TerraformRegistoryに登録されているProviderには4つのランク分けがされており、hashicorp社所有やhashicorp社がメンテナンスしている Official の物から、ユーザやコミュニティによって登録されたものなどがあります。メジャーどころのGCPAWSOfficial に該当しているため、バージョンアップ等のメンテナンスが継続して行われていることも安心して使用することができる一因となっています。

registry.terraform.io

実装方法

Terraformでは冒頭で述べたようにIF文が存在しません。
そこで、IF文を実現するためにリソースブロックの個数を定義することが可能な count のメタパラメータと三項演算子を組み合わせて、IF文のような振る舞いを定義していきます。

developer.hashicorp.com

今回は参考例として GCP 上での ComputeEngine のリソース定義を題材として、実際の記述方法を記していこうと思います。

まず初めにディレクトリ構成です。
terraform ディレクトリをルートディレクトリとして環境別のディレクトリとモジュールのディレクトリを配置しております。モジュールディレクトリでは GCP で扱うComputeEngine のテンプレートを用意し、環境別のディレクトリに配置した dev ディレクトリ以下からこれらのテンプレートを呼び出してリソース定義をするようにします。

terraform/
  ├ environments
  │   └ dev
  │     ├ backend.tf
  │     └ main.tf
  └ modules/
      └ vm
        ├ main.tf
        ├ variables.tf
        └ output.tf 

terraform/environments/dev/backend.tf ファイルには state ファイルを管理・共有するための backend 機能を利用するためのリモートストレージの場所を記述します。今回は GCP ということで GSC を対象としています。

terraform {
  backend "gcs" {
    bucket = "gsbucket-terraform"
    prefix = "terraform/dev"
  }
}

続いて、terraform/modules/main.tf ファイルです。
モジュールでは実際のインフラにて利用するリソースに関する情報を定義します。ポイントは ComputeEngine を定義するリソースブロックの中で count パラメータと三項演算子を組み合わせて、変数の値に応じてリソース数を 0 か 1 に振り分けている点になります。 この場合、vm_instance_type にcpuを指定した場合は上段のリソースブロックが採用され、gpuを指定した場合は下段のGPUカードを付与されたリソースブロックを採用することになります。

resource "google_compute_instance" "vm_instance" {
  count        = var.vm_instance_type == "cpu" ? 1 : 0
  name         = var.vm_instance_name
  machine_type = var.vm_instance_machine_type
  zone         = "asia-northeast1"

  boot_disk {
    initialize_params {
      image = var.vm_instance_image
    }
  }
  network_interface {
    network = "default"
    access_config {
    }
  }
}


resource "google_compute_instance" "vm_instance" {
  count        = var.vm_instance_type == "gpu" ? 1 : 0
  name         = var.vm_instance_name
  machine_type = var.vm_instance_machine_type
  zone         = "asia-northeast1"

  boot_disk {
    initialize_params {
      image = var.vm_instance_image
    }
  }
  network_interface {
    network = "default"
    access_config {
    }
  }
  guest_accelerator {
    type = "nvidia-tesla-k80"
    count = 1
  }
}

最後にterraform/environments/dev/main.tf ファイルです。
モジュールで定義したテンプレートを呼び出して、変数としていた要素に適切なパラメータを与えてリソース定義を完成させます。 今回は vm_instance_typecpu を指定していることから、モジュールで定義したCPUのみのVMインスタンスを立ち上げる このようにして Terraform にてIF文を再現することを行います。

# terraformの設定
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "4.4.0"
    }
  }
}

# terraformで利用するプロバイダーの設定
provider "google" {
  project = " <GCPのプロジェクト名> "
  region  = "asia-northeast1"
}

# モジュールを利用した構成定義
module "vm-dev" {
  source                   = "../../modules/vm"
  vm_instance_type         = "cpu"
  vm_instance_name         = "vm-instance-dev"
  vm_instance_machine_type = "custom-4-10240"
  vm_instance_image        = "ubuntu-os-cloud/ubuntu-2204-lts"
}

まとめ

いかがでしたでしょうか。今回はあくまで自分の備忘録として Terraform でIF文を再現する方法をまとめてみました。
TerraformでIF文を定義することは推奨されていませんが、どうしてもIF文を使う必要がある方の参考になれば幸いです。