【連載】terraform によるAWS環境構築入門 第3回 ~ ネットワークとセキュリティーグループ ~
はじめに
この記事ではterraformを用いたVPC構築・セキュリティグループの作成方法について学びます。
マルチAZにも対応させましょう。
連載記事一覧
【連載】terraform によるAWS環境構築入門 最終回 ~ RDSとElastiCache ~
【連載】terraform によるAWS環境構築入門 おまけ1 ~ piplineを用いた継続的インテグレーション ~
本連載での使用技術
1. パブリックネットワークの作成
VPC・インターネットゲートウェイ定義
まずはじめにVPCを定義しましょう。
「enable_dns_support」「enable_dns_hostnames」をtrueに設定し、
AWSのDNSサーバーによる名前解決とホスト名の割り当てを有効にしましょう。
resource "aws_vpc" "example" { cidr_block = "10.0.0.0/16" enable_dns_support = true enable_dns_hostnames = true tags = { Name = "example" } }
次にインターネットゲートウェイを定義し、VPCとインターネットが通信できるようにしましょう。「vpc_id」に↑で作成したVPCを指定して紐付けましょう。
# インターネットゲートウェイ resource "aws_internet_gateway" "example" { vpc_id = aws_vpc.example.id }
パブリックサブネットの構築
パブリックサブネットを定義しましょう。CIDRは「10.0.1.0/24」、アベイラビリティーゾーンは1aと1cの双方を作成しマルチAZに対応させましょう。
また、「map_public_ip_on_launch」をtrueにし、当該サブネットで起動したインスタンスにパブリックIPアドレスを付与させるようにしましょう。
# パブリックサブネット 1a resource "aws_subnet" "public_0" { vpc_id = aws_vpc.example.id cidr_block = "10.0.1.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = true } # パブリックサブネット 1c resource "aws_subnet" "public_1" { vpc_id = aws_vpc.example.id cidr_block = "10.0.2.0/24" availability_zone = "ap-northeast-1c" map_public_ip_on_launch = true }
ルートテーブルの作成
パブリックネットワーク用のルートテーブルを作成しましょう。
# パブリックルートテーブル resource "aws_route_table" "public" { vpc_id = aws_vpc.example.id } # ルートの作成、ルートテーブルと紐付ける resource "aws_route" "public" { route_table_id = aws_route_table.public.id gateway_id = aws_internet_gateway.example.id destination_cidr_block = "0.0.0.0/0" }
サブネットとルートテーブルの関連付け
最後に作成したサブネットとルートテーブルを紐付けましょう。
# サブネットとルートテーブルの紐付け resource "aws_route_table_association" "public_0" { subnet_id = aws_subnet.public_0.id route_table_id = aws_route_table.public.id } # サブネットとルートテーブルの紐付け resource "aws_route_table_association" "public_1" { subnet_id = aws_subnet.public_1.id route_table_id = aws_route_table.public.id }
2. プライベートネットワークの作成
プライベートサブネットの定義
先に作成したパブリックサブネットとは違うCIDR ブロックを指定することに注意しましょう。
また、パブリックIPは不要なので、map_public_ip_on_launchはfalseにします。
# プライベートサブネット 1a resource "aws_subnet" "private_0" { vpc_id = aws_vpc.example.id cidr_block = "10.0.65.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = false } # プライベートサブネット 1c resource "aws_subnet" "private_1" { vpc_id = aws_vpc.example.id cidr_block = "10.0.66.0/24" availability_zone = "ap-northeast-1c" map_public_ip_on_launch = false }
NATゲートウェイの作成とEIPの割当
NATゲートウェイを作成し、プライベートネットワークからインターネットにアクセスできるようにしましょう。NATゲートウェイはパブリックサブネットに紐付けます。
また、NATゲートウェイにはEIPが必要なので、こちらも付与しましょう。
depends_onを使って依存を明示すると、インターネットゲートウェイ作成後に、EIP や NAT ゲートウェイを作成するよう保証できます。
# EIP (NATゲートウェイ 1a) resource "aws_eip" "nat_gateway_0" { vpc = true depends_on = [aws_internet_gateway.example] } # EIP (NATゲートウェイ 1c) resource "aws_eip" "nat_gateway_1" { vpc = true depends_on = [aws_internet_gateway.example] } # NATゲートウェイ 1a resource "aws_nat_gateway" "nat_gateway_0" { allocation_id = aws_eip.nat_gateway_0.id subnet_id = aws_subnet.public_0.id depends_on = [aws_internet_gateway.example] } # NATゲートウェイ 1c resource "aws_nat_gateway" "nat_gateway_1" { allocation_id = aws_eip.nat_gateway_1.id subnet_id = aws_subnet.public_1.id depends_on = [aws_internet_gateway.example] }
ルートテーブルの作成
プライベートネットワークからインターネットへ通信するために、ルートを定義しま す。゙デフォルトルートをdestination_cidr_blockに指定し、NAT ゙ートウェイにルーティングするよう設定します。
「gateway_id」を設定していましたが、リスト7.11では「nat_gateway_id」を設定しています
# プライベート用ルートテーブル 1a resource "aws_route_table" "private_0" { vpc_id = aws_vpc.example.id } # プライベート用ルートテーブル 1c resource "aws_route_table" "private_1" { vpc_id = aws_vpc.example.id } # プライベート用ルート 1a resource "aws_route" "private_0" { route_table_id = aws_route_table.private_0.id nat_gateway_id = aws_nat_gateway.nat_gateway_0.id destination_cidr_block = "0.0.0.0/0" } # プライベート用ルート 1c resource "aws_route" "private_1" { route_table_id = aws_route_table.private_1.id nat_gateway_id = aws_nat_gateway.nat_gateway_1.id destination_cidr_block = "0.0.0.0/0" }
サブネットとルートテーブルの関連付け
最後に作成したサブネットとルートテーブルを紐付けましょう。
# サブネットとルートテーブルの紐付け resource "aws_route_table_association" "private_0" { subnet_id = aws_subnet.private_0.id route_table_id = aws_route_table.private_0.id } # サブネットとルートテーブルの紐付け resource "aws_route_table_association" "private_1" { subnet_id = aws_subnet.private_1.id route_table_id = aws_route_table.private_1.id }
3. セキュリティグループの作成
セキュリテイグループの定義
# セキュリテイグループの定義 resource "aws_security_group" "example" { name = "example" vpc_id = aws_vpc.example.id } # セキュリテイグループの定義(インバウンド) resource "aws_security_group_rule" "ingress_example" { type = "ingress" from_port = "80" to_port = "80" protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.example.id } # セキュリテイグループの定義(アウトバウンド) resource "aws_security_group_rule" "egress_example" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.example.id }
4. まとめ(最終成果物の作成)
以上の解説をまとめて、最終成果物を作成しましょう。
セキュリティグループの作成は頻繁に使うのでモジュール化しましょう。
フォルダ構成は以下の通り。
exaple | |--- iam_role | |___ main.tf | |--- security_group (新) | |___ main.tf (新) | |--- network.tf (新) | |___ main.tf (変更)
ネットワーク定義
「network.tf」にネットワーク定義を記載しましょう。
以下のようになります。
# VPC定義 resource "aws_vpc" "example" { cidr_block = "10.0.0.0/16" enable_dns_support = true enable_dns_hostnames = true tags = { Name = "example" } } # インターネットゲートウェイ resource "aws_internet_gateway" "example" { vpc_id = aws_vpc.example.id } # パブリックルートテーブル resource "aws_route_table" "public" { vpc_id = aws_vpc.example.id } # パブリックルート resource "aws_route" "public" { route_table_id = aws_route_table.public.id gateway_id = aws_internet_gateway.example.id destination_cidr_block = "0.0.0.0/0" } # パブリックサブネット 1a resource "aws_subnet" "public_0" { vpc_id = aws_vpc.example.id cidr_block = "10.0.1.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = true } # パブリックサブネット 1c resource "aws_subnet" "public_1" { vpc_id = aws_vpc.example.id cidr_block = "10.0.2.0/24" availability_zone = "ap-northeast-1c" map_public_ip_on_launch = true } # サブネットとルートテーブルの紐付け resource "aws_route_table_association" "public_0" { subnet_id = aws_subnet.public_0.id route_table_id = aws_route_table.public.id } # サブネットとルートテーブルの紐付け resource "aws_route_table_association" "public_1" { subnet_id = aws_subnet.public_1.id route_table_id = aws_route_table.public.id } # EIP (NATゲートウェイ 1a) resource "aws_eip" "nat_gateway_0" { vpc = true depends_on = [aws_internet_gateway.example] } # EIP (NATゲートウェイ 1c) resource "aws_eip" "nat_gateway_1" { vpc = true depends_on = [aws_internet_gateway.example] } # NATゲートウェイ 1a resource "aws_nat_gateway" "nat_gateway_0" { allocation_id = aws_eip.nat_gateway_0.id subnet_id = aws_subnet.public_0.id depends_on = [aws_internet_gateway.example] } # NATゲートウェイ 1c resource "aws_nat_gateway" "nat_gateway_1" { allocation_id = aws_eip.nat_gateway_1.id subnet_id = aws_subnet.public_1.id depends_on = [aws_internet_gateway.example] } # プライベートルートテーブル 1a resource "aws_route_table" "private_0" { vpc_id = aws_vpc.example.id } # プライベートルートテーブル 1c resource "aws_route_table" "private_1" { vpc_id = aws_vpc.example.id } # プライベートルート 1a resource "aws_route" "private_0" { route_table_id = aws_route_table.private_0.id nat_gateway_id = aws_nat_gateway.nat_gateway_0.id destination_cidr_block = "0.0.0.0/0" } # プライベートルート 1c resource "aws_route" "private_1" { route_table_id = aws_route_table.private_1.id nat_gateway_id = aws_nat_gateway.nat_gateway_1.id destination_cidr_block = "0.0.0.0/0" } # プライベートサブネット 1a resource "aws_subnet" "private_0" { vpc_id = aws_vpc.example.id cidr_block = "10.0.65.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = false } # プライベートサブネット 1c resource "aws_subnet" "private_1" { vpc_id = aws_vpc.example.id cidr_block = "10.0.66.0/24" availability_zone = "ap-northeast-1c" map_public_ip_on_launch = false } # サブネットとルートテーブルの紐付け resource "aws_route_table_association" "private_0" { subnet_id = aws_subnet.private_0.id route_table_id = aws_route_table.private_0.id } # サブネットとルートテーブルの紐付け resource "aws_route_table_association" "private_1" { subnet_id = aws_subnet.private_1.id route_table_id = aws_route_table.private_1.id }
セキュリティグループのモジュール化
セキュリティグループの作成は頻繁に使うのでモジュール化しましょう。
「./security_group/main.tf」を以下のように記述します。
「name」「vpc_id」「port」「cidr_blocks」を外部パラメータとし、再利用可能としています。
variable "name" {} variable "vpc_id" {} variable "port" {} variable "cidr_blocks" { type = list(string) } resource "aws_security_group" "default" { name = var.name vpc_id = var.vpc_id } resource "aws_security_group_rule" "ingress" { type = "ingress" from_port = var.port to_port = var.port protocol = "tcp" cidr_blocks = var.cidr_blocks security_group_id = aws_security_group.default.id } resource "aws_security_group_rule" "egress" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.default.id } output "security_group_id" { value = aws_security_group.default.id }
セキュリティグループモジュールの呼び出し
先に作成したセキュリティグループモジュールを呼び出し、セキュリティグループを定義しましょう。
「./main.tf」に以下を追記します。
# モジュール[security_group]呼出 module "example_sg" { source = "./security_group" name = "module-sg" vpc_id = aws_vpc.example.id port = 80 cidr_blocks = ["0.0.0.0/0"] }
5. 実行
まずはinitしましょう。これをやらないと、モジュールの呼び出しができません。
$ terraform init
実行計画の確認。前回にも書きましたが、破壊的な変更が入っていないか入念に確認しましょう。
$ terraform plan
実行。実行されたら、AWSの管理画面から確認しましょう。
うまくいっていればIAMページ左上の「Search IAM」のフォームに「describe-regions-for-ec2」と入力て検索ができるはずです。
$ terraform apply