[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的情况下, 报错如下
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:
不能删除,否则会出现如下报错。
鉴于报错里只报了我们用到的variables的缺失,因而得出结论:
可以完全拷贝源头variables.tf的所有内容,也可以只单独选取你需要的variables进行精炼。此处我选择保留全部。
此时,我们已经可以成功terraform plan,更改资源了。然而,新的改动摧毁了6个原先的resources(security-group x 1, ingress x 4, egress x 1), 却只新增了一个security-group, 缺少ingress和egress rules的相关资源
而解决方案就在
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
}
总结下来就是注意两点:
- Module版本的控制在这里得以实现
- 任何需要用到的variable,都需要在这里定义一下,否则新Module将无法识别,以至于在后续的消费中,不会添加诸如ingress_with_cidr_blocks和egress_with_source_security_group_id的相关resource。
加入后,一切修复,老母猪顺利产仔:
References
https://shinglyu.com/web/2020/02/06/update-aws-security-groups-with-terraform.html