地形地图中的可变键

  • 本文关键字:地图 terraform
  • 更新时间 :
  • 英文 :


在Terraform中,我正在尝试创建一个模块,其中包含一个带有可变键的地图。 我不确定这是否可能,但我尝试了以下方法但没有成功。

resource "aws_instance" "web" {
    ami = "${var.base_ami}"
    availability_zone = "${var.region_a}"
    instance_type = "${var.ec2_instance_size}"
    security_groups = ["sec1"]
    count = "${var.ec2_instance_count}"
    tags {
        Name = "${var.role} ${var_env}"
        role = "${var.app_role}"
        ${var.app_role} = "${var_env}"
    }
}

而这个:

tags {
   Name = "${var.role} ${var_env}"
}
tags."${var.role}" = "${var.env}"

有什么想法吗?目前在Terraform中无法做到这一点吗?

(现在)terraform 插值语法中支持一个 lookup 函数,它允许您在地图中查找动态键。

使用它,我现在可以做这样的事情:

output "image_bucket_name" {
  value = "${lookup(var.image_bucket_names, var.environment, "No way this should happen")}"
}

哪里:

variable "image_bucket_names" {
  type = "map"
  default = {
    development = "bucket-dev"
    staging = "bucket-for-staging"
    preprod = "bucket-name-for-preprod"
    production = "bucket-for-production"
  }
}

environment是一个简单的字符串变量。

更新

接受的答案描述了如何在现有地图中执行动态查找。对于使用动态键构建地图,在 HCL2 (0.12) 中,可以在键中使用带引号的插值表达式:

resource "aws_instance" "web" {
  count = "${var.ec2_instance_count}"
  
  ami = "${var.base_ami}"
  availability_zone = "${var.region_a}"
  instance_type = "${var.ec2_instance_size}"
  security_groups = ["sec1"]
  tags = {
    Name              = "${var.role} ${var.env}"
    role              = "${var.app_role}"
    "${var.app_role}" = "${var.env}"               # <------ like this
  }
}

修复问题#21566后,您可以将"${var.app_role}"替换为 (var.app_role) ,这是文档中描述的方法。

(与下面相同的警告也适用于此处:如果var.app_role包含其中一个文本键作为其值,那么它将替换它。

旧答案

接受的答案描述了如何在现有地图中执行动态查找。对于使用动态键构建映射,在 HCL2 (0.12) 中,有两种方法:

对于表达式 + merge

您可以使用 for 表达式从键的一个或多个变量动态构建映射,然后将其与 merge 函数结合使用以混合使用静态键和动态键构建新映射:

variable "app_role" {
  type = string
}
locals {
  tags = merge(
    {
      Name = "${var.role} ${var.env}"
      role = "${var.app_role}"
    },
    {
      for k in [var.app_role]: k => "${var.env}"
    }
  )
}

zipmap

或者,您可以使用zipmap一次性构建它:

locals {
  tags = zipmap(
    [
       "Name",
       "role",
       var.app_role
    ],
    [
       "${var.role} ${var.env}",
       var.app_role,
       var.env
    ]
  )
}

然后,您可以在资源中使用此映射:

resource "aws_instance" "web" {
  count = "${var.ec2_instance_count}"
  
  ami = "${var.base_ami}"
  availability_zone = "${var.region_a}"
  instance_type = "${var.ec2_instance_size}"
  security_groups = ["sec1"]
  tags = local.tags // or inline the above here
}

需要注意的是,如果var.app_role等于 "Name""role" ,那么它将覆盖您的静态值。您可以通过交换merge中的参数或对zipmap中的列表重新排序来避免这种情况,尽管这种冲突更有可能是配置错误,应在应用之前捕获并修复。

以下内容适用于地形版本 0.11.7。 此解决方案使用地图功能。

resource "aws_instance" "web" {
  ...
  tags = "${map(
    "Name", "${var.role} ${var_env}",
    "role", "${var.app_role}",
    "${var.app_role}", "${var_env}"
  )}"
}

我最近也需要动态设置标签键,并设法使用 zipmap 来做到这一点:

locals {
  ec2_tag_keys = ["some/prefix/${var.some_var}", "another_tag"]
  ec2_tag_vals = ["some value", "another value"]
}
resource "aws_instance", "foo" {
  ...
  tags = "${zipmap(local.ec2_tag_keys, local.ec2_tag_vals)}"
}

它有点笨拙,但它有效。

我不确定它是什么时候添加的,但至少从 0.11.7 版开始,Terraform 支持使用变量作为地图键。以下是我当前如何使用它来选择 AWS 实例类型的示例:

.tf文件中:

variable "environment" {}
...
variable "instance_types_webserver" {
  type = "map"
  default = {
    testing    = "t2.small"
    qa         = "t2.medium"
    staging    = "t2.xlarge"
    production = "t2.xlarge"
  }
}
...
resource "aws_opsworks_instance" "verification" {
  stack_id      = "${aws_opsworks_stack.verification.id}"
  layer_ids     = ["${aws_opsworks_custom_layer.verification.id}"]
  instance_type = "${var.instance_types_webserver["${var.environment}"]}"
  state         = "running"
  count         = 1
}

.tfvars文件中:

...
environment = "testing"
...

实现此目的的另一种(新)方法是通过(var.myvar): "somevalue"

resource "kubernetes_secret" "redis" {
  metadata {
    name = local.redis_secret_name
    namespace = var.namespace
  }
  data = {
    (local.redis_secret_password_key): base64encode(random_password.redis.result)
  }
}

最新更新