『実践Terraform』でaws_instance.example must be replacedではなくaws_instance.example will be updated in-placeと表示された

## はじめに

Terraform初学者です。

『[実践Terraform](https://www.amazon.co.jp/dp/4844378139)』という書籍で勉強しています。

書籍の中で`aws_instance.example must be replaced`という出力が想定されている箇所がありました。

しかし、現在のバージョンで試してみると`aws_instance.example will be updated in-place`と出力されてしまいます。

この違いの原因について調べたので備忘録としてまとめます。

この記事におけるバージョンは **Terraform v1.9.5** です。

一方で書籍のバージョンは **Terraform v0.12.5** です。

## 問題

`第2章 基本操作`の`2.2 リソースの更新`のサンプルコードが対象です。

まず、次のようにEC2インスタンスにNameタグを追加して`terraform apply`した状態があります。

```terraform:example/main.tf
resource "aws_instance" "example" {
  ami = "ami-0c3fd0f5d33134a76"
  instance_type = "t3.micro"

  tags = {
    Name = "example"
  }
}
```

その後、Apacheをインストールするために次のように`main.tf`を修正する流れです。

```terraform:example/main.tf
resource "aws_instance" "example" {
  ami = "ami-0c3fd0f5d33134a76"
  instance_type = "t3.micro"

  user_data = <<EOF
    #!/bin/bash
    yum install -y httpd
    systemctl start httpd.service
  EOF
}
```

ここで`terraform apply`を実行すると、`aws_instance.example must be replaced`というメッセージが出る想定となっています。

このメッセージは「既存のリソースを削除して新しいリソースを作成する」という意味です。

### 実際の動作

ところが僕のローカル環境では代わりに、`aws_instance.example will be updated in-place`というメッセージが出ました。

```bash
example % terraform apply
aws_instance.example: Refreshing state... [id=i-0473f6955dab192a1]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.example will be updated in-place
  ~ resource "aws_instance" "example" {
        id                                   = "i-0473f6955dab192a1"
      ~ tags                                 = {
          - "Name" = "example" -> null
        }
      ~ tags_all                             = {
          - "Name" = "example" -> null
        }
      + user_data                            = "5f11f9fb893f5c269243dd3d8efe2bc57983f538"
        # (38 unchanged attributes hidden)

        # (8 unchanged blocks hidden)
    }

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

### 実行結果も異なる

書籍では元のEC2インスタンスを削除して新しいEC2インスタンスが作成される想定となっています。

しかし実際には、新しいEC2インスタンスが作成されることなく、元のEC2インスタンスが更新されていました。


## 結論

結論として、Terraform自体の問題ではなく`terraform-provider-aws`のバージョンが原因でした。

### `terraform-provider-aws`とは？

`terraform-provider-aws`とは、TerraformでAWSのリソースを管理するためのプラグインです。

このプラグインによりTerraformへAWS固有の機能を追加できます。

`terraform init`コマンドを実行したときに自動でダウンロードされていました。

`terraform-provider-aws`のバージョン4.2.0から仕様変更されたようです。

- 4.1.0まで: リソースの置き換えで`user_data`を更新
- 4.2.0から: リソースは置き換えずそのまま`user_data`を更新

下記のPRで変更がマージされていました。

https://github.com/hashicorp/terraform-provider-aws/pull/18043

### 再現方法

第2章の最初のコードで`terraform-provider-aws`のバージョン指定を追記した上で、`terraform init`を実行します。

```hcl:example/main.tf
# この部分を追加
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.1.0"  # このバージョンだと must be replaced が出る
    }
  }
}

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

resource "aws_instance" "example" {
  ami = "ami-0c3fd0f5d33134a76"
  instance_type = "t3.micro"
}
```

その後は流れに沿って進めると、書籍と同様に`aws_instance.example must be replaced`のメッセージが出ました。

EC2インスタンスの立ち上がり方も書籍と同様でした。

## おわりに

『[実践Terraform](https://www.amazon.co.jp/dp/4844378139)』はとてもわかりやすいため、初学者にもおすすめです。

ただし出版が2019年なので現在とは異なる点もあります。

今後もそういった箇所が見つかったら、適宜調べてさらにTerraformの理解を深めていきたいと思います。

また、この記事に誤りがありましたらコメントにて教えていただけますと幸いです。

## 参考資料

https://www.amazon.co.jp/dp/4844378139

https://github.com/hashicorp/terraform-provider-aws/blob/release/4.x/CHANGELOG.md#420-february-18-2022