v1.11.0 (24-08-2026)
NEW FEATURES:
- Provision autoprimaries (
pdns_provision_autoprimaries), zones and catalog zones (pdns_provision_zones), records (pdns_provision_records), zone metadata (pdns_provision_zone_metadata), DNSSEC keys (pdns_provision_cryptokeys), TSIG keys (pdns_provision_tsigkeys), view membership (pdns_provision_views) and networks mapped to a view (pdns_provision_networks) through the REST API. Every entry is submitted as written, with only the few keys that build the request URL read by the role and removed from the body, so any field the API accepts can be given whether or not the role knows about it. Provisioning adds and removes nothing: an entry that is already there is left alone, and so is anything on the server that no list mentions (#276) - Add
pdns_provision_recordsfor the records of a zone, one entry per RRset, withchangetypedefaulting toREPLACEandDELETEremoving an RRset. Zone and record lifecycles are separate resources with separate endpoints, so they are declared separately: records inside apdns_provision_zonesentry only apply when that zone is created. Each entry is compared against the zone before anything is written, which keeps a repeated converge atchanged=0and leaves the SOA serial alone - the serial moves on every PATCH the API receives, including one that changes nothing (#276) - Add
pdns_provision_pdnsutils, a list of commands run throughpdnsutil, for what the REST API cannot do. Each entry takesargsand an optionalunlessprobe that makes it safe to repeat, since pdnsutil subcommands are not idempotent by themselves. The commands run aspdns_userrather than root: pdnsutil writes the backend's own files, and root-owned ones leave the unprivileged server unable to write them, which running the role again does not repair. The invocation carries--config-namefor an instance, so it cannot silently act on the default one (#276) - Add
pdns_provision_update_existingto write the declared attributes of a zone entry onto a zone that already exists, andpdns_provision_pruneto delete autoprimaries, TSIG keys and view members that the lists do not declare and to unmap an undeclared network. Both default to false. Zones and DNSSEC keys are never deleted whateverpdns_provision_pruneis set to (#276) - Add
pdns_provision_ignore_missing_zone, default true, so that metadata and DNSSEC keys declared for a zone that does not exist are skipped on the API's 404 instead of failing the play (#276) - Add
pdns_provision_run_onceto provision against the first host of the play instead of once per host, for a cluster whose hosts share one database. Off by default, because with a per-host backend - lmdb, bind, or a local gsqlite3 file - running once would leave every host but the first unprovisioned (#276)
IMPROVEMENTS:
- Normalize the values the API canonicalizes before comparing them, so a repeated converge settles. A TSIG key declared with a trailing dot is stored without one, and a zone
kindofnativeis stored asNative; both used to look different on every run, and the TSIG mismatch repeated the create until the API answered 409 and failed the play (#276)
DEPRECATIONS:
pdns_autoprimariesis deprecated in favour ofpdns_provision_autoprimaries, the name the rest of the provisioning family uses, and will be removed in the next release.pdns_provision_autoprimariesdefaults topdns_autoprimaries, so a playbook that still sets the old name keeps working, and setting the new name takes precedence (#276)
BREAKING CHANGES:
- Autoprimaries are no longer removed unless
pdns_provision_pruneis enabled. Provisioning previously deleted any autoprimary thatpdns_autoprimariesdid not list, on every converge; it now follows the same rule as every other provisioned resource and removes nothing by default. Setpdns_provision_prune: trueto keep the previous behaviour (#276)
IMPROVEMENTS:
- Skip provisioning under
--checkinstead of failing on it.ansible.builtin.urihas no check mode and aborts the play, and there is nothing to simulate, since provisioning talks to a running instance (#276) - Report a provisioning request that ran as changed.
ansible.builtin.urireportschanged: falseeven for a 201, so a converge that created zones and keys used to claim it had changed nothing (#276) - Submit an autoprimary entry as written, rather than rebuilding the body from
ip,nameserverandaccount, so the remaining fields of the API are reachable.accountstill defaults to an empty string, which the API requires (#276) - Split the provisioning tasks into one file per resource, included from
tasks/provision.yml(#276) - Cover provisioning in the same run that changes the API key in the
defaultscenario's side effect. The key is written to the configuration file, so the instance serves the old one until the restart the role flushes before provisioning; without that flush every request is answered 401 (#276) - Document provisioning in the README (#276)
- Read facts through
ansible_factsinstead of the injected top-levelansible_*variables. ansible-core deprecated that injection and removes it in 2.24, after which a role readingansible_distributionwould break. The Molecule configuration setsinject_facts_as_vars: false, so a missed reference fails a test run instead of surfacing on a future ansible-core (#274) - Install
python3-pymysqlinstead ofpython3-mysqldbon Debian. The MySQL modules warn that support of the MySQLdb connector is deprecated, they stop testing it in collection version 4.0.0 and remove it in 5.0.0. Enterprise Linux and Arch Linux already used PyMySQL, so the three families now agree (#274) - Cap every collection in
requirements.yml. A collection that raises itsrequires_ansiblein a new major would otherwise break the ansible-core 2.16 leg on the day it is published, without a change in this repository (#274) - Manage the service with
ansible.builtin.systemd_serviceinstead ofansible.builtin.systemd, matching the module name used by the other PowerDNS roles (#274) - Declare Ubuntu 26.04 in the Galaxy metadata (#274)
- Rework apt and dnf repo file creation to stop using version suffixed file names which are not cleaned up on version changes (#265, @l00d3r)
- Remove version suffixed apt and dnf repo files (#265, @l00d3r)
- Document the check mode support (converged hosts only) and the package/service state variables in the README (#271)
- Document the handler behaviour and the multi-instance usage in the README (#272)
NEW FEATURES:
- Add
pdns_mysql_flavor,mysqlormariadb, to choose which collection creates the database and the user of thegmysqlbackend:ansible.mysqloransible.mariadb. The defaultmysqlkeeps the current behaviour for every existing user, becauseansible.mysqlstill manages MariaDB, while warning that its support ends in 6.0.0. A MariaDB server can be moved to the collection that keeps supporting it by settingmariadb; nothing else about the backend changes (#274) - Add the
pdns_auth_powerdns_repo_51repository preset for the '5.1.x' release series (#274) - Add support for provisioning autoprimaries through the PowerDNS API (#265, @NocDerEchte)
- Add
pdns_flush_handlersto run the notified handlers at the end of the role instead of at the end of the play, which is required when the role runs more than once in a play (#272) - Add the
multi-instanceMolecule scenario, which configures two instances in a single play (#272)
BUG FIXES:
- Skip the restart under
--skip-tags service. Ansible filters tasks by tag but not handlers, so a run that deliberately left the service alone still restarted it on a configuration change - and restarting an inactive unit starts it. The handler readsansible_skip_tagsand still reloads the units, so a--tags configrun is unaffected (#275) - Reload the systemd units in the same task that restarts the service. A restart can no longer run against a unit systemd has not read, and a host left with a drop-in systemd never loaded is repaired by the next change instead of restarting onto the stale unit (#275)
- Reload the systemd units in the service task when this run changed the drop-in. Handlers flush at the end of the play, so a service that was not running yet was started from the unit systemd had loaded before the run and kept the previous settings until the handler restarted it (#275)
- Remove
/etc/systemd/system/<service name>.service.d/override.confwhenpdns_service_overridesis empty, and restart the service. The file used to stay on disk, so emptying the variable kept the previous overrides - including theUserandGroupof the default value - applied forever. Other drop-ins in that directory are left alone (#275) - Remove the drop-in override directory when
pdns_package_state: absent, so a later reinstall does not inherit the overrides of the previous installation. The unit it belongs to is being removed, so the whole directory goes; while the package stays installed only the file this role writes is removed (#275) - Tag the tasks inside
inspect.yml,selinux.yml,database-*.ymlandprovision.yml, and tag theSet fact for repo nametask withinstallandrepository. A dynamicinclude_tasksdoes not pass its tags to the tasks it includes, so--tags installand--tags repositoryfailed with'pdns_auth_repo_regex' is undefined, and--tags backendand--tags selinuxsilently did nothing (#271) - Add
check_mode: falseto the read-only schema location probes of all three SQL backends and to the MySQL empty-database probe, so--checkno longer reasons about schema state from empty registered results (#271) - Flush the handlers before provisioning when
pdns_provisionis enabled. Provisioning calls the API of the running server with the configured address, port and API key, so rotating the API key or changing the webserver port failed with401 Unauthorizedor a refused connection while the pending restart was still queued for the end of the play (#272) - Read the service name and state from facts published per role invocation in the restart handlers. Ansible shares handlers between invocations of the same role and resolves role parameters to the last invocation, so a play with more than one instance restarted the wrong service. Correct restarts need
pdns_flush_handlers: trueas well (#272) - Replace
community.mysql.mysql_dbandcommunity.mysql.mysql_userwithansible.mysql.mysql_dbandansible.mysql.mysql_user.community.mysql5.x redirects both modules and removes them in 6.0.0, which also madeansible-lintfail onfqcn[canonical](#271)
BREAKING CHANGES:
- Require ansible-core 2.16 or later (
min_ansible_version2.15 -> 2.16) and drop the 2.15 CI leg.ansible.mysqldeclaresrequires_ansible: '>=2.16.0'(#271) - Manage Enterprise Linux 8 targets with ansible-core 2.16 only. Their system Python is 3.6, which ansible-core 2.20 modules cannot run (#274)
REMOVED FEATURES:
- Remove
vars/Ubuntu-20.yml. Ubuntu 20.04 hosts now fall through tovars/Debian.yml, which usespython3-mysqldbinstead ofpython3-pymysqlfor the MySQL modules. Ubuntu 20.04 itself stays supported and tested against the 4.9 series, the only one upstream publishesfocalpackages for, with ansible-core 2.16 because of its Python 3.8 (#274) - Stop testing the
auth-masterrepository and the 4.8 series, and test the three most recent release series instead. Thepdns_auth_powerdns_repo_masterandpdns_auth_powerdns_repo_48presets are unchanged and still usable (#274) - Remove the per-backend tags
mysql,pgsql,sqliteandlmdb, and renamedbtobackend. The role tag vocabulary is nowrepository,install,config,service,backendandselinux. The removed tags never selected the backend tasks anyway (#271)