アプローチの比較:自動化パイプラインにおけるTerraform vs. OpenTofu
2023年8月10日、HashiCorpはTerraformのライセンスをオープンソースのMPL 2.0からBusiness Source License(BSL 1.1)へ変更しました。このライセンス改定は、プラットフォームチームに即座に様々な疑問を投げかけました。社内開発者プラットフォーム(IDP)の構築はBSL条項に違反するのか?
マネージドサービスプロバイダーは、Terraformベースの自動化環境を顧客へ提供し続けられるのか?これを受けて、Gruntwork、Spacelift、Env0などの企業がOpenTofuプロジェクトを立ち上げました。現在はLinux Foundationの傘下に置かれ、恒久的なオープンソースライセンスのもとで中立的に管理されるフォークとして提供されています。
現在、継続的デリバリー(CD)を評価しているDevOpsチームは、明確な2つの選択肢に直面しています:
- Terraformを使い続ける:既存のツール群やHashiCorp Cloudとの連携をそのまま維持できます。ただし、法務部門やプラットフォームチームは、すべての商用自動化ツール、競合製品、社内ツールがBSLに準拠しているかを精査する必要があります。
- OpenTofuへ移行する:OpenTofuはTerraform 1.5.6からフォークされており、Terraform 1.5.7の設定と完全な互換性を保持しています。既存のHCLファイル、リモートステートバックエンド、コミュニティプロバイダーを、MPL 2.0ライセンスのもとで一切変更せずにそのまま実行できます。
両方のバイナリは同一の実行ループを共有しています。プロバイダープラグインを取得し、ステートに対するリソースの差分を計算し、承認された変更を適用します。CI/CDパイプラインの移行に必要な作業は、CLIバイナリの差し替え、OpenTofuレジストリへの参照切り替え、そしてステートバックエンドのロック機能の検証という3つのステップのみです。
OpenTofuへの移行におけるメリット・デメリット
50以上のリポジトリにまたがるランナーを更新する前に、移行に伴う運用上のメリットと切り替えの摩擦(コスト)を比較検討しましょう。
主なメリット
- 中立な財団によるガバナンス:OpenTofuはLinux Foundationの下で運営されています。ロードマップ、RFC、リリースサイクルはベンダーニュートラルです。単一の企業が将来のリリースでライセンスを一方的に変更することはできません。
- 完全なCLI互換性(ドロップイン):コマンドは1対1で対応しています。
tofu init、tofu plan、tofu applyは、チームがこれまでTerraformで使ってきた引数やフラグとまったく同じもので実行可能です。 - シームレスなステート互換性:インフラがTerraform 1.5.7以前で稼働している場合、OpenTofuは既存のState Format v4をそのまま読み取ります。複雑なJSONスキーマ変換やステートのリセットは一切不要です。
- クライアント側でのステート暗号化:バージョン1.7.0以降、OpenTofuはネイティブなステート暗号化を導入しました。AWS KMS、GCP KMS、またはAES-GCMを使用して、リモートのオブジェクトストレージに書き込む前に、ランナー上で機密変数やシークレットを直接暗号化できます。
注意すべきデメリット
- 機能の乖離:コアとなるHCLの互換性は高く保たれています。しかし、Terraform 1.7以降で導入された独自のプロプライエタリ機能(テストモックや特定のプロバイダー関数など)は自動的には引き継がれません。
- CIエコシステムのパッケージング:HashiCorpは一部のクラウドマーケットプレイスで事前構成済みのセットアップステップを提供しています。OpenTofuへ切り替える際は、公式の
opentofu/setup-opentofuアクションやコンテナイメージを使用するようにパイプラインのYAMLを更新する必要があります。
ほとんどのプラットフォームチームにとって、リポジトリごとの移行作業は2時間未満で完了します。その見返りとして、アップストリームの商用ライセンス制限からの完全な解放という即効性のあるメリットが得られます。
OpenTofu CI/CDパイプラインの推奨構成
CI/CD環境でIaCを実行するには、厳格な分離が求められます。安全なアーキテクチャは、4つの基本的なガードレールに基づいています:
- キーレスなOIDC認証:リポジトリのシークレットから静的なクラウド認証情報を排除します。OpenID Connect(OIDC)を使用して、CIランナーとクラウドプロバイダー間の認証を連携させます。AWS IAM、Google Cloud Workload Identity、Azure Entra IDはすべて、検証済みのパイプラインジョブに対して短時間(通常1時間)有効なトークンを直接発行します。
- 分散ロックを備えたステートストレージ:DynamoDBをバックエンドにしたAmazon S3や、オブジェクトロックを有効にしたGoogle Cloud Storageなど、専用のリモートストレージにステートを保存します。高速なロック機能により、並行して実行されるプルリクエストが重複してplanをトリガーした際の競合状態やステートの破損を防止します。
- PlanとApplyステージの分離:プルリクエスト時に自動的に
tofu planを実行します。差分の概要をプルリクエストのコメントに直接出力し、マージ前にレビュー担当者が変更点を確認できるようにします。tofu applyは、mainブランチにマージされたコミットに対してのみ厳格に制限します。 - 厳格なバージョン固定:OpenTofuのバイナリバージョン(例:
1.8.2)とプロバイダーバージョン(例: AWSプロバイダー~> 5.40)の両方を明示的に固定します。バージョンを固定することで、アップストリームのレジストリでメジャーバージョンの更新が公開された際に、自動ビルドが破損するのを防ぎます。
実装ガイド:GitHub ActionsでTerraformからOpenTofuへ移行する
以下の手順に従って、既存のTerraformリポジトリを堅牢化されたOpenTofuのGitHub Actionsワークフローへ変換します。
ステップ1:ローカル環境でOpenTofu CLIをテストする
パイプラインの変更をコミットする前に、コードがローカルで正常にコンパイルされることを確認します。OpenTofuをインストールし、初期化をテストします:
# 公式インストーラースクリプトを使用してUbuntu/DebianにOpenTofuをインストール
curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh
chmod +x install-opentofu.sh
./install-opentofu.sh --install-method deb
rm -f install-opentofu.sh
# プロジェクトディレクトリの検証
cd my-infrastructure-repo/
tofu init
tofu plan
スキーマの警告やプロバイダーのエラーなしにplanが完了すれば、コードベースは自動実行の準備が整っています。
ステップ2:バージョン設定ファイルの更新
versions.tfを開きます。OpenTofuのリリースをサポートし、暗号化されたS3バックエンドを維持できるように、必要なバージョンの制約を設定します:
terraform {
# OpenTofu 1.6以上またはTerraform 1.5.7を許容
required_version = ">= 1.5.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
backend "s3" {
bucket = "corp-infrastructure-tfstate-prod"
key = "networking/vpc-production.tfstate"
region = "us-east-1"
dynamodb_table = "corp-tfstate-locks"
encrypt = true
}
}
ステップ3:GitHub Actionsパイプラインの構築
.github/workflows/opentofu.ymlを作成します。このワークフローは、AWS OIDCを介して認証を行い、フォーマットチェックを実施し、プルリクエストにplanの結果を出力し、マージ時に変更を適用します。
name: "OpenTofu Infrastructure Pipeline"
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
tofu:
name: "OpenTofu Plan & Apply"
runs-on: ubuntu-latest
steps:
- name: リポジトリのチェックアウト
uses: actions/checkout@v4
- name: OIDC経由でAWS認証情報を設定
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-opentofu
aws-region: us-east-1
- name: OpenTofuのセットアップ
uses: opentofu/setup-opentofu@v1
with:
tofu_version: 1.8.2
- name: OpenTofuの初期化
run: tofu init
- name: フォーマットの検証
run: tofu fmt -check
- name: 事前Planの実行
id: plan
if: github.event_name == 'pull_request'
run: |
tofu plan -no-color -out=plan.tfplan
continue-on-error: false
- name: プルリクエストへのPlan結果サマリーの投稿
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const body = `#### OpenTofu Plan実行結果: \`成功\`
* **実行者:** @${context.actor}
* **アクション:** \`${context.eventName}\`
* **対象ブランチ:** \`${context.payload.pull_request.base.ref}\``;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
- name: 承認済みステートの適用
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: tofu apply -auto-approve
ステップ4:切り替えの実施とステート整合性の検証
まずステージングブランチに更新したワークフローをデプロイします。GitHub Actionsランナーのログを確認し、tofu initがDynamoDBのロックを取得し、エラーなくS3のステートバケットを読み取れていることを確認します。
本番環境へ展開する前に、不可欠な2つの安全策を完了させてください:
- リモートステートのバックアップ:初回実行前に、
aws s3 cp s3://corp-infrastructure-tfstate-prod/ ./backup-state/ --recursiveを実行して正確なスナップショットを取得しておきます。 - 開発環境の標準化:チームメンバー全員のローカル環境に
tofuをインストールさせます。開発者が新しいプロプライエタリなTerraformバイナリ(Terraform 1.9以降など)をローカルで実行してしまうと、ステートのスキーマバージョンが更新され、OpenTofuパイプラインからロックアウトされるおそれがあります。

