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せんせーさすが