tech.guitarrapc.cóm

Technical updates

Terraform 0.11.14から0.12.0 にアップグレードを行う

Terraform 0.12.0 がリリースされ、すでに 0.12.1 がリリースされました。 いくつかのTerraform 環境で随時0.11.14 から 0.12.0 にアップグレードしているのですが、その中で AzureRM Provider に関して少し困ったのでメモしておきます。

目次

TL;DR

基本は、Upgrade Guide をみること。

Upgrading to Terraform 0.12 - Terraform by HashiCorp

~破壊的変更として、タグの一部を無視できなくなっているので、タグすべてを無視して直変更なりで一時しのぎが良さそう。~ 0.12.3 で対処が入ったので一部無視が可能になり互換性とれるようになりました。

tfenv を入れておくとめちゃめちゃ捗るので、これまで入れてなかった人はこれを気に入れておくとよいです。

操作環境

私は普段、Terraform を5つの環境で動作できるように組んでいます。*1 もっぱらGitHub PR でのCI連携をするので、Docker(Atlantis) なのですが環境によっては Azure Cloud Shell が最も利用しやすい場合はこちらを利用します。

  • Windows 10 Desktop
  • WSL Ubuntu 18.04
  • macOS 10.14 Mojave
  • Azure CloudShell
  • Docker Container (Atlantis)

さて、Azure Cloud Shell 環境はTerraform をはじめとする各種ツールが組み込みで入っており、ツールのアップグレードも勝手にされます。 つまり、おもむろにバージョンが上がります。

運用面を除外すると便利です。 一方で、0.12.0 のような破壊的変更が加わったタイミングでは、ツールのアップデート時に速やかに追随できるように整えておくのも重要になります。

今回は、0.11.14 なterraform 構成をCloudShell で当てようとしたタイミングで0.12.0にアップグレードされており*2 サクッと対応する必要があったのでした。

アップグレードの流れ

  • tfenv で0.11.14 にしておいて、terrafrom 0.12checklistで確認とエラーを潰していく
  • Provider が0.12に対応していない場合があるので、バージョンを上げて 0.12でinit できるかで確認する
  • tfenvで0.12.0 (あるいは0.12.1)にして、terraform 0.12upgrade をかけて微妙に拾えてなかったシンタックスの変更を潰す 0.12upgrade で変更されないModuleが中にはいるので、手でちまちま治す。
  • terraform plan かけて変更でなかればまず大丈夫
  • atlantis など適用環境で terraform apply かけて出力を確認して終わり

よく引っかかる変更点

先に、0.12 の変更点だけ見ておくといいです。

Terraform 0.11→0.12で追加された新機能 | DevelopersIO

さて、次のものは度々0.12.0にアップグレード対応するたびに、あぁまたこれかという感じで引っ掛かります。

Provider not support 0.12

単純にアップグレードすればok なことが多いですが、サポートされているかの確認はしましょう。

azurerm provider なら次のようにかなり細かく出ています。

Releases · terraform-providers/terraform-provider-azurerm

Argument names must not be quoted

Terraform 0.11.14までは、map や リソース内のblock・map のハンドルが微妙にゆるふわでした。 これが、0.12 から比較的しっかり見るようになっています。

結果、例えばこういったmap 宣言が0.11.14まではokでしたが、

locals {
  common_tags {
    environment = "${var.ENV}"
  }
}

0.12.0 からは、 = が必須になっています。

locals {
  common_tags = {
    environment = var.ENV
  }
}

あるいは、AzureRM Provider の azurerm_function_app などが提供する identity ブロックもそうです。

0.11.14まではこれがokでした。

  identity = {
    type = "SystemAssigned"
  }

0.12.0 からはこうなります。

  identity {
    type = "SystemAssigned"
  }

ref: Error: Invalid argument name (Argument names must not be quoted) · Issue #19575 · hashicorp/terraform

Resource の Deprecation

たとえば、azurerm provider の data resource である azurerm_builtin_role_definition は deprecate が予定されており、azurerm_role_definition を使うように促されます。

0.11.14 まで

data "azurerm_builtin_role_definition" "contributor" {
  name = "Contributor"
}
data "azurerm_role_definition" "contributor" {
  name = "Contributor"
}

Resource の返す型から値取り出し時の注意

list/tuple<T, TN>と型を明示的に示せるようになったことで少し変更を受けます。

たとえば、azurerm_fucntion_app リソースが返す identity から principal_id を取得することを考えてみましょう。

これは次で 0.11.14 / 0.12 ともにとれます。

output identity {
  value = "${lookup(azurerm_function_app.main.identity[0], "principal_id")}"
}

しかし、モジュールの外でこの形式で取りたいがために、次のようにリストにラップしていると 0.12.0 では破壊的変更となります。

// Output of Module
output identity {
  value = ["${azurerm_function_app.main.identity}"]
}

0.11.14 まではこのようにして、identity の list からmap で拾えば取れましたが、0.12.0 では取れません。

// Get Principal from module output
output "api_functionapp_msi_identity" {
  value = "${lookup(module.functionapp.identity[0], "principal_id")}"
}

この小細工は意味ないので当初示した例に直しましょう。

このエラーのときに原因がぱっとわからなかったのできびしい。

破壊的変更

0.12.0 で、解消できていない破壊的変更が一件ありますが、これは0.12.3 で解消しました。

lifecycle / ignore_changes blocking upgrade from 0.11 to 0.12 · Issue #21433 · hashicorp/terraform

これは、構文が terraform 0.12upgrade で変わっていない場合、Attribute name required: Dot must be followed by attribute name.というエラーがでます。

ref: terraform 0.12 remote state : Attribute name required · Issue #20835 · hashicorp/terraform

どんな問題かざっくりいうと、「ignore_changes でattirbuteベースで指定するようになったが、これまで無視していたものが0.12.0 でignoreされなくなった」というものです。

私が直面したのは、Azure Web Apps の appsettings のignore ができないことでした。kubernetes のタグも同じ問題があります。

0.12.0 Error: Attribute name required · Issue #21444 · hashicorp/terraform

Issue の中にある通り、全体の無視はできます。

    ignore_changes = [
      tags
    ]
    ignore_changes = [
      appsettings
    ]

しかし、その一部だけ無視できません。これはIssueに提示されている tags["kubernetes.io"]A static variable reference is required. でできないことでもわかります。

というか、ignore_changes は静的な指定しかできないのは0.11でもその前からもそうなのに、この回答者はトンチンカンなこと言っててもんにょりなので、もう少し真面目にどうなるのか知りたいです。

なお、TBD 0.12.1 となっていますが 0.12.1 はすでに出ているため、いったんは運用回避しかなさそうです。terraform で値変更せず、コンソールで直変更..... 事故臭しかない。

うれしい構文変化

変数への型の明示

変数の型が増えました。 特に、list や map が変わったのはうれしいでしょう。

文字列だけしかないmap でも、これまで次のような宣言でした。

type = "map"

これが次のように宣言可能になりました。

type = map(string)

うれしいですね。map(list(string)) とかも行けますね。 map(list) が0.11は言語制約上かなり厳しいものがあったので、こういう型制約は言語の表現力、拡張性に寄与するのでうれしいです。

lookup から要素アクセスへ

map の value に対して key でアクセスするときに lookup をしてきましたが、0.12 から[key]でアクセスが可能になりました。

つまり、0.11まで書いていた次のようなコードが

${lookup(map, "key")}

次のように変わります。

map["key"]

プログラムと違って、ない場合を考慮する必要がなくなければそこで落ちるという割り切りがインフラにはあるので、こういうキーアクセスはちょうどよいです。 lookup 撲滅しました。

VSCode の terraform plugin 対応

hcl2 に追随していないのでIssueが上がっていましたが、すでにPRがマージされたので、もうじきでるんじゃないでしょうか。

Update syntax highlighting to better support terraform 0.12 by MattFenner · Pull Request #181 · mauve/vscode-terraform

*1:といっても大きな違いはなく、別に大したことはしていません。WindowsのCRLFと環境変数を使う場合が一番めんどくさいポイントになりやすいでしょう。

*2:なぜ 0.12.1 じゃないのかというと時間差、Azureせんせーさすが