はじめに

私はこれまで、AWS の構築では主に AWS CloudFormation を利用してきました。

今回、試しに Terraform を利用して構築を行ってみたところ、CloudFormation ではあまり意識することのなかった「state 管理」について考慮する必要があることを知りました。

CloudFormation 利用者の感覚では、「インフラの状態管理は AWS 側で行ってくれるもの」という認識が強かったため、Terraform で state を自分で管理する必要がある点には驚かされました。

CloudFormation では、スタックの状態管理は AWS 側の責務となっており、ユーザーが state の保存場所や排他制御を意識する場面はほとんどありません。

一方 Terraform では、state をどこに保存するか、チームでどのように共有するかまで考慮する必要があります。

本記事では、CloudFormation 経験者として Terraform の state 管理で戸惑った点をまとめます。

AWS CloudFormation では State を意識することが少なかった

AWS CloudFormation の状態管理

Developers can deploy and update compute, database, and many other resources in a simple, declarative style that abstracts away the complexity of specific resource APIs. AWS CloudFormation is designed to allow resource lifecycles to be managed repeatably, predictable, and safely, while allowing for automatic rollbacks, automated state management, and management of resources across accounts and regions.

【参考】What can developers do with AWS CloudFormation?

上記のドキュメントの automated state management の記述より AWS CloudFormation の状態管理は、 AWS CloudFormation の責務であることが読み取れます。

また、実際に私が AWS CloudFormation で AWS 構築を行ってきましたが、State 管理をした経験がありません。

AWS CloudFormation 利用時の感覚

AWS CloudFormation では、Change Set(変更履歴)で差分チェックを行うものの State 管理を行うことはなく、terraform.tfstateを目にすることもありませんでした。

そのため、State 管理する必要があるとは、まったく考えていませんでした。

Terraform で terraform.tfstateが生成された

terraform init を実行すると、.terraform が作成され、その配下に.terraform.tfstate が生成されました。

.
├── .terraform
│   ├── modules
│   │   ├── modules.json
│   │   └── vpc.vpc
~~~~~~~~~~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~~~~
│   └── terraform.tfstate ★ Stateファイル
├── .terraform.lock.hcl
├── backend.tf
├── main.tf
└── terraform.tfvars

terraform.tfstate の内容は以下のとおりです。

terraform.tfstate には、terraform で作成した AWS リソースの状態が記載されています。

  "version": 4,
  "terraform_version": "1.14.8",
  "serial": 24,
  "lineage": "wwwww-wwww-wwww-www-wwwwwwwwwwwwww",
  "outputs": {},
  "resources": [
    {
      "module": "module.vpc.module.vpc",
      "mode": "managed",
      "type": "aws_default_network_acl",
      "name": "this",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "index_key": 0,
          "schema_version": 0,
          "attributes": {
            "arn": "arn:aws:ec2:ap-northeast-1:yyyyyyyyyyyyyy:network-acl/acl-zzzzzzzzzzzzzzzzzzz",
            "default_network_acl_id": "acl-zzzzzzzzzzzzzzzzzzz",
            "egress": [
              {
                "action": "allow",
                "cidr_block": "",
                "from_port": 0,
                "icmp_code": null,
                "icmp_type": null,
                "ipv6_cidr_block": "::/0",
                "protocol": "-1",
                "rule_no": 101,
                "to_port": 0
              },
              {
                "action": "allow",
                "cidr_block": "0.0.0.0/0",
                "from_port": 0,
                "icmp_code": null,
                "icmp_type": null,
                "ipv6_cidr_block": "",
                "protocol": "-1",
                "rule_no": 100,
                "to_port": 0
              }
            ],
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~以下略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

terraform.tfstateの管理が必要

以下、Terraform において terraform.tfstateの管理が不可欠な理由です。

※ terraform.tfstate について、AWS CloudFormation では不要な運用だったので、このファイルをきちんと管理できる状態にしなければいけないということが、一番の戸惑いポイントで、ユーザー側の管理コストがかかってしまうということでした。

terraform.tfstateは Terraform のコードと AWS リソースを紐付ける

Terraform はコードと実際の AWS リソースを terraform.tfstateを介して紐付けています。

このファイルが失われると、Terraform は既存のリソースを認識できなくなり、最悪の場合、既存リソースがあるにもかかわらず新規作成を試みてエラーになるか、重複してリソースを作成してしまいます。

機密情報の保持

terraform.tfstateには、DB のパスワードや秘密鍵などの機密情報がプレーンテキストで含まれることがあります。

そのため、Git などのバージョン管理システムに安易にコミットしてはいけないという、セキュリティ上の管理責任がユーザー側に生じます。

terraform.tfstateの管理方法

terraform.tfstateを安全に保管する方法についてまとめていきます。

CloudFormation がバックエンドで行っていた「安全な状態管理」を Terraform で実現するには、「Backend」 という仕組みを利用します。

Backend とは

Backend とは、terraform.tfstateの保存場所と、扱いを定義することができる機能です。

terraform {
  backend "s3" {
    bucket         = "<バケット名>" # 保存先のバケット
    key            = "dev/terraform.tfstate" # キー
    region         = "ap-northeast-1" # 保存先のリージョン
    encrypt        = true # バケット保存時に暗号化するかどうか
    use_lockfile = true # 排他制御
  }
}

リモートバックエンド(S3)の利用

terraform.tfstateをローカルではなく、AWS の S3 バケットに配置します。ローカルではなく、S3 に保存することによって、チーム開発でもチーム間で State の共有を行うことが可能です。

また、user_lockfile の項目を有効化することで排他制御を有効化することが可能です。この設定を有効にすることで、Terraform の同時更新を防ぐことが可能となります。

バージョニングの有効化

S3 のバージョニング機能を有効にすることで、万が一操作ミスで State が壊れても、過去の正常な状態に復元できるように備えます。

アクセス制御:

State には機密情報が含まれるため、S3 バケットへのアクセス権限を最小限に絞り、IAM ロール等で厳格に管理します。

まとめ

私が Terraform を触ってみて、最も違いを感じたのが、ユーザーに「状態管理」の責務があるということでした。

Terraform において terraform.tfstate は非常に重要な情報になるため、Terraform の運用を開始するにあたって terraform.tfstate の管理は「最初に設計すべき最優先事項」だと言えます。

CloudFormation のように AWS に任せきりにするのではなく、Backend(S3)や use_lockfile を活用して、自分たちで安全な管理場所を定義する。

このステップを踏むことで、ようやくインフラ構築できると感じました。

Terraform について学び始めたばかりなので、その他の Terraform のメリット、デメリットについて理解できればと考えており、プロジェクトごとの最適なツールを選択できるようになれればと思います。

参考

バックエンドブロック構成の概要

Terraform S3

What can developers do with AWS CloudFormation?