4 min read
Manage Vercel DNS records with Ansible
At Mint System we are using Ansible extensively to configure our infrastructure and services. From setting up new hosts up to deploying a customer specific customization for an application, all is managed by Ansible. Only one piece was missing. Until recently we could not update DNS records automatically.
We use Vercel to deploy static sites and manage DNS records. Their well documented API allows you to manage any Vercel resource. Using the Ansible uri
module, I have created a role that manages these ressources. Lets have a look.
This is the inventory template:
vercel_token: "{{ vault_vercel_token }}"
vercel_team_id: example-organization
vercel_dns:
- domain: example.com
records:
- { name: www, type: ALIAS, value: www.example.org, state: present }
- { name: '', type: A, value: 93.184.216.34, state: present }
You can configure domains and their DNS records.
This inventory is used by the vercel-dns
task. I will now step through the task file and explain how DNS records are updated.
roles/vercel/tasks/vercel-dns.yml
- name: Get all vercel domains
uri:
url: "{{ vercel_api_url }}/v4/domains?teamId={{ vercel_team_id }}"
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ vercel_token }}"
return_content: yes
register: vercel_domains
- name: Set domains exist fact
set_fact:
domains_exist: "{{ vercel_domains.json | json_query('domains[*].name') }}"
- name: Fail if domain is not managed by vercel
fail:
msg: Domain is not managed by vercel
when: item.domain not in domains_exist
loop: "{{ vercel_dns }}"
First all managed domains are retrieved from Vercel. The second task checks wether the domain that is in the inventory is also setup in Vercel. If this is not the case, the execution will fail.
- name: Get all vercel dns records
uri:
url: "{{ vercel_api_url }}/v4/domains/{{ item }}/records?teamId={{ vercel_team_id }}"
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ vercel_token }}"
return_content: yes
loop: "{{ domains_exist }}"
register: vercel_dns_records
- name: Set vercel dns simple fact
set_fact:
record: "{{ item.1.name }}.{{ item.0.item }}-{{ item.1.type }}"
with_subelements:
- "{{ vercel_dns_records.results }}"
- json.records
register: vercel_dns_simple
- name: Make a simple list
set_fact:
vercel_dns_simple: "{{ vercel_dns_simple.results | map(attribute='ansible_facts.record') | list }}"
- name: Ensure DNS entry exists
uri:
url: "{{ vercel_api_url }}/v2/domains/{{ item.0.domain }}/records?teamId={{ vercel_team_id }}"
method: POST
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ vercel_token }}"
body: "{{ item.1 | dict2items | rejectattr('key', 'equalto', 'state') | list | items2dict | to_json }}"
with_subelements:
- "{{ vercel_dns }}"
- records
when: ((item.1.name + "." + item.0.domain + "-" + item.1.type) not in vercel_dns_simple) and (item.1.state == 'present')
register: response
changed_when: response.status == 200
The next four tasks retrieve all DNS records from Vercel for each configured domain. In order to compare the inventory list and the Vercel list, the Vercel list is flattened. Ansible cannot compare objects easily, but comparing strings is fine. In the last step the task checks whether the DNS record exists and if not creates one.
- name: Set vercel dns absent fact
set_fact:
record: "{{ item.1.name }}.{{ item.0.domain }}-{{ item.1.type }}"
with_subelements:
- "{{ vercel_dns }}"
- records
when: item.1.state == 'absent'
register: vercel_dns_absent
- name: Make a simple list
set_fact:
vercel_dns_absent: "{{ vercel_dns_absent.results | selectattr('ansible_facts.record','defined') | map(attribute='ansible_facts.record') | list }}"
- name: Ensure DNS entries to be removed
uri:
url: "{{ vercel_api_url }}/v2/domains/{{ item.0.item }}/records/{{ item.1.id }}?teamId={{ vercel_team_id }}"
method: DELETE
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ vercel_token }}"
with_subelements:
- "{{ vercel_dns_records.results }}"
- json.records
when: (item.1.name + "." + item.0.item + "-" + item.1.type) in vercel_dns_absent
register: response
changed_when: response.status == 200
The last three tasks will remove a DNS record, if it has the status absent
. First a simple list is created that contains all records with the absent status. Then foreach Vercel record it will check if it is in the absent list. If so the task will remove the record from Vercel.
As expected the Ansible tasks are idempotent, they will return an ok if nothing has changed. Further it is ensured that DNS records which are configured in Vercel only are not deleted or updated by acccident.
To get the lastes version of the script, have a look here: https://github.com/Mint-System/Ansible-Playbooks/blob/master/roles/vercel/tasks/vercel-dns.yml.
Categories: Continuous DeliveryTags: ansible , dns , vercel
Edit this page
Show statistic for this page