Ponz Dev Log

ゆるくてマイペースな開発日記

Terraform collectionでAnsibleからTerraformを実行する

AnsibleからTerraformを呼び出すAnsible Collection cloud.terraform があることを知りました。 Ansibleのワークフローの中でクラウドリソースの作成だけTerraformに任せたい場合に便利そうなので試してみます。

github.com

Terraform collection for Ansible とは

  • 名前の通りAnsibleのPlaybookからTerraformを呼び出すためのAnsible Collection。
  • Ansible Galaxyからダウンロード可能です。
  • 2つのモジュールが含まれています。
    • cloud.terraform.terraform ... terraform [plan | apply | destroy] 相当
    • cloud.terraform.terraform_output ... terraform output 相当

Collectionを使ってみる

検証環境情報

以下の環境、バージョンで検証します。

  • 開発マシン: macOS Ventura (13.5) Intel
  • Python: 3.11.4
  • Ansible Core: 2.15.3
  • Terraform CLI 1.5.5
  • Terraform collection for Ansible: 1.1.1

本記事で試すこと

Ansible PlaybookからTerraformの実行と実行ログの出力、TerraformではAWSVPCとSubnetを作成してみます。

また、最終系のディレクトリ構成は次の通りです。 Terraformのファイル群は terraform ディレクトリ、ログファイルの出力先は out ディレクトリとします。

.
├── ansible.cfg
├── inventories
│   └── demo.yml
├── out
│   ├── YYYYmmddTHHMMSS_terraform_apply.log
│   └── YYYYmmddTHHMMSS_terraform_apply.log
├── playbook.yml
├── requirements.txt
├── requirements.yml
└── terraform
    ├── main.tf
    ├── outputs.tf
    └── variables.tf

Collectionのインストール

ansible-galaxy コマンドでTerraform collectionをインストールします。

ansible-galaxy collection install cloud.terraform

Terraformリソース定義

terraform ディレクトリの下にAWSリソースを作成するtfファイルを作成します。 main.tfにAWSリソースの作成処理、入力変数定義を variables.tf 、そして出力変数定義を outputs.tf を記述します。

main.tf

# main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.12.0"
    }
  }
}

provider "aws" {
  region = var.region

  default_tags {
    tags = {
      ManagedBy : "Terraform"
    }
  }
}

resource "aws_vpc" "demo" {
  cidr_block = "10.23.0.0/16"
}

resource "aws_subnet" "demo1" {
  cidr_block = "10.23.45.0/24"
  vpc_id     = aws_vpc.demo.id
}

variables.tf

# variables.tf

variable "region" {
  type        = string
  description = "AWS Region"
  default     = "ap-northeast-1"
}

outputs.tf

# outputs.tf

output "vpc_id" {
  value       = aws_vpc.demo.id
  description = "VPC ID"
}

Playbook定義

Terraformを呼び出すAnsible Playbookを記述します。記述例は次のとおりです。

---
- name: Terraform Demo
  hosts: localhost

  tasks:
    - name: Provisioning infrastructure
      cloud.terraform.terraform:
        project_path: "{{ playbook_dir }}/terraform"
        state: present
        force_init: true
      register: ret_tf

    - name: Write provisioning log
      ansible.builtin.copy:
        content: "{{ ret_tf.stdout }}"
        dest: "{{ playbook_dir }}/out/{{ ansible_date_time.iso8601_basic_short }}_terraform_apply.log"
        mode: "0644"

cloud.terraform.terraform モジュールで特定のディレクトリ配下のtfファイルを適用します。ディレクトリは project_path で指定しますが、絶対パスで指定しましょう。相対パスだとエラーとなりました。

state パラメータは present だと terraform applyabsent を指定すると terraform destroy と同じ挙動をします。 terraform plan だけトリッキーで present を指定しつつ ansible-playbook コマンドをチェックモードで実行します。

1点注意すべき点として、Terraform実行途中のログは標準出力に表示されません。Terraformの実行ログはモジュールの stdout 変数に格納されるので ansible.builtin.copy モジュールなどでファイルに出力しておきます。Terraformの処理が中断されたらどこまで適用されたか分からないのは不便ですね。今後の改善に期待です。

いざ実行

ansible-playbook コマンドで Terraform collection を含むPlaybookを実行します。

ansible-playbook -i inventories/demo.yml playbook.yml

Terraformを実行するタスクは処理成功時に changed と表示するはずです。

PLAY [Terraform Demo] ****************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************
ok: [localhost]

TASK [Provisioning infrastructure] ***************************************************************************************
changed: [localhost]

TASK [Write provisioning log] ********************************************************************************************
changed: [localhost]

PLAY RECAP ***************************************************************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Terraformの実行ログ output/YYYYmmddTHHMMSS_terraform_apply.log を参照すると、Terraform CLIと同じような実行ログが出力されていますね。

aws_vpc.demo: Creating...
aws_vpc.demo: Creation complete after 1s [id=vpc-000000000000]
aws_subnet.demo1: Creating...
aws_subnet.demo1: Creation complete after 0s [id=subnet-123412341234]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

vpc_id = "vpc-000000000000"

試しに同じPlaybookを再実行するとTerraformモジュールのタスクが ok に変わります。Terraformの冪等性がAnsible Collectionにも反映されていますね。

PLAY [Terraform Demo] ****************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************
ok: [localhost]

TASK [Provisioning infrastructure] ***************************************************************************************
ok: [localhost]

TASK [Write provisioning log] ********************************************************************************************
changed: [localhost]

PLAY RECAP ***************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

所管

利用できそうなユースケース

Terraform collectionは次のユースケースで使えそうです。

  • TerraformをAnsibleのワークフローに組み込む
  • Terraformの前後処理をAnsibleで実行する
  • 複数のTerraform stateがあるプロジェクト間の依存解決する

以上。