Terraform Backend変更ガイド: -migrate-stateの使用方法

Tech

2024.3.11

Topics

はじめに

こんにちは、Shunです。

Terraformを運用していると、一時的にBackendを変更したり、更新が必要になる場面があると思います。

そんな時に役立つterraform init -migrate-stateコマンドについてご紹介します。

公式ドキュメントでは、以下のように定義されています。

すでに初期化されたバックエンドで init を再実行すると、新しいバックエンド設定を使用するように作業ディレクトリが更新されます。バックエンド構成を更新するには、-migrate-stateか-reconfigureを指定する必要があります。この-migrate-stateオプションは、既存の状態を新しいバックエンドにコピーしようとします。変更内容に応じて、ワークスペース状態の移行を確認する対話型プロンプトが表示される場合があります。

引用:Backend Initialization

関連コマンドのterraform init -reconfigureについては、以下の記事で解説をしています。

関連記事
Terraform Backend変更ガイド: -reconfigureの使用方法

この記事では、terraform init -migrate-stateコマンドの挙動について、具体例を交えながら解説します。

想定読者

  • TerraformのBackend更新を検討している方
  • terraform init -migrate-stateコマンドの使用経験はあるが、詳細な挙動が気になる方

本記事で取り扱う内容

  • TerraformのBackendの切り替え方
  • terraform init -migrate-stateの挙動
  • tfstateファイルなどの関連ファイルの説明

本記事で取り扱わない内容

  • Terraformの基本的な概念解説
  • Terraformの一般的なコマンド説明

実施すること

前提

  • tfstateファイルをS3で管理していること
  • Terraformがセットアップ済みであること

必要ファイルの準備

ディレクトリ構造としては、/blogの下にmain.tfファイルを用意します。

blog
 └ main.tf

/blog/main.tf

terraform {
  required_version = "~> 1.7.0"
    backend "s3" {
      bucket = "[bucket_name]"
      region = "ap-northeast-1"
      key    = "main/terraform.tfstate"
    }
#     backend "local" {
#      path   = "terraform.tfstate"
#    }
}

resource "terraform_data" "main" {}

terraform_dataはterraform1.4.0以上で使用できます。

BackendをS3に設定した状態で、以下のコマンドを実行してください。

$ terraform init
$ terraform apply -auto-approve

tfstateファイルの確認

S3に保存されているtfstateファイルと、/blog/.terraform/terraform.tfstateのファイルを確認します。

S3に保管されているtfstateファイルでは、先ほど立ち上げたリソースの状態が保管されています。

{
  "version": 4,
  "terraform_version": "1.7.0",
  "serial": 1,
  "lineage": "b855417a-5d74-aec3-b503-bfcc1fb5f05a",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "terraform_data",
      "name": "main",
      "provider": "provider[\"terraform.io/builtin/terraform\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "id": "8cf84313-2cb8-16e1-5a91-4565bcd87d13",
            "input": null,
            "output": null,
            "triggers_replace": null
          },
          "sensitive_attributes": []
        }
      ]
    }
  ],
  "check_results": null
}

次に、/blog/.terraform/terraform.tfstateを確認します。

.terraform配下にあるterraform.tfstateはリソース状態を定義するtfstateファイルではなく、指定されたBackendの情報が記述されたファイルです。

中身は以下のようにS3がBackendとして指定されています。

{
    "version": 3,
    "serial": 1,
    "lineage": "e9a0f02a-d1ef-c671-d922-274b44b4991a",
    "backend": {
        "type": "s3",
        "config": {
            "access_key": null,
            "acl": null,
            "allowed_account_ids": null,
            "assume_role": null,
            "assume_role_duration_seconds": null,
            "assume_role_policy": null,
            "assume_role_policy_arns": null,
            "assume_role_tags": null,
            "assume_role_transitive_tag_keys": null,
            "assume_role_with_web_identity": null,
            "bucket": "[bucket_name]",
            "custom_ca_bundle": null,
            "dynamodb_endpoint": null,
            "dynamodb_table": null,
            "ec2_metadata_service_endpoint": null,
            "ec2_metadata_service_endpoint_mode": null,
            "encrypt": null,
            "endpoint": null,
            "endpoints": null,
            "external_id": null,
            "forbidden_account_ids": null,
            "force_path_style": null,
            "http_proxy": null,
            "https_proxy": null,
            "iam_endpoint": null,
            "insecure": null,
            "key": "main/terraform.tfstate",
            "kms_key_id": null,
            "max_retries": null,
            "no_proxy": null,
            "profile": null,
            "region": "ap-northeast-1",
            "retry_mode": null,
            "role_arn": null,
            "secret_key": null,
            "session_name": null,
            "shared_config_files": null,
            "shared_credentials_file": null,
            "shared_credentials_files": null,
            "skip_credentials_validation": null,
            "skip_metadata_api_check": null,
            "skip_region_validation": null,
            "skip_requesting_account_id": null,
            "skip_s3_checksum": null,
            "sse_customer_key": null,
            "sts_endpoint": null,
            "sts_region": null,
            "token": null,
            "use_dualstack_endpoint": null,
            "use_fips_endpoint": null,
            "use_legacy_workflow": null,
            "use_path_style": null,
            "workspace_key_prefix": null
        },
        "hash": 226295838
    },
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {},
            "depends_on": []
        }
    ]
}

実際の作業

terraform init -migrate-stateを用いて、BackendをS3からローカルへ変更

まずは、以下のようにファイルを書き換えます。

terraform {
  required_version = "~> 1.7.0"
    # backend "s3" {
    #   bucket = "[bucket_name]"
    #   region = "ap-northeast-1"
    #   key    = "main/terraform.tfstate"
    # }
    backend "local" {
     path   = "terraform.tfstate"
   }
}

resource "terraform_data" "main" {}

そして、以下のコマンドを実行します。

$ terraform init -migrate-state

以下のように、「BackendをS3からローカルへ変更し、S3のtfstateファイルをローカルにコピーしますよ。」との確認が出ますので、「yes」を入力します。

Initializing the backend...
Terraform detected that the backend type changed from "s3" to "local".

Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "s3" backend to the
  newly configured "local" backend. No existing state was found in the newly
  configured "local" backend. Do you want to copy this state to the new "local"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value:yes

ここでローカルへS3で保管していたtfstateファイルがコピーされます。

また、/.terraform/terraform.tfstateのBackendの向き先がローカルへ切り替わります。

{
    "version": 3,
    "serial": 2,
    "lineage": "e9a0f02a-d1ef-c671-d922-274b44b4991a",
    "backend": {
        "type": "local",
        "config": {
            "path": "terraform.tfstate",
            "workspace_dir": null
        },
        "hash": 73024536
    },
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {},
            "depends_on": []
        }
    ]
}

ローカルでterraform_dataを削除

まず、main.tfファイルからterraform_dataリソースの定義をコメントアウトし、

terraform {
  required_version = "~> 1.7.0"
    # backend "s3" {
    #   bucket = "[bucket_name]"
    #   region = "ap-northeast-1"
    #   key    = "main/terraform.tfstate"
    # }
    backend "local" {
     path   = "terraform.tfstate"
   }
}

# resource "terraform_data" "main" {}

リソースを削除します。

$ terraform apply -auto-approve

この変更を適用することで、ローカルの/terraform.tfstateファイルが更新されます。

またローカルの/terraform.tfstateファイルが更新されたことにより、S3からローカルにコピーされたtfstateファイルのバックアップとして/terraform.tfstate.backupファイルが作成されます。

現在のディレクトリ構造は、以下のようになっています。

blog
├─ .terraform
│  └ terraform.tfstate # Backendの情報が記述されたファイル
├─ main.tf
├─ terraform.tfstate # 現在のリソース状態が記述されたファイル
└─ terraform.tfstate.backup # S3からmigrateしたファイルのバックアップ

terraform init -migrate-stateを用いて、BackendをローカルからS3へ変更

次に、main.tfファイルを編集してBackendをS3に設定し、再びterraform init -migrate-stateを実行します。

以下のようにファイルを書き換えます。

terraform {
  required_version = "~> 1.7.0"
     backend "s3" {
       bucket = "[bucket_name]"
       region = "ap-northeast-1"
       key    = "main/terraform.tfstate"
     }
    # backend "local" {
    #  path   = "terraform.tfstate"
    # }
}

# resource "terraform_data" "main" {}

この操作により、.terraform/terraform.tfstateファイル内のBackend設定がS3に更新されます。

terraform planを実行すると、terraform_dataリソースの削除に関する差分が発生することが示されます。

これはterraform init -migrate-stateが既存のtfstateファイルを上書きしないためです。

terraform init -migrate-stateを用いて、BackendをS3からS3へ変更

最後に、新しいS3のkeyへBackendを変更します。

以下のように新しいkeyを指定して、terraform init -migrate-stateを実行します。

terraform {
  required_version = "~> 1.7.0"
    backend "s3" {
      bucket = "[bucket_name]"
      region = "ap-northeast-1"
      key    = "migrate/terraform.tfstate"
    }
#     backend "local" {
#      path   = "terraform.tfstate"
#    }
}

# resource "terraform_data" "main" {}

この場合、terraform planを実行しても差分が発生しないことから、tfstateファイルが新しいBackendに適切に移行されていることがわかります。

まとめ

terraform init -migrate-stateを検証して分かったことは以下です。

  • Backend設定の.terraform/terraform.tfstateファイルは自動的に更新される
  • ローカルへのterraform init -migrate-state実行時には、terraform.tfstate.backupファイルが作成されるが、S3への実行時には作成されない
  • /terraform.tfstateファイルは既存のBackendから新しいBackendへコピーされるが、既にtfstateファイルが存在する場合には上書きされない

Backupファイルを作成してくれることやtfstateファイルが上書きされないことは、tfstateファイルの整合性を保つことに非常に役立ちます。

terraform initという基本的なコマンドを深掘りすることで、普段から使っているterraformの挙動をコントロールできている感覚になりました。

最後まで読んでいただきありがとうございます!

Shun

好きな言葉は生ビール199円です。日本ビール協会認定1冠、AWS12冠、Google Cloud11冠

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら