playbooks/roles/cloudflare_dns/tasks/main.yml
2026-04-05 16:54:48 +08:00

140 lines
5.8 KiB
YAML

---
- name: Validate Cloudflare DNS inputs
ansible.builtin.assert:
that:
- cloudflare_dns_records is sequence
- cloudflare_dns_records | length > 0
- cloudflare_dns_zone_name | length > 0 or cloudflare_dns_zone_id | length > 0
fail_msg: "cloudflare_dns_records and either cloudflare_dns_zone_name or cloudflare_dns_zone_id are required"
- name: Resolve Cloudflare token from extra vars when needed
ansible.builtin.set_fact:
cloudflare_dns_api_token: >-
{{
vars.get('CLOUDFLARE_DNS_API_TOKEN', '')
| default(vars.get('CLOUDFLARE_API_TOKEN', ''), true)
}}
when: cloudflare_dns_api_token | default('', true) | length == 0
- name: Resolve Cloudflare token from environment when needed
ansible.builtin.set_fact:
cloudflare_dns_api_token: >-
{{
lookup('ansible.builtin.env', 'CLOUDFLARE_DNS_API_TOKEN')
| default(lookup('ansible.builtin.env', 'CLOUDFLARE_API_TOKEN'), true)
}}
when: cloudflare_dns_api_token | default('', true) | length == 0
- name: Validate Cloudflare token is present
ansible.builtin.assert:
that:
- cloudflare_dns_api_token | length > 0
fail_msg: "Export CLOUDFLARE_API_TOKEN or CLOUDFLARE_DNS_API_TOKEN before running this role."
- name: Preview Cloudflare DNS work in check mode
ansible.builtin.debug:
msg: "Check mode: skipping Cloudflare DNS reconciliation for {{ cloudflare_dns_records | length }} record(s) in {{ cloudflare_dns_zone_name | default(cloudflare_dns_zone_id) }}"
when: ansible_check_mode
- name: Reconcile Cloudflare DNS records
when: not ansible_check_mode
block:
- name: Resolve Cloudflare zone id
ansible.builtin.uri:
url: "{{ cloudflare_dns_api_base }}/zones?name={{ cloudflare_dns_zone_name }}"
method: GET
headers:
Authorization: "Bearer {{ cloudflare_dns_api_token }}"
Content-Type: application/json
return_content: true
register: cloudflare_dns_zone_lookup
when: cloudflare_dns_zone_id | default('', true) | length == 0
- name: Validate zone lookup result
ansible.builtin.assert:
that:
- cloudflare_dns_zone_lookup.json.success
- cloudflare_dns_zone_lookup.json.result | length > 0
fail_msg: "Unable to resolve Cloudflare zone id for {{ cloudflare_dns_zone_name }}."
when: cloudflare_dns_zone_id | default('', true) | length == 0
- name: Set Cloudflare zone id
ansible.builtin.set_fact:
cloudflare_dns_zone_id: "{{ cloudflare_dns_zone_lookup.json.result[0].id }}"
when:
- cloudflare_dns_zone_lookup is defined
- cloudflare_dns_zone_id | default('', true) | length == 0
- name: Show zone permissions for current token
ansible.builtin.debug:
msg: "Cloudflare token permissions for {{ cloudflare_dns_zone_name }}: {{ cloudflare_dns_zone_lookup.json.result[0].permissions | default([]) }}"
when:
- cloudflare_dns_zone_lookup is defined
- cloudflare_dns_zone_lookup.json is defined
- name: Fail early when token does not have DNS edit permission
ansible.builtin.assert:
that:
- "'#zone:read' in (cloudflare_dns_zone_lookup.json.result[0].permissions | default([]))"
- "'#dns_records:edit' in (cloudflare_dns_zone_lookup.json.result[0].permissions | default([]))"
fail_msg: >-
CLOUDFLARE_API_TOKEN is valid but lacks DNS edit permission for {{ cloudflare_dns_zone_name }}.
Current permissions: {{ cloudflare_dns_zone_lookup.json.result[0].permissions | default([]) }}.
Required: Zone read + DNS edit on the svc.plus zone.
when:
- cloudflare_dns_zone_lookup is defined
- cloudflare_dns_zone_lookup.json is defined
- name: Query existing DNS records
ansible.builtin.uri:
url: "{{ cloudflare_dns_api_base }}/zones/{{ cloudflare_dns_zone_id }}/dns_records?name={{ item.name }}"
method: GET
headers:
Authorization: "Bearer {{ cloudflare_dns_api_token }}"
Content-Type: application/json
return_content: true
loop: "{{ cloudflare_dns_records }}"
loop_control:
label: "{{ item.name }}"
register: cloudflare_dns_record_queries
- name: Remove existing DNS records for target name
ansible.builtin.uri:
url: "{{ cloudflare_dns_api_base }}/zones/{{ cloudflare_dns_zone_id }}/dns_records/{{ item.1.id }}"
method: DELETE
headers:
Authorization: "Bearer {{ cloudflare_dns_api_token }}"
Content-Type: application/json
loop: "{{ cloudflare_dns_record_queries.results | subelements('json.result', skip_missing=True) }}"
loop_control:
label: "{{ item.0.item.name }} delete {{ item.1.type }}"
- name: Create desired DNS records
ansible.builtin.uri:
url: "{{ cloudflare_dns_api_base }}/zones/{{ cloudflare_dns_zone_id }}/dns_records"
method: POST
headers:
Authorization: "Bearer {{ cloudflare_dns_api_token }}"
Content-Type: application/json
body_format: raw
body: >-
{{
{
'type': item.item.type,
'name': item.item.name,
'content': item.item.content | default(item.item.value),
'ttl': (item.item.ttl | default(1) | int),
'proxied': (item.item.proxied | default(false) | bool)
} | to_json
}}
loop: "{{ cloudflare_dns_record_queries.results }}"
loop_control:
label: "{{ item.item.name }}"
- name: Show managed DNS records
ansible.builtin.debug:
msg: "{{ item.type }} {{ item.name }} -> {{ item.content | default(item.value) }} proxied={{ item.proxied | default(false) }}"
loop: "{{ cloudflare_dns_records }}"
loop_control:
label: "{{ item.name }}"