PR

第2章: Terraformを用いたAWSリソースの構築手順

第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_launchtrueに設定し、このサブネットで起動するインスタンスにパブリック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バケットを作成し、以下のセキュリティ設定を適用しています:

  1. パブリックアクセスブロック – パブリックアクセスを完全にブロックする
  2. サーバーサイド暗号化 – AES256アルゴリズムを使用
  3. バージョニング – オブジェクトの変更履歴を保持
  4. ライフサイクルルール – 古いログファイルを自動的に異なるストレージクラスに移行し、最終的に削除
  5. バケットポリシー – 特定の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を構築しています:

  1. コンテンツを保存するためのS3バケットを作成
  2. パブリックアクセスをブロックし、CloudFrontのみがアクセスできるようにバケットポリシーを設定
  3. CloudFront Origin Access Identity(OAI)を作成して、CloudFrontとS3バケットの間の安全なアクセスを確保
  4. CloudFront配信を設定し、キャッシュ動作、地理的制限、TLS証明書などを定義

最後に、CloudFrontのドメイン名を出力値として表示し、デプロイ後にアクセス可能にしています。

以上が、TerraformでAWSリソースを構築するための基本的な手順です。これらのサンプルコードは、実際のプロジェクトの出発点として使用できます。実際の要件に合わせて調整し、より堅牢で安全なインフラストラクチャを構築することをお勧めします。

コメント

タイトルとURLをコピーしました