「terraform applyを打ったら、Pulumiで作ったリソースまで変更対象に入っていた」
この記事では、TerraformとPulumiを並走させる「移行期」に発生するstate競合の仕組みと、リソース所有権を明確に分ける設計パターンを解説します。RHEL 9系 / Ubuntu 24.04 LTS を想定環境として説明します。
この記事のポイント
・PulumiとTerraformはstateストアが独立しているため「二重所有」が無音で起きる
・terraform state rmとpulumi importを使えばリソース移管は安全に行える
・prevent_destroyとignore_changesで誤操作の影響範囲を限定できる
・ディレクトリとCIゲートで所有権を可視化することが長期運用の要点
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜTerraformとPulumiのstate競合が問題になるのか
TerraformはHCLで記述した構成をtfstateファイルに記録し、そのstateと実際のクラウドリソースとの差分をもとに変更計画(plan)を生成します。PulumiはTypeScript・Python等の汎用言語でインフラを定義し、独自のstateバックエンド(デフォルトはPulumi Cloud、セルフホスト時はS3等)に状態を保存します。この2つはstateストアが完全に独立しているため、同じAWSリソースを両方のツールが「自分が管理している」と記録できてしまいます。片方でリソースを作成し、もう片方がそのリソースを「設定上存在しない」とみなして誤ってdestroyする。これが並走期間中に起きる典型的な事故です。
移行期のアーキテクチャ設計についてはTerraform実践トレーニング(terraform.linuxmaster.jp)でも扱っています。
並走期間中の主なリスクパターン
1. リソースの「二重所有」問題
Terraformで作成したVPCを、Pulumiのコードでも参照先として宣言してしまうケースです。Pulumiのpulumi stack outputでは既存リソースを参照できますが、プロジェクト設定を誤ると新規作成として扱い、同名リソースの競合エラーか意図しない新規リソースの作成が発生します。# 二重所有が起きているときのエラー例(出力例・環境により異なります) error: Preview failed: aws:ec2/vpc:Vpc (main-vpc): error: VPC with CIDR 10.0.0.0/16 already exists Duplicate resource detected
2. terraform destroyが管轄外リソースを巻き込むリスク
同じAWSアカウント・リージョンで作業していると、Terraformのtfstateに登録されたリソースを一括削除するterraform destroyが、意図せずPulumiが作成したリソースに依存するリソースを削除してしまうことがあります。依存関係のあるSecurity GroupやIAM Roleが消えた場合、Pulumiで管理しているEC2がネットワーク接続を失うといった障害に直結します。3. ドリフト検出の片側見逃し
terraform planはTerraformのstateを基準に差分を表示しますが、Pulumiが後から変更したリソースの状態はTerraform側には見えません。「planで変更なし」と表示されていても、Pulumi側でサブネットのCIDRが変更されていた、という状態になります。定期的なドリフト検出を両ツールで個別に実行しなければ、設定の乖離を見逃し続けます。所有権を分ける基本設計(3ステップ)
1. IaC管轄境界をドキュメントで先に定義する
ツールの移行を始める前に、「どのリソース群をどのツールが管轄するか」を明文化します。一般的なパターンはレイヤー分割です。・ネットワーク層(VPC・サブネット・ルートテーブル): Terraform管轄
・アプリケーション層(ECS・Lambda・RDS): Pulumi管轄(移行対象)
・セキュリティ層(IAM・Security Group): 移行完了まで両ツール共存(参照のみ・変更はTerraform)
この境界定義を
docs/iac-ownership.mdなどのファイルに落とし、チーム全員が参照できる状態にします。2. terraform state rmとpulumi importでリソースを移管する
Pulumiの管轄に移すリソースは、TerraformのstateからはRDSを例として次の手順で除籍します。# 手順1: Terraformのstateから対象リソースを取り除く # (実際のリソースは削除しない・stateの記録だけを除外する) terraform state list # 出力例(環境により異なります): # aws_db_instance.main # aws_db_subnet_group.main terraform state rm aws_db_instance.main terraform state rm aws_db_subnet_group.main # 手順2: 実際のリソースIDを取得する terraform show -json | python3 -c "import sys,json; [print(k,v['values']['id']) for k,v in json.load(sys.stdin)['values']['root_module']['resources'] if v['address'] == 'aws_db_instance.main']" 2>/dev/null || true # 手順3: PulumiにリソースIDを登録する(destroyせずインポート) pulumi import aws:rds/instance:Instance main db-prod-2024 # 出力例(環境により異なります): # Importing (dev): # + aws:rds/instance:Instance main imported # Resources: 1 imported
terraform state rmを実行する前にterraform state pull > state-backup.jsonでバックアップを取ってください。state操作は元に戻せないため、作業前のスナップショットは必須です。3. prevent_destroyとignore_changesで誤操作の範囲を限定する
Terraform側に残っている「参照だけしたいリソース」には、誤ったdestroyを防ぐLifecycleルールを追加します。# Pulumi管轄リソースをTerraformが参照だけする場合の保護設定 # (tfstateに残っている間の誤削除防止) resource "aws_security_group" "app" { name = "app-sg" # ... 設定省略 lifecycle { # destroyコマンドで消えないようにロック prevent_destroy = true # Pulumi側が変更してもTerraformが差分を検出して上書きしないよう除外 ignore_changes = [ingress, egress, tags] } }
ignore_changesに指定した属性はPulumi側が変更してもterraform planが差分として表示しなくなります。移管完了後はこのリソース定義ごとtfstateから削除します。並走期間中の運用ルール
1. ディレクトリ構成でTerraform管轄とPulumi管轄を分離する
# 推奨ディレクトリ構成(例) infra/ terraform/ # Terraform管轄(ネットワーク層) modules/ environments/ dev/ prod/ README.md # 管轄範囲を明記する pulumi/ # Pulumi管轄(アプリケーション層) Pulumi.yaml Pulumi.dev.yaml src/ README.md # 管轄範囲を明記する docs/ iac-ownership.md # 境界定義ドキュメント
README.mdやiac-ownership.mdに「terraform配下はTerraform担当チームのみ変更可」「pulumi配下はPulumi担当チームのみ変更可」と明記し、PRレビューのルールとして組み込みます。2. CIで交差アクセスを検出する
GitHub Actions等のCIでPRの差分ファイルをチェックし、Terraform配下からPulumi配下を直接参照しようとする変更を検出します。# .github/workflows/iac-ownership-check.yml(抜粋) - name: Check IaC ownership boundary run: | # Terraform配下のファイル変更がPulumi配下のリソース名を参照していないか確認 changed_tf= if [ -n "" ]; then if grep -r "pulumi" 2>/dev/null | grep -v "#"; then echo "ERROR: Terraform code references Pulumi-managed resources directly" echo "Use data sources or remote_state to cross boundaries" exit 1 fi fi echo "Ownership boundary check: OK"
トラブルシュート|よくある競合パターンと対処
| 症状 | 原因 | 対処 |
|---|---|---|
| terraform planでPulumi作成リソースの変更が出る | 同一リソースがTerraformのstateにも記録されている | terraform state rmでTerraformのstateから除籍する |
| pulumi upで「Resource already exists」エラー | Terraform管轄リソースをPulumiが新規作成しようとしている | pulumi importでインポートして所有権をPulumiに移す |
| terraform destroyで関係ないリソースが削除対象に入る | Terraform stateに移管予定リソースが残っている | 移管対象にprevent_destroy = trueを設定してからstate rm |
| どちらのツールが作ったかわからなくなった | taggingルールが未統一 | 全リソースにmanaged_by = terraformまたはpulumiタグを付与するポリシーを設定する |
まとめ
| 設計ポイント | 具体的な方法 |
|---|---|
| 所有権の明示 | ドキュメントとディレクトリ構成でTerraform・Pulumi管轄を分離 |
| リソース移管 | terraform state rmで除籍 → pulumi importで登録 |
| 誤操作防止 | prevent_destroyとignore_changesで並走中のリソースを保護 |
| CI検出 | PRの差分ファイルから所有権境界の越境を自動検出 |
| 移管完了後 | Terraform stateから除籍済みリソースの定義を削除してHCLをクリーンアップ |
TerraformによるIaC設計の実践的な学習コースを用意しています。
コードレビューの視点から設計パターンまで、現場で使える知識を体系的にカバーしています。
>> Terraform実践トレーニングの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Terraformのstate分割設計を比較する|モノリシックstateとサービス別・レイヤー別の選択基準
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます