terraformで各リージョン毎に作成済みのvpcをworkspace管理に移行する

# terraform.tfstateの整合性

下記の記事では新規にvpcを展開する時の作成方法です。
今回は既に展開されている個別のterraformで作成されたマルチリージョンのvpcをworkspaceで一括管理しましょうといった流れです。

https://qiita.com/instant_baby/items/7552d9e69c8546d2f1fd

簡潔に言うとtfstateを一つにまとめたいって事です。
まずは現状確認。


```
./
├── tokyo
│   ├── main.tf
│   ├── terraform.tfstate
│   ├── terraform.tfstate.backup
│   └── variable.tf
├── oregon
│   ├── main.tf
│   ├── terraform.tfstate
│   ├── terraform.tfstate.backup
│   └── variable.tf
└── virginia
    ├── main.tf
    ├── terraform.tfstate
    ├── terraform.tfstate.backup
    └── variable.tf
```

これだと各リージョン毎に管理している状態です。
これらの管理変更を一括で適用したい場合、煩雑になるので一回のapplyで全リージョンに適用したい。

目標は大体こんな感じの構成。

```
├── client.tf
├── internet_gateway.tf
├── main.tf
├── nat_gateway.tf
├── route_table.tf
├── routing.tf
├── subnet.tf
├── terraform.tfstate.d
│   ├── ap-northeast-1
│   ├── us-east-1
│   └── us-west-2
│       ├── terraform.tfstate
│       └── terraform.tfstate.backup
└── variables.tf
```

現状、各tfファイルmv等でを移行してplanを叩くと新規にリソースが作成されてしまう。
原因はtfstateの中身と今の構成との整合性が取れていない事で、現状の構成に合わせてtfstateファイルを上書きしようとしている為です。

planを叩くとリソースが入れ替わってしまう状況が確認できたので、terraform.tfstateファイルを編集して辻褄合わせをしたい。

余談だが、リソースを一つづつ書いたものをリスト化して纏めようとした時も同様に、
planを叩くと既存のリソースが入れ替わる挙動になる。

planの結果（一例）

```
  # aws_subnet.public[2] will be created
  + resource “aws_subnet” “public” {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = “us-west-2c”
      + availability_zone_id            = (known after apply)
      + cidr_block                      = “10.189.64.0/19"
      + id                              = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = true
      + owner_id                        = (known after apply)
      + tags                            = {
          + “Name”                                 = “public3"
        }
      + vpc_id                          = “vpc-hogehoge”
    }

  # aws_subnet.public_1d will be destroyed
  - resource “aws_subnet” “public_1d” {
      - arn                             = “arn:aws:ec2:us-west-2:332777730118:subnet/subnet-01a89dcb3070d5616" -> null
      - assign_ipv6_address_on_creation = false -> null
      - availability_zone               = “us-west-2d” -> null
      - availability_zone_id            = “usw2-az4" -> null
      - cidr_block                      = “10.189.64.0/19” -> null
      - id                              = “subnet-01a89dcb3070d5616" -> null
      - map_customer_owned_ip_on_launch = false -> null
      - map_public_ip_on_launch         = true -> null
      - owner_id                        = “332777730118” -> null
      - tags                            = {
          - “Name”                                 = “public3”
        } -> null
      - vpc_id                          = “vpc-hogehoge" -> null
    }
```


```
Plan: 19 to add, 1 to change, 19 to destroy.

------------------------------------------------------------------------

Note: You didn’t specify an “-out” parameter to save this plan, so Terraform
can’t guarantee that exactly these actions will be performed if
“terraform apply” is subsequently run.
```

じゃあ statefile自体を書き換えてしまえば良いのでは？となるのですが、このjsonを直接書き換えるのは公式では推奨されて無く、terraform state mvを使用することが推奨されている。

https://www.terraform.io/docs/cli/commands/state/list.html

書き方としては、AのstateをBに合わせたい時は

```
$ terraform state mv A B
```

この様に実行すればstateの書き換えが可能になる。

使用例


既存のリソースを削除せずにstateをあわせる時は下記を実行すればリソースはそのままにリファクタリング可能。

```
$ terraform state mv aws_subnet.public_1d aws_subnet.public[2]
```

stateファイルをmvするコマンドを各リソースに対してポチポチ実行してstateファイルの整合性を取るようにしていけば、各リソースの統合が可能になり、workspaceへの移行ができるようになる。

以下おまけ。

# stateファイルをs3にアップロードする

リソースをgitに上げるなどした時にstateファイルのみをs3に上げて、gitに上げたくない時等に使用。

```:main.tf
provider “aws” {
  region     = “us-west-2"
}

terraform {
  required_version = “>= 0.13.0”
  backend “s3" {
    bucket  = “terraform-state”
    region  = “us-west-2"
    key     = “test/state”
    profile = “terraform”
  }
}

resource “aws_s3_bucket” “terraform_state” {
  bucket = “terraform-state”
}
```

tertraform{}でstateファイルをs3に置くように設定している

```
terraform {

# terraformのバージョン
  required_version = “>= 0.13.0"

# backendをs3にすることでstateファイルをs3においている
  backend “s3” {

# s3バケットを指定。
# 既存のs3バケットを指定する必要がある。
# backend記述をs3作成のリソースを書いてまとめてapplyは出来ない
    bucket  = “terraform-state”
    region  = “us-west-2”
    key     = “test/state”
# ~/.aws/にある、apply先のprofileを指定する。
# ここの項目は必須。たとえここのmain.tfにアカウントキーを入力していても読んではくれない模様
    profile = “terraform”
  }
}
```

また、backendの設定に変数を使用する事は出来ない。
以下はkeyに変数を指定した場合のエラー

```
$ terraform init

Initializing the backend...
Backend configuration changed!

provider “aws” {
Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.



Error: Variables not allowed

  on main.tf line 12, in terraform:
  12:     key     = var.keys
```

 **Error: Variables not allowed** となる。

さらに、backendを書き換えた場合は、都度initしなくてはならない。
以下はbackendのs3バケットを変更した後のエラー

```
$ terraform apply
Backend reinitialization required. Please run “terraform init”.
Reason: Backend configuration changed for “s3"

The “backend” is the interface that Terraform uses to store state,
perform operations, etc. If this message is showing up, it means that the
Terraform configuration you’re using is using a custom configuration for
the Terraform backend.

Changes to backend configurations require reinitialization. This allows
Terraform to setup the new configuration, copy existing state, etc. This is
only done during “terraform init”. Please run that command now then try again.

If the change reason above is incorrect, please verify your configuration
hasn’t changed and try again. At this point, no changes to your existing
configuration or state have been made.


Error: Initialization required. Please see the error message above.
```

backendは一度指定したら気軽に変更が出来ないので注意が必要。