StatefileをAmazon S3からHCP Terraformに移行してみた(HCP Terraform API利用、VCS Driven Workflow)

StatefileをAmazon S3からHCP Terraformに移行してみた(HCP Terraform API利用、VCS Driven Workflow)

Clock Icon2025.03.17

TerraformのStatefileをS3から、HCP Terraform Workspace(VCS Driven Workflow)にHCP Terraform APIを使って移行してみます。

やってみた

vscode-drop-1742172152132-0c6w5hd0f4eh.png

ざっくりした流れは以下です。

  1. S3からStatefileをダウンロード
  2. HCP Terraform Workspaceを作成
  3. HCP Terraform APIを使って、WorkspaceにStatefileをアップロード

https://developer.hashicorp.com/terraform/tutorials/state/cloud-migrate

ディレクトリ構成(移行前)

今回は以下のディレクトリ構成を例にします。

環境差異をtfvarsで表現するパターンです。それに伴い、backend設定もterraform.tfbackendで表現します。

.
├── envs/
│   ├── prod/
│   │   ├── terraform.tfvars
│   │   └── terraform.tfbackend
│   └── dev/
│       ├── terraform.tfvars
│       └── terraform.tfbackend
└── main.tf

backend切り替えや環境ごとのPlan・Applyは以下のように実施します。

terraform init -reconfigure -backend-config="envs/dev/terraform.tfbackend"
terraform plan -var-file envs/dev/terraform.tfvars
terraform apply -var-file envs/dev/terraform.tfvars

Terraformコード(移行前)

VPCを作成するシンプルなコードです。

main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.90.1"
    }
  }
  backend "s3" {}
}

provider "aws" {
  region = "ap-northeast-1"
}

variable "env" {
  description = "The environment name"
  type        = string
}

resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "${var.env}"
  }
}
envs/test/terraform.tfbackend
bucket  = "<Terraform Stateバケット名>"
region  = "ap-northeast-1"
key     = "<Statefileファイルパス>" # example: "hoge/terraform.tfstate"
encrypt = true
envs/test/terraform.tfvars
env = "dev"

HCP Terraformログイン

ローカルからHCP Terraformにログインします。

terraform login

https://zenn.dev/chario/books/tfc-aws-introductory-book/viewer/05_02_aws_iam_role_create#ローカルからhcp-terraformへの接続

Statefileをダウンロード

Statefileをローカルにダウンロードします。

Statefileの数分作業を繰り返します。

# AWS認証情報をセット
terraform init -reconfigure -backend-config="envs/dev/terraform.tfbackend"
terraform state pull > dev.tfstate

以下のようにファイルが保存されていればOKです。

dev.tfstate
prod.tfstate

HCP Terraform Workspaceの作成

HCP Terraformのコンソールを開きます。

Workspace -> New -> Workspace -> [任意のプロジェクト] -> Createの順に選択します。

workflow: Version Control Workflow
VCS Provider: 移行対象のVCS Provider
repository: 移行対象のrepository
Workspace Name: Workspace名 ※
Advanced options > Terraform Working Directory: Terraform実行ディレクトリ
※環境毎にWorkspaceを作成するため環境名(dev,prod)をつけることを推奨します

vscode-drop-1741853607582-y28dpw2kha.png

以下のようにVariableの設定を求められますので、必要な値を設定しています。

terraform.tfvarsの内容を設定するイメージです。

vscode-drop-1741853731018-fjipg8m4hkf.png

WorkspaceからAWSへアクセスするための、Variables Setも設定しておいてください。

https://dev.classmethod.jp/articles/hcp-terraform-iam-role-cfn/

https://dev.classmethod.jp/articles/multi-account-hcp-terraform-iam-role-cfn-stackset/

Statefile移行前の操作ミスによるApplyを防止するために、WorkspaceをLockしておきます。

vscode-drop-1741912949200-x2535s778p.png

上記作業をStatefileの数分繰り返します。

例えば、prod.tfstatedev.tfstateがある場合は、それぞれprod workspacedev workspaceを作成します。

HCP Terraform Tokenの準備

HCP Terraform APIを使うため、トークンを設定します。

terraform login実行済みの場合は、デフォルトでは以下にトークンが保存されています。

cat ~/.terraform.d/credentials.tfrc.json
出力例
{
  "credentials": {
    "app.terraform.io": {
      "token": "<トークン>"
    }
  }
}

トークンをシェル変数にセットします。

TOKEN=<トークン>

HCP Terraform APIを使用してStatefileを移行する

作成したWorkspaceにStatefileを移行します。

以下のAPIを利用します。

https://developer.hashicorp.com/terraform/cloud-docs/api-docs/state-versions#create-a-state-version

payload用のjsonファイルを準備します。

dev_payload.json
{
"data": {
    "type":"state-versions",
    "attributes": {
    "serial": 1,
    "md5": "<Statefileのmd5ハッシュ>",
    "state": "<Statefileをbase64エンコードした値>"
    }
}
}

data.serialはStatefileのserialと一致させる必要があります。

data.m5にセットする値は以下のコマンドで作成します。

md5sum dev.tfstate
出力例
690a3f8ae079c629494a52c68757d585  dev.tfstate

data.stateは値は以下のコマンドで作成します。

cat dev.tfstate | base64
出力例
 dGVycmFmb3JtLnRmc3RhdGUK
WS_ID="<Workspace ID>" # 「ws-」から始まるID、Workspace画面から確認可能
curl \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/vnd.api+json" \
  --request POST \
  --data @prod_payload.json \
  https://app.terraform.io/api/v2/workspaces/$WS_ID/state-versions

Statefile移行が成功すると、HCP Terraform上からStatefileを確認できます。

vscode-drop-1741913040333-nnvtbdmjd.png

vscode-drop-1741913146450-w26kx44x57r.png

この作業も、StatefileとWorkspace IDを変更して、Statefileの数分繰り返します。

https://developer.hashicorp.com/terraform/cloud-docs/migrate#migrate-state-using-the-api

動作確認

HCP Terraform上からPlanを実行して、差分が表示されないことを確認します。

Workspace上部のUnlockを選択しWorkspaceのlockを解除します。

New Run -> Run Type: Plan onlyの順に選択します。

vscode-drop-1741913638953-9zwk65a837.png

Planが正常に実行されて、No changes(差分なし)になっていることを確認します。

vscode-drop-1741913713538-rpcqkqcb1y8.png

一応VCSとの接続が正常に行われているか確認するため、適当なPull Requestを作成しました。こちらでもPlanが実行されることを確認できました。(vpcにタグを付けるPRのため、add,1になっています。)

vscode-drop-1741914076479-vn6iowh5b2f.png

これでStatefileの移行は完了です。

backendブロックの置き換え

最後に、terraform.backendの部分をterraform.cloudに置き換えましょう。

main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.90.1"
    }
  }
-  backend "s3" {}
+  cloud {}
}

VCSへのコミットも忘れずに行いましょう。

terraform.cloudの部分は、HCP Terraform上で実行する際は必要ありません。

次に紹介する「Tips: HCP Terraform移行後のローカルからのTerraform CLIの実行」で必要です。

Tips: HCP Terraform移行後のローカルからのTerraform CLIの実行

tfファイルの変更時にローカルからterraform planを実行して、変更差分を確認したいことがあると思います。

今までは、-backend-config-var-fileを指定して環境を切り替えていました。

terraform init -reconfigure -backend-config="envs/dev/terraform.tfbackend"
terraform plan -var-file envs/dev/terraform.tfvars

移行後は、環境変数で切り替えることができます。

export TF_CLOUD_ORGANIZATION=<HCP Terraform Organizations名>
export TF_WORKSPACE=<ワークスペース名>
terraform plan

CLIで移行する

vars-fileで環境を切り替えるパターンだと、CLIで移行するのは少し手順が煩雑です。

おすすめは、上記で紹介したAPIを使う方法です。

逆に、環境毎ディレクトリを分けるパターン(env/dev/main.tf, env/prod/main.tfのような)ならCLIがおすすめです。

参考までに、vars-fileで環境を分けるパターンでCLIで移行する方法に少し触れます。

vars-fileで環境を分けるパターンでCLI手順が煩雑な理由は、terraform.backendterraform.cloudの記述を同時に書けないためです。

main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.90.1"
    }
  }
  # これはできない
  backend "s3" {}
  cloud {
    organization = "my-org"
    workspaces {
        name = "my-workspace"
    }
  }
}

そのため、以下のように1 Statefileの移行毎に各設定を削除・追加が必要です。

  • (dev・prod)VCS Driven Workflowを作成
  • (dev)reconfigureで環境切り替え
    • terraform init -reconfigure -backend-config="envs/dev/terraform.tfbackend"
  • terraform.backendを削除、terraform.cloudを追加
  • StatefileをS3からHCP Terraformに移行
    • terraform init -migrate-state
  • terraform.cloudを削除・terraform.backendを追加
  • (prod)reconfigureで環境切り替え
    • terraform init -reconfigure -backend-config="envs/dev/terraform.tfbackend"
  • terraform.backendを削除、terraform.cloudを追加
  • StatefileをS3からHCP Terraformに移行
    • terraform init -migrate-state

個人的には、できないことは無いが環境切り替えが多く「ミスしやすそう」という印象です。

一方、Tokenのセットやbase64エンコードが不要な点は良いと思います。

対象Statefileの数が少ない場合はvars-fileで環境を分けるパターンでCLIを採用するのは有りだと思います。

おわりに

HCP TerraformへのStatefileの移行についてでした。

WorkspaceのWorkflow TypeはVCS Driven・CLI Drivenどちらでも手順は変わりません。

Workspaceを作成して、Terraform CLI(terraform init)やHCP Terraform APIを使ってStatefileを移行できます。

APIを使った移行を複数のStatefile移行を自動化するPythonスクリプトを作成する記事もあったので、興味がある方はご確認ください。

https://medium.com/hashicorp-engineering/migrating-a-lot-of-state-with-python-and-the-terraform-cloud-api-997ec798cd11

tf-migrateも今回の用途では利用できると思います。

こちらも追々試していきたいと思います。

以上、AWS事業本部の佐藤(@chari7311)でした。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.