第2章: Terraformを用いたAWSリソースの構築手順
4. VPCとネットワーク環境の構築
VPCの作成
AWSでアプリケーションを展開する最初のステップは、適切に設計されたVPC(Virtual Private Cloud)を構築することです。VPCはAWS内の仮想ネットワークであり、システムのセキュリティと分離の基盤となります。
# VPCの作成
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "main-vpc"
Environment = "production"
}
}
この設定では、CIDRブロック10.0.0.0/16
を使用してVPCを作成し、DNSサポートとDNSホスト名機能を有効にしています。タグを使用して、このVPCに「main-vpc」という名前と「production」という環境タグを付けています。
サブネットの配置設計
VPCを作成した後、異なるアベイラビリティゾーン(AZ)にサブネットを配置することで、高可用性を確保します。一般的に、パブリックサブネット(インターネットからアクセス可能)とプライベートサブネット(内部リソース用)の両方を作成します。
# パブリックサブネットの作成
resource "aws_subnet" "public_1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-1"
Type = "Public"
}
}
resource "aws_subnet" "public_2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-1c"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-2"
Type = "Public"
}
}
# プライベートサブネットの作成
resource "aws_subnet" "private_1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.3.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "private-subnet-1"
Type = "Private"
}
}
resource "aws_subnet" "private_2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.4.0/24"
availability_zone = "ap-northeast-1c"
tags = {
Name = "private-subnet-2"
Type = "Private"
}
}
ここでは、2つのアベイラビリティゾーン(ap-northeast-1aとap-northeast-1c)にそれぞれパブリックサブネットとプライベートサブネットを作成しています。パブリックサブネットではmap_public_ip_on_launch
をtrue
に設定し、このサブネットで起動するインスタンスにパブリックIPを自動的に割り当てます。
ルートテーブルの設定
サブネットを作成した後、適切なルーティングを設定する必要があります。インターネットゲートウェイを作成し、パブリックサブネットからインターネットへのルーティングを設定します。
# インターネットゲートウェイの作成
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main-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.igw.id
}
tags = {
Name = "public-route-table"
}
}
# パブリックサブネットとルートテーブルの関連付け
resource "aws_route_table_association" "public_1" {
subnet_id = aws_subnet.public_1.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "public_2" {
subnet_id = aws_subnet.public_2.id
route_table_id = aws_route_table.public.id
}
# プライベートルートテーブルの作成
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
tags = {
Name = "private-route-table"
}
}
# プライベートサブネットとルートテーブルの関連付け
resource "aws_route_table_association" "private_1" {
subnet_id = aws_subnet.private_1.id
route_table_id = aws_route_table.private.id
}
resource "aws_route_table_association" "private_2" {
subnet_id = aws_subnet.private_2.id
route_table_id = aws_route_table.private.id
}
上記の設定では、インターネットゲートウェイを作成し、パブリックルートテーブルに「0.0.0.0/0」(全てのトラフィック)をインターネットゲートウェイに向けるルートを追加しています。また、パブリックサブネットとプライベートサブネットを適切なルートテーブルに関連付けています。
必要に応じて、NATゲートウェイを設定してプライベートサブネットからインターネットへの一方向の通信を可能にすることもできます。
# Elastic IPの割り当て
resource "aws_eip" "nat" {
domain = "vpc"
tags = {
Name = "nat-eip"
}
}
# NATゲートウェイの作成
resource "aws_nat_gateway" "nat" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public_1.id
tags = {
Name = "main-nat-gateway"
}
}
# プライベートルートテーブルにNATゲートウェイへのルートを追加
resource "aws_route" "private_nat" {
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id
}
5. EC2の構築
TerraformでEC2インスタンスを構築
EC2インスタンスは、AWSで実行されるコンピューティングリソースの基本単位です。Terraformを使用してEC2インスタンスを作成する方法を見てみましょう。
# セキュリティグループの作成
resource "aws_security_group" "web" {
name = "web-sg"
description = "Allow web traffic"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["YOUR_IP_ADDRESS/32"] # 特定のIPアドレスからのみSSHアクセスを許可
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-security-group"
}
}
# EC2インスタンスの作成
resource "aws_instance" "web" {
ami = "ami-0c3fd0f5d33134a76" # Amazon Linux 2 AMI (東京リージョン)
instance_type = "t3.micro"
subnet_id = aws_subnet.public_1.id
vpc_security_group_ids = [aws_security_group.web.id]
key_name = "your-key-pair" # 事前に作成したキーペア名
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from Terraform</h1>" > /var/www/html/index.html
EOF
tags = {
Name = "web-server"
Environment = "production"
}
}
上記の例では、HTTPとHTTPSトラフィックを許可し、特定のIPアドレスからのSSHアクセスを許可するセキュリティグループを作成しています。次に、そのセキュリティグループを使用してEC2インスタンスを作成し、ユーザーデータスクリプトを使用して簡単なWebサーバーをセットアップしています。
IAMロールの済み分け
EC2インスタンスには、他のAWSサービスにアクセスするためのIAMロールを割り当てることができます。Terraformを使用してIAMロールを作成し、EC2インスタンスに割り当てる方法は次のとおりです。
# IAMロールの作成
resource "aws_iam_role" "ec2_s3_access" {
name = "ec2-s3-access-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
},
]
})
tags = {
Name = "ec2-s3-access-role"
}
}
# S3アクセスポリシーの作成
resource "aws_iam_policy" "s3_access" {
name = "s3-access-policy"
description = "Policy to allow EC2 to access specific S3 bucket"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
]
Effect = "Allow"
Resource = [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
})
}
# ポリシーをロールにアタッチ
resource "aws_iam_role_policy_attachment" "s3_access_attachment" {
role = aws_iam_role.ec2_s3_access.name
policy_arn = aws_iam_policy.s3_access.arn
}
# インスタンスプロファイルの作成
resource "aws_iam_instance_profile" "ec2_s3_profile" {
name = "ec2-s3-profile"
role = aws_iam_role.ec2_s3_access.name
}
# EC2インスタンスにIAMロールを割り当て
resource "aws_instance" "web_with_role" {
ami = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
subnet_id = aws_subnet.public_1.id
vpc_security_group_ids = [aws_security_group.web.id]
iam_instance_profile = aws_iam_instance_profile.ec2_s3_profile.name
tags = {
Name = "web-server-with-s3-access"
}
}
この例では、EC2インスタンスがS3バケットにアクセスするためのIAMロールを作成し、特定のS3バケットに対する読み取りと書き込みのアクセス権を付与しています。次に、このロールをインスタンスプロファイルを通じてEC2インスタンスに割り当てています。
Terraformを用いたAuto Scalingの構築
Auto Scalingを使用すると、需要に応じてEC2インスタンスの数を自動的に調整できます。Terraformを使用してAuto Scalingグループを設定する方法を見てみましょう。
# 起動テンプレートの作成
resource "aws_launch_template" "web" {
name_prefix = "web-"
image_id = "ami-0c3fd0f5d33134a76"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
iam_instance_profile {
name = aws_iam_instance_profile.ec2_s3_profile.name
}
user_data = base64encode(<<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from Auto Scaling</h1>" > /var/www/html/index.html
EOF
)
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
}
tag_specifications {
resource_type = "instance"
tags = {
Name = "web-server-asg"
}
}
}
# Auto Scalingグループの作成
resource "aws_autoscaling_group" "web" {
name_prefix = "web-asg-"
min_size = 2
max_size = 5
desired_capacity = 2
health_check_grace_period = 300
health_check_type = "ELB"
vpc_zone_identifier = [aws_subnet.public_1.id, aws_subnet.public_2.id]
launch_template {
id = aws_launch_template.web.id
version = "$Latest"
}
tag {
key = "Name"
value = "web-server-asg"
propagate_at_launch = true
}
}
# スケーリングポリシーの作成
resource "aws_autoscaling_policy" "scale_out" {
name = "scale-out"
scaling_adjustment = 1
adjustment_type = "ChangeInCapacity"
cooldown = 300
autoscaling_group_name = aws_autoscaling_group.web.name
}
resource "aws_autoscaling_policy" "scale_in" {
name = "scale-in"
scaling_adjustment = -1
adjustment_type = "ChangeInCapacity"
cooldown = 300
autoscaling_group_name = aws_autoscaling_group.web.name
}
# CloudWatchアラームの作成
resource "aws_cloudwatch_metric_alarm" "high_cpu" {
alarm_name = "high-cpu-utilization"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 120
statistic = "Average"
threshold = 80
dimensions = {
AutoScalingGroupName = aws_autoscaling_group.web.name
}
alarm_description = "This metric monitors EC2 CPU utilization"
alarm_actions = [aws_autoscaling_policy.scale_out.arn]
}
resource "aws_cloudwatch_metric_alarm" "low_cpu" {
alarm_name = "low-cpu-utilization"
comparison_operator = "LessThanOrEqualToThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 120
statistic = "Average"
threshold = 20
dimensions = {
AutoScalingGroupName = aws_autoscaling_group.web.name
}
alarm_description = "This metric monitors EC2 CPU utilization"
alarm_actions = [aws_autoscaling_policy.scale_in.arn]
}
この例では、起動テンプレートを作成し、それを使用してAuto Scalingグループを設定しています。また、CPUの使用率に基づいてスケールアウトおよびスケールインするためのCloudWatchアラームとスケーリングポリシーも定義しています。
6. S3バケットの管理
S3の作成とセキュリティ設定
S3(Simple Storage Service)は、AWSのスケーラブルなオブジェクトストレージサービスです。Terraformを使用してS3バケットを作成し、セキュリティ設定を行う方法を見てみましょう。
# S3バケットの作成
resource "aws_s3_bucket" "app_bucket" {
bucket = "my-application-bucket-unique-name"
tags = {
Name = "Application Bucket"
Environment = "Production"
}
}
# バケットのアクセスブロック設定
resource "aws_s3_bucket_public_access_block" "app_bucket_block" {
bucket = aws_s3_bucket.app_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# バケットの暗号化設定
resource "aws_s3_bucket_server_side_encryption_configuration" "app_bucket_encryption" {
bucket = aws_s3_bucket.app_bucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
# バケットのバージョニング設定
resource "aws_s3_bucket_versioning" "app_bucket_versioning" {
bucket = aws_s3_bucket.app_bucket.id
versioning_configuration {
status = "Enabled"
}
}
# バケットライフサイクルルールの設定
resource "aws_s3_bucket_lifecycle_configuration" "app_bucket_lifecycle" {
bucket = aws_s3_bucket.app_bucket.id
rule {
id = "log-files"
status = "Enabled"
filter {
prefix = "logs/"
}
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 90
storage_class = "GLACIER"
}
expiration {
days = 365
}
}
}
# バケットポリシーの設定
resource "aws_s3_bucket_policy" "app_bucket_policy" {
bucket = aws_s3_bucket.app_bucket.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetObject"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.app_bucket.arn}/public/*"
Principal = {
AWS = aws_iam_role.ec2_s3_access.arn
}
}
]
})
}
この例では、S3バケットを作成し、以下のセキュリティ設定を適用しています:
- パブリックアクセスブロック – パブリックアクセスを完全にブロックする
- サーバーサイド暗号化 – AES256アルゴリズムを使用
- バージョニング – オブジェクトの変更履歴を保持
- ライフサイクルルール – 古いログファイルを自動的に異なるストレージクラスに移行し、最終的に削除
- バケットポリシー – 特定のIAMロールからのみ特定のフォルダへのアクセスを許可
Terraformでの簡単なCDN構築
CloudFront(AWSのCDNサービス)を使用してS3バケットのコンテンツを配信する方法を見てみましょう。
# CloudFrontディストリビューションのためのS3バケット
resource "aws_s3_bucket" "cdn_bucket" {
bucket = "my-cdn-bucket-unique-name"
tags = {
Name = "CDN Content Bucket"
Environment = "Production"
}
}
# CDN用バケットのアクセスブロック設定
resource "aws_s3_bucket_public_access_block" "cdn_bucket_block" {
bucket = aws_s3_bucket.cdn_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# CloudFrontアクセスアイデンティティの作成
resource "aws_cloudfront_origin_access_identity" "cdn_oai" {
comment = "OAI for CDN bucket access"
}
# S3バケットポリシーの設定(CloudFrontからのアクセスを許可)
resource "aws_s3_bucket_policy" "cdn_bucket_policy" {
bucket = aws_s3_bucket.cdn_bucket.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "s3:GetObject"
Effect = "Allow"
Resource = "${aws_s3_bucket.cdn_bucket.arn}/*"
Principal = {
AWS = "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${aws_cloudfront_origin_access_identity.cdn_oai.id}"
}
}
]
})
}
# CloudFrontディストリビューションの作成
resource "aws_cloudfront_distribution" "s3_distribution" {
origin {
domain_name = aws_s3_bucket.cdn_bucket.bucket_regional_domain_name
origin_id = "S3-${aws_s3_bucket.cdn_bucket.id}"
s3_origin_config {
origin_access_identity = "origin-access-identity/cloudfront/${aws_cloudfront_origin_access_identity.cdn_oai.id}"
}
}
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-${aws_s3_bucket.cdn_bucket.id}"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
price_class = "PriceClass_200"
viewer_certificate {
cloudfront_default_certificate = true
}
tags = {
Name = "CDN Distribution"
Environment = "Production"
}
}
# CloudFrontディストリビューションの出力
output "cloudfront_domain_name" {
value = aws_cloudfront_distribution.s3_distribution.domain_name
}
この例では、以下の手順でS3をベースにしたCDNを構築しています:
- コンテンツを保存するためのS3バケットを作成
- パブリックアクセスをブロックし、CloudFrontのみがアクセスできるようにバケットポリシーを設定
- CloudFront Origin Access Identity(OAI)を作成して、CloudFrontとS3バケットの間の安全なアクセスを確保
- CloudFront配信を設定し、キャッシュ動作、地理的制限、TLS証明書などを定義
最後に、CloudFrontのドメイン名を出力値として表示し、デプロイ後にアクセス可能にしています。
以上が、TerraformでAWSリソースを構築するための基本的な手順です。これらのサンプルコードは、実際のプロジェクトの出発点として使用できます。実際の要件に合わせて調整し、より堅牢で安全なインフラストラクチャを構築することをお勧めします。
コメント