この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
以下のエントリで紹介されていたTransit GatewayでVPC間通信を行なう構成を、Terraformを使って構築してみました。
構成図
ディレクトリ構造
.
├── .terraform-version
├── _main.tf
├── file
│ └── trgw-sandbox-local-bastion-keypair.pub
├── modules
│ ├── ec2
│ │ └── ec2.tf
│ ├── transit-gateway
│ │ └── vpc-attachment.tf
│ └── vpc
│ └── vpc.tf
├── ec2.tf
├── transit-gateway.tf
└── vpc.tf
※ 以下を参考にディレクトリ移動だけで一時クレデンシャルを設定できるようにしています。
方針
二つVPCを作り、双方にEC2インスタンスを立て、それらをTransit Gatewayに接続するということで、A-VPCとB-VPCで重複する処理が複数存在します。
そういった処理はmodule内に記載し、moduleを二度(A-VPC分とB-VPC分)呼び出すような方針で記述しています。
この方針に至った経緯は以下のエントリに記載しておりますので、よければこちらもご覧ください。
各ファイルの説明
_main.tf
ディレクトリ全体に関わる設定をこちらに記載しています。
- terraformのrequired_version
- aws providerのversion
- s3 buckendの設定
- 複数のtfファイルで参照する共通変数
env
環境。local,stag,prod などbasename
プロジェクト名。リソースに名前付けする際に接頭辞として必ず使う
VPC
vpc.tf
variable vpc {
type = map(string)
default = {
A = "10.0.0.0/16",
B = "172.16.0.0/16"
}
}
module A {
source = "./modules/vpc"
env = var.env
basename = var.basename
name = "A"
cidr = var.vpc["A"]
}
module B {
source = "./modules/vpc"
env = var.env
basename = var.basename
name = "B"
cidr = var.vpc["B"]
}
modules/vpc/vpc.tf
variable env {}
variable basename {}
variable name {}
variable cidr {}
data aws_availability_zones az {}
resource aws_vpc vpc {
cidr_block = var.cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.basename}-${var.env}-vpc-${var.name}"
}
}
resource aws_internet_gateway igw {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.basename}-${var.env}-vpc-${var.name}-igw"
}
}
resource aws_subnet public {
count = 2
vpc_id = aws_vpc.vpc.id
cidr_block = cidrsubnet(aws_vpc.vpc.cidr_block, 8, count.index)
availability_zone = data.aws_availability_zones.az.names[count.index]
map_public_ip_on_launch = false
tags = {
Name = "${var.basename}-${var.env}-vpc-${var.name}-public-subnet-${count.index + 1}"
}
}
resource aws_route_table public {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.basename}-${var.env}-vpc-${var.name}-public-subnet-rtb"
}
}
resource aws_route public {
route_table_id = aws_route_table.public.id
gateway_id = aws_internet_gateway.igw.id
destination_cidr_block = "0.0.0.0/0"
}
resource aws_route_table_association public {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource aws_network_acl acl {
vpc_id = aws_vpc.vpc.id
subnet_ids = aws_subnet.public.*.id
ingress {
protocol = "-1"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
egress {
protocol = "-1"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags = {
Name = "${var.basename}-${var.env}-vpc-${var.name}-acl"
}
}
output vpc_id {
value = aws_vpc.vpc.id
}
output cidr {
value = aws_vpc.vpc.cidr_block
}
output public_subnet_ids {
value = aws_subnet.public.*.id
}
output public_route_table_id {
value = aws_route_table.public.id
}
EC2
ec2.tf
事前にSSHキーペアを作成して、公開鍵を file/trgw-sandbox-local-bastion-keypair.pub
に配置します。
locals {
ssh_allowed_cidr = "xxx.xxx.xxx.xxx/32"
}
module ec2-in-A {
source = "./modules/ec2"
env = var.env
basename = "${var.basename}-A"
vpc_id = module.A.vpc_id
subnet_id = module.A.public_subnet_ids[0]
ping_allowed_cidr = module.B.cidr
ssh_allowed_cidr = local.ssh_allowed_cidr
public_key_path = "./file/${var.basename}-${var.env}-bastion-keypair.pub"
private_ip = "10.0.0.161"
}
module ec2-in-B {
source = "./modules/ec2"
env = var.env
basename = "${var.basename}-B"
vpc_id = module.B.vpc_id
subnet_id = module.B.public_subnet_ids[0]
ping_allowed_cidr = module.A.cidr
ssh_allowed_cidr = local.ssh_allowed_cidr
public_key_path = "./file/${var.basename}-${var.env}-bastion-keypair.pub"
private_ip = "172.16.0.107"
}
modules/ec2/ec2.tf
元のブログエントリの内容と合わせるために、private IPを指定できるようにしました。こんなことできるなんて初めて知りました。
ただし、通常private IPを指定する必要がある場合は少ないと思います。ですのでdefault値をnullにし、指定が無い場合は自動採番されるようにしました。
variable env {}
variable basename {}
variable vpc_id {}
variable subnet_id {}
variable ssh_allowed_cidr {}
variable ping_allowed_cidr {}
variable public_key_path {}
variable private_ip {
default = null
}
locals {
ingress_rules = {
ssh = {
protocol = "tcp",
from_port = 22,
to_port = 22,
cidr = var.ssh_allowed_cidr
},
ping = {
protocol = "icmp",
from_port = 8,
to_port = 0,
cidr = var.ping_allowed_cidr
}
}
}
resource "aws_security_group" ec2 {
name = "ec2-sg"
description = "ec2-sg"
vpc_id = var.vpc_id
}
resource aws_security_group_rule egress {
type = "egress"
security_group_id = aws_security_group.ec2.id
protocol = -1
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
resource aws_security_group_rule ingress {
for_each = local.ingress_rules
type = "ingress"
security_group_id = aws_security_group.ec2.id
protocol = each.value.protocol
from_port = each.value.from_port
to_port = each.value.to_port
cidr_blocks = [each.value.cidr]
}
resource aws_key_pair bastion_keypair {
key_name = "${var.basename}-${var.env}-bastion-keypair"
public_key = file(var.public_key_path)
}
data aws_subnet subnet {
id = var.subnet_id
}
resource aws_instance ec2 {
ami = "ami-0ff21806645c5e492"
instance_type = "t2.micro"
availability_zone = data.aws_subnet.subnet.availability_zone
monitoring = false
associate_public_ip_address = true
key_name = aws_key_pair.bastion_keypair.key_name
tags = {
Name = "${var.basename}-${var.env}-bastion"
}
vpc_security_group_ids = [aws_security_group.ec2.id]
subnet_id = var.subnet_id
private_ip = var.private_ip
}
Transit Gateway
transit-gateway.tf
resource aws_ec2_transit_gateway example {
vpn_ecmp_support = "disable"
default_route_table_association = "disable"
default_route_table_propagation = "disable"
auto_accept_shared_attachments = "disable"
}
resource aws_ec2_transit_gateway_route_table example {
transit_gateway_id = aws_ec2_transit_gateway.example.id
}
module A-attachment {
source = "./modules/transit-gateway"
vpc_id = module.A.vpc_id
subnet_ids = module.A.public_subnet_ids
route_table_id = module.A.public_route_table_id
transit_gateway_id = aws_ec2_transit_gateway.example.id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.example.id
destination_vpc_cidr = module.B.cidr
}
module B-attachment {
source = "./modules/transit-gateway"
vpc_id = module.B.vpc_id
subnet_ids = module.B.public_subnet_ids
route_table_id = module.B.public_route_table_id
transit_gateway_id = aws_ec2_transit_gateway.example.id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.example.id
destination_vpc_cidr = module.A.cidr
}
modules/transit-gateway/vpc-attachment.tf
variable vpc_id {}
variable subnet_ids {}
variable route_table_id {}
variable transit_gateway_id {}
variable transit_gateway_route_table_id {}
variable destination_vpc_cidr {}
resource aws_ec2_transit_gateway_vpc_attachment vpc_attachment {
subnet_ids = var.subnet_ids
transit_gateway_id = var.transit_gateway_id
vpc_id = var.vpc_id
transit_gateway_default_route_table_association = false
transit_gateway_default_route_table_propagation = false
}
resource aws_ec2_transit_gateway_route_table_association association {
transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.vpc_attachment.id
transit_gateway_route_table_id = var.transit_gateway_route_table_id
}
resource aws_ec2_transit_gateway_route_table_propagation propagation {
transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.vpc_attachment.id
transit_gateway_route_table_id = var.transit_gateway_route_table_id
}
resource aws_route to-trgw {
route_table_id = var.route_table_id
transit_gateway_id = var.transit_gateway_id
destination_cidr_block = var.destination_vpc_cidr
}
アクセス確認
Transit Gatewayにアタッチメントしたリソース間で通信を確認してみます。
A-Server → B-Server
[ec2-user@ip-10-0-0-161 ~]$ ping -c 3 172.16.0.107
PING 172.16.0.107 (172.16.0.107) 56(84) bytes of data.
64 bytes from 172.16.0.107: icmp_seq=1 ttl=254 time=0.853 ms
64 bytes from 172.16.0.107: icmp_seq=2 ttl=254 time=0.526 ms
64 bytes from 172.16.0.107: icmp_seq=3 ttl=254 time=0.510 ms
--- 172.16.0.107 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2031ms
rtt min/avg/max/mdev = 0.510/0.629/0.853/0.160 ms
B-Server → A-Server
[ec2-user@ip-172-16-0-107 ~]$ ping -c 3 10.0.0.161
PING 10.0.0.161 (10.0.0.161) 56(84) bytes of data.
64 bytes from 10.0.0.161: icmp_seq=1 ttl=254 time=0.878 ms
64 bytes from 10.0.0.161: icmp_seq=2 ttl=254 time=0.547 ms
64 bytes from 10.0.0.161: icmp_seq=3 ttl=254 time=0.539 ms
--- 10.0.0.161 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2039ms
rtt min/avg/max/mdev = 0.539/0.654/0.878/0.160 ms
通信できました!
まとめ
月並みの感想で恐縮ですが、やはり手を動かすのは大事ですね。どんなリソースが必要か理解が深まりました。