BASEプロダクトチームブログ

ネットショップ作成サービス「BASE ( https://thebase.in )」、ショッピングアプリ「BASE ( https://thebase.in/sp )」のプロダクトチームによるブログです。

Terraform 0.14 Upgrade Trouble Shooting

この記事は BASE Advent Calendar 2020 の 18 日目の記事です。

こんにちは。BASE BANK 株式会社 Dev Division にて、 Software Developer をしている東口(@hgsgtk)です。 先月・先々月と連続で Terraform に関連したブログを投稿しているのですが 2020 年最終月も Terraform 話で締めさせていただきます^1

TL;DR

  • Terraform 0.14 が GA(General Availability)になった
  • dependency lock file .terraform.lockが追加され、VSC 管理化に含めるかについてプロジェクトによって扱いの検討が必要
  • 0.14.0 では、ignore_changes = allを使用したリソース定義の扱いにバグがあり 0.14.1 で修正された
    • 当該機能を利用している場合は 0.14.1 >= のリリースがおすすめ

Terraform 0.14がGAになった

12 月 2 日 0.14.0リリースタグが切られ、メジャーバージョン 0.14.x が GA(General Availability) となりました。

www.hashicorp.com

Highlights となっている機能として紹介されているものは 3 つあります。一番体感としてわかりやすいのは Concise Diff という機能です。文字通り差分表示が簡潔になりました。

module.cwl.aws_cloudwatch_log_group.sample will be updated in-place
  ~ resource "aws_cloudwatch_log_group" "sample" {
        id                = "/ecs/sample/sample"
        name              = "/ecs/sample/sample"
      ~ retention_in_days = 0 -> 1
        tags              = {}
        # (1 unchanged attribute hidden)
    }

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

# (1 unchanged attribute hidden)と表示されているように関係がなく変更がない記述に関しては差分表示にて省略され、見やすくなりました。

2つ目が、機密性の高い Variable に対する CLI 表示での扱いについてです。sensitive=trueという設定によって CLI 表示をマスキングすることが出来るようになりました。次の公式の例がわかりやすいですが、sensitive=trueと設定した variable をリソース定義した場合、コンソール表示が(sensitive)と調整されるようになっています。

Terraform will perform the following actions:

  # some_resource.a will be created
  + resource "some_resource" "a" {
      + name    = (sensitive)
      + address = (sensitive)
    }

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

www.hashicorp.com

上記の説明にありますが、state ファイル内にはそのままの機密情報が記載されるようになっています。

Sensitive values are still recorded in the state, and so will be visible to anyone who is able to access the state data.

もう 1 つの新機能が Provider Dependency Lockfile という機能です。こちらについては 0.14.x へアップグレードする際に扱う方法を検討する必要があります。

Provider Dependency Lockfile

Terraform 0.14 では Provider の依存関係を terraform init 時点でバージョンロックする機能が導入されました。

www.hashicorp.com

BASE BANK の開発では、AWS Providerを Provider として利用しています。記事執筆時点(2020 年 12 月 9 日)の最新 3.19.0 の状態でterraform initすると次のような内容で .terraform.lock というファイルが作成されます。

# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.

provider "registry.terraform.io/hashicorp/aws" {
  version     = "3.19.0"
  constraints = "~> 3.19.0"
  hashes = [
"h1:FJwsuowaG5CIdZ0WQyFZH9r6kIJeRKts9+GcRsTz1+Y=",
"h1:c/ntSXrDYM1mUir2KufijYebPcwKqS9CRGd3duDSGfY=",
"h1:yre4Ph76g9H84MbuhZ2z5MuldjSA4FsrX6538O7PCcY=",
"zh:04f0a50bb2ba92f3bea6f0a9e549ace5a4c13ef0cbb6975494cac0ef7d4acb43",
"zh:2082e12548ebcdd6fd73580e83f626ed4ed13f8cdfd51205d8696ffe54f30734",
"zh:246bcc449e9a92679fb30f3c0a77f05513886565e2dcc66b16c4486f51533064",
"zh:24de3930625ac9014594d79bfa42d600eca65e9022b9668b54bfd0d924e21d14",
"zh:2a22893a576ff6f268d9bf81cf4a56406f7ba79f77826f6df51ee787f6d2840a",
"zh:2b27485e19c2aaa9f15f29c4cff46154a9720647610171e30fc6c18ddc42ec28",
"zh:435f24ce1fb2b63f7f02aa3c84ac29c5757cd29ec4d297ed0618423387fe7bd4",
"zh:7d99725923de5240ff8b34b5510569aa4ebdc0bdb27b7bac2aa911a8037a3893",
"zh:7e3b5d0af3b7411dd9dc65ec9ab6caee8c191aee0fa7f20fc4f51716e67f50c0",
"zh:da0af4552bef5a29b88f6a0718253f3bf71ce471c959816eb7602b0dadb469ca",
  ]
}

このファイルによって Provider のバージョンがロックされ例えば違うバージョンの Provider を利用した際にエラーとすることで、どの環境でも同一バージョンを保証できます。このファイルは基本的に.terraform.lockは手で変更することはせず、terreform init -upgradeを用いて更新することが期待されています。

同一バージョンを保証するこの機能を有効活用するかは開発対象のリポジトリによりますが、有効活用する場合は Git 等の VCS の管理対象に含めることが推奨されています。

一方で、バージョン固定は不要だと判断した場合は、.gitignoreにファイルを記載することでこの挙動を無効にすることが可能と説明されています。

You can continue with a model similar to the v0.13 behavior after upgrading to v0.14 by placing .terraform.lock.hcl in your version control system's "ignore" file, such as .gitignore for Git.

www.terraform.io

筆者の現場では、Git 管理対象に含めることを選択しました。Git 管理対象に含める場合の今後のアップグレード作業は次のようなパターンが考えられます。

バージョンを固定していない場合は、「単に最新に上げておきたい」といったモチベーションになるでしょう。terraform init -upgradeをさっと実行することで作業完了です。

一方で、必要な機能が含まれるバージョンに固定したいといった理由で tf ファイルで必要なバージョンを定義する場合は、まず tf ファイル内の version を変更します。

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "3.19.0" # ここのバージョンを更新する
    }
  }

その後、terraform init -upgradeを実行することで期待した Provider のバージョンに変更できます。なにか最新バージョンでは意図しない差分が発生したといった場合にダウングレードしたいケースがありますが、そういった場合にもこのオペレーションを行ないます。

0.14.0 アップグレード時に発生したError

このような目玉機能がある Terraform 0.14 ですが、筆者の現場で最初に 0.14.0 にあげた際に次のようなエラーが発生しました。

Error: Computed attribute cannot be set

  on ../../modules/sample-api/ecs.tf line 56, in resource "aws_ecs_task_definition" "sample-api":
  56: resource "aws_ecs_task_definition" "sample-api" {

Error: Computed attribute cannot be set

  on ../../modules/sample-api/ecs.tf line 56, in resource "aws_ecs_task_definition" "sample-api":
  56: resource "aws_ecs_task_definition" "sample-api" {

Error: Invalid or unknown key

  on ../../modules/sample-api/ecs.tf line 56, in resource "aws_ecs_task_definition" "sample-api":
  56: resource "aws_ecs_task_definition" "sample-api" {

発生した箇所では次のように変更差分をすべて検知しない設定を入れているものでした。

  lifecycle {
    ignore_changes = all
  }

なにが影響したかを確認していくと changelog 内にあるこちらの機能リリースにおいて問題が発生したようでした。

github.com

当該機能に対する Issue はこちらのものです。具体的にはignore_changes = all と設定した場合に意図しない振る舞いになっていた という Issue です。

github.com

それに対して機能を追加したコミッターの方が即座に issue に対して反応、バグ修正が実施されました。

github.com

この修正が入っているバージョンが0.14.1になります(なお、この 3 時間後に新たに Bug Fixes が含まれた0.14.2がリリースされています)。

具体的な 0.14 へのアップグレードについては、Upgrading to Terraform v0.14をご参照いただくことになりますが、端的な手順はこちらになります。

  1. 0.13.x の最新マイナーバージョンにまず上げておく
  2. terraform plan / apply で no changes であることを確認する
  3. terraform バージョンを 0.14 に変更する

deprecatedとなっているProviderのバージョン指定方法

Provider のバージョンを指定する際に次のように version を指定する方法がありました。

provider "aws" {
  region  = var.region
  version = "~> 3.19.0"
 }

しかし、この記述ではterraform plan等をした場合に Warning が発生します。

Warning: Version constraints inside provider configuration blocks are deprecated

  on providers.tf line 8, in provider "aws":
   8:   version = "~> 3.19.0"

この非推奨記述についてはProvider Configuration: version: An Older Way to Manage Provider Versionsにて説明があります。

The version argument in provider configurations is deprecated. In Terraform 0.13 and later, version constraints should always be declared in the required_providers block.

required_providersの記述を用いて Provider のバージョンを指定することで Warning は解消されます。

terraform {
  # (省略)
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.19.0"
    }
  }
}

ちなみに、terraform の内部実装としては次のような処理が加えられて Warning が出るようになっています。

   if attr, exists := content.Attributes["version"]; exists {
        diags = append(diags, &hcl.Diagnostic{
            Severity: hcl.DiagWarning,
            Summary:  "Version constraints inside provider configuration blocks are deprecated",
            Detail:   "Terraform 0.13 and earlier allowed provider version constraints inside the provider configuration block, but that is now deprecated and will be removed in a future version of Terraform. To silence this warning, move the provider version constraint into the required_providers block.",
            Subject:  attr.Expr.Range().Ptr(),
        })
        var versionDiags hcl.Diagnostics
        provider.Version, versionDiags = decodeVersionConstraint(attr)
        diags = append(diags, versionDiags...)
    }

https://github.com/hashicorp/terraform/blob/923e157b5ce4d8650ff0ade755b52aa23423b63c/configs/provider.go#L71-L81

versionというアトリビュートがユーザーの tf ファイルに含まれる場合 Warning を出すという内容ですね。

github.com

2020 年 9 月 8 日にマージされ0.14.0のリリースの内容に含まれました。

The version argument inside provider configuration blocks has been documented as deprecated since Terraform 0.12. As of 0.14 it will now also generate an explicit deprecation warning. To avoid the warning, use provider requirements declarations instead. (#26135)

おわりに

Terraform 0.14.x 自体の目玉機能についておさらいし、当該バージョンへのアップグレードにて発生しうる Error/Warning への対応方法について紹介しました。

BASE BANK の開発チームでは特定レイヤーの技術に限定されることなく、自チームで開発したプロダクト・機能をリリースからグロース・サポートまで担う、フルサイクル開発者の考え方で日々業務に邁進しています。興味のある方はお気軽にカジュアルなお話をしましょう。@hgsgtk宛に DM 頂いても構いません。

open.talentio.com

明日は BASE 株式会社基盤チームの @tenkoma さんです。お楽しみに!