How to keep separate custom DNS records on V6 - via Ansible

I was managing all my Pi-holes through Ansible, mainly for local DNS lists. The way I handled it was by replacing the /etc/pihole/custom.list file and restarting Pi-hole/dnsmasq, Simple and works perfectly.

Now, with version 6, this approach no longer works.

I know I can edit the TOML file, but my custom.list text file contains comments and other sections that I want to keep as they are.

What is the best way to restore the previous custom.list functionality without relaying on API ?
And in API doc, I cannot find any API endpoint to manage custom DNS records.

I tried copying the same file to /etc/dnsmasq.d/, but it failed.

That file has moved to /etc/pihole/hosts/custom.list, but it would be overwritten on interaction with its UI, as prominently disclaimed in the file's header section.

To keep your existing file, try moving and renaming it to /etc/pihole/hosts/ansible.list, then tell Pi-hole to use it by entering the following line into misc.dnsmasq_lines via All Settings | Miscellaneous:
addn-hosts=/etc/pihole/hosts/ansible.list

The drawback of this approach is that you won't see any UI for it, but you can verify that DNS records exist by querying your Pi-hole for them.

2 Likes

I tried the suggested and it didn't work here.

  1. Create initial ansible.list
  2. Change the ansible code to ensure ansible.list is copied over to etc-pihole/hosts/
  3. Confirmed the file is there and correct (with a test domain)
    image
  4. Restarted the pi-hole container to test, the test entry is not added.
  5. Tried removing the custom.list but it gets re-created
  6. Tried with 2-3 additional domains, Ansible transferred, file is updated and restarted the container.
  7. Entries not updated.

Any clue why it isn't working?

Edit: Just noticed the edit by @Bucking_Horn, it indeed works but it's not reflected in the UI.
Which isn't a big deal.
Is it safe to remove the other entries that do show up in the UI as they are now "duplicates"?

How did you verify that?

Please share the output of:

nslookup <test-entry> <pi.ho.le.ip>

I saw your post edit after I created my reply. It is working; just doesn't reflect in the UI :slight_smile:
Thank you for the suggestion!

Restarting the entire container shouldn't be required, at least not when editing pihole.toml.
Pi-hole v6 will pick up changes immediately.
I'm not entirely sure about custom host files, but you could give that a try.

EDIT:

Certainly.
If they are duplicates, I'd even recommend to remove them.
I'd probably only create a made-up hostname entry like 'look-for-addn-hosts-in-ansible-list', just to remind me. :wink:

1 Like

Yes, I mentioned in my edit that I ran: docker exec -t CONTAINER_NAME pihole reloaddns afterwards and it picked up the changes and could query the domain succesfully.

Also tried without reloaddns. This worked! So no need to reloaddns :white_check_mark:

1 Like

I did find it and will change to the API method.
Documentation url: Pi-hole API documentation

POST/PATCH:

{
config: {
dns: {
hosts: [string]
}
}
}
1 Like

I sure hope we can update the DNS records via the REST API. I was looking forwards to using Ansible with the API for custom host management.... It has got to be there, but probably undocumented I would expect?

I was trying to use the toml file, but every time I make updates to it, they get deleted (with or without a comment after the edit) so I cannot add the this way with Ansible.

I should have read your comment more closely. This should be possible with Ansible's URI module:

@Mr_Flibble @semiraue

I have created a playbook in a role to update the entries and have them also shown in the UI.
The old format of entries stays the same so you can use existing entries

Role structure:

files/local-dns.list
Example content:

10.0.0.1 sub.domain.ltd
10.0.0.2 sub.domain.ltd
10.0.0.3 sub.domain.ltd
10.0.0.4 sub.domain.ltd
10.0.0.5 sub.domain.ltd
10.0.0.6 sub.domain.ltd

vars/main.yml

webpassword: "pi.hole.web.password"
api_url: "pi.hole.domain.tld/api"

The /api needs to be added at the end of the url.

tasks/main.yml

- name: Authenticate with Pi-hole API
  shell: |
    curl -X POST "{{ api_url }}/auth" \
      -H "accept: application/json" \
      -H "content-type: application/json" \
      -d '{"password":"{{ webpassword }}"}'
  register: api_auth_response

- name: Show API auth response
  debug:
    var: api_auth_response

- name: Parse API auth response
  set_fact:
    api_sid: "{{ api_auth_response.stdout | from_json | json_query('session.sid') }}"
    api_csrf: "{{ api_auth_response.stdout | from_json | json_query('session.csrf') }}"

- name: Show extracted sid and csrf for debugging
  debug:
    msg:
      - "sid: {{ api_sid }}"
      - "csrf: {{ api_csrf }}"

- name: Read local-dns.list and extract dns hosts
  set_fact:
    dns_hosts: "{{ lookup('file', '../files/local-dns.list') }}"

- name: Prepare dns hosts for the payload
  set_fact:
    dns_hosts_converted: "{{ dns_hosts.split('\n') | map('regex_replace', '^(.*)$', '\"\\1\"') | join(',\n') }}"

- name: Ouput dns hosts with quotes
  debug:
    var: dns_hosts_converted

- name: Send the updated DNS hosts to the API
  shell: |
    curl -X PATCH "{{ api_url }}/config" \
      -H "accept: application/json" \
      -H "content-type: application/json" \
      -H "X-FTL-SID: {{ api_sid }}" \
      -d '{
        "config": {
          "dns": {
            "hosts": [
              {{ dns_hosts_converted }}
            ]
          }
        }
      }'

- name: Delete the sid to logout
  shell: |
    curl -X DELETE "{{ api_url }}/auth" \
      -H "accept: application/json" \
      -H "content-type: application/json" \
      -H "X-FTL-SID: {{ api_sid }}"

Edits:

I'm using shell / curl instead of the buildin uri module to ensure I don't need to worry about modules. This is my preference, you can change main.yml and use uri instead of curl.

1 Like

Damn, I like it. You beat me to the punch. I will probably change to the URI module for my use - that's just my personal preference over curl, as it is more idempotent. Though, ironically, I have much more experience with curl.

1 Like

I'm using pihole-FTL for this in my playbook:

defaults/main.yml:

---
# examples. Please replace with your data

pihole_dns_records:
  - 127.0.0.1 pi.hole
pihole_dns_cname_records:
  - pihole.lan,pi.hole

tasks/main.yml:

---
# tasks file for pihole

- name: Update dns.hosts
  ansible.builtin.command:
    cmd: pihole-FTL --config dns.hosts '{{ pihole_dns_records | to_json }}'
  register: ftl_dns_hosts_response
  changed_when: ftl_dns_hosts_response.rc != 0

- name: Update dns.cnameRecords
  ansible.builtin.command:
    cmd: pihole-FTL --config dns.cnameRecords '{{ pihole_dns_cname_records | to_json }}'
  register: ftl_dns_cname_records_response
  changed_when: ftl_dns_cname_records_response.rc != 0

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.