AI

OSSのIaC比較:OpenTofu vs Terraform vs Pulumi でインフラをコードで管理する

オープンソースラボ編集部2026年6月14日

OSSのIaC比較:OpenTofu vs Terraform vs Pulumi でインフラをコードで管理する

HashiCorp Terraform(BSL変更後・一部有料)・AWS CDK(AWS専用)に対して、OpenTofu(Terraform完全互換OSSフォーク・MPLv2)・Pulumi(Python/TypeScript/Go/C#でIaC)・Crossplane(Kubernetes CRDでクラウドリソース管理)はOSSのInfrastructure as Codeツールです。

IaCが解決する問題

  • 再現性: 同じHCL/コードから同じインフラを何度でも構築できる
  • ドリフト検出: インフラの手動変更を検出して「コードと現実のズレ」を可視化
  • 変更履歴: gitでインフラ変更をトラッキング・PRレビュー・ロールバック
  • マルチクラウド: AWS・GCP・Azureのリソースを1つのツールで管理
  • チームコラボ: Terraform Cloudのリモートstate管理なしで複数人で安全に運用

主要ツールの概要

OpenTofu

2023年にHashiCorpがTerraformのライセンスをMPLv2からBSLに変更したことへの対抗として、Linux Foundation傘下でコミュニティがフォークしたOSSです。GitHubスター24k+。Terraform 1.6と完全互換であり、.tfファイルをそのまま使えます。state・プロバイダー・モジュールはTerraformと共用できます。商用利用制限なし・コミュニティ主導・MPLv2ライセンスが特徴。

# OpenTofuのインストール(Linux/Mac)
brew install opentofu

# またはシェルスクリプト
curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh | sh

# Windowsはchocolateyで
choco install opentofu

# バージョン確認
tofu --version
# main.tf - AWS VPCとEC2インスタンスをOpenTofuで管理
terraform {
  required_version = ">= 1.6"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  # Terraform Cloud互換のリモートstate(S3を使う場合)
  backend "s3" {
    bucket         = "my-tfstate-bucket"
    key            = "prod/vpc/terraform.tfstate"
    region         = "ap-northeast-1"
    encrypt        = true
    dynamodb_table = "tfstate-lock"
  }
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      ManagedBy   = "opentofu"
      Environment = var.environment
      Project     = var.project_name
    }
  }
}

# VPCとサブネット
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

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

resource "aws_subnet" "public" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index}.0/24"
  availability_zone = var.availability_zones[count.index]
  map_public_ip_on_launch = true

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

# ECSクラスター(Next.jsアプリのコンテナ実行環境)
resource "aws_ecs_cluster" "main" {
  name = "${var.project_name}-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

# Supabaseのアプリ設定をSSMパラメータストアで管理
resource "aws_ssm_parameter" "supabase_url" {
  name  = "/${var.project_name}/supabase/url"
  type  = "SecureString"
  value = var.supabase_url
}

output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.main.id
}

output "ecs_cluster_arn" {
  description = "ECSクラスターのARN"
  value       = aws_ecs_cluster.main.arn
}
# OpenTofuの基本的なワークフロー
cd infra/

# 初期化(プロバイダーとモジュールをダウンロード)
tofu init

# 変更のプレビュー(what-if)
tofu plan -out=tfplan

# 変更を実際に適用
tofu apply tfplan

# 現在のインフラ状態を確認
tofu show

# インフラを破棄(開発環境の削除)
tofu destroy -target=aws_ecs_cluster.main

# ドリフト検出(コードとクラウドのズレを確認)
tofu plan -refresh-only
# modules/nextjs-app/main.tf - 再利用可能なモジュール
# Next.jsアプリをECS Fargateにデプロイするモジュール
variable "app_name"       { type = string }
variable "image_uri"      { type = string }
variable "cpu"            { default = 256 }
variable "memory"         { default = 512 }
variable "container_port" { default = 3000 }

resource "aws_ecs_task_definition" "app" {
  family                   = var.app_name
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = var.cpu
  memory                   = var.memory
  execution_role_arn       = aws_iam_role.ecs_task_execution.arn

  container_definitions = jsonencode([
    {
      name      = var.app_name
      image     = var.image_uri
      essential = true
      portMappings = [
        { containerPort = var.container_port, protocol = "tcp" }
      ]
      environment = [
        { name = "NODE_ENV", value = "production" },
      ]
      secrets = [
        { name = "SUPABASE_URL", valueFrom = "/app/supabase/url" },
        { name = "SUPABASE_KEY", valueFrom = "/app/supabase/key" },
      ]
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          "awslogs-group"         = "/ecs/${var.app_name}"
          "awslogs-region"        = "ap-northeast-1"
          "awslogs-stream-prefix" = "ecs"
        }
      }
    }
  ])
}

Pulumi

2017年に公開されたOSSのInfrastructure as Codeツールです。GitHubスター22k+。HCLではなくPython・TypeScript・Go・C#などの汎用言語でインフラを記述できます。既存のプログラミングスキルをそのまま活かせ、ループ・条件分岐・関数・クラスでインフラを抽象化できます。Terraform/OpenTofuのプロバイダーをそのまま使えるPulumiTFプロバイダーブリッジもあります。

# Pulumi Python でGCPのCloud Runにアプリをデプロイ
# pip install pulumi pulumi-gcp
import pulumi
import pulumi_gcp as gcp

config = pulumi.Config()
project = config.require("project")
region  = config.get("region") or "asia-northeast1"

# Cloud Run サービス
cloud_run_service = gcp.cloudrun.Service(
    "nextjs-app",
    location=region,
    template=gcp.cloudrun.ServiceTemplateArgs(
        metadata=gcp.cloudrun.ServiceTemplateMetadataArgs(
            annotations={
                "autoscaling.knative.dev/maxScale": "10",
                "autoscaling.knative.dev/minScale": "1",
            }
        ),
        spec=gcp.cloudrun.ServiceTemplateSpecArgs(
            containers=[
                gcp.cloudrun.ServiceTemplateSpecContainerArgs(
                    image=f"gcr.io/{project}/nextjs-app:latest",
                    resources=gcp.cloudrun.ServiceTemplateSpecContainerResourcesArgs(
                        limits={"cpu": "1000m", "memory": "512Mi"},
                    ),
                    envs=[
                        gcp.cloudrun.ServiceTemplateSpecContainerEnvArgs(
                            name="NODE_ENV",
                            value="production",
                        ),
                        gcp.cloudrun.ServiceTemplateSpecContainerEnvArgs(
                            name="SUPABASE_URL",
                            value_from=gcp.cloudrun.ServiceTemplateSpecContainerEnvValueFromArgs(
                                secret_key_ref=gcp.cloudrun.ServiceTemplateSpecContainerEnvValueFromSecretKeyRefArgs(
                                    name="supabase-url",
                                    key="latest",
                                )
                            ),
                        ),
                    ],
                )
            ]
        ),
    ),
    traffics=[gcp.cloudrun.ServiceTrafficArgs(percent=100, latest_revision=True)],
)

# 全員がアクセスできる公開IAMポリシー
gcp.cloudrun.IamMember(
    "allow-all",
    location=region,
    service=cloud_run_service.name,
    role="roles/run.invoker",
    member="allUsers",
)

pulumi.export("url", cloud_run_service.statuses[0].url)
// Pulumi TypeScript でVercel風にNext.jsをCloudflare Pagesにデプロイ
import * as pulumi from "@pulumi/pulumi";
import * as cloudflare from "@pulumi/cloudflare";

const config = new pulumi.Config();
const accountId = config.require("cloudflareAccountId");

// Cloudflare Pagesプロジェクト
const pagesProject = new cloudflare.PagesProject("opensourcelab", {
  accountId,
  name: "opensourcelab",
  productionBranch: "main",
  buildConfig: {
    buildCommand: "npm run build",
    destinationDir: ".next",
    rootDir: "/",
  },
  source: {
    type: "github",
    config: {
      owner: "myorg",
      repoName: "opensourcelab",
      productionBranch: "main",
      prCommentsEnabled: true,
      deploymentsEnabled: true,
    },
  },
  deploymentConfigs: {
    production: {
      environmentVariables: {
        NODE_ENV: "production",
        NEXT_PUBLIC_SUPABASE_URL: config.requireSecret("supabaseUrl"),
      },
    },
  },
});

export const pagesUrl = pagesProject.subdomain;

機能比較表

比較項目OpenTofuTerraformPulumi
ライセンスMPLv2BSL(商用制限)Apache 2.0
言語HCLHCLPython/TS/Go/C#
Terraform互換✅(完全)△(ブリッジ)
マルチクラウド
ループ・条件分岐HCL限定HCL限定完全
GitHub Stars24k+43k+22k+

IaCで管理するインフラのコンテナをDevOpsカテゴリ/categories/devopsのPortainer・Rancher・k9sで運用管理できます。IaCのCI/CDパイプラインにDevOpsカテゴリ/categories/devopsのGitHub Actionsを組み合わせて変更をPR単位でレビューします。

FAQ

Q. TerraformのコードをOpenTofuに移行するには何が必要ですか?

A. 多くの場合、コードの変更ゼロで移行できます。手順: ①terraformコマンドをtofuに置き換える②terraform.tfstateは共通フォーマットのためそのまま使える③既存のTerraformプロバイダー(AWS・GCP・Azure等)はOpenTofuでもそのまま動く④terraform.required_providerssourceは変更不要。注意点: BSLのHashiCorp商用プロバイダー(HCP Vault・HCP Consul等)の一部はOpenTofuで利用不可の場合がある。移行リスクが最小なのはHCLのままtofuコマンドで動かすOpenTofu、プログラミング言語でインフラを書きたいならPulumiという選択肢です。

Q. OpenTofuとTerraform Cloudのstate管理の互換性はありますか?

A. Terraform Cloud: OpenTofuはTerraform Cloudのリモートstateには非対応です(Terraform Cloudの商用サービスのため)。代替: ①S3+DynamoDB(AWS標準構成・最も一般的)②GCS(GCP環境)③Azure Blob Storage④Spacelift・Env0・Terrateam(Terraform Cloudと互換のOSSバックエンド)⑤OpenTofu Registryのトフォーム: backend "s3"でリモートstateを管理するのが最もシンプルです。OSSのTerraform Cloudクローン「Atlantis」もOpenTofuと互換性があります。

Q. PulumiとTerraformの学習コストはどのくらい違いますか?

A. HCLを知っているチーム: TerraformよりOpenTofuの方が圧倒的に移行が楽(同じコードで動く)。Pythonエンジニアが多いチーム: PulumiのPythonがHCLより直感的。for文・if文・クラス継承でDRYなインフラコードが書ける。TypeScriptチーム(Next.jsメイン): PulumiのTypeScriptで型安全なインフラコードが書ける。学習曲線: HCL(OpenTofu/Terraform)は2〜3日で基礎習得。Pulumiは対象言語を知っていれば1〜2日で習得。IaCをゼロから始めるならPulumiのPythonがプログラマにとって最も自然です。

Q. モノレポでOpenTofuのstateを環境別(dev/staging/prod)に分けるには?

A. ディレクトリ構造でワークスペースを分けるのが最もシンプルです。

infra/
  environments/
    dev/
      main.tf      # devの設定をimport
      backend.tf   # S3 state: infra/dev/terraform.tfstate
    staging/
      main.tf
      backend.tf   # S3 state: infra/staging/terraform.tfstate
    prod/
      main.tf
      backend.tf   # S3 state: infra/prod/terraform.tfstate
  modules/
    vpc/
    ecs/
    rds/

各環境ディレクトリでtofu inittofu plantofu apply。モジュールを使ってdevprodでパラメータだけ変える構成が再現性が高く推奨です。

まとめ

ユースケース推奨ツール
Terraform移行・HCL継続・OSS化OpenTofu
Python/TypeScript/Goでインフラをコード化Pulumi
Kubernetes上でクラウドリソースをCRD管理Crossplane

関連外部リソース

他の記事も読む

Let's Build Together

OSS導入、自社だけで悩まない。

ツール選定から構築・運用・AI活用まで、オープンソースラボ運営元のClasslessが伴走します。初回のご相談は無料です。