具有嵌套动态块的Terraform资源for_each继续重新应用相同的更改

时间:2020-03-26 17:44:16

标签: terraform terraform-provider-gcp

所以我创建了一个google_bigquery模块来创建数据集并设置访问权限。

该模块遍历一个地图列表。它使用each.key创建数据集,然后遍历地图列表以创建动态访问。

该模块的工作方式如下:

  • 没有错误也没有警告
  • 它部署资源
  • 它会适当地填充远程状态文件。

问题在于,每次我运行地形时,它都想一遍又一遍地重新应用相同的更改。

很明显,有些事情是不对的,但不确定是什么。

这是代码

main.tf

locals {
  env           = basename(path.cwd)
  project       = basename(abspath("${path.cwd}/../.."))
  project_name  = coalesce(var.project_name, format("%s-%s", local.project, local.env))
}

data "google_compute_zones" "available" {
  project = local.project_name
  region  = var.region
}

provider "google" {
  project = local.project_name
  region  = var.region
  version = "~> 2.0" #until 3.0 goes out of beta
}

terraform {
  required_version = ">= 0.12.12"
}

resource "google_bigquery_dataset" "main" {
  for_each                   = var.datasets
  dataset_id                 = upper("${each.key}_${local.env}")
  location                   = var.region
  delete_contents_on_destroy = true

  dynamic "access" {
    for_each = flatten([ for k, v in var.datasets : [
                 for i in each.value : {
                   role           = i.role
                   user_by_email  = i.user_by_email
                   group_by_email = i.group_by_email
                   dataset_id     = i.dataset_id
                   project_id     = i.project_id
                   table_id       = i.table_id
    }]])
    content {
      role           = lookup(access.value,"role", "")
      user_by_email  = lookup(access.value,"user_by_email","")
      group_by_email = lookup(access.value,"group_by_email","")
      view {
        dataset_id   = lookup(access.value,"dataset_id","")
        project_id   = lookup(access.value,"project_id","")
        table_id     = lookup(access.value,"table_id", "")
        }
    }
  }



  access {
    role          = "READER"
    special_group = "projectReaders"
  }

  access {
    role           = "OWNER"
    group_by_email = "Group"
  }

  access {
    role           = "OWNER"
    user_by_email  = "ServiceAccount"
  }

  access {
    role          = "WRITER"
    special_group = "projectWriters"
  }

}

variables.tf

variable "region" {
  description = ""
  default     = ""
}

variable "env" {
  default = ""
}

variable "project_name" {
  default = ""
}

variable "owner_group" {
  description = ""
  default     = ""
}

variable "owner_sa" {
  description = ""
  default = ""
}

variable "datasets" {
  description = "A map of objects, including dataset_isd abd access"
  type = map(list(map(string)))
}

terraform.tfvars

datasets = {
  dataset01 = [
    {
      role           = "WRITER"
      user_by_email  = "email_address"
      group_by_email = ""
      dataset_id     = ""
      project_id     = ""
      table_id       = ""
    },
    {
      role           = ""
      user_by_email  = ""
      group_by_email = ""
      dataset_id     ="MY_OTHER_DATASET"
      project_id     ="my_other_project"
      table_id       ="my_test_view"
    }
  ]
  dataset02 = [
    {
      role           = "READER"
      user_by_email  = ""
      group_by_email = "group"
      dataset_id     = ""
      project_id     = ""
      table_id       = ""
    },
    {
      role           = ""
      user_by_email  = ""
      group_by_email = ""
      dataset_id     ="MY_OTHER_DATASET"
      project_id     ="my_other_project"
      table_id       ="my_test_view_2"
    }
  ]
}

所以问题在于动态块(我编写它的方式)可以生成此输出

      + access {
          + role          = "WRITER"
          + special_group = "projectWriters"

          + view {}
        }

此选项已应用,没有错误,但是会一遍又一遍地重新应用

问题似乎是提供商API响应中不包含空的view{}

有什么建议可以使view块的条件不为空吗?

1 个答案:

答案 0 :(得分:0)

我解决了这个问题。我稍稍更改了模块和变量类型。

我已将角色和视图拆分为数据集父地图中各自的地图列表。

每个块中都有条件,因此仅当角色存在或视图存在时才应用动态块。

还意识到动态块在错误的迭代器上进行迭代。

动态块在var.datasets上进行迭代,这导致分配给每个数据集的权限应用于所有数据集。因此,现在已更改为遍历each.value(来自for_each资源)。

这是有效的新代码

MAIN.TF

resource "google_bigquery_dataset" "main" {
  for_each                   = var.datasets
  dataset_id                 = upper("${each.key}_${local.env}")
  location                   = var.region
  delete_contents_on_destroy = true

  dynamic "access" {
    for_each = flatten([for i in each.value : [
      for k, v in i : [
        for l in v :
        {
          role           = l.role
          user_by_email  = l.user_by_email
          group_by_email = l.group_by_email
          special_group  = l.special_group
      }]
      if k == "roles"
    ]])
    content {
      role           = access.value["role"]
      user_by_email  = access.value["user_by_email"]
      group_by_email = access.value["group_by_email"]
      special_group  = access.value["special_group"]
    }
  }

  dynamic "access" {
    for_each = flatten([for i in each.value : [
      for k, v in i : [
        for l in v :
        {
          dataset_id = l.dataset_id
          project_id = l.project_id
          table_id   = l.table_id
      }]
      if k == "views"
    ]])
    content {
      view {
        dataset_id = access.value["dataset_id"]
        project_id = access.value["project_id"]
        table_id   = access.value["table_id"]
      }
    }
  }
}

VARIABLES.TF

variable "datasets" {
  description = "A map of objects, including datasets IDs, roles and views"
  type        = map(list(map(list(map(string)))))
  default     = {}
}

continued....

Terraform.tfvars

datasets = {
  dataset01 = [
    {
      roles = [
        {
          role="WRITER"
          user_by_email="email_address"
          group_by_email=""
          special_group=""
        }
      ]
    views = [
        {
          dataset_id="MY_OTHER_DATASET"
          project_id="my_other_project"
          table_id="my_test_view"
        }
      ]
    }
  ]
  dataset02 = [
    {
      roles = [
        {
          role="READER"
          user_by_email=""
          group_by_email="group"
          special_group=""
        }
      ]
      views=[
        {
          dataset_id="MY_OTHER_DATASET"
          project_id="my_other_project"
          table_id="my_test_view_2"
        }
      ]
    }     
  ]
}