SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

AnsibleとTerraformの違いは?使い分けと連携方法

TerraformはVPSを作成し、Ansibleは内部を設定します。provisionerが両方で問題になる理由、引き継ぎコマンド、Ansibleだけで足りる条件を解説します。

Ansible と Terraform の違いを一言で説明すると

Ansible と Terraform は、同じ役割を担う2つのツールから選ぶものではありません。Terraform は、サーバー、ディスク、ネットワーク、DNS レコードなど、どのインフラストラクチャが存在するかを宣言します。Ansible は、すでに存在するマシンの内部で、パッケージ、ユーザー、設定ファイル、稼働中のサービスなどがどの状態であるべきかを宣言します。Terraform は VPS を作成します。Ansible は、その VPS を Web サーバーに設定します。

どちらも宣言的であり、Infrastructure as Code (IaC) と呼ばれます。実際の違いは、何を記録するかです。Terraform は、コード内の各リソースと、API を通じて作成した実際のオブジェクトとの対応を state file に記録します。そのため、5 行を削除すると1台のサーバーを破棄する必要があると判断できます。Ansible は、実行の間に状態を記録しません。SSH で接続してマシンを調査し、playbook と一致しない部分だけを変更します。

この1つの違いが、このガイドの残りの内容を説明します。2つの役割を1つのツールに混在させると問題が起きる理由も同じです。

Terraform が実際に行うこと

Terraform は provider plugin を介して API と通信します。使用する provider の registry ページでは、記述できる resource type が定義されています。そのため、あるホスト上のサーバーと別のホスト上のサーバーでは、resource name も引数も異なります。

resource "cloud_server" "web" {
  name  = "web1"
  image = "ubuntu-24.04"
  type  = "small"
}

output "web_ip" {
  value = cloud_server.web.ipv4_address
}

cloud_server は、使用する provider がドキュメントで定義している resource type に置き換えてください。output ブロックは、このガイドで重要な部分です。ここで指定したアドレスが Terraform の外部へ公開されるためです。

terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplan

terraform init は provider をダウンロードし、lock file を書き込みます。terraform plan は、コードと state file の差分を表示し、最後に Plan: 1 to add, 0 to change, 0 to destroy. のような行を出力します。この行は毎回確認してください。一部の引数は既存リソース上で変更できません。plan では、その属性の横に # forces replacement を付け、続けて 1 to add, 0 to change, 1 to destroy と表示します。この plan を適用すると、サーバーが削除されて空の新しいサーバーが作成されます。その結果、安全だと思っていたデータを失うことがあります。

plan を file に保存してからその file を適用すると、単独で terraform apply を実行する場合と異なり、レビューした内容がそのまま実行されます。2 つのコマンドの実行間に、別のユーザーがインフラストラクチャを変更する可能性があるためです。

terraform.tfstate は状態情報です。これを失うと、Terraform はそれらのサーバーが自分の管理対象であることを認識できなくなり、次の apply で重複したサーバーを作成しようとします。2 人以上がコマンドを実行する場合は、すぐに remote backend に保存してください。2 人が同時に apply すると、次の状態になります。

Error: Error acquiring the state lock

OpenTofu は Terraform の fork であり、同じコマンドと同じ file format を使用します。2026 年 7 月時点では、terraform の代わりに tofu と入力すれば、このガイドのすべての手順を実行できます。

Ansible が実際に行うこと

Ansible にはエージェントも API も必要ありません。SSH 接続を確立し、小さな Python モジュールを対象ホストへコピーして実行し、その後削除します。SSH と sudo パスワードで接続できる対象であれば、Ansible で構成を管理できます。

- name: Base web server
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Ensure nginx is running at boot
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

ping モジュールは、playbook のデバッグを始める前に SSH、Python、sudo を検証します。正常な結果は web1 | SUCCESS => {"ping": "pong"} です。--check --diff の実行は、Ansible における計画表示に最も近い機能です。変更を適用せずに、適用した場合の変更内容を報告します。ただし、前のタスクに依存するタスクは、check mode で誤った結果を報告することがあります。前の変更が実際には適用されていないためです。

すべての実行は、ok=6 changed=2 unreachable=0 failed=0 のような recap で終了します。同じ playbook を 2 回実行してください。2 回目の実行では changed=0 と報告されるはずです。毎回 changed と報告するタスクは冪等ではありません。通常は command または shell のタスクであり、本来は適切なモジュールを使用すべきものです。初めて扱う場合は、単一の VPS で最初の Ansible playbook を作成するところから始め、そこから拡張してください。

2 つのツールが重なる領域と、競合する領域

Terraform は remote-exec provisioner を使用して、新しいサーバー上でコマンドを実行できます。HashiCorp の公式ドキュメントでも、provisioner は最終手段とされています。これには明確な理由があります。

provisioner はリソースの作成時にだけ実行されます。スクリプトを編集しても、既存のサーバーでは何も起こりません。Terraform から見ると、そのリソースはすでにコードと一致しているためです。provisioner の処理は terraform plan に表示されないため、レビューしてもその存在を確認できません。スクリプトが失敗すると、Terraform はリソースを tainted として扱います。その結果、次回の apply で、実際には正常だった可能性があるサーバーが破棄され、再作成されます。

失敗するタイミングも適切ではありません。API がサーバーの作成完了を返した時点で、provider はサーバーを作成済みと報告します。しかし、その時点ではオペレーティングシステムの起動中で、sshd はまだ待ち受けていません。

Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refused

Ansible には逆の誘惑があります。Cloud モジュールでサーバーを作成でき、少数のマシンであれば問題なく動作します。ただし、依存関係グラフと state file は失われます。Ansible はリソースを作成できますが、playbook からそのタスクを削除しても、リソースは稼働したまま課金も続きます。そのリソースを自分が作成したことを記録したものがないためです。

ここから導かれる原則は明確です。API が作成・削除するオブジェクトは Terraform に管理させ、起動済みのオペレーティングシステム内部は Ansible に管理させます。

引き渡しは統合ではなく、境界として機能します

Terraform は処理を完了し、アドレスを出力して終了します。Ansible はそのアドレスから開始します。

terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml

terraform output -raw は、引用符や JSON のラッパーなしで 1 つの値を出力します。これは、シェルのコマンド置換内で使う場合に適しています。複数のサーバーでは terraform output -json を使用し、その結果からインベントリを作成します。-raw は 1 つの文字列、数値、またはブール値だけを処理するためです。

2 つのツールの間に ping の手順を残す価値があります。これにより、「Terraform が誤ったアドレスを返した」場合と「playbook にバグがある」場合を切り分けられます。新しいサーバーに最初にアクセスするのが playbook だけだと、この 2 つの問題は同じように見えます。

Terraform の state を Ansible inventory として読み取る

inventory ファイルを作成したくない場合は、cloud.terraform collection で state を直接読み取れます。

ansible-galaxy collection install cloud.terraform

playbook の隣に terraform.yml を記述します。

plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infra
ansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml

利用する前に、2 点確認してください。plugin は project_path に対して terraform show を実行するため、そのディレクトリがあらかじめ初期化されていないと plugin は失敗します。また、サーバー resource から host を自動生成するわけではありません。Terraform コードで Ansible provider を使って宣言した ansible_host resource と ansible_group resource を読み取ります。ansible-inventory --graph に追加するまで、何も表示されません。

生成した通常の inventory ファイルは、デバッグしやすく、どの provider でも使用できます。inventory が数台を超え、手作業での編集による入力ミスが増え始めると、plugin の利点が生きます。これは、1 台の control machine から複数の Linux server を管理することが、単なる習慣ではなく実際の運用手順になる段階でもあります。

Terraform は本当に必要ですか?

これを読んでいる人の多くには、少なくとも今は必要ありません。Terraform の導入コストに見合うのは、インフラストラクチャの作成と破棄自体を繰り返し行う場合です。コントロールパネルで VPS を 1 台注文し、2 年間使い続ける予定なら、Terraform は 1 回しか発生しない作業を記述することになります。そのうえ、失ってはならない state ファイルも追加されます。

環境を頻繁に再構築する場合、staging を production と完全に一致させる必要がある場合、複数人でインフラストラクチャを変更し、何かが削除される前にレビュー可能な plan を確認したい場合は、Terraform を使います。また、管理対象がサーバーにとどまらず、provider API 上の DNS レコード、ロードバランサー、ファイアウォールルールに及ぶ場合にも適しています。

サーバーの稼働期間が長く台数も少ない場合は、Ansible だけを使います。日々の問題が「このサーバーは正しく構成されているか」であり、「このサーバーは存在するか」ではない場合も同様です。新しいサーバーを初期設定してセキュリティを強化する単一の playbook で、新しい VPS で最初の 10 分間に行う作業と同じ範囲をカバーできます。次のサーバーでも同じ手順を実行できる点が利点です。

学習する順序もこれに従います。Ansible は、最初に所有するサーバーで効果を発揮します。Terraform は、3 つ目の環境を再構築するときに効果を発揮します。

引き継ぎで問題が起きる場合

サーバーの準備が完了していません。 Terraform は成功しますが、Ansible はすぐに失敗します。

fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}

API が sshd の待ち受け開始前にアドレスを返しました。固定時間の sleep を追加せず、ポートが利用可能になるまで待機してください。Ansible にはこの用途専用の ansible.builtin.wait_for_connection があるため、play の最初のタスクとして実行します。同じ playbook で新規ホスト 1 台ではなくグループを対象にする場合は、1 台のホストに到達できない状態が続いた場合の動作を事前に決めてください。Ansible はそのホストを以降の実行対象から除外し、recap の行だけでそのことを通知するためです。

ホストキーが変更されました。 サーバーを破棄して再作成したため、新しいサーバーが同じアドレスで新しいキーを返しています。

Host key verification failed.

ssh-keygen -R 203.0.113.10 で古いエントリを削除してください。Terraform が再構築を実行するようになると頻繁に発生するため、データを保持するマシンでは再構築の回数を少なくするのが適切です。

Sudo が失敗します。 fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} は、そのホストで become: true にパスワードが必要であることを示します。デプロイユーザーにパスワードなしの sudo を設定するか、--ask-become-pass を渡してください。

Terraform が、変更していないリソースを破棄しようとします。 plan にコードへ記述していない変更が表示される場合、実際のインフラストラクチャがコードとずれています。通常は、誰かがプロバイダーの Web パネルで設定を変更したことが原因です。terraform plan -refresh-only を実行して差分だけを確認し、コードと実際のリソースのどちらが正しいかを判断してください。行単位で説明できない破壊的な plan は、決して apply しないでください。

Ansible が毎回 changed を報告します。 creates または when のガードがない shell タスクは、無条件で実行されます。これは見た目だけの問題ではありません。サーバーが要求した状態になったことを示すシグナルとして、changed=0 を使用できなくなるためです。

FAQ

Terraform で Ansible を置き換えられますか?

サーバー内部の構成管理には使えません。Terraform は remote-exec provisioner でスクリプトを実行できますが、実行されるのはリソースの作成時だけです。スクリプトは terraform plan に現れず、失敗するとリソースが tainted になります。次回の apply では、リソースの destroy と再作成が予定されます。Terraform には、nginx がすでにインストールされているかを確認し、インストール済みなら何もしない module に相当する機能がありません。Terraform でマシンを作成し、その後の構成管理は Ansible に引き継ぎます。

Ansible で Terraform を置き換えられますか?

長期間運用するサーバーが少数であれば可能です。Ansible にはサーバーを作成する cloud module があります。VPS インスタンスを 2 台注文して、そのまま保持するだけなら十分です。ただし、state file と依存関係グラフは失われます。playbook から task を削除しても、Ansible はそのリソースを作成したことを記録していないため、リソースは稼働し続け、課金も続きます。Terraform なら destroy が計画されていた状態です。

どちらを先に学ぶべきですか?

現在サーバーを所有しているなら、Ansible です。最初のマシンから効果が得られ、SSH 以外は必要ありません。手作業で注文したサーバーにも、そのスキルを適用できます。Terraform の効果が得られるのは、環境を繰り返し再構築する場合や、サーバー以外の DNS レコードやファイアウォールルールなど、provider のリソースも管理する場合です。

Terraform から Ansible に新しいサーバーの IP を渡すにはどうすればよいですか?

Terraform コードで output を宣言し、apply 後に読み取ります。terraform output -raw web_ip はシェル置換用に値だけを出力します。複数のホストがある場合は、terraform output -json ですべての output を一度に取得できます。その値を inventory file に書き込むか、cloud.terraform collection をインストールして、ansible-inventory -i terraform.yml --graph をプロジェクトディレクトリに指定します。

Terraform の完了直後に playbook が失敗するのはなぜですか?

provider は API が作成済みと報告した時点でサーバーを作成済みとして扱います。しかし、その時点では operating system の起動中であることが多く、最初の数秒間は SSH が拒否されます。エラーは UNREACHABLE! と Connection refused です。起動時間を推測して sleep の時間を決めるのではなく、ansible.builtin.wait_for_connection を play の最初の task にします。起動時間は image と plan によって異なるためです。