Terraform 条件性创建 VPC 解决方案

80次阅读
没有评论

问题描述

在创建 Terraform 模板的过程中,有一个部分涉及创建 VPC。用户可以选择指定参数 create-vpc。这个参数将传递给模块 terraform-aws-vpc,该模块将基于此参数创建 VPC,如下所示:

module "vpc" {
  source     = "terraform-aws-modules/vpc/aws"
  create_vpc = var.create-vpc
  # ... 省略其他配置
}

现在,我正试图使用一个 data 源来与我的模板的其余部分共享 VPC,如下所示:

data "aws_vpc" "myvpc" {
  id = "${module.vpc.vpc_id}"
}

如果 VPC 尚未创建,我该如何更新上述代码,以使用变量 vpc-id 替代模块的输出?理想情况下,我想实现类似如下的操作:

data "aws_vpc" "myvpc" {
  count = var.vpc-id != "" ? 1 : 0
  id    = "${module.vpc.vpc_id}"
}

data "aws_vpc" "myvpc" {
  count = var.vpc-id != "" ? 0 : 1
  id    = var.vpc-id
}

但由于资源名称需要唯一,上述方法无法实现。

解决方案

请注意以下操作可能会因版本差异而有所不同,修改前请做好备份。

方案1:使用局部变量控制 VPC 创建

首先,我们需要声明一个用于自己模块的 vpc_id 变量,其余部分将使用它来做出决策:

variable "vpc_id" {
  type    = string
  default = null # 可选,没有默认值
}

然后,我们可以使用这个变量来决定是否创建 VPC。我们可以使用局部变量来达到这个目的:

locals {
  create_vpc = var.vpc_id != null ? true : false
}

现在,我们可以在模块和 data 资源中使用这个值,以便我们将要么创建新的 VPC,要么检索现有 VPC 的数据:

module "vpc" {
  source     = "terraform-aws-modules/vpc/aws"
  create_vpc = local.create_vpc
  # ... 省略其他配置
}

data "aws_vpc" "selected" {
  count = local.create_vpc ? 0 : 1
  id    = var.vpc_id
}

接下来,我们可以创建另一个局部变量,根据需要从这两个位置派生其他与 VPC 相关的值,例如 idcidr_block

locals {
  vpc = local.create_vpc ? {
    id         = module.vpc.vpc_id
    cidr_block = module.vpc.vpc_cidr_block
  } : {
    id         = data.aws_vpc.selected.id
    cidr_block = data.aws_vpc.selected.cidr_block
  }
}

现在,无论这个模块是创建自己的 VPC 还是使用已有的 VPC,你都可以在配置的其他地方使用 local.vpc.idlocal.vpc.cidr_block 来获取适当的值。

方案2:使用模块组合(Module Composition)

使用模块组合的方法可能适合你,特别是当你构建一个旨在由另一个 Terraform 模块调用的共享模块时。

另一种方法是,你的模块永远不创建自己的 VPC,始终期望有一个 VPC 被提供给它。这是“依赖反转”原则的一个例子。在这种情况下,你的模块专注于解决新问题,而与创建 VPC 的问题解耦。调用模块可以根据适用情况决定如何获取 VPC 信息。

在你的模块中,你可以声明一个名为 vpc 的输入变量,其结构与我们在上一个方法中创建的 local.vpc 相同:

variable "vpc" {
  type = object({
    id         = string
    cidr_block = string
  })
  # 这次 VPC 是必需的。调用方决定如何获得它。
}

然后,你的模块中的表达式将以 var.vpc.id 而不是前一种方法中的 local.vpc.id 获取 VPC id。

调用模块现在可以在几种不同的方式中选择为该变量提供值:

使用 terraform-aws-modules/vpc/aws 模块:

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  # ... 其他配置
}

module "your_module" {
  source = "./modules/your-module"
  vpc    = {
    id         = module.vpc.vpc_id
    cidr_block = module.vpc.vpc_cidr_block
  }
}

直接使用 aws_vpc 资源类型:

resource "aws_vpc" "example" {
  # ... 其他配置
}

module "your_module" {
  source = "./modules/your-module"
  vpc    = aws_vpc.example
}

使用 aws_vpc 数据源查找现有的 VPC:

data "aws_vpc" "example" {
  tags = {
    Environment = "STAGE"
  }
}

module "your_module" {
  source = "./modules/your-module"
  vpc    = data.aws_vpc.example
}

换句话说,这使得你的新模块专注于解决它尝试解决的任何新问题,而与创建 VPC 的问题解耦。调用模块可以根据适当情况决定如何获得 VPC 信息

正文完