Transit GatewayでVPC間通信する構成をTerraformで作成してみた
以下のエントリで紹介されていた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
通信できました!
まとめ
月並みの感想で恐縮ですが、やはり手を動かすのは大事ですね。どんなリソースが必要か理解が深まりました。