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

Tech

2024.3.1

Topics

はじめに

こんにちは、Shunです。

今回は、Backendの変更や更新の際に使用するterraform init -reconfigureコマンドについてご紹介します。

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

すでに初期化されたバックエンドで init を再実行すると、新しいバックエンド設定を使用するように作業ディレクトリが更新されます。バックエンド構成を更新するには、-migrate-stateか-reconfigureを指定する必要があります。この-reconfigureオプションは既存の構成を無視し、既存の状態の移行を防ぎます。

引用:Backend Initialization

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

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

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

想定読者

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

本記事で取り扱う内容

  • TerraformのBackendの切り替え方
  • terraform init -reconfigureの挙動
  • 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" "sapporo" {}
resource "terraform_data" "yebisu" {}

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": "sapporo",
      "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": []
        }
      ]
    },
    {
      "mode": "managed",
      "type": "terraform_data",
      "name": "yebisu",
      "provider": "provider[\"terraform.io/builtin/terraform\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "id": "622dced0-b35d-a01c-8a32-4b14d928dd5d",
            "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 -reconfigureを用いて、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" "sapporo" {}
resource "terraform_data" "yebisu" {}

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

$ terraform init -reconfigure

/.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": []
        }
    ]
}

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

blog
├─ .terraform
│  └ terraform.tfstate # Backendの情報が記述されたファイル
└─ main.tf

ここで、terraform planを実行します。

すると、以下のように新規リソースの作成が実行結果として表示されます。

$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # terraform_data.sapporo will be created
  + resource "terraform_data" "sapporo" {
      + id = (known after apply)
    }

  # terraform_data.yebisu will be created
  + resource "terraform_data" "yebisu" {
      + id = (known after apply)
    }

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

これはS3で作成したterraform_dataがローカルの状態ファイルへ移行されていないことを示しています。

terraform applyを実行することで、ローカルへ/terraform.tfstateが作成されます。

blog
├─ .terraform
│  └ terraform.tfstate # Backendの情報が記述されたファイル
├─ main.tf
└─ terraform.tfstate

ローカルでterraform_dataを削除

続いて、S3とローカルで差分を作るために、main.tfファイルから"terraform_data" "sapporo"リソースの定義をコメントアウトし、

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" "sapporo" {}
resource "terraform_data" "yebisu" {}

リソースを削除します。

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

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

/terraform.tfstate.backupは最終更新の一つ前の状態がバックアップとして、保存されます。

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

blog
├─ .terraform
│  └ terraform.tfstate # Backendの情報が記述されたファイル
├─ main.tf
├─ terraform.tfstate # 現在のリソース状態が記述されたファイル
└─ terraform.tfstate.backup # 現在のリソース状態へ変更する前の状態ファイルのバックアップ

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

次に、main.tfファイルを編集して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" "sapporo" {}
resource "terraform_data" "yebisu" {}

再びterraform init -reconfigureを実行すると、「既にファイルが存在するが、上書きをするかどうか」を問われますので、「yes」を選択します。

$ terraform init -reconfigure    

Initializing the backend...
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend to the
  newly configured "s3" backend. An existing non-empty state already exists in
  the new backend. The two states have been saved to temporary files that will be
  removed after responding to this query.

  Previous (type "local"): xxxx/1-local.tfstate
  New      (type "s3"): xxxx/2-s3.tfstate

  Do you want to overwrite the state in the new backend with the previous state?
  Enter "yes" to copy and "no" to start with the existing state in the newly
  configured "s3" backend.

  Enter a value:yes

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

terraform planを実行すると、terraform_dataリソースの削除に関する差分が発生しません。

これはterraform init -reconfigureによって、既存のtfstateファイルを上書きされるためです。

ローカルのtfstateファイルの中身は空っぽになっており、/terraform.tfstate.backupファイルには、ローカルからS3に移した際の状態が記録されております。

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

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

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

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

# resource "terraform_data" "sapporo" {}
resource "terraform_data" "yebisu" {}

移行後にterraform planを実行すると、新しいリソースの作成が表示されます。

これにより、tfstateファイルが再構成されていることがわかります。

terraform applyすると、新しいリソースの作成に成功します。

terraform apply -auto-approve

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # terraform_data.yebisu will be created
  + resource "terraform_data" "yebisu" {
      + id = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
terraform_data.yebisu: Creating...
terraform_data.yebisu: Creation complete after 0s [id=ab9d0a01-afc6-b2bc-b9cc-2ca32cd10fb5]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

ローカルでBackendをterraform init -reconfigureした際は、バックアップファイルが作成されましたが、S3で行なった場合は作成されませんでした。

注意点

一つだけterraform init -reconfigureを実施するときに注意が必要です。

以下のように、Backendを明示的に記述しなかった場合、デフォルトのBackendであるローカルが設定されます。

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

resource "terraform_data" "sapporo" {}
resource "terraform_data" "yebisu" {}

しかし、terraform init -reconfigureを上記のコードで実行すると、Backendが記述されている/.terraform/terraform.tfstate内が書き替わらず、Backendの不整合が生じてしまいます。

$ terraform plan
╷
│ Error: Backend initialization required, please run "terraform init"
│
│ Reason: Unsetting the previously set backend "s3"
│
│ The "backend" is the interface that Terraform uses to store state,
│ perform operations, etc. If this message is showing up, it means that the
│ Terraform configuration you're using is using a custom configuration for
│ the Terraform backend.
│
│ Changes to backend configurations require reinitialization. This allows
│ Terraform to set up the new configuration, copy existing state, etc. Please run
│ "terraform init" with either the "-reconfigure" or "-migrate-state" flags to
│ use the current configuration.
│
│ If the change reason above is incorrect, please verify your configuration
│ hasn't changed and try again. At this point, no changes to your existing
│ configuration or state have been made.

そのため、S3からローカルへterraform init -reconfigureする際は、以下のように明示的にBackendを記述する必要があります。

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

resource "terraform_data" "sapporo" {}
resource "terraform_data" "yebisu" {}

ただ、S3からローカルへterraform init -migrate-stateを行う際は、明示的に記述を行わなくても、自動で/.terraform/terraform.tfstateファイルは更新されます。

まとめ

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

  • Backend設定の.terraform/terraform.tfstateファイルは自動的に更新される
  • ローカルへのterraform init -reconfigure実行時には、ローカルにterraform.tfstate.backupファイルが作成されるが、S3への実行時には作成されない
  • /terraform.tfstateファイルは既存のBackendから新しいBackendへコピーされるが、既にtfstateファイルが存在する場合には、上書きされるかどうかを問われる
  • S3からローカルへterraform init -reconfigureする際は、ローカルのBackendを明示的に記述する必要がある

既存のtfstateファイルが存在する場所に、terraform init -reconfigureを実行すると、tfstateファイルが上書きされてしまうので、この点にも注意が必要です。

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

Shun

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

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら