模块中的 Terraform try/catch?

Terraform try/catch in a module?

Terraform v0.12.x,AWS 提供商

我正在尝试编写一个通用模块,该模块将 return 传入的 EBS 卷名的现有 EBS 快照,或者拍摄传入的 EBS 卷名的快照。无论哪种方式,它都应该 return 一个快照 ID。

这是获取现有快照的代码。

data "aws_ebs_snapshot" "snapshot" {
  most_recent = true
  filter {
    name   = "tag:Name"
    values = [var.ebs_id]
  }
  filter {
    name   = "status"
    values = ["completed"]
  }
}

output "snapshot_id" {
  value       = data.aws_ebs_snapshot.snapshot.id
  description = "Jenkins master snapshot id"
}

这是拍摄快照的代码。

data "aws_ebs_volume" "ebs" {
  most_recent = true
  filter {
    name   = "tag:Name"
    values = [var.ebs_id]
  }
}

// Take a snapshot of the green EBS resource
resource "aws_ebs_snapshot" "snapshot" {
  volume_id = data.aws_ebs_volume.ebs.id
}

output "snapshot_id" {
  value       = aws_ebs_snapshot.snapshot.id
  description = "Jenkins master snapshot id"
}

这可以吗?如果是这样怎么办?我知道我可以将它们分成 2 个独立的模块,但请原谅我。

# try/catch block is of course pseudo-code
try {
  # Get an existing snapshot id
  data "aws_ebs_snapshot" "snapshot" {
    most_recent = true
    filter {
      name   = "tag:Name"
      values = [var.ebs_name]
    }
    filter {
      name   = "status"
      values = ["completed"]
    }
  }

  output "snapshot_id" {
    value       = data.aws_ebs_snapshot.snapshot.id
    description = "Jenkins master snapshot id"
  }
}
catch() {
  # Get the volume id and take a snapshot
  data "aws_ebs_volume" "ebs" {
    most_recent = true
    filter {
      name   = "tag:Name"
      values = [var.ebs_id]
    }
  }
   
  // Take a snapshot of the green EBS resource
  resource "aws_ebs_snapshot" "snapshot" {
    volume_id = data.aws_ebs_volume.ebs.id
  }

  output "snapshot_id" {
    value       = aws_ebs_snapshot.snapshot.id
    description = "Jenkins master snapshot id"
  }
}

我知道 try/catch 块在 Terraform 中不是这样使用的,那么我怎样才能实现我想要的?

乍一看,您可能认为可以通过数据源检查快照是否存在,然后如果数据源不存在,则在资源上使用类似 count 的东西来创建快照 return 任何东西。不幸的是,这不是 Terraform 的工作方式,因为如果找不到匹配项,数据源将抛出错误,导致 Terraform 退出。

请参阅 HashiCorp 的官方回复 here,当被问及您正在寻找的功能类型时。

The sort of dynamic decision-making that is being requested here runs counter to Terraform's design goals, since it makes the configuration a description of what might possibly be rather than what is.

一般来说,这种事情通过 AWS CLI 脚本或类似 Python/boto3 脚本的东西处理得更好,而不是 Terraform。

你所描述的情况看起来不像是需要根据远程系统做出动态决定的情况,因为你可以完全从输入变量中判断调用者是指定快照 ID 还是卷 ID:

variable "ebs_name" {
  type    = string
  default = null
}

variable "ebs_id" {
  type    = string
  default = null
}

data "aws_ebs_snapshot" "snapshot" {
  count = var.ebs_name != null ? 1 : 0

  most_recent = true

  filter {
    name   = "tag:Name"
    values = [var.ebs_name]
  }
  filter {
    name   = "status"
    values = ["completed"]
  }
}

data "aws_ebs_volume" "ebs" {
  count = var.ebs_id != null ? 1 : 0

  most_recent = true

  filter {
    name   = "tag:Name"
    values = [var.ebs_id]
  }
}
   
// Take a snapshot of the green EBS resource
resource "aws_ebs_snapshot" "snapshot" {
  count = var.ebs_id != null ? 1 : 0

  volume_id = data.aws_ebs_volume.ebs[count.index].id
}

output "snapshot_id" {
  # Return either the generated snapshot or the given
  # snapshot. If the caller specified both for some
  # reason then the generated snapshot takes priority.
  # This will produce an error if neither var.ebs_name
  # nor var.ebs_id is set, because the result will have
  # no elements.
  value = concat(
    aws_ebs_snapshot.snapshot[*].id,
    data.aws_ebs_snapshot.snapshot[*].id,
  )[0]
  description = "Jenkins master snapshot id"
}

为了完整起见,以防其他人将来找到这个答案,我想指出 the Module Composition guide 建议直接写出直接阅读或为每个案例创建代码,而不是在这些案例中做出动态决策,但我展示了上面的动态示例,因为你建议(通过参考使用两个模块来解决这个问题的可能性)你已经考虑过并决定不使用组合风格。