how to make zone config file for bind9 in ansible with a template and read variables from a csv file?

230 views Asked by At

I want to use ansible to generate zone configuration files separately for bind9 dns server with 3 zones by reading data from a csv file

for example, in csv file we have:

hostname ,network1      ,network2     ,network3

server1  ,192.168.101.1 ,172.16.101.1 ,10.185.101.1

server2  ,192.168.101.2 ,172.16.101.2 ,10.185.101.2

<etc...>

I want at the end ansible to generate 3 files named like network1.zone.conf, network2.zone.conf, network3.zone.conf. I have no idea how to do that.

I tried:

- name: read from csv file
  read_csv:
    unique: false
    path: "{{ CSV_File_Path }}"
  delegate_to: localhost
  register: OUTPUT_CSV

- name: print output
  template:
    src: zones.rfc.j2 # template path
    dest: "/path/to/create/files/{{item}}.txt"
  delegate to: dns_server
  loop: "{{ OUTPUT_CSV.list }}"
  loop_control:
    extended: true
    label: "{{ ansible_loop.index0 }}"

This made a bunch of files with whole list in the name of the files.

1

There are 1 answers

0
Zeitounator On BEST ANSWER

Demo file structure:

$ tree
.
├── inventory.yml
├── playbook.yml
├── templates
│   └── zones.rfc.j2
└── zones.csv

1 directory, 4 files

I made a fake inventory.yml for the occasion which will play everything on localhost:

---
dnsservers:
  hosts:
    dnsserver1:
      ansible_connection: local
      ansible_python_interpreter: /usr/bin/python3

I fixed your zones.csv so that it does not contain parasite space we later have to trim:

hostname,network1,network2,network3
server1,192.168.101.1,172.16.101.1,10.185.101.1
server2,192.168.101.2,172.16.101.2,10.185.101.2

Since it is not part of your question, I created what should more or less look like your template in templates/zones.rfc.j2

$TTL 604800 ; 1 week
{{ network }}.local     IN SOA  ns0.local. ns1.local. (
                11278      ; serial
                10800      ; refresh (3 hours)
                3600       ; retry (1 hour)
                604800     ; expire (1 week)
                38400      ; minimum (10 hours 40 minutes)
                )
            NS  ns0.local.
            NS  ns1.local.
$ORIGIN {{ network }}.local
$TTL 3600   ; 1 hour
{%  for server in server_list %}
{{ server.hostname }} A {{ server[network] }}
{% endfor %}

We can now put all this together with a playbook. The difficulty here is that reading the csv gives a list element for each line in your csv while we want to create a file for each networks given as columns.

If we read the csv into output.csv then we can get output_csv.list[0] which is a dictionary:

{
  "hostname": "server1",
  "network1": "192.168.101.1",
  "network2": "172.16.101.1",
  "network3": "10.185.101.1"
}

If we extract the key names and remove the hostname key, we have our list of networks. Hence output_csv.list[0].keys() | difference(['hosname']) gives:

[
  "network1",
  "network2",
  "network3"
]

The below playbook uses that technique with an alias in the task (i.e. output_csv.list => server_list) to match the variables used to make the template more readable.

- name: Make bind zone files from csv
  hosts: dnsservers
  gather_facts: false

  vars:
    csv_file_path: zones.csv
    zone_output_dir: /tmp/test_bind_zones/{{ inventory_hostname }}/

  tasks:
    - name: Read zone info from csv file
      community.general.read_csv:
        unique: false
        path: "{{ csv_file_path }}"
      delegate_to: localhost
      run_once: true
      register: output_csv

    - name: Make sure our output dir exists
      ansible.builtin.file:
        path: "{{ zone_output_dir }}"
        state: directory

    - name: Push result files in output dir
      vars:
        server_list: "{{ output_csv.list }}"
      ansible.builtin.template:
        src: zones.rfc.j2
        dest: "{{ zone_output_dir }}/{{ network }}.zone.conf"
      loop: "{{ server_list[0].keys() | difference(['hostname']) }}"
      loop_control:
        loop_var: network

Running the playbook gives:

$ ansible-playbook -i inventory.yml playbook.yml 

PLAY [Make bind zone files from csv] ****************************************************************************************************************************************************************************************

TASK [Read zone info from csv file] *****************************************************************************************************************************************************************************************
ok: [dnsserver1 -> localhost]

TASK [Make sure our output dir exists] **************************************************************************************************************************************************************************************
changed: [dnsserver1]

TASK [Push result files in output dir] **************************************************************************************************************************************************************************************
changed: [dnsserver1] => (item=network1)
changed: [dnsserver1] => (item=network2)
changed: [dnsserver1] => (item=network3)

PLAY RECAP ******************************************************************************************************************************************************************************************************************
dnsserver1                 : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

We can check all files have been created in the target dir:

$ ls -l /tmp/test_bind_zones/dnsserver1/
total 15
-rw-r--r-- 1 user users 369 nov 19 15:58 network1.zone.conf
-rw-r--r-- 1 user users 367 nov 19 15:58 network2.zone.conf
-rw-r--r-- 1 user users 367 nov 19 15:58 network3.zone.conf

I'll only show result from the first one here as a proof, you can check the others testing on your own environment:

$ cat /tmp/test_bind_zones/dnsserver1/network1.zone.conf 
$TTL 604800     ; 1 week
network1.local          IN SOA  ns0.local. ns1.local. (
                                11278      ; serial
                                10800      ; refresh (3 hours)
                                3600       ; retry (1 hour)
                                604800     ; expire (1 week)
                                38400      ; minimum (10 hours 40 minutes)
                                )
                        NS      ns0.local.
                        NS      ns1.local.
$ORIGIN network1.local
$TTL 3600       ; 1 hour
server1 A 192.168.101.1
server2 A 192.168.101.2