HCP Terraform における安全な state 管理 ~ serial と lineage ~

## はじめに

terraform を利用して state ファイルを覗いてみると、いくつかのメタ情報が含まれることに気付くかもしれません。
この中で `serial` と `lineage` は state を安全に管理するために重要な役目を持ちます。
HCP Terraform を利用する場合を想定し、これらの周辺の概念をご紹介します。

## 1. State のバージョン管理

HCP Terraform では、Workspace の **state がバージョン管理**され、常に最新の state が利用される仕組みとなっています。

Terraform は最新の状態情報を基にリソースを管理・適用するため、意図しない古い設定が反映されるリスクを軽減します。

:::note info
**無料利用枠における注意点**
無料プランの場合、100 件を超える state のバージョンは 6 ヶ月後に削除されるという制限があります。
https://support.hashicorp.com/hc/en-us/articles/4414055267603-HCP-Terraform-Limits
このルールにより、長期間の履歴を保持する必要がある場合は有料プランの利用も検討する必要があります
:::

## 2. 任意バージョンの State ダウンロード

HCP Terraform の大きな特徴のひとつに、**任意のバージョンの state をダウンロードできる**点があります。

- 過去の状態に戻って変更点を確認したい場合や、特定のバージョンとの比較を行いたい場合に、必要な情報を直接取り出すことが可能です
- この柔軟性により、トラブルシューティングやリソースの監査が容易になります

下記のように、Workspace の States の画面で過去の state が一覧表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769407/ac68cc4d-7717-4825-9736-7a50a599d202.png)

リンクを辿ると対象の state の内容が表示され、「Download」ボタンでダウンロード可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769407/eb78c4f1-466e-4ad1-bfcd-a0490ffbc754.png)

## 3. Serial と Lineage の重要性

state 管理の核心をなすのが、**serial** と **lineage** の概念です。
state の内容を確認すると、下記のような記載になっていると思います。

```json
{
  ...
  "serial": 123
  "lineage": "e6e9a4c3-7a17-4f0b-9eaa-402e35fbd82b"
  ...
}
```

- **Serial**

  - 各 state のバージョンごとにインクリメントされる番号で、バージョンの一貫性と順序を管理する役割を持っています
  - 新しい state をアップロードする際、serial は通常、前回の state に比べて増加している必要があり、これによりバージョン間の整合性が保たれます

- **Lineage**
  - state の「系統」を表す一意の識別子であり、特定の terraform コードから生成された state だけが有効であることを保証する仕組みです

https://developer.hashicorp.com/terraform/cli/commands/state/push

この仕組みにより、予期せぬ誤操作や不整合の発生しにくい安全な state 管理が実現されています。

## 4. コマンドによる state 操作

state は terraform apply 時に自動的に更新されますが、state の誤操作時や特殊な状況の備えとしていくつかコマンドが用意されています。

https://developer.hashicorp.com/terraform/cli/state/recover

- **terraform state pull**
  - 最新の state をバックエンドから取得します
- **terraform state push**
  - 指定したパスにある state をバックエンドに追加します
- **terraform force-unlock**
  - バックエンドがロック状態の場合、強制的にロックを解除します

### terraform state push 時の誤操作防止

`terraform state push` コマンドの使用時に任意の state ファイルをバックエンドに追加できます。
このため、全く関係無い terraform コードから作成された state を追加することも理論上は可能であり、その場合は予期しない問題が発生する可能性が高いです。
また、誤って古い state をアップロードすることで問題が発生する可能性もあります。

このため、`terraform state push` コマンド実行時は、下記の場合のみ成功します。

- `serial` が現在の Workspace の最新の state より大きい
  - `serial` が一致している場合、state の内容に違いがなければコマンド自体は成功しますが state は変化しません
- `lineage` が現在の Workspace の state と等しい

#### serial が小さい場合のエラー

```bash
$ terraform state push sv-xxx.tfstate
Failed to write state: cannot import state with serial 1 over newer state with serial 3
```

#### lineage が異なる場合のエラー

```bash
$ terraform state push sv-xxx.tfstate
Failed to write state: cannot import state with lineage "xxx-xxx-xxx-xxx-xxx" over unrelated state with lineage "yyy-yyy-yyy-yyy-yyy"
```

## 5. API による Workspace への state 追加

state 操作について何らかの自動化を実現したい場合、terraform コマンドを使用すると都度 Workspace への接続が必要になるため、必要な権限を持ったトークンを用意して API で操作するのも効果的です。
例えば下記の API を使用することでも既存の Workspace に State を追加することができます。

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

下記のチュートリアルにあるように、state の md5 や base64 エンコードした値を生成し、準備した state の lineage や serial の値を使用してペイロードを構成することで、state の追加を実施できます。

https://developer.hashicorp.com/terraform/tutorials/cloud/cloud-state-api#modify-and-create-the-state-payload

```bash
$ curl \
  --header "Authorization: Bearer "$TFC_TOKEN"" \
  --header "Content-Type: application/vnd.api+json" \
  --request POST \
  --data @payload.json \
  "https://app.terraform.io/api/v2/workspaces/"$WORKSPACE_ID"/state-versions" | jq .
{
  "data": {
    "id": "sv-xxx",
    "type": "state-versions",
    "attributes": {
      ...
      "serial": 1,
      ...
    }
  }
}
```

この際に lineage の値を間違えると下記のようにエラーになります。

```bash
$ curl ...
{
  "errors": [
    "The lineage provided in the state file does not match the value\ncurrently known by HCP Terraform. This means that the provided\nstate is most likely not related to the target workspace. Check that\nthe backend configuration is correct and try again."
  ]
}
```

## おわりに

本記事では、HCP Terraform における state 管理の仕組み、特に serial と lineage の役割について解説しました。
この2つの値は、チーム開発における state の整合性を保ち、誤操作を防止するための重要な要素です。

state のバージョン管理や任意のバージョンへのアクセス機能は、変更履歴の確認やトラブルシューティングを容易にします。
また、`terraform state push` コマンドや API による state 操作時に serial と lineage が整合性チェックとして機能することで、安全な state 管理が実現されています。

これらの機能を正しく理解し活用することで、複数人での開発環境や CI/CD パイプラインにおいても、state の不整合リスクを最小限に抑えた Terraform 運用が可能になります。
特に本番環境のインフラ管理では、こうした安全機構の重要性が一層高まります。

まずはご自身のプロジェクトで実際に state ファイルを確認し、serial と lineage の値がどのように異なr変化するか観察してみることをお勧めします。
state 管理の理解を深めることで、より安全で効率的な Terraform 運用につながるでしょう。