LoginSignup
4
0

More than 1 year has passed since last update.

Terraform のバージョンをアップグレードしようとしたら謎の差分に悩まされた話

Last updated at Posted at 2021-12-10

※この記事はミクシィグループ Advent Calendar 2021の11日目の記事です

先日弊プロジェクトで作成している Terraform コードを、最新の 1.0.11 にアップグレードしました。
その際に、バージョンを上げようとすると謎の差分が発生し、どういうこっちゃ?ということで調べた時のメモ。

要約

  • Terraform コードを 0.14.11 -> 1.0.11 にアップグレードしようと plan を実行したところ、謎の差分が発生
  • 調べると、 AWS Security Manager を使用して設定した値で差分が発生している
  • どうも 0.15 以降は provider_sensitive_attrs の機能があり、sensitive な値を変数に入れて使うと、使っている resource の値も sensitive 扱いになってくれるらしい
  • sensitive の扱いが変更されると、物によっては Terraform はそれを resource の差分として認識する(っぽい)
  • というわけでアップグレードに伴って変更される resource はなかったので、みんな幸せ

発端

事の起こりは Terraform をアップグレードしようと Terraform の required_version を変更しただけの簡単な PR を作成した時に発生。

スクリーンショット 2021-12-09 13.57.06.png

CodeBuild で実行した terraform plan 結果に謎の差分が発生。
差分の発生箇所は AWS WAF で設定している rule。
そして肝心の差分の内容は、

      ~ rule {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }

sensitive だから差分は見せられないときてる。
さて困った :thinking:

調査

なんとか差分を調べる方法がないかと思ってググってみると、以下のフォーラムの投稿が見つかる。
https://discuss.hashicorp.com/t/how-to-show-sensitive-values/24076

それによると、下記のコマンドを実行すれば詳細な plan の実行計画を json として吐き出すことができて、差分を確認できるとのこと。

terraform plan -out=tfplan
terraform show -json tfplan

json の中身は以下の公式のドキュメントで詳しく説明されている。
https://www.terraform.io/docs/internals/json-format.html

これによると、 terraform plan で出力した時の構成は大まかには以下の通り。

  • 現在 Terraform で管理している resources の状態(Values Representation)
  • 現在の Terraform の設定(Configuration Representation)
  • 今回の変更点(Change Representation)

この中の Change Representation の中に、before / after という項目でそれぞれ変更点が詳細に記述される。
今回の差分はここの before と after を見比べれば良さそう。
なので、before と after を抜き出して diff をとってみる。

14c14
<         "before": {
---
>         "after": {
37c37
<                       "custom_request_handling": []
---
>                       "custom_request_handling": null
116c116
<         "before_sensitive": {
---
>         "after_sensitive": {
132,134c132
<                     {
<                       "custom_request_handling": []
<                     }
---
>                     {}
187c185
<         },
---
>         }
203c201
<         "before": {
---
>         "after": {
305c303,304
<         "before_sensitive": {
---
>         "after_unknown": {},
>         "after_sensitive": {
316,368c315
<           "rule": [
<             {
<               "action": [
<                 {
<                   "allow": [
<                     {}
<                   ],
<                   "block": [],
<                   "count": []
<                 }
<               ],
<               "override_action": [],
<               "statement": [
<                 {
<                   "and_statement": [],
<                   "byte_match_statement": [
<                     {
<                       "field_to_match": [
<                         {
<                           "all_query_arguments": [],
<                           "body": [],
<                           "method": [],
<                           "query_string": [],
<                           "single_header": [
<                             {}
<                           ],
<                           "single_query_argument": [],
<                           "uri_path": []
<                         }
<                       ],
<                       "text_transformation": [
<                         {}
<                       ]
<                     }
<                   ],
<                   "geo_match_statement": [],
<                   "ip_set_reference_statement": [],
<                   "managed_rule_group_statement": [],
<                   "not_statement": [],
<                   "or_statement": [],
<                   "rate_based_statement": [],
<                   "regex_pattern_set_reference_statement": [],
<                   "rule_group_reference_statement": [],
<                   "size_constraint_statement": [],
<                   "sqli_match_statement": [],
<                   "xss_match_statement": []
<                 }
<               ],
<               "visibility_config": [
<                 {}
<               ]
<             }
<           ],
---
>           "rule": true,
374c321
<         },
---
>         }

これをみると、before_sensitive にある WAF の rule がまるっと after_sensitive で true に置き換わっていることがわかる。
before/after_sensive については、

  // "before_sensitive" and "after_sensitive" are object values with similar
  // structure to "before" and "after", but with all sensitive leaf values
  // replaced with true, and all non-sensitive leaf values omitted. 

とあり、どうやら sensitive 扱いになるパラメータは、すべて true という値になる模様。
つまり、AWS WAF の rule がまるっと sensitive 扱いされたということがわかる。

はて、 rule に sensitive なデータなんかあったっけ…? :thinking: と思ったのだが、ここで rule に、AWS Secret Manager で取得した値を入れていたことを思い出す。

今回のシステムは、固定の値をリクエストヘッダーにもつシステムからのみを許可する WAF の rule を設定しており、その固定の値を Secret Manager で管理していたのでした。

0.15 で導入された provider_sensitive_attrs

となると、アップグレードに伴って sensitive value の扱いが変わったのかしら?と思って調べると、 0.15 で provider_sensitive_attrs というのが GA になったことがわかる。
https://dev.classmethod.jp/articles/terraform-015/#toc-4

provider_sensitive_attrs というのは、ざっくりいうと sensitive な値を使っている resource も同様に sensitive 扱いにするよ!という機能。
元々は sensitive な値を変数に入れて使いまわしていたりすると、 terraform plan とかで sensitive の値がプレーンテキストとして表示されちゃうという問題があり、それを解決するために導入された機能とのこと。

Security Manager の値は元々 sensitive 扱いなので、これにより WAF の rule がまるっと sensitive 扱いにされたことがわかる。

resource の変更?

実は、通常は sensitive 扱いの変更だけなら、下記のような The value is unchanged. 表示が出るようになっているらしい。

      # Warning: this block will be marked as sensitive and will not
      # display in UI output after applying this change. The value is unchanged. <= ココ!
      ~ rule {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }

この文字が出ていれば値は変更されないことがわかるのだが、今回はこれが表示されていなかった。
どうも resource によっては The value is unchanged. の文字が表示されないことがあるっぽい?

結局どうしたか

The value is unchanged. の表示がない上に、今回の環境が production 環境だったので、エイヤッと apply するのはさすがにちょっと怖かった。
そのため、今回は問題となる WAF と Secret Manager をだけを作成する簡単な Terraform を作成し、実際にどうなるか検証した。

検証

まずは 0.14.11 で作成。

main.tf
terraform {
  required_version = "~> 0.14.11"
}

provider "aws" {
  profile = "test"
  region  = "ap-northeast-1"
}

provider "aws" {
  profile = "test"
  region  = "us-east-1"
  alias   = "virginia"
}

data "aws_secretsmanager_secret" "secret" {
  name = "test"
}

data "aws_secretsmanager_secret_version" "secret" {
  secret_id = data.aws_secretsmanager_secret.secret.id
}

resource "aws_wafv2_web_acl" "waf" {
  provider    = aws.virginia
  name        = "test-waf-allow-header"
  scope       = "CLOUDFRONT"
  description = "test"
  tags        = {}

  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "test-waf-allow-header"
    sampled_requests_enabled   = false
  }

  default_action {
    block {}
  }

  rule {
    name     = "test-waf-rule-allow-header"
    priority = 1

    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "test-waf-rule-allow-header"
      sampled_requests_enabled   = false
    }

    action {
      allow {}
    }

    statement {
      byte_match_statement {
        field_to_match {
          single_header {
            name = "x-cdn-key"
          }
        }
        positional_constraint = "EXACTLY"
        search_string         = jsondecode(data.aws_secretsmanager_secret_version.secret.secret_string)["x-cdn-key"]
        text_transformation {
          priority = 0
          type     = "NONE"
        }
      }
    }
  }
}

作った main.tf を apply。

 % terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_wafv2_web_acl.waf will be created
  + resource "aws_wafv2_web_acl" "waf" {
      + arn         = (known after apply)
      + capacity    = (known after apply)
      + description = "test"
      + id          = (known after apply)
      + lock_token  = (known after apply)
      + name        = "test-waf-allow-header"
      + scope       = "CLOUDFRONT"
      + tags_all    = (known after apply)

      + default_action {

          + block {
            }
        }

      + rule {
          + name     = "test-waf-rule-allow-header"
          + priority = 1

          + action {
              + allow {
                }
            }

          + statement {

              + byte_match_statement {
                  + positional_constraint = "EXACTLY"
                  + search_string         = "hogehoge"

                  + field_to_match {

                      + single_header {
                          + name = "x-cdn-key"
                        }
                    }

                  + text_transformation {
                      + priority = 0
                      + type     = "NONE"
                    }
                }
            }

          + visibility_config {
              + cloudwatch_metrics_enabled = false
              + metric_name                = "test-waf-rule-allow-header"
              + sampled_requests_enabled   = false
            }
        }

      + visibility_config {
          + cloudwatch_metrics_enabled = false
          + metric_name                = "test-waf-allow-header"
          + sampled_requests_enabled   = false
        }
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_wafv2_web_acl.waf: Creating...
aws_wafv2_web_acl.waf: Creation complete after 2s [id=ebd18307-4665-4bfa-8807-a53ae9df4779]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

この時に WAF に設定された sensitive な値は以下。

                  + search_string         = "hogehoge"

required_version を変更する。

diff --git a/main.tf b/main.tf
index 699ddc8..e8af984 100644
--- a/main.tf
+++ b/main.tf
@@ -1,5 +1,5 @@
 terraform {
-  required_version = "~> 0.14.11"
+  required_version = "~> 1.0.11"
 }

 provider "aws" {

そして terraform plan。

 % terraform plan
aws_wafv2_web_acl.waf: Refreshing state... [id=ebd18307-4665-4bfa-8807-a53ae9df4779]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # aws_wafv2_web_acl.waf has been changed
  ~ resource "aws_wafv2_web_acl" "waf" {
        id          = "ebd18307-4665-4bfa-8807-a53ae9df4779"
        name        = "test-waf-allow-header"
      + tags        = {}
        # (6 unchanged attributes hidden)



        # (3 unchanged blocks hidden)
    }

Unless you have made equivalent changes to your configuration, or ignored the relevant attributes
using ignore_changes, the following plan may include actions to undo or respond to these changes.

────────────────────────────────────────────────────────────────────────────────────────────────────

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_wafv2_web_acl.waf will be updated in-place
  ~ resource "aws_wafv2_web_acl" "waf" {
        id          = "ebd18307-4665-4bfa-8807-a53ae9df4779"
        name        = "test-waf-allow-header"
        tags        = {}
        # (6 unchanged attributes hidden)


      # Warning: this block will be marked as sensitive and will not
      # display in UI output after applying this change.
      ~ rule {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }

        # (2 unchanged blocks hidden)
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly
these actions if you run "terraform apply" now.

やはり The value is unchanged. の表記はない。
このまま apply すると値はどうなるか確認する。

 % terraform apply                                                                          [ main ]
aws_wafv2_web_acl.waf: Refreshing state... [id=ebd18307-4665-4bfa-8807-a53ae9df4779]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # aws_wafv2_web_acl.waf has been changed
  ~ resource "aws_wafv2_web_acl" "waf" {
        id          = "ebd18307-4665-4bfa-8807-a53ae9df4779"
        name        = "test-waf-allow-header"
      + tags        = {}
        # (6 unchanged attributes hidden)



        # (3 unchanged blocks hidden)
    }

Unless you have made equivalent changes to your configuration, or ignored the relevant attributes
using ignore_changes, the following plan may include actions to undo or respond to these changes.

────────────────────────────────────────────────────────────────────────────────────────────────────

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_wafv2_web_acl.waf will be updated in-place
  ~ resource "aws_wafv2_web_acl" "waf" {
        id          = "ebd18307-4665-4bfa-8807-a53ae9df4779"
        name        = "test-waf-allow-header"
        tags        = {}
        # (6 unchanged attributes hidden)


      # Warning: this block will be marked as sensitive and will not
      # display in UI output after applying this change.
      ~ rule {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }

        # (2 unchanged blocks hidden)
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_wafv2_web_acl.waf: Modifying... [id=ebd18307-4665-4bfa-8807-a53ae9df4779]
aws_wafv2_web_acl.waf: Modifications complete after 0s [id=ebd18307-4665-4bfa-8807-a53ae9df4779]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

AWS コンソール上で WAF に設定された値を見ると、変更はなさそう。
スクリーンショット 2021-12-09 13.53.13.png

CloudTrail 上も特に何も記録されておらず、AWS 上の変更は何もされていなさそう。

以上より問題ないと判断して、applyを実行しました。
特に問題は発生しなかったので、良かったですね。

おわりに

この記事が書いているうちに Terraform 1.1.0 が出ちゃったので、またアップグレードしないといけない :innocent:

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0