AnsibleとTerraformの違いと使い分け
TerraformはVPSを作成し、Ansibleは内部を設定します。state fileの違い、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が定義されています。そのため、あるホスト上のserverと別のホスト上のserverでは、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からaddressが渡されるためです。
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplanterraform 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を適用するとserverが削除され、新しい空のserverが作成されます。その結果、安全だと思っていたデータを失うことがあります。
planをfileに保存してそのfileを適用すると、単にterraform applyを実行する場合とは異なり、確認した内容が実行されます。2つのコマンドの間に、別の人がinfrastructureを変更する可能性があります。
terraform.tfstateは記録情報です。これを失うと、Terraformはそのserverが自分の管理対象であることを認識できなくなります。そのため、次のapplyで重複したserverを作成しようとします。複数の人がコマンドを実行する場合は、すぐにremote backendに保存してください。2人が同時にapplyすると、次の状態になります。
Error: Error acquiring the state lockOpenTofuは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: trueansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.ymlpingモジュールは、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はまだlistenしていません。
Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refusedAnsibleには逆の問題があります。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.ymlterraform output -raw は、引用符や JSON のラッパーなしで1つの値を出力します。これはシェルのコマンド置換内で使用する場合に適しています。複数のサーバーでは、terraform output -json を使用してインベントリを作成します。-raw は単一の文字列、数値、またはブール値だけを処理するためです。
2つのツールの間に ping の手順を残しておく価値があります。これにより、「Terraform が誤ったアドレスを渡した」場合と「playbook にバグがある」場合を切り分けられます。playbook が新しいサーバーに初めてアクセスする処理である場合、この2つの問題は同じように見えます。
Terraform の state を Ansible インベントリとして読み取る
インベントリファイルをまったく作成したくない場合は、cloud.terraform collection が state を直接読み取ります。
ansible-galaxy collection install cloud.terraformterraform.yml を playbook の隣に作成します。
plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infraansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml利用する前に、2点確認してください。プラグインは project_path に対して terraform show を実行するため、そのディレクトリは事前に初期化されている必要があります。初期化されていない場合、プラグインは失敗します。また、サーバーリソースからホストを自動生成するわけではありません。Terraform コードで Ansible provider を使用して宣言した ansible_host リソースと ansible_group リソースを読み取ります。追加するまで、ansible-inventory --graph には何も表示されません。
生成した通常のインベントリファイルのほうがデバッグしやすく、どの provider でも使用できます。インベントリが数台を超え、手作業による編集で入力ミスが発生し始めた段階では、プラグインの利点が生まれます。この段階は、1台の管理用マシンから複数の Linux サーバーを管理することが、単なる習慣ではなく実際の運用手順になる段階でもあります。
Terraformは本当に必要ですか?
この記事を読んでいる人の多くは、少なくとも現時点ではTerraformを必要としません。Terraformのコストに見合うのは、インフラストラクチャの作成と破棄自体を繰り返し行う場合です。コントロールパネルでVPSを1台注文し、それを2年間維持する予定なら、Terraformで記述する処理は1回だけです。そのうえ、失ってはいけないstate fileも追加されます。
環境を頻繁に再構築する場合、stagingをproductionと完全に一致させる必要がある場合、複数の担当者がインフラストラクチャを変更し、何かを削除する前にレビュー可能なplanが必要な場合は、Terraformを使用してください。また、管理対象がサーバーだけでなく、provider API上に存在するDNS records、load balancers、firewall rulesにまで及ぶ場合にも適しています。
サーバーの稼働期間が長く台数も少ない場合は、Ansibleだけを使用してください。日々の課題が「このサーバーは正しく設定されているか」であり、「このサーバーは存在するか」ではない場合も同様です。新しいサーバーをhardeningする1つの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 の listening 開始前にアドレスを返しました。固定時間の sleep を追加せず、ポートが利用可能になるまで待機してください。Ansible にはこの用途専用の ansible.builtin.wait_for_connection があるため、play の最初のタスクとして実行します。
ホストキーが変更されています。 サーバーを破棄して再作成したため、新しいサーバーが同じアドレスで新しいキーを使用して応答しています。
Host key verification failed.ssh-keygen -R 203.0.113.10 で古いエントリを削除してください。Terraform で再構築を行うと頻繁に発生します。そのため、データを保持するマシンでは再構築をできるだけ少なくしてください。
Sudo が失敗します。 fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} は、そのホストで become: true にパスワードが必要であることを示します。deploy user に passwordless sudo を設定するか、--ask-become-pass を渡してください。
Terraform が、変更していないものを破棄しようとします。 plan に、作成していない変更が表示されます。これは、通常は provider の web panel で誰かが設定を変更したため、実際の infrastructure が code と異なる状態になったことを意味します。terraform plan -refresh-only を実行して、その差分だけを確認してください。その後、code と live resource のどちらが正しくないかを判断します。行ごとに説明できない破壊的な plan は、決して apply しないでください。
Ansible が毎回 changed と報告します。 creates または when の guard がない shell task は、無条件で実行されます。これは外観上の問題ではありません。サーバーが要求した状態にあることを示すシグナルとして、changed=0 を使用できなくなるためです。
FAQ
Terraform は Ansible を置き換えられますか?
サーバー内部の構成管理には使えません。Terraform は remote-exec provisioner でスクリプトを実行できますが、実行されるのはリソース作成時だけです。スクリプトは terraform plan に表示されず、失敗するとリソースが taint され、次回の apply で破棄と再作成が予定されます。Terraform には、nginx がすでにインストールされているかを確認し、インストール済みなら何もしない module に相当する機能がありません。Terraform でマシンを作成し、その後の構成管理を Ansible に引き継いでください。
Ansible は Terraform を置き換えられますか?
長期間稼働させるサーバーが少数であれば可能です。Ansible にはサーバーを作成する cloud module があります。VPS instance を2つ注文して維持するだけなら、それで十分です。ただし、state file と dependency graph は失われます。playbook から task を削除しても、リソースは稼働し続け、料金も発生し続けます。Ansible はそのリソースを作成したことを記録していないためです。Terraform なら破棄を計画できます。
先にどちらを学ぶべきですか?
現在サーバーを所有しているなら、Ansible です。最初のマシンから効果があり、SSH 以外は必要ありません。手動で注文したサーバーにも、そのスキルを適用できます。Terraform の効果が現れるのは、環境を繰り返し再構築するときや、DNS record や firewall rule など、サーバー以外の provider resource も管理するときです。
Terraform から Ansible に新しいサーバーの IP を渡すにはどうすればよいですか?
Terraform code で output を宣言し、apply 後に読み取ります。terraform output -raw web_ip は shell substitution 用の値だけを出力します。複数の host がある場合は、terraform output -json ですべての output を一度に取得できます。その値を inventory file に書き込むか、cloud.terraform collection をインストールして ansible-inventory -i terraform.yml --graph に project directory を指定してください。
Terraform の完了直後に playbook が失敗するのはなぜですか?
provider は API が作成完了を返した時点でサーバーを作成済みとして報告します。しかし、その時点では operating system がまだ boot 中のため、最初の数秒間は SSH が拒否されます。エラーは UNREACHABLE! と Connection refused です。sleep の時間を推測するのではなく、play の最初の task として ansible.builtin.wait_for_connection を実行してください。boot 時間は image と plan によって異なるためです。