Terraform でインスタンススケジュールを構築・VM に適用し、スケジュールの付け替えは手動変更を許容する

に公開

はじめに

こんにちは、クラウドエース株式会社 第三開発部の小薗です。

先日、実務で Google Cloud のインスタンススケジュールを Terraform で構築しました。
インスタンススケジュールとは、指定した時間に VM インスタンスを自動的に起動・停止できる機能で、開発環境やテスト環境のコスト最適化に有効です。

構築を進める中で、ひとつ考慮が必要なポイントがありました。
インスタンススケジュールの設定は、運用中にコンソールから手動で変更したいケースがあるという点です。
例えば、「今日は作業が長引くので停止スケジュールを一時的に外したい」「別のスケジュールに付け替えたい」といった場面です。

Terraform で構築したリソースは、手動で変更すると次回の terraform plan で差分として検出され、terraform apply で元に戻されてしまいます。
この問題を解決するために、lifecycle ブロックの ignore_changes を活用しました。

本記事では、Terraform によるインスタンススケジュールの構築と、コンソールからの手動変更を許容する設計について解説します。

本記事の対象読者と前提知識

対象読者

本記事は以下の方を対象としています。

  • インスタンススケジュールを Terraform で構築したい方
  • Terraform で構築したリソースの一部属性を Terraform の管理対象外にしたい方
  • lifecycle ブロックの ignore_changes の実践的な活用方法を知りたい方

前提知識

本記事では以下の知識を前提として解説を進めます。

  • Google Cloud の基本的な知識
  • Terraform の基本知識(コードの読み書き、init/plan/apply コマンド)

インスタンススケジュールとは

インスタンススケジュールは、VM インスタンスの起動・停止を自動化できる機能です。

例えば、以下のようなユースケースで活用できます。

  • 開発環境のコスト削減:業務時間外(21 時以降など)に自動停止し、停止忘れによる課金を防ぐ
  • テスト環境の管理:平日の業務時間帯のみ稼働

VM インスタンスに対してインスタンススケジュールをアタッチすることで、指定した時間に自動的に起動・停止が実行されます。
なお、1 つの VM インスタンスに設定できるインスタンススケジュールは 1 つのみです。そのため、起動用と停止用で別々のスケジュールを作成し、それぞれを VM に適用することはできません。1 つのスケジュール内で起動スケジュールと停止スケジュールを定義する必要があります。

インスタンススケジュールのコンソールでの作成や、VM インスタンスへの適用手順については、公式ドキュメント を参照してください。
本記事では Terraform での構築方法を紹介します。

Terraform による構築

まずはインスタンススケジュールの構築と、それを適用した VM インスタンスの Terraform コードを紹介します。
設計上の考慮点や ignore_changes の解説については、後半のセクションで詳しく説明します。

インスタンススケジュールの作成

まず、google_compute_resource_policy リソースを使用して、インスタンススケジュールを作成します。

# インスタンススケジュール(毎日21時に自動停止)
resource "google_compute_resource_policy" "daily_stop" {
  name    = "daily-stop-at-21"
  region  = "asia-northeast1"
  project = "your-project-id"

  instance_schedule_policy {
    vm_stop_schedule {
      schedule = "0 21 * * *"
    }
    time_zone = "Asia/Tokyo"
  }
}

instance_schedule_policy ブロック内で vm_stop_schedule を定義しています。
schedule には cron 式を指定し、time_zone でタイムゾーンを設定します。

この例では、毎日 21 時(日本時間)に VM インスタンスを自動停止するインスタンススケジュールを定義しています。各項目の詳細は Terraform の公式ドキュメント(google_compute_resource_policy) を参照してください。

VM インスタンスの作成

続けて、インスタンススケジュールを適用した VM インスタンスを定義します。以下の例では、e2-micro(東京リージョン)、Debian 12、20GB のブートディスクを持つ VM インスタンスを、インスタンススケジュール付きで作成します。サンプルでは network = "default" を使用していますが、本番環境では専用の VPC の利用を推奨します。

resource "google_compute_instance" "example" {
  name         = "example-instance"
  machine_type = "e2-micro"
  zone         = "asia-northeast1-a"
  project      = "your-project-id"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-12"
      size  = 20
    }
  }

  network_interface {
    network    = "default"
    subnetwork = "default"
  }

  # インスタンススケジュールを適用
  resource_policies = [google_compute_resource_policy.daily_stop.self_link]

  lifecycle {
    ignore_changes = [resource_policies]
  }
}

ポイントは 2 つです。

  1. resource_policies にインスタンススケジュールの self_link を指定し、VM インスタンスに適用
  2. lifecycleignore_changesresource_policies を指定して、手動変更を許容

lifecycle ブロックの詳細については、次のセクションで解説します。

ignore_changes による手動変更の許容

ここからは、前のセクションのコードで使用した lifecycle ブロックの ignore_changes について、その必要性や、設定しない場合に発生する問題点を詳しく解説します。

課題:Terraform 管理と運用の柔軟性の両立

Terraform でインフラを管理する大きなメリットは、コードによる一元管理と再現性です。
しかし、すべての属性を厳密に Terraform で管理すると、運用上の柔軟性が失われるケースがあります。

インスタンススケジュールの場合、以下のような運用シーンが考えられます。

シーン 操作内容
作業延長 「今日は 21 時以降も作業するのでスケジュールを一時的に外したい」
スケジュール変更 「別の停止時間に変更したい」
一時的な解除 「メンテナンス中はスケジュールを無効にしたい」

これらの変更のたびに Terraform コードを修正して apply するのは、運用負荷が高くなります。
特にインスタンススケジュールの一時的な変更は緊急性が高いケースもあり、迅速な対応が求められます。

ignore_changes を設定しない場合の問題

ignore_changes を設定しない場合、以下のような問題が発生します。

  1. コンソールからインスタンススケジュールを手動で外す
  2. 次回の terraform plan でインスタンススケジュールの差分が検出される
  3. terraform apply を実行すると、手動で外したインスタンススケジュールが再び適用される

実際に ignore_changes を設定せずに、コンソールからインスタンススケジュールを外した後に terraform plan を実行すると、以下のような差分が検出されます。

  # google_compute_instance.example will be updated in-place
  ~ resource "google_compute_instance" "example" {
        name                       = "example-instance"
      ~ resource_policies          = [
          + "https://www.googleapis.com/compute/v1/projects/your-project-id/regions/asia-northeast1/resourcePolicies/daily-stop-at-21",
        ]
        ...
    }

Plan: 0 to add, 1 to change, 0 to destroy.

コンソールでインスタンススケジュールを外したにもかかわらず、Terraform は「コードで定義されたインスタンススケジュールが適用されていない」と判断し、インスタンススケジュールを再度適用しようとします。
この状態で terraform apply を実行すると、手動で外したインスタンススケジュールが意図せず元に戻されてしまいます。

解決策:ignore_changes の活用

この課題を解決するために、lifecycle ブロックの ignore_changes を使用します。

ignore_changes は、Terraform の lifecycle ブロック内で使用できる設定で、指定した属性を Terraform の差分検出の対象外にします。
リソースの初回作成時にはコードの値が反映されますが、作成後に手動で変更されても Terraform はその変更を検知しません。

今回のケースでは、resource_policiesignore_changes に指定します。

lifecycle {
  ignore_changes = [resource_policies]
}

これにより、コンソールや gcloud コマンドでインスタンススケジュールを手動変更しても、次回の terraform plan で差分として検出されず、terraform apply で元に戻されることもありません。

ignore_changes 使用時の注意点

ignore_changes は便利な機能ですが、以下の点に注意が必要です。

1. 初回作成時の値は反映される

ignore_changes初回作成後の変更を無視する設定です。
terraform apply でリソースを初めて作成する際は、コードに記述した値が正しく反映されます。

つまり、初回構築時にインスタンススケジュールが適用された状態で VM インスタンスが作成され、その後の手動変更のみが無視されます。

2. Terraform の状態と実際の状態が乖離する可能性

手動変更を許容するということは、Terraform の tfstate に記録された状態と、実際のリソースの状態が異なる可能性があることを意味します。
この乖離を許容できる属性のみ ignore_changes に設定してください。

3. コード上の変更も無視される

ignore_changes を設定した属性は、Terraform コード上で値を変更しても反映されません。
コードでインスタンススケジュールを変更する場合は、一度 ignore_changes から resource_policies を外し、terraform apply を実行する必要があります。

Terraform 管理と手動変更の使い分け

ignore_changes を活用することで、Terraform 管理と運用の柔軟性を以下のように使い分けることができます。

区分 対象属性 変更方法
Terraform で厳密に管理 name, machine_type など Terraform コードを変更して apply
手動変更を許容 resource_policies コンソールから即座に変更可能(terraform plan で差分なし)

この使い分けにより、インフラの一貫性を保ちながら、現場の運用ニーズにも柔軟に対応できます。

おわりに

本記事では、Terraform によるインスタンススケジュールの構築と、lifecycle ブロックの ignore_changes を活用してコンソールからの手動変更を許容する設計について解説しました。

Terraform ですべてを厳密に管理することが理想的ではありますが、実際の運用では柔軟性が求められる場面もあります。
「何を Terraform で管理し、何を手動変更可能にするか」を明確に設計することで、管理の一貫性と運用の柔軟性を両立できます。

最後までご覧いただき、ありがとうございました。

Discussion