Using Terraform from Ansible

The sr2c.terraform collection lets a playbook deploy cloud resources from a Terraform module and then carry on using the module’s outputs, for example to create a server and then configure it in the same play. It works with Terraform 1.5.7 and later, and with OpenTofu.

The collection contains two roles:

  • sr2c.terraform.terraform_module deploys a single Terraform module: it plans, asks for approval, applies (or destroys) and stores the module’s outputs in a host variable

  • sr2c.terraform.terraform_s3_backend creates the AWS resources for keeping Terraform state in S3, and registers a backend configuration that can be passed straight to terraform_module

Both roles run Terraform and the AWS modules on the host they are run against, so they are normally used in a play targeting localhost to run on the controller.

Example

For a hypothetical application myapp that needs a Tor bridge, the tasks file looks like this:

---
- name: Myapp Infra | PATCH | Ensure the Terraform state backend exists
  ansible.builtin.include_role:
    name: sr2c.terraform.terraform_s3_backend
  vars:
    terraform_s3_backend_bucket_name: myapp-terraform-state
    terraform_s3_backend_sops_kms_key_name: myapp-sops

- name: Myapp Infra | PATCH | Deploy the Tor bridge
  ansible.builtin.include_role:
    name: sr2c.terraform.terraform_module
  vars:
    terraform_module_backend_config:
      s3: "{{ terraform_s3_backend_config | combine({'key': 'myapp/tor-bridge'}) }}"
    terraform_module_source: sr2c/tor-bridge/aws
    terraform_module_version: "0.1.0"
    terraform_module_inputs:
      name: myapp-bridge
    terraform_module_outputs_var: _myapp_bridge

- name: Myapp Infra | PATCH | Record the bridge fingerprint
  ansible.builtin.copy:
    content: "{{ _myapp_bridge.hashed_fingerprint }}\n"
    dest: "{{ playbook_dir }}/files/myapp-bridge-fingerprint"
    mode: "0644"