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

ざっくりした流れは以下です。
- S3からStatefileをダウンロード
- HCP Terraform Workspaceを作成
- HCP Terraform APIを使って、WorkspaceにStatefileをアップロード
ディレクトリ構成(移行前)
今回は以下のディレクトリ構成を例にします。
環境差異を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を作成するシンプルなコードです。
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}"
}
}
bucket = "<Terraform Stateバケット名>"
region = "ap-northeast-1"
key = "<Statefileファイルパス>" # example: "hoge/terraform.tfstate"
encrypt = true
env = "dev"
HCP Terraformログイン
ローカルからHCP Terraformにログインします。
terraform login
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)をつけることを推奨します

以下のようにVariableの設定を求められますので、必要な値を設定しています。
terraform.tfvarsの内容を設定するイメージです。

WorkspaceからAWSへアクセスするための、Variables Setも設定しておいてください。
Statefile移行前の操作ミスによるApplyを防止するために、WorkspaceをLockしておきます。

上記作業をStatefileの数分繰り返します。
例えば、prod.tfstateとdev.tfstateがある場合は、それぞれprod workspaceとdev 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を利用します。
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を確認できます。


この作業も、StatefileとWorkspace IDを変更して、Statefileの数分繰り返します。
動作確認
HCP Terraform上からPlanを実行して、差分が表示されないことを確認します。
Workspace上部のUnlockを選択しWorkspaceのlockを解除します。
New Run -> Run Type: Plan onlyの順に選択します。

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

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

これでStatefileの移行は完了です。
backendブロックの置き換え
最後に、terraform.backendの部分をterraform.cloudに置き換えましょう。
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.backendとterraform.cloudの記述を同時に書けないためです。
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スクリプトを作成する記事もあったので、興味がある方はご確認ください。
tf-migrateも今回の用途では利用できると思います。
こちらも追々試していきたいと思います。
以上、AWS事業本部の佐藤(@chari7311)でした。








