Terraform 系列-使用Dynamic Blocks對Blocks進行迭代

東風微鳴發表於2023-10-31

系列文章

概述

Terraform 系列文章 介紹了使用 Grafana Terraform Provider, 基於 Terraform 的 IaC 方法論, 來批次自動化建立 Grafana 的各類資源, 包括 Dashboard/Datasource 等.

現在有這麼一個現實需求:

出於許可權控制的需求, 需要啟用 Folder Permissions, 限制指定的某幾個 team 可以有該 Folder 的 view 許可權.

該如何實現??

解決方案

透過 Terraform 的 for_eachdynamic blocks 實現.

基本概念

Dynamic Blocks

在資源(resource)等頂級塊結構中,表示式通常只能在使用 name = expression 形式為引數賦值時使用。這涵蓋了許多用途,但有些資源型別的引數中包含可重複巢狀的塊(block),這些塊通常代表與包含物件相關(或嵌入其中)的獨立物件:

resource "aws_elastic_beanstalk_environment" "tfenvtest" {
  name = "tf-test-name" # can use expressions here

  setting {
    # but the "setting" block is always a literal block
  }
}

您可以使用特殊的 dynamic 塊型別動態構建可重複巢狀的塊,如resourcedataproviderprovisioner 塊都支援這種型別:

resource "aws_elastic_beanstalk_environment" "tfenvtest" {
  name                = "tf-test-name"
  application         = "${aws_elastic_beanstalk_application.tftest.name}"
  solution_stack_name = "64bit Amazon Linux 2018.03 v2.11.4 running Go 1.12.6"

  dynamic "setting" {
    for_each = var.settings
    content {
      namespace = setting.value["namespace"]
      name = setting.value["name"]
      value = setting.value["value"]
    }
  }
}

動態塊的作用與for表示式很相似,但它產生的是巢狀程式碼塊,而不是複數值。它遍歷給定的複數值,併為複數值的每個元素生成一個巢狀塊。

  • 動態程式碼塊的標籤(上例中的 "setting")指定了要生成的巢狀程式碼塊的型別。
  • for_each "引數提供了要遍歷的複合值。
  • iterator 引數(可選)設定了一個臨時變數的名稱,該變數代表複數值的當前元素。如果省略,變數名預設為 dynamic 塊(上例中為 "setting")的標籤。
  • labels 引數(可選)是一個字串列表,它按順序指定了要用於每個生成塊的塊標籤。你可以在此值中使用臨時迭代變數。
  • 巢狀的 content 塊定義了每個生成塊的主體。你可以在此塊中使用臨時迭代變數。

由於 for_each 引數可接受任何集合或結構值,因此可以使用 for 表示式或 splat 表示式來轉換現有集合。

迭代器物件(上例中的 setting)有兩個屬性:

  • key 是當前元素的對映鍵或列表元素索引。如果 for_each 表示式產生了一個 set 值,則 keyvalue 相同。
  • value 是當前元素的值。

dynamic 程式碼塊只能生成屬於正在配置的 resource 型別、data 源、provider 或 provisioner 的引數。不能生成 meta-argument 塊,如lifecycleprovisioner塊,因為 Terraform 必須先處理這些引數塊,然後才能安全地評估表示式。

for_each 值必須是一個集合,每個所需的巢狀塊包含一個元素。如果需要根據巢狀資料結構或多個資料結構的元素組合宣告資源例項,可以使用 Terraform 表示式和函式推匯出合適的值。有關此類情況的一些常見示例,請參閱 flattensetproduct函式。

有些提供程式定義的資源型別包括相互巢狀的多層區塊。您可以在必要時動態生成這些巢狀結構,方法是將 dynamic 模組巢狀在其他 dynamic 模組的 content 部分中。

例如,一個模組可能會接受如下複雜的資料結構:

variable "load_balancer_origin_groups" {
  type = map(object({
    origins = set(object({
      hostname = string
    }))
  }))
}

如果要定義一個 resource,其型別需要為每個 origin group 建立一個塊,然後為組內的每個 origin 建立巢狀塊,則可以要求 Terraform 使用以下巢狀的 dynamic 塊動態生成該資源:

  dynamic "origin_group" {
    for_each = var.load_balancer_origin_groups
    content {
      name = origin_group.key

      dynamic "origin" {
        for_each = origin_group.value.origins
        content {
          hostname = origin.value.hostname
        }
      }
    }
  }

在使用巢狀的 dynamic 程式碼塊時,尤其要注意每個程式碼塊的迭代符號。在上例中,origin_group.value 指向外層程式碼塊的當前元素,而 origin.value 指向內層程式碼塊的當前元素。

如果一個特定的資源型別定義了巢狀塊,而這些巢狀塊的型別名稱與其父類中的一個型別名稱相同,則可以在每個 dynamic 塊中使用 iterator 引數來選擇一個不同的迭代器符號,使兩者更容易區分。

過度使用 dynamic 塊會使配置變得難以閱讀和維護,因此我們建議僅在需要隱藏細節以便為可重用模組構建簡潔的使用者介面時使用它們。在可能的情況下,一定要按字面意思寫出巢狀模組。

實戰

需求:

出於許可權控制的需求, 需要啟用 Folder Permissions, 限制指定的某幾個 team 可以有該 Folder 的 view 許可權.

對應的 Terraform 程式碼如下:

locals {
    teams = {
        "dev",
        "busi",
        "ops",
        "data",
        "pm"
    }
}

resource "grafana_folder_permission" "foldersPermission" {

  folder_uid = "demo"

  dynamic "permissions" {
    for_each = local.teams
    content {
      team_id    = grafana_team.teams[each.key].id
      permission = "View"
    }
  }
}

說明:

  • permissions (Block Set, Min: 1) 要新增/更新的許可權專案。列表中沒有的專案將被刪除。

完成???

?️參考文件

三人行, 必有我師; 知識共享, 天下為公. 本文由東風微鳴技術部落格 EWhisper.cn 編寫.

相關文章