140 lines
5.8 KiB
YAML
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 }}"
|