当我试图升级Terraform版本时,我遇到了一个令人困惑的差异问题

※ 这篇文章是关于Mixi Group 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 {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }

因为敏感的原因,无法展示差异。
这下麻烦了。

调查

在搜索了一下有没有办法检查差异之后,我找到了以下论坛的帖子:
https://discuss.hashicorp.com/t/how-to-show-sensitive-values/24076

根据它所说,执行以下命令可以将详细的计划执行方案以 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)

在“变更表现方式”部分中,有一个“before/after”选项,其中详细描述了每个变更点。
这次的差异可以通过比较这里的“before”和“after”来了解。
因此,我们可以提取“before”和“after”,进行差异比较。

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 规则完全被替换为 after_sensitive 中的 true。关于 before/after_sensitive,

  // "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. 

据说,所有可能被视为敏感的参数都被设置为了 true。换句话说,可以看出 AWS WAF 的规则被全部视为敏感处理了。

哎呀,我在想是否有一些对规则敏感的数据…?但在这里,我记起了将AWS Secret Manager中获取的值放入规则中。

这个系统设置了一个WAF规则,只允许带有固定值的请求头来自系统。然后我们通过密钥管理器Secret Manager来管理这个固定值。

新增了 provider_sensitive_attrs 特性的版本是 0.15。

如果这样的话,我会想知道升级后是否有关于敏感值的处理变化?查阅后发现在0.15版本中提供了provider_sensitive_attrs选项并已成为通用可用。
https://dev.classmethod.jp/articles/terraform-015/#toc-4

提供者敏感属性(provider_sensitive_attrs)是指用于处理敏感数据的资源时同样将其视为敏感处理的功能。最初,当将敏感数据存储在变量中并在terraform plan等操作中以明文形式显示时,存在问题。为了解决这个问题,引入了这个功能。

由于Security Manager的值本身就是敏感的,因此可以看出WAF的规则也被完全视为敏感处理。

资源的更改?

其实,通常情况下,只要是敏感处理的更改,似乎会显示为“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.
        }

如果这个字出现了,就可以知道值没有被改变,但是这次它没有被显示出来。
看起来似乎是由于资源的原因,可能有时候不会显示出”The value is unchanged.”这个字来?

最后怎么样了?

价值没有改变。由于没有任何标识,并且这次的环境是生产环境,所以对其进行立即应用有点恐怖。因此,我创建了仅包括问题WAF和Secret Manager的简单Terraform,以验证实际情况如何。

验证

首先,在0.14.11版本中创建。

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 文件。

 % 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 中的敏感值如下。

                  + search_string         = "hogehoge"

更改所需版本。

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规划。

 % 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.

果然,数值未变。没有”やはり”的表示。
直接应用这样,确认数值会如何变化。

 % 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.
スクリーンショット 2021-12-09 13.53.13.png

在CloudTrail上没有任何记录,也没有看到任何在AWS上的更改。

因此,我判断没有问题,进行了申请。由于没有出现任何问题,所以很好。

最后

这篇文章写的时候,Terraform 1.1.0 已经发布了,所以还需要升级一下。

bannerAds