[Terraform][AWS]Terraform Module使用技巧

[Terraform][AWS]Terraform Module使用技巧

关于一些先置的技巧,比如Profile自动导入和Backend同步State File, 请移步这里
顺便温故一下Terraform传统艺能:

cd terraform/resources/common/dev
terraform init -backend-config=./backend.tfvars
terraform plan -out dev-$(date +%s).plan
terraform apply "dev-1591347481.plan"

Modules的基本介绍

这个视频介绍得非常清楚,演讲者就是<Terraform Up&Running>的作者,建议先行收看
How to Build Reusable, Composable, Battle tested Terraform Modules
而相对进阶一些的操作,比如Module Composition, 可以参考官方文档
Module Compositiond

第一阶段:自定义

直接定义需要重复使用的资源,简单明了。缺点是,随着定制化需求愈高,自定义Module的复杂度也越来越来高,维护成本水涨船高。

第二阶段:引用registry

此做法的优点是不用重复造轮子,减少自定义Module的维护,尤其是使用官方registry,省心省力爽歪歪。
但是用了一段时间后,你会发现每次消费引用时,都需要声明registry,比如典型的:

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  version = "3.10.0"
  # insert the 2 required variables here
}

而如果registry版本更迭,你需要修改所有调用它的resources,那时茫茫大海千头万绪,手工操作也非常容易疏漏。所以这时,你需要多一层抽象/剥离/本地控制。

第三阶段:基于registry的改造

这么做的好处主要就是方便版本控制,同时可以定制精简(这点我实践还不多)。
首先介绍一下directory tree。定制化的组件全部放在modules目录下,而消费者放在resources目录下。

backend
   |-- main
   |   |-- dev
   |   |-- prod
modules
   |-- alb
   |   |-- data.tf
   |   |-- main.tf
   |   |-- outputs.tf
   |   |-- variables.tf
   |   |-- versions.tf
   |-- ec2
   |   |-- data.tf
   |   |-- main.tf
   |   |-- outputs.tf
   |   |-- variables.tf
   |   |-- versions.tf
   |-- iam
   |   |-- main.tf
   |   |-- outputs.tf
   |   |-- variables.tf
   |-- securitygroup
   |   |-- main.tf
   |   |-- outputs.tf
   |   |-- variables.tf
   |   |-- rules.tf
resources
   |-- common
   |   |-- dev
   |   |   |-- main.tf
   |   |   |-- variables.tf
   |   |   |-- outputs.tf
   |   |   |-- terraform.tfvars
   |   |   |-- ...

举一个新建security group的例子,在消费者(resources-common-dev-main.tf)中,声明如下:

module "securitygroup" {
  source              = "../../../modules/securitygroup"
  vpc_id              = var.vpc_id
  name                = "SG-ELB-Public-xxxxxxxx"
  tags                = local.common_tags

  ingress_with_cidr_blocks = [
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      description = "xxxxxx public IP server"
      cidr_blocks = "xxx.xxx.xxx.xxx/32"
    },
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      description = "xxxxxx public IP"
      cidr_blocks = "xxx.xxx.xxx.xx/32"
    },
    {
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      description = "xxxxxx public IP server"
      cidr_blocks = "xxx.xxx.xxx.xxx/32"
    },
    {
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      description = "xxxxxx public IP"
      cidr_blocks = "xxx.xxx.xxx.xxx/32"
    }
  ]
  egress_with_source_security_group_id = [
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      description = "SG-EC2-Web&App"
      source_security_group_id = "sg-bac126df"
    }
  ]
}

刚开始时为了让这个组件先跑起来,我们采取的是类似阶段一的自定义方式:在自建的securitygroup module中,简单粗暴地直接拷贝了官方registry内main, variables, output的所有内容。运行成功后,我们就可以着手下一步的改造。

那针对第三阶段的具体改造步骤,涉及修改自定义securitygroup module中的如下文件:

  • output.tf
  • variables.tf
  • main.tf
    注:此解说顺序与我的踩坑顺序一致。

首先我做的是,将main.tf里从源头拷贝的内容一并删除,替换为引用AWS官方Module, 同时加入一些基础变量定义。

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  version = "3.10.0"

  name = var.name
  vpc_id = var.vpc_id
  tags = var.tags
}

在没有更改output的情况下, 报错如下
WX20200612-220450
output.tf:
将原先的诸如

output "this_security_group_id" {
  description = "The ID of the security group"
  value = concat(
    aws_security_group.this.*.id,
    aws_security_group.this_name_prefix.*.id,
    [""],
  )[0]
}

output "this_security_group_vpc_id" {
  description = "The VPC ID"
  value = concat(
    aws_security_group.this.*.vpc_id,
    aws_security_group.this_name_prefix.*.vpc_id,
    [""],
  )[0]
}

等等修改为

output "this_security_group_id" {
  description = "The ID of the security group"
  value = module.security-group.this_security_group_id
}

output "this_security_group_vpc_id" {
  description = "The VPC ID"
  value = module.security-group.this_security_group_vpc_id
}

其次我们尝试一下删除module中的variables.tf, 失败。
variables.tf:
不能删除,否则会出现如下报错。
WX20200613-011923
鉴于报错里只报了我们用到的variables的缺失,因而得出结论:
可以完全拷贝源头variables.tf的所有内容,也可以只单独选取你需要的variables进行精炼。此处我选择保留全部。

此时,我们已经可以成功terraform plan,更改资源了。然而,新的改动摧毁了6个原先的resources(security-group x 1, ingress x 4, egress x 1), 却只新增了一个security-group, 缺少ingress和egress rules的相关资源
WX20200612-222904
而解决方案就在
main.tf:
在引用registry module与定义基本变量之外,添加ingress_with_cidr_block和egress_with_source_security_group_id的定义:

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  version = "3.10.0"

  name = var.name
  vpc_id = var.vpc_id
  tags = var.tags

  ingress_with_cidr_blocks = var.ingress_with_cidr_blocks
  egress_with_source_security_group_id = var.egress_with_source_security_group_id
}

总结下来就是注意两点:

  1. Module版本的控制在这里得以实现
  2. 任何需要用到的variable,都需要在这里定义一下,否则新Module将无法识别,以至于在后续的消费中,不会添加诸如ingress_with_cidr_blocks和egress_with_source_security_group_id的相关resource。

加入后,一切修复,老母猪顺利产仔:
WX20200613-014822
WX20200613-031921

References

https://shinglyu.com/web/2020/02/06/update-aws-security-groups-with-terraform.html

Subscribe to 隅

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe