Troang bài viết naỳh, mình xin chia sẽ cách để khởi tạo một module Terraform.
Mục tiêu
Module được tạo ra với mục đích giúp cho code DRY, tái sử dụng lại code
Design Pattern
Đối với mình, một module sẽ có input và output và tag, những resource bên trong modules sẽ được đặt tên một cách hợp lý, để có thể sử dụng cho nhiều trường hợp là lý tưởng nhất.
Khởi tạo module đầu tiên
Trong bài viết này mình sẽ tạo một module vpc dành cho AWS.
link mình để ở đây, các bạn có thể clone về để tham khảo.
Tổng quan
.
├── README.md
├── eip.tf
├── gw.tf
├── outputs.tf
├── rt.tf
├── sn.tf
├── variables.tf
└── vpc.tf
ngoài tạo vpc ra thì mình cón tạo thêm subnet, route, gateway, nat và eip dùng cho nat.
Taging
Tag là một phần vô cũng quan trọng trong quá trình tạo cũng như quản lý resource sau này. Tùy thuộc vào mỗi dự án mà tag có thể khác nhau, nhưng nhình chung tag sẽ chia thành hai loại là tag bắt buộc phải có, mình gọi nó là common tags, và tag còn lại là những tag tùy thuộc vào resource.
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_hostnames = "true"
enable_classiclink = "false"
assign_generated_ipv6_cidr_block = false
tags = merge(
var.common_tags,
tomap({ Name = "${var.prefix}-vpc" })
)
}
common tags mình sẻ khai báo riêng thông qua block local, sau đó sẽ được gắn vào resource thông qua hàm merge()
locals {
common_tags = {
Environment = "${terraform.workspace}"
Project = "${var.project}"
Owner = "${var.contact}"
ManagedBy = "Terraform"
}
}
Input
Khi tạo ra module này mình muốn có thể tùy ý thêm hoặc bớt số lượng subnet bằng cách thay đổi input, vì vậy mình sẽ chọ input có kiểu dữ liệu là array.
cidr_publish_subnet = ["10.1.1.0/24", "10.1.2.0/24"]
cidr_private_subnet = ["10.1.10.0/24", "10.1.11.0/24"]
để có thể đạt được điều này mình cần sử dụng vòng lặp để đếm số lượng subnet truyền vào thông qua hàm length() sau đó tạo số subnet tương ứng thông qua attribute count.
resource "aws_subnet" "subnets_publish" {
count = length(var.availability_zone)
vpc_id = aws_vpc.main.id
map_public_ip_on_launch = true
cidr_block = element(var.cidr_publish_subnet, count.index)
availability_zone = element(var.availability_zone, count.index)
tags = merge(
var.common_tags,
tomap({ Name = "${var.prefix}-publish-${count.index + 1}" })
)
}
resource "aws_subnet" "subnets_private" {
count = length(var.availability_zone)
vpc_id = aws_vpc.main.id
map_public_ip_on_launch = true
cidr_block = element(var.cidr_private_subnet, count.index)
availability_zone = element(var.availability_zone, count.index)
tags = merge(
var.common_tags,
tomap({ Name = "${var.prefix}-private-${count.index + 1}" })
)
}
mình sử dụng hàm element() để lấy giá trị cidr trong array truyền vào tương ứng với từng index của count().
data "aws_region" "current" {}
availability_zone = ["${data.aws_region.current.name}a", "${data.aws_region.current.name}b"]
về AZ, mình cũng sẽ truyền một aray về AZ vào module, mình cũng sử dụng hàm element() để lấy giá trị cidr trong array truyền vào tương ứng với từng index của count(). Thì sau khi tạo xong subnet đầu tiên sẽ nằm trông az-a, và subnet thứ hai sẽ nằm trong az-c. áp dụng cho cả public và private subnet nhé, các bạn tham khảo và tùy biến lại cho phù hợp với trường hợp sử dụng nhé
Về nat, mình sẽ có một tùy chọn tạo hoặc không tạo , tùy vào mục đích sử dụng, mặc định các public subnet để được routing qua internet gateway, còn các private subnet sẽ được routing qua nat gateway
nat_gateway_enabled = false
Để đạt được điều này mình sẽ sử dụng điều kiện trong block tạo nat
resource "aws_nat_gateway" "nat_gw" {
count = var.nat_gateway_enabled == true ? length(var.cidr_publish_subnet) : 0
allocation_id = element(aws_eip.eip.*.id, count.index)
subnet_id = element(aws_subnet.subnets_publish.*.id, count.index)
tags = merge(
var.common_tags,
tomap({ Name = "${var.prefix}-public-${count.index + 1}" })
)
}
Mình sẽ sử dụng attribute count nếu count này có giá trị bằng 0 thì resource này sẽ không được tạo,
Input hoàn thiện sẽ như thế này.
module "vpc" {
source = "../"
common_tags = local.common_tags
prefix = local.prefix
cidr_block = "10.1.0.0/16"
cidr_publish_subnet = ["10.1.1.0/24", "10.1.2.0/24"]
cidr_private_subnet = ["10.1.10.0/24", "10.1.11.0/24"]
availability_zone = ["${data.aws_region.current.name}a", "${data.aws_region.current.name}b"]
nat_gateway_enabled = false
}
Output
Mình sẽ output ra những gì mình đã tạo, và các bạn dũng để ý là mình sẽ đặt tên cho từng resource một cách chung nhất, để có thể sử dụng cho nhiều trường hợp.
output "vpc_id" {
value = aws_vpc.main.id
sensitive = false
}
output "subnet_publish" {
value = aws_subnet.subnets_publish[*].id
}
output "subnets_private" {
value = aws_subnet.subnets_private[*].id
}
output "publish_subnets_cidr_blocks" {
value = aws_subnet.subnets_publish[*].cidr_block
}
output "private_subnets_cidr_blocks" {
value = aws_subnet.subnets_private[*].cidr_block
}