• 6 Min. Lesezeit

Manage PiHole Custom DNS entries with Ansible (Update 2)

ansiblelinux

Hi everyone, the old ansible role does not work anymore with the latest version of PiHole. I will leave it, just in case, but below you can find the new role I am currently using.

I cannot take any credit for this playbook, unfortunately I cannot find the source anymore.

Here my current ansible role / playbook for the new version.

New Ansible role

Here the new folder structure for the ansible role.

Alright. And here is the file content. If you add additional PiHole servers, just copy the api-dns-configuration-pihole-01.yml file and replace the variable “api_url_pihole_server_01” with the new variable. Also adjust the vars file “main.yml” and add the new variable in there.

If you want to add new DNS entries, just edit the custom.list file.

custom.list

10.10.0.100 pihole-01.example.local
10.10.0.101 pihole-02.example.local
10.10.0.102 other-server.example.local

api-dns-configuration-pihole-01.yml

[code lang=“diff”] - name: Authenticate with Pi-hole API shell: | curl -X POST “{{ api_url_pihole_server_01 }}/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 custom.list and extract dns hosts set_fact: dns_hosts: “{{ lookup(‘file’, ‘../files/config/pihole/custom.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_docker_router }}/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_docker_router }}/auth” \ -H “accept: application/json” \ -H “content-type: application/json” \ -H “X-FTL-SID: {{ api_sid }}”

[/code]

api-dns-configuration-pihole-02.yml

[code lang=“diff”] - name: Authenticate with Pi-hole API shell: | curl -X POST “{{ api_url_pihole_server_02 }}/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 custom.list and extract dns hosts set_fact: dns_hosts: “{{ lookup(‘file’, ‘../files/config/pihole/custom.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_docker_router }}/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_docker_router }}/auth” \ -H “accept: application/json” \ -H “content-type: application/json” \ -H “X-FTL-SID: {{ api_sid }}”

[/code]

tasks - main.yml

[code lang=“diff”] - import_tasks: api-dns-configuration-pihole.yml when: inventory_hostname == “pihole” [/code]

vars - main.yml

[code lang=“diff”] api_url_pihole_server_01: “https://pihole-01.example.local/api" api_url_pihole_server_02: “https://pihole-02.example.local/api" webpassword: “<your-web-login-password” [/code]

Hope this helps.
Sorry that I cannot find the original source.

(Update 2) Anything below does not work anymore, with the latest version of PiHole.

Hi there,

Today, I’d like to share a simple way to manage custom DNS entries on multiple Pi-hole servers using Ansible. I’m currently running three Pi-hole servers as Docker containers - two in the cloud and one on-premises - to provide redundant DNS for my Netbird network.

However, having to update all three servers manually can get tedious very quickly.

So, I will be using Ansible to make this way more efficient.

The setup is straightforward: we’ll replace the “custom.list” file in the Pi-hole config folder.

I assume you have already set up SSH keys and hosts configuration for your servers.

Creating the Ansible role

For this, I’ll create a role, since I prefer the flexibility and I am more used to this kind of set up.

We create a folder and the required subfolders.

ansible :: ~ » cd Ansible/roles# Create foldersansible :: roles » mkdir -p ansible-role-pihole/tasks/copy-config ansible-role-pihole/files/config/pihole # List foldersansible :: roles » tree ansible-role-piholeansible-role-pihole├── files│   └── config│       └── pihole├── tasks│   ├── copy-config

Next, we create the ansible role yml file we will use to execute the playbooks.

ansible :: roles » vim ansible-role-pihole.yml--- - hosts: pihole   gather_facts: true   become: true   roles:    - ansible-role-pihole

Create a task to copy the “custom.list” file to the Pi-hole servers and place this task in the “tasks/copy-config” folder.

Update the path to match your Pi-hole configuration. In my case it would be "/etc/pihole/pihole”, but that’s not the default.

ansible :: roles » cd ansible-role-pihole/tasks/copy-configansible :: copy-config » vim copy-config-pihole-custom-dns.yml--- - name: pihole custom dns configuration    copy:      src: files/config/pihole/custom.list      dest: <path-to-pihole-folder>/custom.list      owner: root      mode: 0644      backup: yes

Create a “main.yml” file in the tasks folder “ansible-role-pihole”.

ansible :: roles » cd ansible-role-pihole/tasksansible :: tasks » vim main.yml- import_tasks: copy-config/copy-config-pihole-custom-dns.yml  tags: custom-dns

The last file we need, is the “custom.list”. Create it in the “files/config/pihole” folder. The content is a simple list of IP address and hostname.

ansible :: roles » cd ansible-role-pihole/files/config/pihole/ansible :: pihole » vim custom.list10.10.0.103 ipa.example.local10.10.0.108 file.exmaple.local

Alright. Just a quick look into the ansible hosts file.

ansible :: Ansible » vim hostsall:  children:    pihole:      hosts:        pihole-01:          ansible_host: <ip-address>        pihole-02:          ansible_host: <ip-address>        pihole-03:          ansible_host: <ip-address>

Let’s take another look at the file structure.

ansible :: Ansible » tree roles/ansible-role-piholeroles/ansible-role-pihole├── files│   └── config│       └── pihole│           └── custom.list├── tasks│   ├── copy-config│   │   └── copy-config-pihole-custom-dns.yml│   └── main.yml

Ok. Now execute the playbook.

ansible :: Ansible » ansible-playbook roles/ansible-role-pihole.yml -i hostsPLAY [pihole] ***********************************************************************************************************TASK [Gathering Facts] **************************************************************************************************ok: [pihole-01]ok: [pihole-02]ok: [pihole-03]TASK [ansible-role-pihole : pihole custom dns configuration] ************************************************************changed: [pihole-01]changed: [pihole-02]changed: [pihole-03]PLAY RECAP **************************************************************************************************************pihole-01              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0pihole-02              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0pihole-03              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

And that is it.

(Update)

I am going to restart the DNS resolver service after the custom.list has been replaced, since it takes a while until the resolver picks it up.

Add the following (marked in orange) to the “copy-config-pihole-custom-dns.yml” file.

--- - name: pihole custom dns configuration    copy:      src: files/config/pihole/custom.list      dest: <path-to-pihole-folder>/custom.list      owner: root      mode: 0644      backup: yes    register: pihole_custom_dns

Now create a new yml file and type in the following. This will execute the “pihole restartdns” command within the docker container. Make sure the container name fits your environment.

- name: Restart PiHole DNS Resolver  community.docker.docker_container_exec:    container: <container-name>    command: /usr/local/bin/pihole restartdns    chdir: /  when: pihole_custom_dns.changed

Once that’s done, add the task to the “main.yml” file.

- import_tasks: copy-config/copy-config-pihole-custom-dns.yml  tags: custom-dns- import_tasks: docker-exec.yml  tags: pihole-docker-exec

(Update)

Of course, we could manage much more than that. For instance, we could update the upstream DNS servers or modify specific settings like DNSSEC.

Thanks. Till next time.

Kommentare

Suche