Terraform を使用したコードとしてのインフラストラクチャ: 手動から自動化されたクラウド管理へ

Terraform を使用してクラウド インフラストラクチャを管理します。 HCL 構文、モジュール、状態管理、CI/CD 統合、およびマルチ環境展開戦略について説明します。

E
ECOSIRE Research and Development Team
|2026年3月16日5 分で読める949 語数|

Terraform を使用したコードとしてのインフラストラクチャ: 手動から自動化されたクラウド管理へ

Infrafraction as Code (IaC) を使用している組織は、インフラストラクチャを手動で管理している組織に比べて環境のプロビジョニングが 90% 速くなり、構成関連の停止が 60% 少なくなります。 Terraform は主要な IaC ツールとして台頭しており、3,000 を超えるプロバイダーがあらゆる主要なクラウド プラットフォームと SaaS サービスをサポートしています。

このガイドでは、最初のリソース定義から運用グレードのマルチ環境展開まで、Web アプリケーション、ERP システム、および e コマース プラットフォームでの Terraform の実践的な使用法について説明します。

重要なポイント

  • Terraform により、バージョン管理を通じてインフラストラクチャの変更がレビュー可能、テスト可能、元に戻せるようになります
  • リモート状態管理により、複数のエンジニアがインフラストラクチャを変更する場合の競合を防止します
  • モジュールは再利用可能なパターンをカプセル化し、構成を数百行から数パラメータに削減します
  • Terraform Cloud または CI/CD の統合により、安全な変更を実現するための適用前計画の規律が強化されます

SMB に Terraform を使用する理由

手動インフラストラクチャの問題

IaC がなければ、インフラストラクチャの知識は次の場所に存在します。

  • 誰も文書化していない AWS コンソールのクリックパス
  • SSH コマンドは数か月前に実行されましたが、誰も覚えていません
  • サーバー上で直接編集された設定ファイル
  • あるエンジニアの「ネットワークがどのように機能するか」に関するメンタル モデル

Terraform を使用すると、インフラストラクチャは Git 内に存在します。すべての変更はプルリクエストです。すべての展開は再現可能です。すべてのエンジニアは全体像を理解できます。

コアコンセプト

コンセプト説明
プロバイダークラウドプラットフォーム(AWS、GCP、Azure、Cloudflare)と連携するプラグイン
リソース単一のインフラストラクチャ コンポーネント (EC2 インスタンス、RDS データベース、S3 バケット)
データソース既存のインフラストラクチャへの読み取り専用の参照
変数再利用可能な構成の入力パラメータ
出力Terraform 構成からエクスポートされた値
状態Terraform が管理するものとその現在の属性の記録
モジュール定義されたインターフェースを持つ再利用可能なリソースのグループ

最初の Terraform 構成

Web アプリケーション用の AWS VPC と EC2

# providers.tf
terraform {
  required_version = ">= 1.7"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    bucket = "ecosire-terraform-state"
    key    = "production/terraform.tfstate"
    region = "us-east-1"
    encrypt = true
    dynamodb_table = "terraform-locks"
  }
}

provider "aws" {
  region = var.aws_region
}

# variables.tf
variable "aws_region" {
  type    = string
  default = "us-east-1"
}

variable "environment" {
  type    = string
  default = "production"
}

variable "instance_type" {
  type    = string
  default = "t3.large"
}

# main.tf
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_subnet" "public" {
  count             = 2
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 1}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]

  map_public_ip_on_launch = true

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

resource "aws_instance" "app" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.public[0].id

  vpc_security_group_ids = [aws_security_group.app.id]
  key_name               = aws_key_pair.deploy.key_name

  root_block_device {
    volume_size = 50
    volume_type = "gp3"
    encrypted   = true
  }

  tags = {
    Name        = "${var.environment}-app"
    Environment = var.environment
  }
}

resource "aws_db_instance" "postgres" {
  identifier     = "${var.environment}-db"
  engine         = "postgres"
  engine_version = "17"
  instance_class = "db.t3.medium"

  allocated_storage     = 50
  max_allocated_storage = 200
  storage_encrypted     = true

  db_name  = "ecosire"
  username = "app"
  password = var.db_password

  vpc_security_group_ids = [aws_security_group.db.id]
  db_subnet_group_name   = aws_db_subnet_group.main.name

  backup_retention_period = 7
  backup_window           = "03:00-04:00"
  maintenance_window      = "sun:04:00-sun:05:00"

  skip_final_snapshot = false
  final_snapshot_identifier = "${var.environment}-db-final"

  tags = {
    Environment = var.environment
  }
}

再利用可能なインフラストラクチャ用のモジュール

Web アプリケーション モジュールの作成

# modules/web-app/main.tf
variable "name" {
  type = string
}

variable "environment" {
  type = string
}

variable "instance_type" {
  type    = string
  default = "t3.medium"
}

variable "vpc_id" {
  type = string
}

variable "subnet_ids" {
  type = list(string)
}

resource "aws_lb" "app" {
  name               = "${var.name}-${var.environment}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = var.subnet_ids
}

resource "aws_lb_target_group" "app" {
  name     = "${var.name}-${var.environment}-tg"
  port     = 3000
  protocol = "HTTP"
  vpc_id   = var.vpc_id

  health_check {
    path                = "/health"
    healthy_threshold   = 2
    unhealthy_threshold = 3
    interval            = 30
  }
}

resource "aws_autoscaling_group" "app" {
  name                = "${var.name}-${var.environment}-asg"
  min_size            = 2
  max_size            = 10
  desired_capacity    = 2
  vpc_zone_identifier = var.subnet_ids
  target_group_arns   = [aws_lb_target_group.app.arn]

  launch_template {
    id      = aws_launch_template.app.id
    version = "$Latest"
  }

  tag {
    key                 = "Name"
    value               = "${var.name}-${var.environment}"
    propagate_at_launch = true
  }
}

output "alb_dns_name" {
  value = aws_lb.app.dns_name
}

モジュールの使用

# environments/production/main.tf
module "web" {
  source = "../../modules/web-app"

  name          = "ecosire-web"
  environment   = "production"
  instance_type = "t3.large"
  vpc_id        = module.network.vpc_id
  subnet_ids    = module.network.public_subnet_ids
}

module "api" {
  source = "../../modules/web-app"

  name          = "ecosire-api"
  environment   = "production"
  instance_type = "t3.large"
  vpc_id        = module.network.vpc_id
  subnet_ids    = module.network.public_subnet_ids
}

状態管理

S3 によるリモート状態

# Bootstrap: create the state bucket and DynamoDB table manually or with a separate config
resource "aws_s3_bucket" "terraform_state" {
  bucket = "ecosire-terraform-state"

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

DynamoDB を介した状態ロックにより、2 人のエンジニアが terraform apply を同時に実行することがなくなり、状態が破損する可能性があります。

状態ファイルのセキュリティ

Terraform 状態ファイルには、データベース パスワード、API キー、リソース ID などの機密情報が含まれています。保護してください:

  • 保存時の暗号化: S3 バケットのバージョン管理 + サーバー側の暗号化
  • 転送中の暗号化: 状態アクセスの場合のみ HTTPS
  • アクセスを制限: 状態を読み取り/書き込みできるユーザーを制限する IAM ポリシー
  • Git には決してコミットしないでください: 状態ファイルをバージョン管理に含めてはなりません
  • バージョニングを有効にする: S3 バージョニングにより、破損した状態から回復できます

CI/CD の統合

GitHub アクション Terraform パイプライン

name: Terraform
on:
  pull_request:
    paths: ['infrastructure/**']
  push:
    branches: [main]
    paths: ['infrastructure/**']

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        run: terraform init
        working-directory: infrastructure/environments/production

      - name: Terraform Plan
        run: terraform plan -out=tfplan
        working-directory: infrastructure/environments/production

      - name: Comment PR with plan
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const plan = require('fs').readFileSync('infrastructure/environments/production/tfplan.txt', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Terraform Plan\n\`\`\`\n${plan}\n\`\`\``
            });

  apply:
    needs: plan
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3

      - name: Terraform Apply
        run: terraform apply -auto-approve
        working-directory: infrastructure/environments/production

マルチ環境戦略

環境目的インスタンスのサイズコスト目標
開発機能テストt3.micro / t3.small<$100/月
ステージング実稼働前検証ミラーの生産(小型)生産量の ~30%
制作ライブ交通状況荷物に適したサイズ最適化された

Terraform ワークスペースまたは環境ごとに別のディレクトリを使用します。

infrastructure/
  modules/
    web-app/
    database/
    network/
  environments/
    development/
      main.tf
      terraform.tfvars
    staging/
      main.tf
      terraform.tfvars
    production/
      main.tf
      terraform.tfvars

よくある質問

Terraform と Pulumi --- どちらを選択すべきですか?

宣言型構成を好む運用エンジニアがチームに含まれている場合は、Terraform を使用します。チームが開発者中心で、TypeScript または Python でインフラストラクチャを記述することを好む場合は、Pulumi。 Terraform には、より大きなエコシステムとより多くのコミュニティ モジュールがあります。 Pulumi は初期学習曲線が急になりますが、複雑なロジックに対してより柔軟です。

既存のインフラストラクチャを Terraform にインポートするにはどうすればよいですか?

terraform import を使用して、既存のリソースを Terraform 管理下に置きます。例: terraform import aws_instance.app i-1234567890abcdef0。インポート後、一致する構成を書き込みます。 Terraform 1.5 以降では、一括インポート用の構成ファイル内のインポート ブロックがサポートされています。

Terraform でシークレットをどのように処理すればよいですか?

シークレットを Terraform ファイルに決してコミットしないでください。 terraform.tfvars (Git から除外)、環境変数 (TF_VAR_db_password)、またはシークレット マネージャー (AWS Secrets Manager、HashiCorp Vault) を使用します。機密変数を sensitive = true でマークして、プラン出力に表示されないようにします。

Terraform の管理コストはいくらですか?

Terraform 自体は無料でオープンソースです。 Terraform Cloud には、リモート状態および計画/適用を備えた最大 5 ユーザーの無料利用枠があります。主なコストは、学習曲線 (経験豊富なエンジニアの場合 20 ~ 40 時間) と継続的なメンテナンス (月あたり 2 ~ 4 時間) です。これは、手動のインフラストラクチャ管理で節約された時間によって相殺されます。


次に何が起こるか

Terraform は、自動化されたインフラストラクチャの基盤を提供します。これを、自動展開のために CI/CD パイプライン、運用の可視化のために 監視、復元力のために 災害復旧 と組み合わせます。

インフラストラクチャ自動化コンサルティングについては ECOSIRE にお問い合わせ、完全なロードマップについては 中小企業向け DevOps ガイド をご覧ください。


ECOSIRE によって発行 -- 企業のクラウド インフラストラクチャの自動化を支援します。

E

執筆者

ECOSIRE Research and Development Team

ECOSIREでエンタープライズグレードのデジタル製品を開発。Odoo統合、eコマース自動化、AI搭載ビジネスソリューションに関するインサイトを共有しています。

WhatsAppでチャット