Ранее на этой неделе я создал пост, в котором подробно описал, как создать хорошо спроектированный vpc в AWS. Я решил вернуться к этой теме и поговорить о том, как это реализовать в реальном проекте.
Если вы работаете с AWS для своего работодателя или для личных нужд, у вас, вероятно, есть несколько ресурсов, которые вы создаете и используете в различных сервисах AWS. Отслеживание того, из чего состоит ваш стек, может надоесть, если все, на что вы можете ориентироваться, — это ваша собственная память или документ с некоторым списком ресурсов. Именно здесь на помощь приходит инфраструктура как код. Идея заключается в том, что у вас есть шаблон определенной формы, который развертывает для вас необходимый стек. Затем вы можете развернуть несколько экземпляров этого шаблона для использования в качестве различных сред, просто передавая различные входные параметры. Это становится стандартным процессом управления программными проектами, поскольку инженеры пишут код приложения, а затем также пишут шаблон, описывающий инфраструктуру, на которой будет работать их код.
В этой заметке я расскажу вам о том, как я делал это ранее, но с использованием Terraform для всей инфраструктуры. Я не буду рассказывать о том, что такое Terraform (по крайней мере, не в этом посте) или как настроить провайдера AWS. Я буду описывать только создание сети, как в моем другом посте.
В этом шаблоне я использую одну из самых необычных функций Terraform, cidrsubnet. Это ОЧЕНЬ полезная функция, и я рекомендую ознакомиться с ней. Вкратце, первый аргумент — это cidr вашего VPC, второй — подсеть, для которой вы пытаетесь получить блок cidr, а третий — количество бит, которые вы добавляете к маске подсети.
# For this example we will assume that the value being given to this variable is ["Public", "Private", "Isolated"]
variable "subnet_names" {
type = list(string)
}
# Create the VPC.
resource "aws_vpc" "this" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "MyVpc"
}
}
# Adopt the default route table into our terraform state
resource "aws_default_route_table" "this" {
default_route_table_id = aws_vpc.this.default_route_table_id
tags = {
Name = "Unused"
}
}
# Adopt the default network ACL into our terraform state
resource "aws_default_network_acl" "this" {
default_network_acl_id = aws_vpc.this.default_network_acl_id
# By adding no rules this resource defaults to deny all
tags = {
Name = "Unused"
}
}
# Create our Public, Private and Isolated subnets
resource "aws_subnet" "this" {
count = length(var.subnet_names)
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet("10.0.0.0/16", count.index, 2)
tags = {
Name = var.subnet_names[count.index]
}
}
# Create a route table for each of our subnets
resource "aws_route_table" "this" {
count = length(var.subnet_names)
vpc_id = aws_vpc.this.id
tags = {
Name = var.subnet_names[count.index]
}
}
# Associate our route tables with the appropriate subnets
resource "aws_route_table_association" "this"
count = length(var.subnet_names)
route_table_id = aws_route_table.this[count.index]
subnet_id = aws_subnet.this[count.index]
}
# Create the internet gateway
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = {
Name = "InternetGateway"
}
}
# Create the Elastic IP
resource "aws_eip" "this" {
vpc = true
tags = {
Name = "My Elastic IP"
}
}
# Create the NAT Gateway
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.this.id
subnet = aws_subnet.this[0].id
connectivity_type = "public"
tags = {
Name = "NAT Gateway"
}
}
# Create our Public subnet route
resource "aws_route" "public" {
route_table_id = aws_route_table.this[0].id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
}
# Create our Private subnet route
resource "aws_route" "private" {
route_table_id = aws_route_table.this[1].id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this.id
}
# Create our Network ACLs
resource "aws_network_acl" "this" {
count = length(var.subnet_names)
vpc_id = aws_vpc.this.id
tags = {
Name = var.subnet_names[count.index]
}
}
# Associate each ACL with its subnet
resource "aws_network_acl_association" "this" {
count = length(var.subnet_names)
network_acl_id = aws_network_acl.this[count.index].id
subnet_id = aws_subnet.this[count.index].id
}
# Create the ACL rule for our Public subnet
resource "aws_network_acl_rule" "public" {
network_acl_id = aws_network_acl.this[0].id
rule_number = 100
rule_action = "allow"
protocol = "tcp"
to_port = 443
from_port = 443
cidr_block = "0.0.0.0/0"
}
# Create the ACL rule for our Private subnet
resource "aws_network_acl_rule" "private" {
network_acl_id = aws_network_acl.this[1].id
rule_number = 100
rule_action = "allow"
protocol = "tcp"
to_port = 1024
from_port = 65535
cidr_block = aws_vpc.this.cidr_block
}
# Create the ACL rule for our Isolated subnet
resource "aws_network_acl_rule" "isolated" {
network_acl_id = aws_network_acl.this[2].id
rule_number = 100
rule_action = "allow"
protocol = "tcp"
to_port = 5432
from_port = 5432
cidr_block = aws_vpc.this.cidr_block
}