Architecting Cloudflare Infrastructure with Ansible: From DNS Orchestration to Zero Trust Tunneling

The integration of Ansible into the Cloudflare ecosystem represents a paradigm shift from manual dashboard management to Infrastructure as Code (IaC). By leveraging Ansible's agentless architecture—which requires only an SSH connection and Python on the target machine—administrators can transition their network configuration into version-controlled YAML declarations. This capability extends across the entire Cloudflare suite, encompassing the management of the Global DNS authoritative servers and the deployment of cloudflared for secure, outbound-only tunnels. Whether automating the creation of A, CNAME, or MX records through the community-supported modules or establishing a Zero Trust connection via Cloudflare Tunnels on Debian-based systems, the use of Ansible eliminates human error and ensures that the desired state of the network is maintained across multiple environments.

The Mechanics of Cloudflare DNS Automation

Automating DNS is a critical requirement for modern DevOps pipelines, especially when managing dynamic environments where IP addresses change frequently. Cloudflare's API is designed for this specific purpose, providing a robust interface that allows Ansible to synchronize YAML-defined records with the actual state of the DNS zone.

API Authentication and Security Layers

To interact with Cloudflare's DNS via Ansible, a secure handshake must be established. There are two primary methods of authentication:

  1. Global API Keys: These are legacy keys that provide full account access. While functional, they are generally discouraged for automated scripts due to the lack of granular control.
  2. Scoped API Tokens: The modern, recommended approach. These tokens allow administrators to grant specific permissions to a script without exposing the entire account.

For DNS management, the API token must be granted the following specific permissions: - Zone: DNS: Edit (Allows the modification of records). - Zone: Zone: Read (Allows the module to verify the existence of the zone).

Implementation via Community General Collection

The community.general.cloudflare_dns module is the primary tool for managing records. This module abstracts the complex API calls into a simple YAML structure, allowing for the creation, updating, and deletion of records without direct interaction with the Cloudflare dashboard.

To utilize this functionality, the collection must first be installed using the following command:

bash ansible-galaxy collection install community.general

Specialized DNS Modules and Third-Party Roles

Beyond the community collection, there are specialized implementations such as the ansible-cloudflare module (associated with the DrMegahertz.cloudflare role). This module specifically utilizes rec_new and rec_delete API calls to manage records.

The parameters supported by this implementation include: - z: The zone identifier. - type: The DNS record type (e.g., A, AAAA, CNAME). - id: The unique identifier of the record. - name: The hostname of the record. - content: The value the record points to. - ttl: The Time to Live value.

To install this specific role from Ansible Galaxy, execute:

bash ansible-galaxy install DrMegahertz.cloudflare

For those preferring to keep all provisioning code within a single repository for better version control and auditability, the role can be integrated as a git submodule:

bash cd playbook-directory mkdir vendor git submodule add https://github.com/DrMegahertz/ansible-cloudflare.git vendor/ansible-cloudflare mkdir library ln -s vendor/ansible-cloudflare/library/cloudflare_domain.py library/cloudflare_domain.py

Deployment of Cloudflare Tunnels with Ansible

Cloudflare Tunnels (via the cloudflared daemon) allow an origin server to expose a web service to the internet without opening any inbound ports on the local firewall. This is achieved by creating an outbound-only connection to the Cloudflare edge.

Installing cloudflared on Debian Systems

Deploying cloudflared on a Debian environment requires a sequence of tasks to ensure the binary is authentic and the service is persistent.

The process involves three primary technical stages: 1. Key Verification: Importing the Cloudflare signing key from https://pkg.cloudflare.com/cloudflare-main.gpg to ensure package integrity. 2. Repository Configuration: Adding the official Debian repository (deb https://pkg.cloudflare.com/cloudflared bookworm main). 3. Package Installation: Using the apt module to install the cloudflared package.

The following Ansible task sequence demonstrates the implementation:

```yaml - name: Add Cloudflare signing key ansible.builtin.apt_key: url: https://pkg.cloudflare.com/cloudflare-main.gpg state: present

  • name: Add Cloudflare repository ansible.builtin.apt_repository: repo: deb https://pkg.cloudflare.com/cloudflared bookworm main state: present

  • name: Install cloudflared ansible.builtin.apt: name: cloudflared state: present ```

Tunnel Configuration and Idempotency

A critical aspect of Ansible is idempotency—ensuring that running a playbook multiple times does not change the system if it is already in the desired state. When connecting a tunnel using a token, the command cloudflared service install {{ cloudflare_tunnel_token }} is used.

To prevent the service from being re-installed on every run, the creates argument is used to check for the existence of the systemd service file at /etc/systemd/system/cloudflared.service.

yaml - name: Connect to tunnel ansible.builtin.command: > cloudflared service install {{ cloudflare_tunnel_token }} args: creates: /etc/systemd/system/cloudflared.service

The cloudflare_tunnel_token should be stored in host_vars because tunnels are typically mapped 1:1 between a specific origin server and the Cloudflare network. If a token needs to be rotated or changed, the administrator must remove all files matching /etc/systemd/system/cloudflare* before re-running the task.

Integrated Infrastructure Orchestration: Terraform and Ansible

For complex deployments, such as those utilizing Google Cloud Platform (GCP), Cloudflare recommends a hybrid approach where Terraform handles the hardware provisioning and Ansible handles the software configuration.

The Terraform-Ansible Workflow

In this architecture, Terraform is used to: - Provision a virtual machine on GCP. - Configure Cloudflare resources (including the tunnel secret). - Generate a tunnel token. - Install Python 3 on the GCP instance (enabling Ansible's agentless communication).

Terraform then exports the necessary variables, such as the tunnel token, into a YAML file (e.g., tf_ansible_vars_file.yml). This file acts as the bridge, allowing Ansible to read the dynamically generated token and apply it to the cloudflared installation.

Deployment Execution

The deployment is triggered within the Terraform workflow via the ansible-playbook command. The sequence of operations is as follows:

  1. Initialize the configuration directory: bash terraform init
  2. Preview the changes: bash terraform plan
  3. Apply the configuration: bash terraform apply

Once the GCP instance is online and the tunnel is established, the service can be verified by navigating to a configured hostname (e.g., http_app.example.com), which should resolve to the "Hello Cloudflare!" test page.

Technical Specification Summary

The following table provides a detailed comparison of the different methods of managing Cloudflare via Ansible.

Feature community.general.cloudflare_dns DrMegahertz.cloudflare cloudflared (Ansible Tasks)
Primary Goal DNS Record Management DNS Record Management Zero Trust Tunneling
Installation Method ansible-galaxy collection install ansible-galaxy install or Git Submodule apt repository installation
Auth Method API Token (Scoped) API Token / Email Tunnel Token
Key Functions Create, Update, Delete Records rec_new, rec_delete service install
Target OS Control Node (Localhost) Control Node (Localhost) Target Node (Debian/Linux)
Ideal Use Case Bulk DNS migrations Simple DNS record tasks Exposing origin servers

Configuration Example: DNS Management

For those utilizing the DrMegahertz.cloudflare role, the following configuration illustrates how to define a DNS record in a playbook.

yaml - hosts: localhost connection: local gather_facts: no roles: - DrMegahertz.cloudflare tasks: - name: Create DNS record www.example.com cloudflare_domain: > state=present name=www zone=example.com type=A content=127.0.0.1 [email protected] token=77a54a4c36858cfc10321fcfce22378e19e20

In this example, the cloudflare_domain task ensures that the www record for example.com points to 127.0.0.1. To avoid hardcoding sensitive data, the CLOUDFLARE_API_EMAIL and CLOUDFLARE_API_TOKEN environment variables can be used as alternatives to the email and token parameters within the task.

Conclusion

The utilization of Ansible for Cloudflare management transforms a manual, GUI-based process into a scalable, repeatable, and auditable engineering practice. By separating the concerns of infrastructure provisioning (Terraform) from configuration management (Ansible), organizations can achieve a high degree of agility. The ability to manage DNS records through the community.general collection ensures that network changes are documented in code, while the deployment of cloudflared via Ansible provides a secure method of exposing internal services without compromising firewall integrity. The transition from using global API keys to scoped tokens further enhances the security posture, ensuring that automation scripts possess only the minimum necessary privileges. Ultimately, this integration allows for the rapid deployment of secure, internet-facing services that benefit from Cloudflare's edge protection while maintaining the operational efficiency of an automated CI/CD pipeline.

Sources

  1. cloudflared on Debian with Ansible
  2. ansible-cloudflare GitHub Repository
  3. Ansible Manage Cloudflare DNS Guide
  4. Cloudflare Tunnel Ansible Deployment Guide

Related Posts