Files
valid.nsupdate_zone/docs/QUICK_START.md
Daniel Akulenok 9dc42d99c1 Add ignore_ns_records flag and support for extra keys in zones/records parameters
- Add ignore_ns_records flag (default: true) to automatically ignore NS records
- Add options_ignore_list to zones and records to allow extra keys (e.g., comment, type)
- Update documentation to reflect NS records are now ignored by default
- Add changelog entry for v1.3.3 release
2026-01-29 21:40:41 +01:00

6.0 KiB

Quick Start Guide - nsupdate_zone Module

Installation

The module is part of the community.general collection:

ansible-galaxy collection install community.general

Install Python dependencies:

pip install dnspython

Minimal Example

- name: Update DNS zone
  community.general.nsupdate_zone:
    key_name: "nsupdate"
    key_secret: "your-tsig-key-here=="
    zones:
      - name: example.com
        dns_server: ns1.example.com
        records:
          - record: 'example.com.'
            type: A
            value: 192.168.1.1
          
          - record: www
            type: A
            value: 192.168.1.10

Note: By default, SOA, NS, and DNSSEC records are ignored, and record validation is enabled. Tip: Run with -v flag to see detailed per-record actions.

DNS Server Setup (BIND Example)

  1. Generate TSIG key:
tsig-keygen -a hmac-sha256 nsupdate
  1. Configure BIND (/etc/named.conf):
key "nsupdate" {
    algorithm hmac-sha256;
    secret "generated-secret-here==";
};

zone "example.com" {
    type master;
    file "/var/named/example.com.zone";
    allow-update { key nsupdate; };
    allow-transfer { key nsupdate; };
};
  1. Reload BIND:
sudo systemctl reload named

First Playbook

Create update-dns.yml:

---
- name: Manage DNS zones
  hosts: localhost
  gather_facts: false
  
  tasks:
    - name: Update example.com zone
      community.general.nsupdate_zone:
        key_name: "nsupdate"
        key_secret: "{{ lookup('env', 'DNS_KEY') }}"
        key_algorithm: hmac-sha256
        protocol: tcp
        zones:
          - name: example.com
            dns_server: ns1.example.com
            records:
              # Zone apex
              - record: 'example.com.'
                type: A
                value: 192.168.1.1
                ttl: 3600
              
              # Web server
              - record: www
                type: A
                value:
                  - 192.168.1.10
                  - 192.168.1.11
                ttl: 300
              
              # Email
              - record: 'example.com.'
                type: MX
                value:
                  - "10 mail.example.com."
              
              - record: mail
                type: A
                value: 192.168.1.20
      register: result
    
    - name: Show what changed
      debug:
        msg: "Zone {{ item.zone }}: {{ item.changes.adds }} adds, {{ item.changes.deletes }} deletes, {{ item.changes.updates }} updates"
      loop: "{{ result.results }}"

Run it:

export DNS_KEY="your-tsig-key-here=="
ansible-playbook update-dns.yml

Verify It Works

Check mode (dry run):

ansible-playbook update-dns.yml --check

Verify DNS records:

dig @ns1.example.com example.com A
dig @ns1.example.com www.example.com A
dig @ns1.example.com example.com MX

Common Use Cases

1. Load Zone from Variable File

zones/example.com.yml:

---
- record: 'example.com.'
  type: A
  value: 192.168.1.1

- record: www
  type: A
  value: 192.168.1.10

Playbook:

- name: Load and apply zone
  hosts: localhost
  vars_files:
    - zones/example.com.yml
  
  tasks:
    - name: Update zone
      community.general.nsupdate_zone:
        key_name: "nsupdate"
        key_secret: "{{ vault_dns_key }}"
        zones:
          - name: example.com
            dns_server: ns1.example.com
            records: "{{ zones }}"

2. Ignore Dynamic Records and Use Global Server

- name: Update zone (ignore ACME challenges, use global server)
  community.general.nsupdate_zone:
    key_name: "nsupdate"
    key_secret: "{{ vault_dns_key }}"
    dns_server: ns1.dns.com  # Global server for all zones
    # SOA and DNSSEC records are ignored by default
    ignore_record_patterns:
      - '^_acme-challenge\..*'
    zones:
      - name: example.com
        records: "{{ static_records }}"

3. Multiple Zones with Shared Server

- name: Update all zones
  community.general.nsupdate_zone:
    key_name: "nsupdate"
    key_secret: "{{ vault_dns_key }}"
    dns_server: ns1.dns.com  # Shared server for all zones
    zones:
      - name: example.com
        records: "{{ example_com_records }}"
      
      - name: example.org
        records: "{{ example_org_records }}"

Troubleshooting

"AXFR failed: connection timeout"

  • Check firewall allows port 53
  • Verify allow-transfer in BIND config
  • Test manually: dig @ns1.example.com example.com AXFR

"UPDATE failed: REFUSED"

  • Check allow-update in BIND config
  • Verify TSIG key matches
  • Check zone name is correct

"TSIG key error"

  • Verify key_secret is base64-encoded
  • Check key_algorithm matches server config
  • Ensure key_name matches server key name

"CNAME conflict"

  • Cannot have CNAME with other record types at same name
  • Remove conflicting records from YAML
  • Or use different subdomain

Best Practices

  1. Use ansible-vault for keys:

    ansible-vault encrypt_string 'your-key' --name dns_key
    
  2. Use TCP protocol:

    protocol: tcp  # More reliable for large zones
    
  3. Leverage defaults:

    # SOA and DNSSEC records are ignored by default
    # Record validation is enabled by default
    # Just add patterns for dynamic records
    ignore_record_patterns:
      - '^_acme-challenge\..*'
    
  4. Use verbose mode for visibility:

    verbose: true  # See Added, Removed, Changed, Skipped for each record
    
  5. Test with check and diff mode:

    ansible-playbook playbook.yml --check --diff
    
  6. Use global dns_server:

    dns_server: ns1.dns.com  # Applies to all zones without dns_server
    

Next Steps

  • Read full documentation: docs/nsupdate_zone_guide.md
  • See examples: examples/nsupdate_zone_example.yml
  • Check sample zone: examples/sample_zone_format.yml

Support

For issues and questions: