TerraformでAWS VPCを変更する(再作成が発生する場合、しない場合)

TerraformでAWS VPCを変更するコード(コマンド)

CIDRのように一度設定した後は変更できないようなパラメータ(※)を変更しようとした場合、VPCの再作成することになるはず。
再作成が発生してしまう場合、どのような挙動やメッセージとなるのかを確認する。

※ だいぶ前から[CIDRの拡張は可能になってた](https://aws.amazon.com/jp/premiumsupport/knowledge-center/vpc-ip-address-range/)んですね。知らなかった。

## 実行環境
- Windows 10 Home (1919)
- Git Bash (git version 2.25.1.windows.1)
- AWS CLI (aws-cli/2.0.3 Python/3.7.5 Windows/10 botocore/2.0.0dev7)
- Terraform (v0.12.26)


## 再作成が発生してしまうパターン

### 実行前の状態確認

```
$ aws ec2 describe-vpcs  --region=us-west-2
{
    "Vpcs": [
        {
            "CidrBlock": "10.10.0.0/16",
            "DhcpOptionsId": "dopt-0ebee8b328487036e",
            "State": "available",
            "VpcId": "vpc-0aad429d595d7ac0d",
            "OwnerId": "679788997248",
            "InstanceTenancy": "default",
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-0a18a1435cc1d68ab",
                    "CidrBlock": "10.10.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": [
                {
                    "Key": "CostGroup",
                    "Value": "prj01"
                },
                {
                    "Key": "Name",
                    "Value": "prj01VPC"
                }
            ]
        }
    ]
}
```

VPC作成時のmain.tfと同じ。[こちら](https://qiita.com/TG1050/items/202862069c0b89544275)。

#### main.tfを編集して、CIDRを変更

```
$ diff main.tf main.tf.old
7c7
<   cidr_block = "10.20.0.0/16"
---
>   cidr_block = "10.10.0.0/16"
```

#### planで変更点を確認

```
$ ../terraform.exe plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aws_vpc.prj01VPC: Refreshing state... [id=vpc-0aad429d595d7ac0d]

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

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_vpc.prj01VPC must be replaced
-/+ resource "aws_vpc" "prj01VPC" {
      ~ arn                              = "arn:aws:ec2:us-west-2:679788997248:vpc/vpc-0aad429d595d7ac0d" -> (known after apply)
        assign_generated_ipv6_cidr_block = false
      ~ cidr_block                       = "10.10.0.0/16" -> "10.20.0.0/16" # forces replacement
      ~ default_network_acl_id           = "acl-01ca95405b2203904" -> (known after apply)
      ~ default_route_table_id           = "rtb-000535d0bee883194" -> (known after apply)
      ~ default_security_group_id        = "sg-00c024ac152dee0af" -> (known after apply)
      ~ dhcp_options_id                  = "dopt-0ebee8b328487036e" -> (known after apply)
      ~ enable_classiclink               = false -> (known after apply)
      ~ enable_classiclink_dns_support   = false -> (known after apply)
      ~ enable_dns_hostnames             = false -> (known after apply)
        enable_dns_support               = true
      ~ id                               = "vpc-0aad429d595d7ac0d" -> (known after apply)
        instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      ~ main_route_table_id              = "rtb-000535d0bee883194" -> (known after apply)
      ~ owner_id                         = "679788997248" -> (known after apply)
        tags                             = {
            "CostGroup" = "prj01"
            "Name"      = "prj01VPC"
        }
    }

Plan: 1 to add, 0 to change, 1 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.
```

- 「must be replaced」と表示がある
- 変更するCIDRに「forces replacement」と表示がある
- 「1 to add」と「1 to destroy」と表示がある
上記より、設定の変更ではなく、再作成が発生してしまうことがplanからもわかる。


#### apply

```
$ ../terraform.exe apply
aws_vpc.prj01VPC: Refreshing state... [id=vpc-0aad429d595d7ac0d]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_vpc.prj01VPC must be replaced
-/+ resource "aws_vpc" "prj01VPC" {
      ~ arn                              = "arn:aws:ec2:us-west-2:679788997248:vpc/vpc-0aad429d595d7ac0d" -> (known after apply)
        assign_generated_ipv6_cidr_block = false
      ~ cidr_block                       = "10.10.0.0/16" -> "10.20.0.0/16" # forces replacement
      ~ default_network_acl_id           = "acl-01ca95405b2203904" -> (known after apply)
      ~ default_route_table_id           = "rtb-000535d0bee883194" -> (known after apply)
      ~ default_security_group_id        = "sg-00c024ac152dee0af" -> (known after apply)
      ~ dhcp_options_id                  = "dopt-0ebee8b328487036e" -> (known after apply)
      ~ enable_classiclink               = false -> (known after apply)
      ~ enable_classiclink_dns_support   = false -> (known after apply)
      ~ enable_dns_hostnames             = false -> (known after apply)
        enable_dns_support               = true
      ~ id                               = "vpc-0aad429d595d7ac0d" -> (known after apply)
        instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      ~ main_route_table_id              = "rtb-000535d0bee883194" -> (known after apply)
      ~ owner_id                         = "679788997248" -> (known after apply)
        tags                             = {
            "CostGroup" = "prj01"
            "Name"      = "prj01VPC"
        }
    }

Plan: 1 to add, 0 to change, 1 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc.prj01VPC: Destroying... [id=vpc-0aad429d595d7ac0d]
aws_vpc.prj01VPC: Destruction complete after 1s
aws_vpc.prj01VPC: Creating...
aws_vpc.prj01VPC: Creation complete after 9s [id=vpc-06bc5f188ef3b2fe8]

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
```

#### 変更後の確認

```
$ aws ec2 describe-vpcs  --region=us-west-2
{
    "Vpcs": [
        {
            "CidrBlock": "10.20.0.0/16",
            "DhcpOptionsId": "dopt-0ebee8b328487036e",
            "State": "available",
            "VpcId": "vpc-06bc5f188ef3b2fe8",
            "OwnerId": "679788997248",
            "InstanceTenancy": "default",
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-0373fb92a40bc4aba",
                    "CidrBlock": "10.20.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": [
                {
                    "Key": "CostGroup",
                    "Value": "prj01"
                },
                {
                    "Key": "Name",
                    "Value": "prj01VPC"
                }
            ]
        }
    ]
}
```

「"CidrBlock": "10.20.0.0/16"」となっており、また、VpcIdが「vpc-0aad429d595d7ac0d」から「vpc-06bc5f188ef3b2fe8」に代わっており、確かに再作成されていることがわかります。


## 再作成は発生しないパターン

#### main.tfを編集して、タグを変更

```
$ diff main.tf main.tf.old2
10c10
<     Name = "prj01VPC version2"
---
>     Name = "prj01VPC"
```

### planで変更点を確認

```
$ ../terraform.exe plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aws_vpc.prj01VPC: Refreshing state... [id=vpc-06bc5f188ef3b2fe8]

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

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_vpc.prj01VPC will be updated in-place
  ~ resource "aws_vpc" "prj01VPC" {
        arn                              = "arn:aws:ec2:us-west-2:679788997248:vpc/vpc-06bc5f188ef3b2fe8"
        assign_generated_ipv6_cidr_block = false
        cidr_block                       = "10.20.0.0/16"
        default_network_acl_id           = "acl-0ec7d4e945ff1d7f0"
        default_route_table_id           = "rtb-0d64bb221c3f9d1ff"
        default_security_group_id        = "sg-03b425d2c42c1e984"
        dhcp_options_id                  = "dopt-0ebee8b328487036e"
        enable_classiclink               = false
        enable_classiclink_dns_support   = false
        enable_dns_hostnames             = false
        enable_dns_support               = true
        id                               = "vpc-06bc5f188ef3b2fe8"
        instance_tenancy                 = "default"
        main_route_table_id              = "rtb-0d64bb221c3f9d1ff"
        owner_id                         = "679788997248"
      ~ tags                             = {
            "CostGroup" = "prj01"
          ~ "Name"      = "prj01VPC" -> "prj01VPC version2"
        }
    }

Plan: 0 to add, 1 to change, 0 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.
```

さきほどとは異なり、「will be updated in-place」との表記になっていることを確認。
また、「add」と「destroy」は0で、「1 to change」となっていることも確認。

#### applyで変更を適用

```
$ ../terraform.exe apply
aws_vpc.prj01VPC: Refreshing state... [id=vpc-06bc5f188ef3b2fe8]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_vpc.prj01VPC will be updated in-place
  ~ resource "aws_vpc" "prj01VPC" {
        arn                              = "arn:aws:ec2:us-west-2:679788997248:vpc/vpc-06bc5f188ef3b2fe8"
        assign_generated_ipv6_cidr_block = false
        cidr_block                       = "10.20.0.0/16"
        default_network_acl_id           = "acl-0ec7d4e945ff1d7f0"
        default_route_table_id           = "rtb-0d64bb221c3f9d1ff"
        default_security_group_id        = "sg-03b425d2c42c1e984"
        dhcp_options_id                  = "dopt-0ebee8b328487036e"
        enable_classiclink               = false
        enable_classiclink_dns_support   = false
        enable_dns_hostnames             = false
        enable_dns_support               = true
        id                               = "vpc-06bc5f188ef3b2fe8"
        instance_tenancy                 = "default"
        main_route_table_id              = "rtb-0d64bb221c3f9d1ff"
        owner_id                         = "679788997248"
      ~ tags                             = {
            "CostGroup" = "prj01"
          ~ "Name"      = "prj01VPC" -> "prj01VPC version2"
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc.prj01VPC: Modifying... [id=vpc-06bc5f188ef3b2fe8]
aws_vpc.prj01VPC: Still modifying... [id=vpc-06bc5f188ef3b2fe8, 10s elapsed]
aws_vpc.prj01VPC: Modifications complete after 17s [id=vpc-06bc5f188ef3b2fe8]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
```

#### 実行後の確認

```
$ aws ec2 describe-vpcs  --region=us-west-2
{
    "Vpcs": [
        {
            "CidrBlock": "10.20.0.0/16",
            "DhcpOptionsId": "dopt-0ebee8b328487036e",
            "State": "available",
            "VpcId": "vpc-06bc5f188ef3b2fe8",
            "OwnerId": "679788997248",
            "InstanceTenancy": "default",
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-0373fb92a40bc4aba",
                    "CidrBlock": "10.20.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": [
                {
                    "Key": "CostGroup",
                    "Value": "prj01"
                },
                {
                    "Key": "Name",
                    "Value": "prj01VPC version2"
                }
            ]
        }
    ]
}
```

VpcIdも「vpc-06bc5f188ef3b2fe8」のままであり、再作成されていないことがわかる。

## メモ

#### 「-out」パラメータ

> 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.

コマンドの末尾に上記のメッセージが出力されていた。
「-out」パラメータでプランを保存できる、と。

公式マニュアルは[これ](https://www.terraform.io/docs/commands/plan.html)と[これ](https://learn.hashicorp.com/terraform/development/running-terraform-in-automation)。

planで確認した際とapplyする際に、環境やTerraformのコードが変更されてしまったことによる想定外の変更を回避するための機能とのこと。