Terraform 0.12.0がリリースされ、すでに0.12.1がリリースされました。 いくつかのTerraform環境で随時0.11.14から0.12.0にアップグレードしているのですが、その中でAzureRM Providerに関して少し困ったのでメモしておきます。
TL;DR
基本は、Upgrade Guideをみること。
~破壊的変更として、タグの一部を無視できなくなっているので、タグすべてを無視して直変更なりで一時しのぎが良さそう。~ 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の変更点だけ見ておくといいです。
さて、次のものは度々0.12.0にアップグレード対応するたび、あぁまたこれかという感じで引っ掛かります。
Provider not support 0.12
単純にアップグレードすればokなことが多いですが、サポートされているかの確認はしましょう。
azurerm providerなら次のようにかなり細かく出ています。
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" }
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
たとえば、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がマージされたので、もうじきでるんじゃないでしょうか。