# TerraformによるAWSインフラ構築

はじめに

AWSコンソールで手動でクラウドインフラを管理するのは、ミスが起こりやすくスケールしません。HashiCorpのTerraformを使えば、インフラストラクチャをコードとして定義(IaC)でき、バージョン管理、再現性、自動化が実現します。

このチュートリアルでは、以下の本番対応AWS環境を構築します:

  • パブリックとプライベートサブネットを持つカスタムVPC
  • セキュリティグループ付きのEC2ウェブサーバー
  • プライベートサブネット内のRDS MySQLデータベース
  • リソースへの簡単アクセスのための出力値
  • 前提条件

  • IAM認証情報を持つAWSアカウント
  • Terraform >= 1.6 がインストール済み
  • AWSネットワーキングの基本的な理解
  • ステップ1: Terraformのインストールと設定

    # Terraformをインストール (macOS)
    

    brew tap hashicorp/tap

    brew install hashicorp/tap/terraform

    # インストールを確認

    terraform version

    # AWS認証情報を設定

    export AWS_ACCESS_KEY_ID="your-access-key"

    export AWS_SECRET_ACCESS_KEY="your-secret-key"

    export AWS_DEFAULT_REGION="ap-northeast-1"

    ステップ2: プロジェクト構造

    以下のディレクトリ構造を作成します:

    terraform-aws-demo/
    

    ├── main.tf # ルートモジュール

    ├── variables.tf # 入力変数

    ├── outputs.tf # 出力値

    ├── terraform.tfvars # 変数の値

    ├── modules/

    │ ├── vpc/

    │ ├── ec2/

    │ └── rds/

    ステップ3: VPCモジュールの定義

    # modules/vpc/main.tf
    

    resource "aws_vpc" "main" {

    cidr_block = var.vpc_cidr

    enable_dns_hostnames = true

    enable_dns_support = true

    tags = {

    Name = "${var.project_name}-vpc"

    Environment = var.environment

    }

    }

    resource "aws_subnet" "public" {

    count = length(var.public_subnet_cidrs)

    vpc_id = aws_vpc.main.id

    cidr_block = var.public_subnet_cidrs[count.index]

    availability_zone = var.availability_zones[count.index]

    map_public_ip_on_launch = true

    tags = {

    Name = "${var.project_name}-public-${count.index + 1}"

    }

    }

    resource "aws_subnet" "private" {

    count = length(var.private_subnet_cidrs)

    vpc_id = aws_vpc.main.id

    cidr_block = var.private_subnet_cidrs[count.index]

    availability_zone = var.availability_zones[count.index]

    tags = {

    Name = "${var.project_name}-private-${count.index + 1}"

    }

    }

    resource "aws_internet_gateway" "main" {

    vpc_id = aws_vpc.main.id

    }

    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

    }

    }

    ステップ4: EC2モジュールの定義

    # modules/ec2/main.tf
    

    resource "aws_security_group" "web" {

    name_prefix = "${var.project_name}-web-"

    vpc_id = var.vpc_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 = [var.ssh_cidr]

    }

    egress {

    from_port = 0

    to_port = 0

    protocol = "-1"

    cidr_blocks = ["0.0.0.0/0"]

    }

    }

    resource "aws_instance" "web" {

    ami = data.aws_ami.amazon_linux.id

    instance_type = var.instance_type

    subnet_id = var.subnet_id

    vpc_security_group_ids = [aws_security_group.web.id]

    user_data = <<-EOF

    #!/bin/bash

    yum update -y

    yum install -y httpd php php-mysqlnd

    systemctl start httpd

    systemctl enable httpd

    EOF

    root_block_device {

    volume_size = 20

    volume_type = "gp3"

    encrypted = true

    }

    tags = {

    Name = "${var.project_name}-web"

    }

    }

    ステップ5: RDSモジュールの定義

    # modules/rds/main.tf
    

    resource "aws_db_subnet_group" "main" {

    name = "${var.project_name}-db-subnet"

    subnet_ids = var.private_subnet_ids

    }

    resource "aws_security_group" "db" {

    name_prefix = "${var.project_name}-db-"

    vpc_id = var.vpc_id

    ingress {

    from_port = 3306

    to_port = 3306

    protocol = "tcp"

    security_groups = [var.web_sg_id]

    }

    }

    resource "aws_db_instance" "main" {

    identifier = "${var.project_name}-db"

    engine = "mysql"

    engine_version = "8.0"

    instance_class = var.db_instance_class

    allocated_storage = 20

    storage_encrypted = true

    db_name = var.db_name

    username = var.db_username

    password = var.db_password

    db_subnet_group_name = aws_db_subnet_group.main.name

    vpc_security_group_ids = [aws_security_group.db.id]

    skip_final_snapshot = true

    }

    ステップ6: デプロイ

    # 初期化
    

    terraform init

    # プレビュー

    terraform plan

    # 適用

    terraform apply -auto-approve

    # 出力を確認

    terraform output

    # 削除(課金を避けるため!)

    terraform destroy -auto-approve

    ベストプラクティス

  • リモートステート: S3 + DynamoDB でステートを共有管理
  • 環境分離: tfvars ファイルで環境ごとの設定を管理
  • コードフォーマット: terraform fmtterraform validate を常に実行
  • シークレット管理: パスワードは terraform.tfvars に入れず、環境変数やAWS Secrets Managerを使用
  • まとめ

    本チュートリアルで学んだこと:

  • 再利用可能なモジュールによるTerraformプロジェクトの構造化
  • VPC、EC2、RDSの完全な環境構築
  • IaCのベストプラクティス

Terraformにより、インフラの再現性と監査可能性が確保されます。CI/CDパイプラインと組み合わせて完全自動化を実現しましょう。