この記事のポイント
・Terraform provisionerでAnsibleを呼ぶのはアンチパターン——設計の問題点を解説する
・terraform outputでIPを取り出し、Ansibleのインベントリに動的に渡すのが正しい連携設計
・GitHub ActionsでTerraform jobとAnsible jobを順次実行してCI/CDで自動化できる
・AWS SSMセッションマネージャー経由でSSHなしにAnsibleを実行できる設計も選択肢になる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
TerraformとAnsibleはなぜ役割を分けるべきか
Terraformが得意とするのはAWSのリソース定義(VPC・EC2・RDS・IAMなど)の宣言的管理です。一方Ansibleが得意とするのは、起動したサーバーに対してパッケージのインストール・設定ファイルの配置・サービスの起動を行う構成管理です。 この2つの役割は本質的に異なります。 ・Terraform:インフラの「何を」「どこに」「いくつ」作るかを管理する(プロビジョニング層)・Ansible:起動したサーバーの中身を「どうする」かを管理する(構成管理層)
この役割分担を守ることで、Terraformのtfstateがサーバー内部の状態を管理しなくて済み、AnsibleのPlaybookがインフラの差分計算に巻き込まれることもなくなります。
Terraform provisionerをAnsible起動に使ってはいけない理由
Terraform provisionerにはlocal-execとremote-execがあり、EC2作成後にAnsibleを起動することは技術的には可能です。しかしHashiCorp自身が「provisionerは最後の手段であり、本来の用途ではない」と明示しており、以下の問題があります。
・冪等性の崩壊:Terraformのapplyが失敗してリトライすると、provisionerが2回実行されてAnsibleが重複適用されることがある・エラーが捕捉しづらい:Ansible Playbookの失敗がTerraformのapplyエラーとして報告されるため、原因の切り分けが難しくなる
・stateに副作用が残る:provisioner実行の成否がtfstateに記録されず、次回applyでの挙動が予測しにくくなる
・SSH依存が増える:remote-execはEC2のSSHポートが開いている前提で動作するため、SSMベースのセキュア設計と相性が悪い
代わりにCI/CDパイプラインでTerraformとAnsibleを別ステップとして順次実行する設計が推奨されます。
terraform outputでEC2のIPをAnsibleに渡す設計
TerraformとAnsibleを連携する鍵は、terraform outputコマンドでEC2のIPアドレスを取り出し、Ansibleのインベントリとして渡すことです。
1. TerraformでEC2のIPをoutputに定義する
# outputs.tf output "web_instance_ips" { description = "Web servers public IP list" value = aws_instance.web[*].public_ip } output "db_instance_ip" { description = "DB server private IP" value = aws_instance.db.private_ip }
aws_instance.web[*].public_ipはfor_eachやcountで複数EC2を作成した場合にIPのリストを返します。
2. terraform outputからAnsibleのインベントリを生成する
Terraform applyが完了した後、terraform output -jsonで出力をJSON形式で取り出し、シェルスクリプトでAnsibleのインベントリファイルに変換します。
#!/bin/bash # generate-inventory.sh # terraform outputのJSON出力からAnsibleのINI形式インベントリを生成する WEB_IPS=$(terraform output -json web_instance_ips | jq -r '.[]') DB_IP=$(terraform output -raw db_instance_ip) # [web]セクションにIPを追記してインベントリを生成する echo "[web]" > inventory.ini echo "${WEB_IPS}" >> inventory.ini echo "" >> inventory.ini echo "[db]" >> inventory.ini echo "${DB_IP}" >> inventory.ini echo "" >> inventory.ini echo "[all:vars]" >> inventory.ini echo "ansible_user=ec2-user" >> inventory.ini echo "ansible_ssh_private_key_file=~/.ssh/deploy.pem" >> inventory.ini
jqコマンドが必要です。GitHub Actionsのubuntu-latestランナーには標準でインストールされています。
3. Ansible Playbookを実行する
# site.yml の例(Webサーバーにnginxをインストール) --- - hosts: web become: true roles: - nginx - php-fpm - app-deploy - hosts: db become: true roles: - mariadb - database-init
# 実行コマンド例 ansible-playbook -i inventory.ini site.yml --check # ドライラン確認 ansible-playbook -i inventory.ini site.yml # 本番実行 # 出力例(環境により異なります) # PLAY [web] ************************************* # TASK [nginx : Install nginx] ******************* # ok: [10.0.1.100] # TASK [nginx : Enable nginx service] ************ # changed: [10.0.1.100] # PLAY RECAP ************************************* # 10.0.1.100 : ok=12 changed=3 unreachable=0 failed=0
GitHub ActionsでTerraform→Ansible順次実行パイプラインを設計する
CI/CDでTerraformとAnsibleを自動連携する場合、1つのワークフローファイルに2つのjobを定義してneedsで依存関係を宣言します。
Terraformの実践的なIaC設計については、Linux Master Pro Seminar(Terraform・IaC実践)で体系的に学べます。
# .github/workflows/deploy.yml name: Infrastructure and Configuration Deploy on: push: branches: [main] jobs: terraform: name: Terraform Apply runs-on: ubuntu-latest outputs: web_ips: ${{ steps.tf_output.outputs.web_ips }} steps: - uses: actions/checkout@v4 - uses: hashicorp/setup-terraform@v3 with: terraform_version: "1.9.0" - name: Terraform Init run: terraform init env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - name: Terraform Apply run: terraform apply -auto-approve - name: Get Terraform Outputs id: tf_output run: | echo "web_ips=$(terraform output -json web_instance_ips)" >> "$GITHUB_OUTPUT" ansible: name: Ansible Configuration runs-on: ubuntu-latest needs: terraform # terraform jobが完了してから実行 steps: - uses: actions/checkout@v4 - name: Install Ansible run: pip3 install ansible - name: Generate Inventory from Terraform Output run: | echo '[web]' > inventory.ini echo '${{ needs.terraform.outputs.web_ips }}' | jq -r '.[]' >> inventory.ini echo '' >> inventory.ini echo '[all:vars]' >> inventory.ini echo 'ansible_user=ec2-user' >> inventory.ini - name: Run Ansible Playbook run: | ansible-playbook -i inventory.ini site.yml env: ANSIBLE_HOST_KEY_CHECKING: "false"
needs: terraformを指定することで、Terraform jobが失敗した場合はAnsible jobが実行されません。インフラのプロビジョニングに失敗したままAnsibleが動いて接続エラーを大量に出す、という問題を防げます。
AWS SSM経由でAnsibleを実行するSSHレス設計
EC2にSSHポートを開けたくない場合、AWS Systems Manager Session Manager経由でAnsibleを実行できます。community.aws.aws_ssmconnectionプラグインを使う方法です。
# inventory.ini(SSM経由の場合) [web] i-0abc1234defg5678 # インスタンスIDで指定(IPではなく) [all:vars] ansible_connection=community.aws.aws_ssm ansible_aws_ssm_region=ap-northeast-1 ansible_user=ec2-user
AmazonSSMManagedInstanceCoreポリシーを付けたIAMロールが必要です。TerraformでIAMロールを作る際に設定してください。
# Terraform側でSSMアクセス用IAMロールを定義する例 resource "aws_iam_role_policy_attachment" "ssm_managed" { role = aws_iam_role.web_instance.name policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" }
トラブルシュート:AnsibleがホストにUNREACHABLEを返す場合
「UNREACHABLE: Failed to connect to the host」が出る場合
・セキュリティグループ:CIのrunner IPからポート22が許可されているか確認する・SSH鍵の改行コード:GitHub SecretsのSSH_PRIVATE_KEYが正しいPEM形式かどうか確認する(base64エンコードして保管すると改行問題を回避できる)
・インスタンスの起動待ち:EC2が
running状態でも、SSHdが起動するまで数秒かかる。Playbookの冒頭にwait_for_connectionを設けると安全# Playbookの冒頭にwait_for_connectionを追加する例 - hosts: web gather_facts: false tasks: - name: Wait for SSH to become available ansible.builtin.wait_for_connection: delay: 10 timeout: 120 - hosts: web become: true roles: - nginx
「terraform output」で空の値が返る場合
TerraformがまだapplyされていないかEC2が作成される前にoutputを取得しようとすると、値が空になります。GitHub ActionsではTerraform applyのstepが完了した後のstepでoutputを取得しているか順序を確認してください。本記事のまとめ
| 役割 | ツール | 担当する領域 |
|---|---|---|
| インフラのプロビジョニング | Terraform | VPC・EC2・RDS・IAMなどAWSリソースの作成・変更・削除 |
| サーバーの構成管理 | Ansible | パッケージインストール・設定ファイル配置・サービス起動 |
| 連携の橋渡し | terraform output + シェル | EC2のIPをAnsibleのインベントリに動的に渡す |
| CI/CDでの実行順序 | GitHub Actions(needs) | Terraform成功後にAnsibleを実行する依存関係の宣言 |
| SSHレス設計 | AWS SSM + Ansible | ポート22を開けずにAnsibleでEC2を設定管理する |
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:TerraformのIaCセキュリティをCheckovで自動検査する設計|S3・IAM・SGの設定ミスをPRマージ前に止める方法
- この記事の属するカテゴリ:Terraformへ戻る

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