Terraformのstateをリモートで管理する

CloudFormationでは、スタックの状態(state)はAWS側で管理されているが、Terraformの場合、デフォルトでは管理されません。

状態管理を設定せずに`terraform apply`コマンドを実行すると、ローカル端末に状態管理ファイルが生成されます。(`terraform.tfstate`という名で作成されます。 )



## 状態管理のメリット

メリットは以下の通りです。

- リモートに状態を保存と状態のロックができるため、状態の破損を防ぐことができる
- リモートに状態を保存することで、複数人で同じ状態を共有できる



ローカルで管理していたら、PCが壊れてインフラ管理できなくなったり、状態管理ファイルを紛失したりするリスクがなくなります。



## 実装してみる



### 状態管理用のスタックを用意する

Terraformでは、状態管理にS3とDynamoDBを使用できます。

S3に状態管理ファイルを保管して、DynamoDBでコンフリクトを防ぐ構成となっています。

また、状態管理用スタックについて公式では以下のように管理することを推奨しているので、別管理した方が良さそうです。



以下引用 : 

> Terraform is an administrative tool that manages your infrastructure, and so ideally the infrastructure that is used by Terraform should exist outside of the infrastructure that Terraform manages. 

引用元 : [公式サイト - Multi-account AWS Architecture](https://www.terraform.io/docs/backends/types/s3.html#multi-account-aws-architecture)



ここでは、面倒なので以下のスタックを実行して構築します。

テーブルは`LockID`という名前のプライマリーキーを持つ必要があるのでその設定だけは入れましょう。

```
/**
 * state管理用のS3 & DynamoDB 作成用スタック
 * stack ディレクトリ内のtf実行前にこちらを先に実行する
*/

terraform {
  required_version = "0.12.5"
}

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

resource "aws_s3_bucket" "terraform_state" {
  // ダメだったら別の名前にする
  bucket = "terraform-state-kento75"
  versioning {
    enabled = true
  }
}

resource "aws_dynamodb_table" "terraform_state_lock" {
  name           = "terraform_state_lock"
  read_capacity  = 1
  write_capacity = 1
  hash_key       = "LockID"

  attribute {
  　// 必須
    name = "LockID"
    type = "S"
  }
}

```



### 簡単なスタックを作成する



`backend`を設定することで先ほど作成したS3とDynamoDBによる状態管理を実装できます。

```
terraform {
  required_version = "0.12.5"

  // state lock 設定
  // state管理用 S3, DynamoDB は別管理
  backend "s3" {
    bucket         = "terraform-state-kento75" # state管理用バケット
    region         = "ap-northeast-1"
    key            = "terraform.tfstate"
    encrypt        = true
    dynamodb_table = "terraform_state_lock"
  }
}

```



その他に実装したスタックは[こちら](https://github.com/automation-tips/terraform-state-demo) を参照



`terraform init`コマンドを実行すると、backendがS3になっていることが確認できます。

```console
$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```



スタックを作成してみます。

```console
$ terraform apply
```



実行に成功すると、S3バケット内に`terraform.tfstate`が作成されます。
![スクリーンショット 2020-02-29 19.40.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180939/28f9b607-122f-cbfb-f5da-134c601cb097.png)


DynamoDBにも追加されます。

![スクリーンショット 2020-02-29 19.42.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180939/9a7b5ba7-5cb3-67ef-9a59-178e85cf5628.png)



### 本当にロックされるのか試す

別セッションで同時に`terraform plan`を実行した時にどのような挙動をするのかを確認してみます。

先に実行した方は正常に動作しましたが、後から実行した方は以下のようにエラーとなります。

```console
$ terraform plan

Error: Error locking state: Error acquiring the state lock: ConditionalCheckFailedException: The conditional request failed
        status code: 400, request id: 86SS1900A1DIKG5ORN24PRLDG3VV4KQNSO5AEMVJF66Q9ASUAAJG
Lock Info:
  ID:        aff55b1a-0cbf-d759-7e3b-e778a391925a
  Path:      terraform-state-kento75/terraform.tfstate
  Operation: OperationTypePlan
  Who:       kento@KentonoMacBook-Pro.local
  Version:   0.12.5
  Created:   2020-02-29 10:46:17.720234 +0000 UTC
  Info:      


Terraform acquires a state lock to protect the state from being written
by multiple users at the same time. Please resolve the issue above and try
again. For most commands, you can disable locking with the "-lock=false"
flag, but this is not recommended.

```



## まとめ

Terraformのbackend周りの実装を行うことで、複数人での運用時の事故を防ぐことができるようになるので、なるべくなら実装した方が良いと思います。