【備忘録】Terraformをv0.12にあげたときの話

# Terraform歴
直近一年くらいAWS基盤をTerraformで構築・更改するっていう感じでした。
利用バージョンの変遷としては以下な感じ。
`v0.11.3` -> `v0.11.5` -> `v0.11.7`

まあ`v0.11.*`太郎なわけです。
環境も変わったことだし、いっちょ`v0.12.*`するか〜と`v0.12.5`で書き始めたところです。
その中での備忘録です。（まだ全然途中なのでちょくちょく更新入ると思います。）

# Variables
すごく見やすく && 書きやすくなりました。
反面、一部同様に書いてあげないとうまくいかないケースがありました。

## 以前の書き方
`v0.11.7`時代の書き方

```go:v0.11.7&nbsp;&nbsp;ec2.tf
resource "aws_instance" "bastion" {
  ami                         = "${var.ec2_ami}"
  ebs_optimized               = false
  instance_type               = "${var.ec2_type}"
  monitoring                  = false
  key_name                    = "${var.secret_key}"
  subnet_id                   = "${aws_subnet.public.id}"
  vpc_security_group_ids      = ["${aws_security_group.bastion.id}"]
  associate_public_ip_address = true
  source_dest_check           = true
  iam_instance_profile        = "${var.service_name}-bastion"

  user_data = "${data.ec2_userdata.bastion.rendered}"

  root_block_device {
    volume_type           = "gp2"
    volume_size           = 50
    delete_on_termination = false
  }

  tags {
    "Name"    = "${var.service_name}-bastion-${var.environment}"
    "Service" = "${var.service_name}"
    "Env"     = "${var.environment}"
  }

  volume_tags {
    "Name"    = "${var.service_name}-bastion-${var.environment}"
    "Service" = "${var.service_name}"
    "Env"     = "${var.environment}"
  }

  lifecycle {
    ignore_changes = [
      "user_data",
      "key_name",
      "root_block_device.0.volume_type",
      "subnet_id",
    ]
  }
}

```

## v0.12.5での書き方
`v0.12.5`で書くと

```go:v0.12.5&nbsp;&nbsp;ec2.tf
resource "aws_instance" "bastion" {
  ami                         = var.ec2_ami
  ebs_optimized               = false
  instance_type               = var.ec2_type
  monitoring                  = false
  key_name                    = var.secret_key
  subnet_id                   = aws_subnet.public.id
  vpc_security_group_ids      = aws_security_group.bastion.id
  associate_public_ip_address = true
  source_dest_check           = true
  iam_instance_profile        = "${var.service_name}-bastion"

  user_data = data.ec2_userdata.bastion.rendered

  root_block_device {
    volume_type           = "gp2"
    volume_size           = 50
    delete_on_termination = false
  }

  tags {
    "Name"    = "${var.service_name}-bastion-${var.environment}"
    "Service" = var.service_name
    "Env"     = var.environment
  }

  volume_tags {
    "Name"    = "${var.service_name}-bastion-${var.environment}"
    "Service" = var.service_name
    "Env"     = var.environment
  }

  lifecycle {
    ignore_changes = [
      "user_data",
      "key_name",
      "root_block_device.0.volume_type",
      "subnet_id",
    ]
  }
}
```

とまあすごくスッキリしてますし、実際大量に書くときは楽です。
が、一部旧形式で書いている部分がありますね。
`String`形式でもVariable単体では無い場合、やはりこういう書き方をしてあげないとplanで怒られます。

## 沼にハマる
個人的にはまったのはこれ。

```go:v0.11.7&nbsp;&nbsp;subnet.tf
resource "aws_subnet" "private" {
  count                   = "${length(split(",", lookup(local.availability_zones, var.region)))}"
  vpc_id                  = "${aws_vpc.service-vpc.id}"
  cidr_block              = "${cidrsubnet(aws_vpc.service-vpc.cidr_block, 4, count.index + length(split(",", lookup(local.availability_zones, var.region))) * 1)}"
  availability_zone       = "${element(split(",", lookup(local.availability_zones, var.region)), count.index)}"
  map_public_ip_on_launch = false
  depends_on              = ["aws_vpc.service-vpc"]

  tags = {
    Name         = "${var.service_name}-private-${var.environment}-${count.index}"
    Env          = "${var.environment}"
    Service      = ["${var.service_name}"]
  }
}

resource "aws_db_subnet_group" "db" {
  name        = "${var.service_name}-${var.short_env}-db"
  description = "${var.service_name} subnet group for db"
  subnet_ids  = ["${aws_subnet.private.*.id}"]

  tags = {
    Name         = "${var.service_name}-${var.environment}"
    Env          = "${var.environment}"
    Service      = "${var.service_name}"
  }
}

```

指定しているAZの数に応じてprivateサブネットを作成するよう`count`を利用しているのですが、そのidを参照して作成しようとしたら怒られました

```bash:Error
Error: Incorrect attribute value type

  on rds.tf line 75, in resource "aws_db_subnet_group" "db":
  75:   subnet_ids  = ["${aws_subnet.private.*.id}"]

Inappropriate value for attribute "subnet_ids": element 0: string required.
```

え、リストで指定せいゆーてたやん、、ドキュメントにも書いてあるやん。。
>・subnet_ids - (Required) A list of VPC subnet IDs.

と思いつつ下記に変更したら通りました。。

```go:correct.tf
resource "aws_db_subnet_group" "db" {
  name        = "${var.service_name}-${var.short_env}-db"
  description = "${var.service_name} subnet group for db"
  subnet_ids  = "${aws_subnet.private.*.id}"

  tags = {
    Name         = "${var.service_name}-${var.environment}"
    Env          = "${var.environment}"
    Service      = "${var.service_name}"
  }
}
```
`v0.11.*`にあんなに怒られたのになあ…（遠い目）と思いを馳せつつ実装を進めました。

ちなみに`terraform 0.12upgrade`で実施してみたら下記のように変更されてました。
※ やる前にちゃんとGithubにあげとくなりなんなりしないと戻すの大変なので注意！！！！

```go:v0.12.5&nbsp;&nbsp;subnet.tf
resource "aws_subnet" "private" {
  count                   = length(split(",", local.availability_zones[var.region]))
  vpc_id                  = aws_vpc.service-vpc.id
  cidr_block              = cidrsubnet(aws_vpc.service-vpc.cidr_block, 4, count.index + length(split(",", local.availability_zones[var.region])) * 1,)
  availability_zone       = element(split(",", local.availability_zones[var.region]), count.index,)
  map_public_ip_on_launch = false
  depends_on              = [aws_vpc.service-vpc]

  tags = {
    Name         = "${var.service_name}-private-${var.environment}-${count.index}"
    Env          = var.environment
    Service      = var.service_name
  }
}

resource "aws_db_subnet_group" "mysql-serverless" {
  name        = "${var.service_name}-${var.short_env}-db"
  description = "${var.service_name} subnet group for db"
  subnet_ids  = aws_subnet.private.*.id

  tags = {
    Name         = "${var.service_name}-${var.environment}"
    Env          = var.environment
    Service      = var.service_name
  }
}
```
余談ですが`terraform 0.12upgrade`で実施すると`length`とか`cidrsubnet`みたいなところはめちゃめちゃ改行されて出力されるんでびっくりします。

# Error Message
まあこればっかりは仕方ないんですが、「こんなエラー出るかもな」でplan流したら結構長めの文で怒られます。凹みます。
いや、エラーメッセージの内容がすごく細かくなっていて、指摘通りに修正してあげると割とWeb徘徊しなくても解決しちゃうことが多いです。
助かる反面、最初はビビりました…大量に出たんでそんな変わったんか！？と
自分がショボかっただけでした。

## Warining Sample
初歩的な変数設定のミスなのに、こんなやりかたもあんでという母のような優しさ。

```bash:&nbsp;Warning
Warning: Value for undeclared variable

  on sandbox.tfvars line 3:
   3: service = "sandbox"

The root module does not declare a variable named "service". To use this
value, add a "variable" block to the configuration.

Using a variables file to set an undeclared variable is deprecated and will
become an error in a future release. If you wish to provide certain "global"
settings to all configurations in your organization, use TF_VAR_...
environment variables to set these instead.
```
## Error Sample
こっちでやってみたらどうだ？という父のような頼もしさ。

```bash:&nbsp;Error
Error: Missing resource instance key

  on iam.tf line 18, in resource "aws_iam_role_policy_attachment" "policy-attach":
 273:   role       = "${aws_iam_role.role.name}"

Because aws_iam_role.role has "count" set, its attributes must be
accessed on specific instances.

For example, to correlate with indices of a referring resource, use:
    aws_iam_role.role[count.index]
```
Terraformは僕の両親です。

# おまけ
新しい記法で書くとエディタがうまく反応してくれねっす。
Pycharmを使ってるんですが、バージョンが古いからかブロックの認識がうまくいかず…可読性の問題で結局元の書き方で書いて最後にぴゃぴゃっと直す的な。。

**v0.11.7**
![1_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423088/aec19c3c-dc66-4249-5c39-65b5abaf9648.png)


**v0.12.5**
![1_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423088/f3a8b827-9e0a-4dca-2240-16a39a0e3cc2.png)
なんとイケてないやり方…精進します。。

`module`も使わんとなあと思いつつ、なかなか癖が抜けず。。

# 参考
このページだけで十分でした…神様🙏
[terraform v0.12 アップデート terraform 0.12upgrade,terraform 0.12checklistサブコマンド実行結果と、ファイルの変更例](https://qiita.com/miyazawa214/items/4cc69cf67ac17f7b418d)