メインコンテンツまでスキップ

VPC: CIDR設計と2 AZ×3層ネットワークのコスト判断

前章までで、Terraformを実行する専用のIAMユーザーができました。ここから実際にAWSのリソースを積み上げていきます。最初に作るのはVPCです。ALBもECSもRDSも、自分がどのサブネットに属するかを作成時に指定する必要があり、そのサブネットを持つVPCが先になければ作れません。1

VPCそのものはCIDRブロック1つを持つだけのリソースで、実質的な設計判断はサブネットの区切り方に現れます。この章では、Tasukuが使う10.0.0.0/16というCIDRの根拠、2つのアベイラビリティゾーンにpublic・private-app・private-dataの3層を配置する理由、そしてプライベートサブネットの出口となるNAT Gatewayを2つ用意する根拠を扱います。

4.1. VPC と CIDR の基礎

VPC (Virtual Private Cloud) は、AWSアカウント内に論理的に切り出された仮想ネットワークです。作成時に指定するのはIPv4のCIDRブロック1つだけで、そのアドレス空間の中にサブネットを切り出していきます。

CIDRブロックのサイズには制約があります。AWSが許可するのは/16(65,536個のIPアドレス)から/28(16個)までの範囲で、これより大きくも小さくもできません。2 プライベートIPアドレスの範囲 (RFC 1918) を使うのが通例で、10.0.0.0/8172.16.0.0/12192.168.0.0/16のいずれかから選びます。

サブネットにも同じ/16/28の制約があります。ただしサブネットでは、CIDRブロックの先頭4個と末尾1個、計5個のIPアドレスをAWSが予約するため、実際に使えるIP数はブロックサイズより5少なくなります。/24なら256個から5個引いて251個です。3 4 予約される5個の内訳は、ネットワークアドレス、VPCルータ用、DNSサーバー用、AWSの将来利用向け、そしてブロードキャストアドレスです。3

VPC全体のCIDR設計では、使い切らずに余裕を持たせることがAWS Well-Architected Frameworkでも推奨されています。5 小さすぎるVPCやサブネットを作ってしまうと、後から追加のCIDRブロックを足すか、サブネットを作り直す羽目になります。

4.2. Tasuku の CIDR 設計

Tasukuのネットワークには10.0.0.0/16を割り当てます。RFC 1918の範囲内で、AWSが提供するVPCの例でもそのまま使われる値です。2 65,536個のIPアドレス空間のうち、この章で実際に使うのは6個の/24サブネットだけです。

AZ1AZ2用途
public10.0.0.0/2410.0.1.0/24ALB (第6章)
private-app10.0.10.0/2410.0.11.0/24ECS Fargate (第8・10章)
private-data10.0.20.0/2410.0.21.0/24RDS (第9章)

採番は詰めていません。層ごとに10刻みで空番を残し、10.0.2.0/24から10.0.9.0/24のような間を空けています。第11章で環境を分離するときや、将来サブネットを追加するときに、既存の番号を採番し直さずに済むようにするためです。

/24は251個のIPアドレスを持ちます。3 Fargateタスクは1つにつき1つのENI (Elastic Network Interface) を専有するため、6 実行するタスク数がそのままこの251個を消費しますが、6つのサブネット全体で256個の/24のうち6個しか使っておらず、CIDR空間そのものには十分な余裕があります。

4.3. 2 AZ × 3 層のサブネット構成

Tasukuのネットワークは、2つのアベイラビリティゾーンにpublic・private-app・private-dataの3層を配置します。

publicはインターネットゲートウェイへの経路を持つサブネットで、ALBを置きます。private-appはインターネットゲートウェイへの経路を持たず、NAT Gateway経由でのみ外向き通信ができるサブネットで、ECS Fargateのタスクを置きます。private-dataはprivate-appと同じくインターネットゲートウェイへの経路を持ちませんが、NAT Gatewayへの経路も持たせず、RDSを置きます。

3層に分ける理由は、層ごとに必要な通信の向きが違うためです。ALBは不特定多数からの着信を受ける必要があり、publicサブネット以外の選択肢がありません。ECS Fargateのタスクは、着信は不要な一方、コンテナイメージの取得やログ送信のために外向きの通信が必要です。7 RDSは管理サービスでありインターネットからアクセスされるべきではなく8、かつこの構成ではRDSからインターネットへの発信も必要としません。

アベイラビリティゾーンの指定には、ap-northeast-1aのような文字列を直接書き込みません。aws_availability_zonesデータソースで実行時に取得した一覧の先頭2つを使います。AWSはアベイラビリティゾーンの物理的な場所をアカウントごとにランダムな名前へ割り当てており、あるアカウントのus-east-1aが別のアカウントのus-east-1aと同じ場所を指すとは限りません。9 コードに具体的な名前を固定してしまうと、別のアカウントで動かしたときに意図と違うゾーンに展開されかねません。

なお、この一覧に2つ以上のアベイラビリティゾーンが含まれることはコード上でチェックしていません。Tasukuが使うap-northeast-1は4つのアベイラビリティゾーンを持つため実害はありませんが、10 アベイラビリティゾーンが1つしかないリージョンに同じコードを持ち込む場合は、先に一覧の件数を確認してください。

4.4. インターネットゲートウェイとパブリックサブネットの経路

インターネットゲートウェイ (IGW) は、VPCとインターネットの間の出入り口です。VPCに1つだけアタッチし、vpc_idを指定するだけで済みます。11

publicサブネット2つの経路は、どちらも同じIGWに向かうだけです。IGWはアベイラビリティゾーンに紐づかないため、2つのサブネットで別々のルートテーブルを用意する意味がなく、1枚を共有します。ルート内容は次の1行だけです。

0.0.0.0/0 → インターネットゲートウェイ

4.5. NAT Gateway: プライベートサブネットの出口とコスト判断

ここから常時課金が始まります

NAT Gatewayは、稼働している時間に対する課金と、処理したデータ量に対する課金の2軸で費用が発生します。12 このリソースを作った時点で、この連載ではじめて「使っていなくても時間課金される」リソースが登場します。具体的な料金は改定されるため本文には書きません。試す場合はAWSの料金ページで現行の料金を確認し、使い終えたら削除してください。

private-appサブネットに置くECS Fargateのタスクは、コンテナイメージの取得やログ送信のために外向きの通信が必要です。7 private-appサブネットはIGWへの経路を持たないため、この外向き通信はNAT Gatewayを経由させます。

NAT Gatewayの数には2つの選択肢があります。

1つ目は、2つのアベイラビリティゾーンで1個のNAT Gatewayを共有する構成です。時間課金を半分に抑えられますが、そのNAT Gatewayが乗っているアベイラビリティゾーンが利用不可になると、もう一方の健全なアベイラビリティゾーンにあるprivate-appサブネットもインターネットへの出口を失います。

2つ目は、アベイラビリティゾーンごとに1個ずつ、計2個のNAT Gatewayを用意する構成です。NAT Gatewayの高可用性は単一のアベイラビリティゾーン内に閉じており、アベイラビリティゾーンを跨いだ耐障害性を得るにはアベイラビリティゾーンごとに1個が必要だというのが、AWSの複数の公式ドキュメントで一致している見解です。13 14 15 16

Tasukuでは2個の構成を選びます。第1章で、1つのアベイラビリティゾーンが利用不可になってもサービスを継続する構成にすると決めているためです。17 NAT Gatewayだけアベイラビリティゾーンをまたぐ単一障害点にしてしまっては、この前提と矛盾します。

コストの増分は、時間課金の分だけです。NAT Gatewayが処理するデータの量に対する課金は、同じアベイラビリティゾーン内で見る限りNAT Gatewayの個数に依存せず、そのアベイラビリティゾーンを通過するトラフィックの総量だけで決まります。18 2個にして増えるのは、稼働時間分の固定費だけです。

なお、AWSは「Regional NAT Gateway」という可用性モードも提供しています。1つの論理的なNAT Gatewayが、ワークロードの存在するアベイラビリティゾーンへ自動的に展開されるしくみで、Terraform上もaws_nat_gateway1個で書けます。19 ただし、この機能はアベイラビリティゾーンごとに帯域を確保する動作をしており、20 コードの見た目が1個になることと、実際の課金がアベイラビリティゾーンごとに2個用意する構成より安くなることは別の話です。後者は執筆時点で確認できていません。コスト構造が確定していないこの選択肢を主教材にはせず、Tasukuでは採用せず、選択肢の1つとしてここで触れるにとどめます。

4.6. ルートテーブルの設計

Tasukuのネットワークには4枚のルートテーブルを用意します。

ルートテーブル対象サブネットルート
publicpublic-az1, public-az2 (共有)0.0.0.0/0 → IGW
private-app-az1private-app-az10.0.0.0/0 → NAT Gateway (az1)
private-app-az2private-app-az20.0.0.0/0 → NAT Gateway (az2)
private-dataprivate-data-az1, private-data-az2 (共有)なし

publicが1枚で足りる理由は前節で述べた通りです。private-appは、自分のアベイラビリティゾーンにあるNAT Gatewayにしか向けられないため、アベイラビリティゾーンごとに1枚必要です。

private-dataには、VPC内部向けの暗黙のlocalルート(全ルートテーブルに自動的に入る、CIDR内向けの削除不能なルート)以外、意図的にルートを追加しません。RDSはインターネットからアクセスされるべきではなく8、かつこの構成ではインターネットへの能動的な通信も必要としないためです。21 public・private-app・private-dataという3層に分けた意味は、このルートテーブルの中身の違いにそのまま表れています。publicはどこへでも出ていける、private-appはNAT Gateway越しにだけ出ていける、private-dataはVPC内部以外どこへも出ていけない(ECSタスクからの接続はVPC内部通信のため到達可能)、という3段階です。

いずれのサブネットも、VPC作成時に暗黙で使われる「メインルートテーブル」には乗せません。6つのサブネットすべてに、どのルートテーブルに属するかを明示的に関連付けます。

4.7. セキュリティグループチェーンの方針 (実装は後続章で)

ここまでのルートテーブルの設計は、通信が「どこまで届きうるか」を決めるものでした。実際にどの通信を許可するかは、セキュリティグループが担います。

Tasukuで最終的に必要になるセキュリティグループは3つで、許可の連鎖は次の図の通りです。

セキュリティグループ許可する送信元作成する章
ALB用インターネット (80番→443番ポート)第6章→第7章
ECSタスク用ALB用SGのみ第8章
RDS用ECSタスク用SGのみ第9章

この章の時点では、ALB・ECS・RDSのいずれもまだ存在しません。存在しないリソースへの通信を許可するルールは書けないため、実体の作成はそれぞれのリソースを作る章に委ねます。第3章でIAMロールを予告表だけにとどめたのと同じ考え方です。

この章で実際に手を入れるのは、VPC作成時に自動的にできる既定のセキュリティグループだけです。既定のセキュリティグループは、何も設定しなければ同一セキュリティグループ内からの通信をすべて許可し、外向きの通信もすべて許可した状態のままになります。使う予定のないリソースにこの既定のセキュリティグループが誤って付いてしまったときの影響を抑えるため、ingressとegressのルールを両方とも空にして閉じます。22 23

4.8. Terraform 実装

ここまでの設計をvpc.tfに書いていきます。既存のvariables.tfにはVPC全体のCIDRを変数として追加し、VPC・サブネット・ルーティングといったこの章の主要なリソースはvpc.tf1ファイルに収めます。第2章のbudget.tf、第3章のiam.tfと同じく、1つの章が主に足すリソースを1つのファイルにまとめる方針です。あわせて、後続の章が参照する値をまとめるoutputs.tfをこの章から新設します。

まず、6つのサブネットの定義をローカル値としてまとめます。

locals {
# 2 AZ × 3 層 (public / private-app / private-data) の6サブネット定義
# az_key ("az1"/"az2") を全リソースの for_each キーに揃え、AZ の対応関係を一貫させる
subnets = {
"public-az1" = { tier = "public", az_key = "az1", cidr = "10.0.0.0/24", public_ip_launch = true }
"public-az2" = { tier = "public", az_key = "az2", cidr = "10.0.1.0/24", public_ip_launch = true }
"private-app-az1" = { tier = "private-app", az_key = "az1", cidr = "10.0.10.0/24", public_ip_launch = false }
"private-app-az2" = { tier = "private-app", az_key = "az2", cidr = "10.0.11.0/24", public_ip_launch = false }
"private-data-az1" = { tier = "private-data", az_key = "az1", cidr = "10.0.20.0/24", public_ip_launch = false }
"private-data-az2" = { tier = "private-data", az_key = "az2", cidr = "10.0.21.0/24", public_ip_launch = false }
}

# az1 → names[0]、az2 → names[1] (data.aws_availability_zones.available の取得順)
az_key_to_index = { az1 = 0, az2 = 1 }

# 層ごとに az_key をキーにしたサブネットキーの逆引き。NAT Gateway とルートテーブルの
# for_each をこの az_key で揃えることで、AZ の取り違えを構造的に防ぐ
public_subnets_by_az = { for k, v in local.subnets : v.az_key => k if v.tier == "public" }
private_app_subnets_by_az = { for k, v in local.subnets : v.az_key => k if v.tier == "private-app" }
private_data_subnet_keys = { for k, v in local.subnets : k => k if v.tier == "private-data" }
}

for_eachはこの連載ではじめて使うメタ引数です。6つの似た形のサブネットを個別のブロックとして書く代わりに、1つのresourceブロックに定義のマップを渡し、Terraformにブロック分の展開をまかせます。ポイントは、public・private-appそれぞれのfor_eachのキーを"az1"/"az2"という共通の文字列に揃えていることです。あるアベイラビリティゾーンのNAT Gatewayが、別のアベイラビリティゾーンのルートテーブルに紐づいてしまうような取り違えを、この共通キーによって防ぎます。

アベイラビリティゾーンの一覧は、実行時にデータソースとして取得します。

data "aws_availability_zones" "available" {
state = "available"
}

VPC本体とサブネットを作ります。

resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr

# DNS ホスト名は既定 false のため明示する
enable_dns_support = true
enable_dns_hostnames = true

tags = {
Name = "${local.name_prefix}-vpc"
}
}

resource "aws_subnet" "this" {
for_each = local.subnets

vpc_id = aws_vpc.main.id
cidr_block = each.value.cidr
availability_zone = data.aws_availability_zones.available.names[local.az_key_to_index[each.value.az_key]]
map_public_ip_on_launch = each.value.public_ip_launch

tags = {
Name = "${local.name_prefix}-${each.key}"
}
}

enable_dns_hostnamesは既定でfalseのため、明示的にtrueを指定しています。24 aws_subnetmap_public_ip_on_launchも既定はfalseで、25 publicサブネットの2つだけeach.value.public_ip_launch経由でtrueを渡しています。

インターネットゲートウェイと、publicサブネット用のルートテーブルです。

resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id

tags = {
Name = "${local.name_prefix}-igw"
}
}

resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}

tags = {
Name = "${local.name_prefix}-public-rt"
}
}

resource "aws_route_table_association" "public" {
for_each = local.public_subnets_by_az

subnet_id = aws_subnet.this[each.value].id
route_table_id = aws_route_table.public.id
}

続いて、NAT Gatewayとprivate-app用のルートテーブルです。

resource "aws_eip" "nat" {
for_each = local.public_subnets_by_az

domain = "vpc"

tags = {
Name = "${local.name_prefix}-nat-eip-${each.key}"
}

depends_on = [aws_internet_gateway.main]
}

resource "aws_nat_gateway" "this" {
for_each = local.public_subnets_by_az

allocation_id = aws_eip.nat[each.key].id
subnet_id = aws_subnet.this[each.value].id

tags = {
Name = "${local.name_prefix}-nat-${each.key}"
}

depends_on = [aws_internet_gateway.main]
}

resource "aws_route_table" "private_app" {
for_each = local.private_app_subnets_by_az

vpc_id = aws_vpc.main.id

route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this[each.key].id
}

tags = {
Name = "${local.name_prefix}-private-app-rt-${each.key}"
}
}

resource "aws_route_table_association" "private_app" {
for_each = local.private_app_subnets_by_az

subnet_id = aws_subnet.this[each.value].id
route_table_id = aws_route_table.private_app[each.key].id
}

aws_eipaws_nat_gatewayのどちらにも、インターネットゲートウェイへのdepends_onを明示しています。EIPやNAT Gatewayは内部的にインターネットゲートウェイの存在を前提にしており、暗黙の依存関係の解決順に任せるとエラーになることがあるため、明示するのが公式の推奨です。26 27

private-data用のルートテーブルと、既定のセキュリティグループです。

resource "aws_route_table" "private_data" {
vpc_id = aws_vpc.main.id

tags = {
Name = "${local.name_prefix}-private-data-rt"
}
}

resource "aws_route_table_association" "private_data" {
for_each = local.private_data_subnet_keys

subnet_id = aws_subnet.this[each.value].id
route_table_id = aws_route_table.private_data.id
}

resource "aws_default_security_group" "main" {
vpc_id = aws_vpc.main.id
}

aws_default_security_groupは、他のリソースと性質が異なります。新しく何かを作るのではなく、VPC作成時に自動でできる既定のセキュリティグループをTerraformの管理下に引き取り、ingress・egressのルールを何も書かないことで、既存のルールをすべて削除します。28

最後に、後続の章が参照する値をoutputs.tfにまとめます。

output "vpc_id" {
description = "第4章で作成したVPCのID (第6/8/9章で参照)"
value = aws_vpc.main.id
}

output "public_subnet_ids" {
description = "publicサブネット (ALB用、第6章で参照) のIDリスト"
value = [for k, v in aws_subnet.this : v.id if local.subnets[k].tier == "public"]
}

output "private_app_subnet_ids" {
description = "private-appサブネット (ECS Fargate用、第8/10章で参照) のIDリスト"
value = [for k, v in aws_subnet.this : v.id if local.subnets[k].tier == "private-app"]
}

output "private_data_subnet_ids" {
description = "private-dataサブネット (RDS用、第9章で参照) のIDリスト"
value = [for k, v in aws_subnet.this : v.id if local.subnets[k].tier == "private-data"]
}

output "nat_gateway_public_ips" {
description = "NAT GatewayのパブリックIP (動作確認・将来の外部API許可リスト用)"
value = [for k, v in aws_nat_gateway.this : v.public_ip]
}

この連載は第11章までモジュール化を行わない単一構成のため、後続の章はoutputs.tfを経由せずaws_subnet.this["public-az1"].idのように直接参照できます。それでもoutputs.tfを用意するのは、terraform outputで値を確認できるようにするためと、この章が公開する値を1か所にまとめておくためです。

4.9. 動作確認

terraform fmt -check -recursive -diffterraform validateを実行し、構文とプロバイダスキーマに問題がないことを確認します。29 この2つのコマンドが保証するのは構文とスキーマの整合までで、実際にAWSへapplyした結果までは保証しません。

terraform planを実行すると、次の22件の追加が計画に表示されるはずです。

  • aws_vpc 1件
  • aws_subnet 6件
  • aws_internet_gateway 1件
  • aws_route_table (public) 1件 + aws_route_table_association 2件
  • aws_eip 2件
  • aws_nat_gateway 2件
  • aws_route_table (private-app) 2件 + aws_route_table_association 2件
  • aws_route_table (private-data) 1件 + aws_route_table_association 2件

aws_default_security_groupは新しくリソースを作るのではなく既存の既定セキュリティグループを引き取る特殊な動作のため、28 上の22件とは別枠で、変更として表示される可能性があります。

applyのあとは、terraform outputでVPC IDやNAT GatewayのパブリックIPを確認できます。NAT GatewayのパブリックIPは、外部のAPIをIPアドレスで許可リスト化するような場面で、後の章から参照することになります。

本章のまとめ

  • VPCが持つ実質的な設計判断は、CIDRの割り当てとサブネットの区切り方に現れます。Tasukuは10.0.0.0/16に2 AZ × 3層 (public / private-app / private-data) の6サブネットを切り出しました
  • NAT Gatewayはアベイラビリティゾーンごとに2個用意します。単一構成よりコストはかかりますが、第1章で決めた「1アベイラビリティゾーン障害でも継続する」構成に必要な選択です
  • ルートテーブルは4枚で、public・private-app・private-dataという3層の性質の違いが、そのままルートの中身の違いに表れています
  • セキュリティグループは、この章では既定のものを閉じるだけにとどめ、ALB・ECS・RDS用の実体は使う章でそれぞれ作ります
  • for_eachをこの連載ではじめて導入し、6つの同型なサブネットとNAT Gatewayをまとめて定義しました

次に読む

  • 第 5 章: S3: バケット設計原則とtfstate管理の定石
  • 第 3 章: IAM: ポリシー JSON の読み書きと、Terraform 実行権限の現実解
  • 連載の目次: 全 12 章の構成と読み進め方

Footnotes

  1. 出典: 第 1 章。ALB・ECS・RDSがいずれも作成時にサブネットの指定を要求すること、置き場となるサブネットを持つVPCが先に必要であることを説明しています。

  2. 出典: VPC CIDR blocks。VPCのCIDRブロックが/16netmaskから/28netmaskの範囲であることを示しています。 2

  3. 出典: Subnet CIDR blocks。サブネットCIDRの先頭4個と末尾1個、計5個のIPアドレスがAWSによって予約されることと、その内訳を示しています。 2 3

  4. 出典: When there are not enough IP addresses for launching instances or scaling。使用可能IPアドレス数を2^(32 - prefix length) - 5で計算する式と、/24が251個になる例を示しています。

  5. 出典: REL02-BP03 Ensure IP subnet allocation accounts for expansion and availability。将来の拡張のためVPC内に未使用のCIDR空間を残しておくことを推奨しています。

  6. 出典: Amazon ECS task networking options for Fargate。Fargateタスクには既定でENIが1つ割り当てられ、1つのタスクが同時に持てるENIは1つだけだとしています。

  7. 出典: Amazon ECS task definition differences for Fargate。privateサブネットのFargateタスクがコンテナイメージを取得するには、インターネットへの経路をルーティングできるNAT Gatewayが必要だとしています。 2

  8. 出典: Best practices for creating a VPC for Amazon RDS for Db2。RDSのようにインターネットからアクセスされるべきでないリソースにはprivate subnetを使うとしています。 2

  9. 出典: Availability Zone IDs for your AWS resources。AWSがアベイラビリティゾーンの物理的な場所をアカウントごとにランダムな名前へ割り当てていることを示しています。

  10. 出典: AWS Availability Zonesap-northeast-1のAZ IDがapne1-az1からapne1-az4の4つであることを示しています。

  11. 出典: Resource: aws_internet_gatewayvpc_idを指定するだけでVPCへのアタッチが完結するとしています。

  12. 出典: Pricing for NAT gateways。NAT Gatewayが稼働時間と処理データ量の両方に対して課金されるとしています。

  13. 出典: Using the NAT gateway for centralized IPv4 egress。高可用性のためにはアベイラビリティゾーンごとに1つのNAT Gatewayを使うべきだとしています。

  14. 出典: One to Many: Evolving VPC Design。NAT Gatewayの高可用性は単一のアベイラビリティゾーンに閉じており、アベイラビリティゾーンを跨ぐ高可用性には最低2つのNAT Gatewayが必要だとしています。

  15. 出典: Using NAT Gateways with multiple-Amazon VPCs at scale。NAT Gatewayはアベイラビリティゾーンごとに動作するため、アベイラビリティゾーンごとに1つずつ配置することを推奨しています。

  16. 出典: VPC and Subnet Considerations。アベイラビリティゾーンをまたぐ独立性のため、アベイラビリティゾーンごとにNAT Gatewayを作成することを推奨しています。

  17. 出典: 第 1 章。1つのアベイラビリティゾーンが利用不可になってもサービスを継続する構成にすることを非機能要件として明示しています。

  18. 出典: Using NAT Gateways with multiple-Amazon VPCs at scale。同一アベイラビリティゾーン内で処理されるトラフィックの総コストはNAT Gatewayの個数に依存せず、増えるのは稼働時間分の課金だけだと、具体例つきで説明しています。

  19. 出典: Introducing Amazon VPC Regional NAT Gateway。1つのNAT Gatewayがワークロードの存在するアベイラビリティゾーンへ自動的に展開される、リージョナルな可用性モードを紹介しています。

  20. 出典: 同上。「RNAT supports 5 Gbps of bandwidth for each AZ and automatically scales up to 100 Gbps」とあり、アベイラビリティゾーンごとに帯域を確保する動作をしていることを示しています。同ブログは料金の詳細を公式料金ページに委ねており、単一アベイラビリティゾーン相当の課金で済むとは述べていません。

  21. 出典: Security best practices for Amazon RDS for MySQL and MariaDB instances。RDSインスタンスをprivate subnetに配置し、同一VPC内のアプリケーションサーバーからのみアクセスを許可することを推奨しています。

  22. 出典: Security Hub CSPM controls for Amazon EC2。既定のセキュリティグループがinbound・outboundどちらのトラフィックも許可しないことを検証するコントロール[EC2.2]を定義しています。

  23. 出典: Security control recommendations for protecting infrastructure。既定のセキュリティグループは削除できないため、ルールを制限する設定に変更することを推奨しています。

  24. 出典: Resource: aws_vpcenable_dns_hostnamesの既定値がfalseであるとしています。

  25. 出典: Resource: aws_subnetmap_public_ip_on_launchの既定値がfalseであるとしています。

  26. 出典: Resource: aws_nat_gateway。インターネットゲートウェイへの明示的な依存関係をdepends_onで追加することを推奨する例を示しています。

  27. 出典: Resource: aws_eip。EIPの関連付けにインターネットゲートウェイの存在が前提になる場合があり、depends_onで明示的な依存関係を設定するよう注記しています。

  28. 出典: Resource: aws_default_security_group。この resource は新規作成ではなく既存の既定セキュリティグループを管理下に「adopt」し、設定を書かなければ既存のingress・egressルールをすべて削除するとしています。 2

  29. 出典: 連載の目次。本連載のTerraformコードはterraform fmtterraform validateを通しており、validateが保証するのは構文とproviderスキーマとの整合までで、実際にAWSへapplyした結果は保証しないと明示しています。