Combining locals and variables

huangapple go评论57阅读模式
英文:

Combining locals and variables

问题

尝试在变量定义中使用本地变量似乎不起作用,是否知道原因或如何解决?

locals {
  project_id = {
    dev   = "dev01-a7"
    prod  = "prd01-b2"
  }
}

variable "project_id" {
    default = local.project_id[terraform.workspace]
}

provider "google" {
  project = var.project_id
  region  = var.region
  credentials = "xx.json"
}

错误:

│ 错误: 不允许使用变量
│   在 main.tf 的第 13 行,位于 variable "project_id":
│   13:     default = local.project_id[terraform.workspace]
│ 此处不允许使用变量。
│ 错误: 不允许使用变量
│   在 main.tf 的第 13 行,位于 variable "project_id":
│   13:     default = local.project_id[terraform.workspace]
│ 此处不允许使用变量。
英文:

I try to use a local inside the definition of a variable but it does not seems to work, do you know why or how to do it ?

The goal behind that is it keep the possibilty for user to override default value with it's own without editing the tf file

locals {
  project_id = {
    dev   = "dev01-a7"
    prod  = "prd01-b2"
  }
}

variable "project_id" {
    default = local.project_id[terraform.workspace]
}

provider "google" {
  project = var.project_id
  region  = var.region
  credentials = "xx.json"
}

Error :

│ Error: Variables not allowed
│
│   on main.tf line 13, in variable "project_id":
│   13:     default = local.project_id[terraform.workspace]
│
│ Variables may not be used here.
╵
╷
│ Error: Variables not allowed
│
│   on main.tf line 13, in variable "project_id":
│   13:     default = local.project_id[terraform.workspace]
│
│ Variables may not be used here.

答案1

得分: 1

这不能按照你尝试的方式完成,但你可以使用类似的引用正确配置文件的方法:

locals {
  project_id = {
    dev   = "dev01-a7"
    prod  = "prd01-b2"
  }
}

provider "google" {
  project     = local.project_id[terraform.workspace]
  region      = var.region
  credentials = "xx.json"
}
英文:

That cannot be done the way you are trying, but you can use a similar way of referencing the correct profile:

locals {
  project_id = {
    dev   = "dev01-a7"
    prod  = "prd01-b2"
  }
}

provider "google" {
  project     = local.project_id[terraform.workspace]
  region      = var.region
  credentials = "xx.json"
}

答案2

得分: 1

以下是您要的翻译内容:

输入变量的默认值必须始终是调用模块的人可以在其自己的module块中编写的内容;不能从调用模块的子模块内部的信息构建,调用模块的人无法引用该信息。

然而,默认值并不是提供后备选项的唯一方法,您可以在模块内使用表达式来描述当变量被设置与未被设置时,Terraform应该采取的行为。例如:

variable "project_id" {
  type = string

  # 这使变量是可选的,而不需要明确的默认值。
  default = null
}

locals {
  default_project_ids = tomap({
    dev  = "dev01-a7"
    prod = "prd01-b2"
  })

  project_id = coalesce(
    var.project_id,
    local.default_project_ids[terraform.workspace],
  )
}

provider "google" {
  project     = local.project_id
  region      = var.region
  credentials = "xx.json"
}

上述内容的重点是:

  • 变量的默认值是null,这意味着它是可选的,但对于模块的用户来说,没有明确的默认值可见,当变量未设置时,var.project_id将为null。
  • 本地值project_id使用coalesce来选择其参数中的第一个非null参数。这意味着如果var.project_id被设置,那么local.project_id将正好是该值,如果未设置,则Terraform将使用local.default_project_ids中的适当条目作为后备。
  • provider "google"块的project参数现在引用local.project_id,而不是var.project_id,因此可以从默认值中受益。

请注意,在子模块中使用terraform.workspace而不是根模块稍微存在一定的可维护性风险,因为工作区概念上属于每个根模块,因此在共享模块中直接依赖于工作区名称将使其无法从具有不同工作区命名方案的不同根模块中重用该模块。

如果这对您的情况不是问题,那么您使用terraform.workspace的方法是有效的,并且将起作用,但如果您想要更强的关注分离,我建议调用者传递一个环境名称,由调用者决定如何确定该名称:

variable "environment" {
  type = string

  validation {
    condition     = contains(["dev", "prod"], var.environment)
    error_message = "必须是“dev”或“prod”."
  }
}

# ...

locals {
  default_project_ids = tomap({
    dev  = "dev01-a7"
    prod = "prd01-b2"
  })

  project_id = coalesce(
    var.project_id,
    local.default_project_ids[var.environment],
  )
}

# ...

然后,模块的调用者可以决定将其工作区用作环境名称的决定者:

module "example" {
  # ...

  environment = terraform.workspace
}
英文:

The default value for an input variable must always be something that the caller of the module could have written in their own module block; it cannot be built from information inside the child module that the caller of the module would not be able to refer to.

However, default values aren't the only way to provide a fallback when something isn't set. You can use expressions within your module to describe to Terraform what behavior you intend when the variable is set vs. not set. For example:

variable "project_id" {
  type = string

  # This makes the variable optional without
  # specifying an explicit default.
  default = null
}

locals {
  default_project_ids = tomap({
    dev  = "dev01-a7"
    prod = "prd01-b2"
  })

  project_id = coalesce(
    var.project_id,
    local.default_project_ids[terraform.workspace],
  )
}

provider "google" {
  project     = local.project_id
  region      = var.region
  credentials = "xx.json"
}

The important parts of the above:

  • The variable's default value is null, which means that it's optional but it doesn't have an explicit default that's visible to the user of the module, and var.project_id will be null when the variable isn't set.
  • The local value project_id uses coalesce to choose the first of its arguments that isn't null. This means that if var.project_id is set then local.project_id will be exactly that value, while if it isn't set then Terraform will use the appropriate entry from local.default_project_ids as a fallback.
  • The provider "google" block's project argument now refers to local.project_id instead of var.project_id, so it can benefit from the fallback to the default.

Note that using terraform.workspace in a child module rather than a root module is a bit of a maintainability hazard, because workspaces conceptually belong to each root module separately and so relying directly on workspace names in a shared module will make it impossible to reuse that module from a different root module that has a different naming scheme for its workspaces.

If that isn't a concern for your situation then what you did with terraform.workspace is valid and will work, but if you want to employ a stronger separation of concerns then I'd suggest a variant where the caller passes in an environment name and it's up to the caller to decide how it will determine that:

variable "environment" {
  type = string

  validation {
    condition     = contains(["dev", "prod"], var.environment)
    error_message = "Must be \"dev\" or \"prod\"."
  }
}

# ...

locals {
  default_project_ids = tomap({
    dev  = "dev01-a7"
    prod = "prd01-b2"
  })

  project_id = coalesce(
    var.project_id,
    local.default_project_ids[var.environment],
  )
}

# ...

Then the caller of the module can be the one to decide that it will use its workspaces as the determinant for the environment name:

module "example" {
  # ...

  environment = terraform.workspace
}

huangapple
  • 本文由 发表于 2023年5月25日 21:06:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76332617.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定