--- - name: Update Cloudflare DNS for observability.svc.plus hosts: localhost connection: local gather_facts: false vars: cloudflare_zone_name: svc.plus cloudflare_api_base: https://api.cloudflare.com/client/v4 observability_domain: observability.svc.plus observability_dns_target: us-xhttp.svc.plus observability_dns_type: CNAME observability_dns_ttl: 1 observability_dns_proxied: false dns_wait_retries: 30 dns_wait_delay: 10 tasks: - name: Validate Cloudflare token is present in environment ansible.builtin.assert: that: - lookup('ansible.builtin.env', 'CLOUDFLARE_API_TOKEN') | length > 0 fail_msg: "CLOUDFLARE_API_TOKEN must be exported before running this playbook." - name: Resolve Cloudflare zone id ansible.builtin.uri: url: "{{ cloudflare_api_base }}/zones?name={{ cloudflare_zone_name }}" method: GET headers: Authorization: "Bearer {{ lookup('ansible.builtin.env', 'CLOUDFLARE_API_TOKEN') }}" Content-Type: application/json return_content: true register: cloudflare_zone_lookup - name: Validate zone lookup result ansible.builtin.assert: that: - cloudflare_zone_lookup.json.success - cloudflare_zone_lookup.json.result | length > 0 fail_msg: "Unable to resolve Cloudflare zone id for {{ cloudflare_zone_name }}." - name: Set Cloudflare zone id ansible.builtin.set_fact: cloudflare_zone_id: "{{ cloudflare_zone_lookup.json.result[0].id }}" - name: Query existing observability DNS records ansible.builtin.uri: url: "{{ cloudflare_api_base }}/zones/{{ cloudflare_zone_id }}/dns_records?name={{ observability_domain }}" method: GET headers: Authorization: "Bearer {{ lookup('ansible.builtin.env', 'CLOUDFLARE_API_TOKEN') }}" Content-Type: application/json return_content: true register: observability_dns_records - name: Remove conflicting observability DNS records with different type ansible.builtin.uri: url: "{{ cloudflare_api_base }}/zones/{{ cloudflare_zone_id }}/dns_records/{{ item.id }}" method: DELETE headers: Authorization: "Bearer {{ lookup('ansible.builtin.env', 'CLOUDFLARE_API_TOKEN') }}" Content-Type: application/json loop: "{{ observability_dns_records.json.result | default([]) }}" loop_control: label: "{{ item.type }} {{ item.name }}" when: item.type != observability_dns_type - name: Create observability DNS record when missing ansible.builtin.uri: url: "{{ cloudflare_api_base }}/zones/{{ cloudflare_zone_id }}/dns_records" method: POST headers: Authorization: "Bearer {{ lookup('ansible.builtin.env', 'CLOUDFLARE_API_TOKEN') }}" Content-Type: application/json body_format: raw body: >- {{ { 'type': observability_dns_type, 'name': observability_domain, 'content': observability_dns_target, 'ttl': (observability_dns_ttl | int), 'proxied': (observability_dns_proxied | bool) } | to_json }} when: (observability_dns_records.json.result | selectattr('type', 'equalto', observability_dns_type) | list | length) == 0 - name: Update observability DNS record when target changes ansible.builtin.uri: url: "{{ cloudflare_api_base }}/zones/{{ cloudflare_zone_id }}/dns_records/{{ (observability_dns_records.json.result | selectattr('type', 'equalto', observability_dns_type) | list | first).id }}" method: PUT headers: Authorization: "Bearer {{ lookup('ansible.builtin.env', 'CLOUDFLARE_API_TOKEN') }}" Content-Type: application/json body_format: raw body: >- {{ { 'type': observability_dns_type, 'name': observability_domain, 'content': observability_dns_target, 'ttl': (observability_dns_ttl | int), 'proxied': (observability_dns_proxied | bool) } | to_json }} when: - (observability_dns_records.json.result | selectattr('type', 'equalto', observability_dns_type) | list | length) > 0 - > ((observability_dns_records.json.result | selectattr('type', 'equalto', observability_dns_type) | list | first).content != observability_dns_target) or (((observability_dns_records.json.result | selectattr('type', 'equalto', observability_dns_type) | list | first).proxied | default(false)) != observability_dns_proxied) - name: Wait for public DNS to expose observability CNAME ansible.builtin.uri: url: "https://cloudflare-dns.com/dns-query?name={{ observability_domain }}&type=CNAME" method: GET headers: Accept: application/dns-json return_content: true register: observability_dns_public until: - observability_dns_public.status == 200 - > ( observability_dns_public.json.Status if (observability_dns_public.json is defined) else ((observability_dns_public.content | from_json).Status | default(1)) ) == 0 - > ( observability_dns_public.json.Answer if (observability_dns_public.json is defined) else ((observability_dns_public.content | from_json).Answer | default([])) ) | selectattr('data', 'equalto', observability_dns_target ~ '.') | list | length > 0 retries: "{{ dns_wait_retries }}" delay: "{{ dns_wait_delay }}" - name: Show effective observability DNS target ansible.builtin.debug: msg: "{{ observability_domain }} -> {{ observability_dns_target }} proxied={{ observability_dns_proxied }}" - import_playbook: infra.yml vars: infra_domain: observability.svc.plus infra_portal: home: { domain: observability.svc.plus } caddy_enabled: true nginx_enabled: false