Terraform 0.12-从嵌套的for循环生成地图/对象

时间:2019-07-30 22:02:33

标签: terraform

作为对Terraform 0.12 nested for loops的跟踪。我试图从嵌套循环中产生一个对象,但惨不忍睹:(

您将如何制作:

  Outputs:

  association-list = {
    "policy1" = "user1"
    "policy2" = "user1"
    "policy2" = "user2"
  }

发件人:

iam-policy-users-map = {
  "policy1" = [ "user1" ]
  "policy2" = [ "user1", "user2" ]
}

我尝试了以下各种变化:

variable iam-policy-users-map {
  default = {
    "policy1" = [ "user1" ]
    "policy2" = [ "user1", "user2" ]
  }
}

locals {
  association-map = merge({
    for policy, users in var.iam-policy-users-map : {
      for user in users : {
        policy => user
      }
    }
  })

output association-map {
  value = local.association-map
}
到目前为止,成功率为零。只能根据变化获得以下信息:

  

错误:“ for”表达式无效。   “ for”表达式末尾的多余字符。

     

错误:缺少属性值。   期望的属性值,由等号(“ =”)引入。

     

错误:“ for”表达式无效。   构建对象时需要键表达式。

     

错误:缺少键/值分隔符。预期等号(“ =”)   标记属性值的开头。

作为参考,以下代码可以生成地图列表:

variable iam-policy-users-map {
  default = {
    "policy1" = [ "user1" ]
    "policy2" = [ "user1", "user2" ]
  }
}

locals {    
  association-list = flatten([
    for policy, users in var.iam-policy-users-map : [
      for user in users : {
        user   = user
        policy = policy
      }
    ]
  ])
}

output association-list {
  value = local.association-list
}
  

输出:

     

association-list = [{       “ policy” =“ policy1”       “ user” =“ user1”},{       “ policy” =“ policy2”       “ user” =“ user1”},{       “ policy” =“ policy2”       “ user” =“ user2”},]

2 个答案:

答案 0 :(得分:0)

可以在https://github.com/hashicorp/terraform/issues/22263找到部分答案。 长话短说:这是一个愚蠢的尝试,地图不能包含重复的键。

但是,我仍然对理解如何从嵌套的for循环中生成一张地图的地图感兴趣。参见上面的第二个代码示例,生成地图列表。

编辑:上面链接的github问题上给出了完整的答案。

“(显然)这是一个无用的结构,但我想说明这是可能的:

locals {
  association-list = {
    for policy, users in var.iam-policy-users-map:
      policy => {      // can't have the nested for expression before the key!
        for u in users:
           policy => u...
      }
  }
}

Outputs:

association-list = {
  "policy1" = {
    "policy1" = [
      "user1",
    ]
  }
  "policy2" = {
    "policy2" = [
      "user1",
      "user2",
    ]
  }
}

答案 1 :(得分:0)

我也能做这样的事情。分享这些 terraform 列表理解想法是否有助于其他人尝试变异以制作衍生地图。 (我最初通过这个错误 Invalid 'for' expression: Key expression is not valid when building a tuple. 发现了这个和上面链接的问题。

我的目标是让 tfstate 中的资源不成为列表,因为这些资源很难对它们进行 tfstate 操作...[0]...[1] 不清楚它们是什么资源。

我有一个这样的变量,它代表用户在某些 s3 存储桶中创建一些默认文件夹。

locals { 
  #really in a tfvars file
  bucket_users = 
    "testuser-abc" = {
        make_extra_dirs = ["this-dir", "that-dir-2"]
    }
}


# Create extra folders for non-standard use cases per their configuration.
locals {
    # This is the easy way to get an array of extra directories to create
    # But, we don't want to create it or the `tfstate list` command shows non-helpful indices.
    # e.g. 
    # aws_s3_bucket_object.loop_user_folders_extra_customized_folders["0"]
    # aws_s3_bucket_object.loop_user_folders_extra_customized_folders["1"]
    # aws_s3_bucket_object.loop_user_folders_extra_customized_folders["2"]
    # So use the below trick
    users_with_extra_dirs_flat = flatten([
        for username, user_data in local.bucket_users : [
            for extra_dir in user_data["make_extra_dirs"]: {
                username = username,
                extra_dir = extra_dir,
                unique_key = format("%s--%s", username, extra_dir)
            }
        ] if length(user_data["make_extra_dirs"]) > 0
    ])

    # This is much easier to reason about as it creates nice `tfstate list` 
    # output in case we need to do any future refactorings.
    # e.g.
    # aws_s3_bucket_object.loop_user_folders_extra_customized_folders["testuser-abc--this-dir"]
    # aws_s3_bucket_object.loop_user_folders_extra_customized_folders["testuser-abc--that-dir-2"]
    users_with_extra_dirs_hash_makes_more_maintainable_tfstate_keys_vs_array = {
        for each in local.users_with_extra_dirs_flat : each.unique_key => { username = each.username, extra_dir = each.extra_dir }
    }
}

resource "aws_s3_bucket_object" "loop_user_folders_extra_customized_folders" {
    for_each = local.users_with_extra_dirs_hash_makes_more_maintainable_tfstate_keys_vs_array
    # lesser way for_each = {for idx, value in local.users_with_extra_dirs_flat : idx => value }

    bucket = "my-example-bucket"
    acl = "private"
    key = "somepath/${each.value.username}/${each.value.extra_dir}/" # Note this must end in a "/" to make that appear as 'directory'
    source = "/dev/null"
}

可能有几种方法可以进一步清理这个问题,尽管有一个中间变量,然后进行转换,这有助于解释正在发生的事情和调试。