Terraform:如果另一个变量为 x,则将变量默认值设置为 null

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

Terraform: set variable default as null if another variable is x

问题

我正在尝试编写一些 Terraform 代码,用于创建一个函数,并且最好创建一个新的执行角色,但用户可以选择附加现有角色。

variable "create_role" {
  description = "控制是否应创建 Lambda 函数的 IAM 角色"
  type        = bool
  default     = true
}

variable "lambda_role_arn" {
  description = "附加到 Lambda 函数的 IAM 角色 ARN。这决定了谁/什么可以调用您的 Lambda 函数,以及我们的 Lambda 函数可以访问哪些资源。有关更多详细信息,请参阅 Lambda 权限模型。"
  type        = string
}

resource "aws_lambda_function" "lambda_function" {
    function_name                   = function_name
    role                            = var.create_role ? aws_iam_role.lambda[0].arn : var.lambda_role_arn
}

使用这种方法的缺点是 create_role = false 且 lambda_role_arn 未定义的可能性。

是否有一种方法只允许 lambda_role_arn 在 create_role 为 true 时为 null?

提前感谢。

英文:

I'm trying to write up some terraform code which will create a function and preferably create a new execution role, however the user does have the option of attaching an existing role.

variable "create_role" {
  description = "Controls whether IAM role for Lambda Function should be created"
  type        = bool
  default     = true
}

variable "lambda_role_arn" {
  description = " IAM role ARN attached to the Lambda Function. This governs both who / what can invoke your Lambda Function, as well as what resources our Lambda Function has access to. See Lambda Permission Model for more details."
  type        = string
}

resource "aws_lambda_function" "lambda_function" {
    function_name                   = function_name
    role                            = var.create_role ? aws_iam_role.lambda[0].arn : var.lambda_role_arn

The drawback with this method is that there is a chance of create_role = false and lambda_role_arn being undefined.

If there a method of only allowing lambda_role_arn to be null IF create_role is true?

Thanks in advance.

答案1

得分: 0

为了解决这个问题,一种方法是通过在第一次发生无效情况之前阻止它,方法是创建一个控制这两种行为的单一变量。这种设计通常也更容易让模块的用户理解,因为他们可以在一个地方看到与特定行为相关的所有内容。

根据您的要求,我可能会设计成这样,使得模块只在调用者未提供时声明其自己的角色:

variable "lambda_role_arn" {
  type    = string
  default = null

  description = "要用于 Lambda 函数的现有角色的 ARN。如果未设置,模块将声明一个新的角色以供使用。"
}

resource "aws_iam_role" "lambda" {
  count = var.lambda_role_arn == null ? 1 : 0

  # ...
}

locals {
  final_role_arn = coalesce(
    var.lambda_role_arn,
    aws_iam_role.lambda[*].arn...
  )
}

resource "aws_lambda_function" "lambda_function" {
  function_name = function_name
  role          = local.final_role_arn
  # ...
}

当参数未设置时,自动使用默认行为的想法在 Terraform 中是一个相当常见的情况 -- 例如,许多资源类型的参数都是这样工作的 -- 因此,上述设计与典型的 Terraform 设计习惯相吻合:要么在调用者的 module 块中指定一个显式的 lambda_role_arn,要么将其省略以获取默认值。

然而,在某些情况下,这可能被认为是有点过于“神奇”。例如,对于这个特定模块来说,常见情况 可能是传入一个角色,而自动声明内联角色可能是必须明确选择的不寻常情况。如果是这样,那么我会选择略有不同的方法,使模块的使用者 必须 指定如何分配角色 ARN,但可以选择任一选项:

variable "lambda_role" {
  type = object({
    arn       = optional(string)
    automatic = optional(bool, false)
  })
  nullable = false

  validation {
    condition = (
      (var.lambda_role.arn != null && !var.automatic) ||
      (var.lambda_role.arn == null && var.automatic)
    )
    error_message = "必须设置 \\"arn\\" 或 \\"automatic\\" 属性,但不能同时设置两者。"
  }
}

resource "aws_iam_role" "lambda" {
  count = var.lambda_role.automatic ? 1 : 0

  # ...
}

locals {
  final_role_arn = coalesce(
    var.lambda_role.arn,
    aws_iam_role.lambda[*].arn...
  )
}

resource "aws_lambda_function" "lambda_function" {
  function_name = function_name
  role          = local.final_role_arn
  # ...
}

在这种变体中,模块的调用者 必须 在两种可能的模式之间选择一种;省略参数是无效的:

  lambda_role = {
    arn = "..."
  }
  lambda_role = {
    automatic = true
  }

(旁注:我将布尔属性称为 "automatic" 而不是 "create",因为说模块“创建”角色有点不准确。相反,模块会 声明 角色,然后在第一次 terraform apply 时,Terraform 将计划创建它,但此后角色将已经存在,因此模块将继续以保持远程对象与随时间变化的配置的变化相匹配所需的方式来管理它。如果您仍然想称其为 create 也没有关系,但我发现这往往会让一些对 Terraform 还不熟悉其声明式编程模型的新手感到困惑。)

英文:

One way to solve this would be to prevent the invalid situation from occurring in the first place by making a single variable which controls both of these behaviors together. This sort of design is also often easier for users of a module to understand, because they can see everything related to a particular behavior all in one place.

Based on your requirements, I would probably design this so that the module declares its own role only if the caller doesn't provide one:

variable "lambda_role_arn" {
  type    = string
  default = null

  description = "The ARN of a pre-existing role to use for the Lambda function. If unset, the module will declare a new role to use."
}

resource "aws_iam_role" "lambda" {
  count = var.lambda_role_arn == null ? 1 : 0

  # ...
}

locals {
  # This is either the caller's provided ARN or
  # the ARN of the inline-declared role.
  final_role_arn = coalesce(
    var.lambda_role_arn,
    aws_iam_role.lambda[*].arn...
  )
}

resource "aws_lambda_function" "lambda_function" {
  function_name = function_name
  role          = local.final_role_arn
  # ...
}

The idea of there being an automatic default behavior when an argument is left unset is a pretty common situation in Terraform -- lots of resource type arguments work like that, for example -- so a design like the above fits in with typical Terraform design idiom: either specify an explicit lambda_role_arn in the caller's module block, or omit it to get the default one.

However, in some situations that might be considered a little too "magical". For example, it might be true that for this particular module the common case is to pass in a role while having it automatically declared inline is the unusual case that must be explicitly selected. If that were true then I would choose a slightly different approach where the user of the module must specify how the role ARN is to be assigned, but can choose either option:

variable "lambda_role" {
  type = object({
    arn       = optional(string)
    automatic = optional(bool, false)
  })
  nullable = false

  validation {
    condition = (
      (var.lambda_role.arn != null && !var.automatic) ||
      (var.lambda_role.arn == null && var.automatic)
    )
    error_message = "Must set either the \"arn\" or \"automatic\" attribute, but not both."
  }
}

resource "aws_iam_role" "lambda" {
  count = var.lambda_role.automatic ? 1 : 0

  # ...
}

locals {
  # This is either the caller's provided ARN or
  # the ARN of the inline-declared role.
  final_role_arn = coalesce(
    var.lambda_role.arn,
    aws_iam_role.lambda[*].arn...
  )
}


resource "aws_lambda_function" "lambda_function" {
  function_name = function_name
  role          = local.final_role_arn
  # ...
}

In this variation the caller of the module must choose between one of the two possible modes; omitting the argument altogether is invalid:

  lambda_role = {
    arn = "..."
  }
  lambda_role = {
    automatic = true
  }

(Side note: I called the boolean attribute "automatic" instead of "create" because it's a bit of a misnomer to say that the module "creates" the role. Instead, the module declares the role and then on the first terraform apply Terraform will plan to create it, but after that the role will already exist and so the module will just continue to manage it in whatever way needed to keep the remote object matching the changes to the configuration over time. Not a big deal if you do still want to call it create, but I find that tends to confuse some folks new to Terraform who aren't yet accustomed to its declarative programming model.)

huangapple
  • 本文由 发表于 2023年7月27日 21:09:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76780081.html
匿名

发表评论

匿名网友

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

确定