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)でした。








