デベロッパー

Terraform リソースの再作成の強制

※この投稿は米国時間 2022 年 4 月 1 日に、Google Cloud blog に投稿されたものの抄訳です。


Google Cloud などのパブリック クラウド上で実行されている環境で Terraform リソースをプロビジョニングしていて、そのリソースを破棄、再作成してクリーンな状態から開始する必要があるとしましょう。  Terraform には、これを実現するための 2 つのツールが備わっています。

  • terraform taint コマンド。Terraform 状態でオブジェクトを tainted としてマークするように Terraform に指示します。オブジェクトが tainted としてマークされている場合、Terraform はそのオブジェクトの置き換えを提案します。(このコマンドは非推奨とされています)。

  • terraform applyterraform plan コマンドの -replace オプション。オブジェクトを置き換えるように Terraform に指示します。

どちらのツールもインタラクティブなセッションで使いやすいものの、次の欠点があります。

  • 自動で実行する場合には、外部ツールによるサポートが必要になります。たとえば、何かしらの条件に基づいてリソースを破棄および再作成する必要がある場合には、その条件を Terraform の外部でチェックする機能を実装したうえで、その条件が満たされたときに適切な Terraform コマンドを実行してリソースを破棄、再作成する必要があります。

  • 条件のチェック、リソースを破棄および再作成するコマンドの実行は Terraform の用語では記述できないため、リソースの状態が Terraform の外部に漏れることになります。

これらの問題を解決するために、新しいリソースの作成を強制する Terraform リソース内のフィールド、ForceNew を利用できます。

  1. 動的に破棄および再作成したい Terraform リソースの実装を評価して、String 型のリソース属性のリストを作成します。  評価を String 型のリソース属性のみに制限する理由は、他の型(ブール値など)と比較して、その値の設定に関してより高い柔軟性がある可能性が高いためです。

  2. リソース属性には数多くのフィールドがあり、それらのフィールドの 1 つがブール値型の ForceNew です。ForceNew ブール値フィールドが false に設定されたリソース属性をリストから除外します。リソース属性で ForceNew フィールドが true に設定されている場合、そのリソースを破棄して再作成しないとその属性に変更を加えることができません。

  3. リソースのドキュメントの説明に従い、その制限のために限られた値のセットから値を選択する必要のあるリソース属性をリストから除外します。

  4. ForceNew フィールドが true に設定されていて、(検証制約の範囲内で)任意の値の設定を許可している String 型のリソース属性のリストから、少なくとも 1 つの属性を選択します。

  5. 選択した属性の値を動的に設定して、リソースの破棄および再作成が必要になったときに値が変更されるようにします。

当然ながら、次の例で説明するように、この戦略を使用して異なるリソースの状態間に明示的な依存関係を追加できます。

例: Google Compute Engine 仮想マシンを削除して再作成する

この例では、次の操作を行います。

  1. google_compute_instance リソースを作成して、Terraform で Compute Engine 仮想マシン(VM)をプロビジョニングします。

  2. この google_compute_instance リソースを構成して、このリソースやその他のリソースに変更があった場合に VM の削除と再作成を強制するようにします。

Google Compute Engine VM をプロビジョニングする

以下のスニペットで、Compute Engine VM をプロビジョニングするための Terraform リソースを準備します。

  hcl

locals {
  my_meta_data = "my-meta-data-value"
}

resource "google_compute_instance" "development-workstation" {
  allow_stopping_for_update = true
  name                   = "development-workstation"
  machine_type     = "n1-standard-8"

  boot_disk {
    initialize_params {
      image = "ubuntu-2004-lts"
      size  = "100"
      type  = "pd-ssd"
    }
  }

 metadata = {
    my-meta-data = local.my_meta_data
  }


  network_interface {
    subnetwork = google_compute_subnetwork.development-workstation-subnet.self_link

    access_config {
      network_tier = "PREMIUM"
    }
  }

  service_account {
    # Set to cloud-platform scope, then use IAM to limit access to the service account that this instance uses
    # https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam
    scopes = ["cloud-platform"]
  }
}

VM の削除と再作成を強制する

ここで、このリソースやその他の Terraform リソースの状態が変更されるたびにプロビジョニングした VM を自動的に破棄および再作成し、このロジックを Terraform リソース自体の内部に収めたいとしましょう。この例では、metadata.my-meta-data 属性の値が変更されるたびに、VM の削除と再作成が強制されるようにします。

この戦略に従い、以下を行います。

1. google_compute_instance リソースの実装を評価して、ForceNew フィールドが true に設定されている String 型の属性を探します。現時点での google_compute_instanceリソースの実装には、このような属性が 15 以上あります。

2. google_compute_instance リソースのドキュメントの説明に従い、その制限のために限られた値のセットから値を選択する必要のあるリソース属性をリストから除外します。その結果として次のようになります。

  • device_name: 除外します。変更されると、このような ID を持つディスクに依存するワークロードが破損する可能性があるためです。また、この目的のためだけにディスクを構成する必要性はありません。

  • name と hostname: 除外します。これらが変更されると VM の「ID」も変更されるためです。許容される場合もありますが、ほとんどの場合で許容されません。

  • description: 候補として残します。

  • metadata_startup_script: 面白い候補ですが、除外します。VM のプロビジョニングと構成のプロセスが変更されるため、また、起動スクリプトを導入する必要性があるためです。

  • projectzone: 除外します。VM をプロビジョニングする場所を動的に変更しない可能性が高いためです。

3. ForceNew フィールドが true に設定されていて、(検証制約の範囲内で)任意の値の設定を許可している String 型のリソース属性のリストから、少なくとも 1 つの属性を選択します。ここでは、リストに残されたただ 1 つの属性、description を選択して続行します。

4. 選択した属性の値を動的に設定して、リソースの破棄および再作成が必要になったときに値が変更されるようにします。この例では、Terraform で metadata.my-meta-data 属性の値が変更されるたびに VM の破棄と再作成が強制されるようにします。これを行うために、VM の description 属性の値を動的に設定して、この値が metadata.my-meta-data 属性の値が変更されるたびに変更されるようにします。

注: この時点で、リストにある属性は 1 つだけですが、1 つより多くなる可能性はあるため、ユースケースによっては選択する必要性がある場合や、複数の属性を結果として選択する場合があります。

 description = "my-meta-data SHA512 hash (base64): ${base64sha512(local.my_meta_data)}"


この例では、base64sha512 関数を使用して文字列の SHA512 ハッシュを計算し、主に以下の目的のためにこれを Base64 にエンコードします。

  • 属性値の動的部分を、SHA512 ハッシュのサイズに等しい既知のサイズに制限する。

  • base64 にエンコードして、サポートされない文字を含む属性値が動的に設定される可能性を減らす。

Terraform マニフェストは次のようになります。

  hcl

locals {
  my_meta_data = "my-meta-data-value"
}

resource "google_compute_instance" "development-workstation" {
  allow_stopping_for_update = true

  description      = "my-meta-data SHA512 hash (base64): ${base64sha512(local.my_meta_data)}"
  name             = "development-workstation"
  machine_type     = "n1-standard-8"

  boot_disk {
    initialize_params {
      image = "ubuntu-2004-lts"
      size  = "100"
      type  = "pd-ssd"
    }
  }

  metadata = {
    my-meta-data = local.my_meta_data
  }


  network_interface {
    subnetwork = google_compute_subnetwork.development-workstation-subnet.self_link

    access_config {
      network_tier = "PREMIUM"
    }
  }

  service_account {
    # Set to cloud-platform scope, then use IAM to limit access to the service account that this instance uses
    # https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam
    scopes = ["cloud-platform"]
  }
}

これで、metadata.my_meta_data の値を更新すると、Terraform がその更新を説明フィールドの値の変更とみなすようになるため、development-workstation リソースが強制的に破棄および再作成されます。

この記事では、Terraform で、別のリソースの変数、ローカル変数、属性の値が変更されたときにリソースの破棄と再作成を強制するための戦略について説明しました。この戦略であれば、外部ツールで何かしらの条件を確認する必要がなく、また、リソースの状態をすべて Terraform の内部に収めることができます。

詳細

Terraform Google Cloud Provider で Google Cloud リソースをプロビジョニングする。


- ソリューション アーキテクト Marco Ferrari