はじめに
前回のPhase2ではTerraformの基礎とリモートステートを学んだ。
Phase3ではいよいよ**ネットワーク(VPC)とサーバー(EC2)**をTerraformでコード化した記録。
目次
1. 作った構成
VPC (10.0.0.0/16)
└── パブリックサブネット (10.0.1.0/24)
└── EC2 (t3.micro)
└── セキュリティグループ(SSH許可)
└── インターネットゲートウェイ
└── ルートテーブル → サブネットに紐付け
登場するリソースは7つ:
| リソース | 役割 |
|---|---|
| aws_vpc | ネットワーク全体 |
| aws_subnet | ネットワークの区画 |
| aws_internet_gateway | インターネットへの出入り口 |
| aws_route_table | 通信の経路設定 |
| aws_route_table_association | ルートテーブルとサブネットの紐付け |
| aws_security_group | ファイアウォール |
| aws_instance | EC2サーバー本体 |
2. ファイル構成
phase3/
├── provider.tf # AWSプロバイダー + S3バックエンド
├── variables.tf # 変数定義
├── main.tf # リソース定義
└── outputs.tf # 出力定義
variables.tf
variable "project_name" {
type = string
default = "tf-practice"
}
variable "az" {
type = string
default = "ap-northeast-1a"
}
変数化することで、project_name を変えるだけで全リソース名が一括変更できる。
outputs.tf
output "ec2_public_ip" {
value = aws_instance.web.public_ip
}
output "vpc_id" {
value = aws_vpc.main.id
}
apply 後に自動表示される。public_ip や .id はAWSが決めた属性名で、Terraformのドキュメントで確認できる。
3. 各リソースの解説
VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "${var.project_name}-vpc"
}
}
10.0.0.0/16 はこのVPC内で使えるIPアドレスの範囲。約65,000個のIPが使える。
パブリックサブネット
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = var.az
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-subnet"
}
}
map_public_ip_on_launch = true でこのサブネットにEC2を置くと自動でパブリックIPが割り当てられる。
インターネットゲートウェイ
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
}
}
VPCとインターネットをつなぐ出入り口。これがないと外と通信できない。
ルートテーブル + サブネット紐付け
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 = "${var.project_name}-public-rt"
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
IGWを作っただけでは通信できない。ルートテーブルで「全ての通信をIGWに流す」という道案内を設定し、サブネットに紐付ける必要がある。
2つに分かれている理由は、ルートテーブル自体の定義と、どのサブネットに適用するかの設定を分離するため。
セキュリティグループ
resource "aws_security_group" "web" {
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-sg"
}
}
ingress = 入ってくる通信の制御
└── ポート22(SSH)を全IPから許可
egress = 出ていく通信の制御
└── protocol = "-1" は全プロトコルを意味する
└── 全ての通信を許可
今回は学習用なので 0.0.0.0/0(全IP許可)にしているが、本番環境では自分のIPだけに絞るべき。
EC2
resource "aws_instance" "web" {
ami = "ami-0599b6e53ca798bb2"
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
tags = {
Name = "${var.project_name}-ec2"
}
}
ami → EC2のOSイメージ(Amazon Linux 2023)
instance_type → サーバーのスペック(t3.microは無料枠対象)
4. デプロイ結果
terraform plan
# Plan: 7 to add, 0 to change, 0 to destroy.
terraform apply
# Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
#
# Outputs:
# ec2_public_ip = "13.113.1.68"
# vpc_id = "vpc-0ec33bc5ba81dbd35"
AWSコンソールでEC2が 実行中 になっていることを確認。
outputsで出たIPアドレスとコンソールの表示が一致していた。
5. 学んだこと
リソース間の参照
# VPCのIDをサブネットで参照
vpc_id = aws_vpc.main.id
# サブネットのIDをEC2で参照
subnet_id = aws_subnet.public.id
Terraformはリソース同士を参照できる。これによりTerraformが依存関係を自動で判断して正しい順番でデプロイしてくれる。
IGWだけでは通信できない
IGW作成だけでは不十分
└── ルートテーブルで「IGWに流す」設定
└── サブネットに紐付け
└── これで初めてインターネットと通信できる
パブリック vs プライベートサブネット
パブリック → インターネットから直接アクセス可能(Webサーバー向け)
プライベート → インターネットから直接アクセス不可(DBサーバー向け)
DBをパブリックに置くと漏洩リスクがある。プライベートサブネット + NAT Gatewayが本番構成の基本。
コードはGitHubで公開しています:
https://github.com/Allure2140/aws-iac-learning
次のPhase4ではAWS CDKに挑戦します。