Skip to content

feat(win_dns_zone): add support for custom DNS application directory partitions#908

Open
stevefulme1 wants to merge 2 commits into
ansible-collections:mainfrom
stevefulme1:feat/win-dns-zone-directory-partition
Open

feat(win_dns_zone): add support for custom DNS application directory partitions#908
stevefulme1 wants to merge 2 commits into
ansible-collections:mainfrom
stevefulme1:feat/win-dns-zone-directory-partition

Conversation

@stevefulme1

Copy link
Copy Markdown
Collaborator

Summary

Extends win_dns_zone to support custom DNS application directory partitions for AD-integrated zones, as requested in #901.

Currently, the replication parameter only accepts forest, domain, legacy, and none. This PR extends it to also accept a distinguished name (DN) representing a DNS application directory partition (e.g., DC=AppDnsPartition-East,DC=contoso,DC=com), enabling conditional forwarders and other zone types to be scoped to custom partitions for fine-grained replication control.

Changes

plugins/modules/win_dns_zone.ps1:

  • Removed choices constraint from replication argument spec (now accepts any string)
  • Added detection logic: values not in forest/domain/legacy/none are treated as custom partition DNs
  • Routes custom partitions to -DirectoryPartitionName on Add-DnsServer*Zone and Set-DnsServer*Zone cmdlets
  • Added DirectoryPartitionName to zone comparison for idempotency
  • Added directory_partition to Get-DnsZoneObject output
  • Validates that custom partitions cannot be used with secondary (file-backed) zones
  • Handles create and update paths for primary, stub, and forwarder zone types

plugins/modules/win_dns_zone.py:

  • Updated replication parameter documentation — removed choices, added DN description and examples
  • Added new example: conditional forwarder in a custom application partition
  • Updated RETURN block to include directory_partition field

Backward Compatibility

Fully backward compatible:

  • Existing forest, domain, legacy, none values work exactly as before
  • Custom partition behavior only activates when the value doesn't match any standard option
  • No parameter name changes

Example Usage

- name: Ensure conditional forwarder in custom application partition
  ansible.windows.win_dns_zone:
    name: partner.contoso.com
    type: forwarder
    replication: \"DC=AppDnsPartition-East,DC=contoso,DC=com\"
    dns_servers:
      - 10.10.1.10
      - 10.10.1.11

Test Plan

  • Verify standard replication values (forest, domain, legacy, none) still work unchanged
  • Create a conditional forwarder with a custom directory partition DN
  • Verify idempotency — re-running with the same DN produces no changes
  • Verify update path — changing from domain to a custom partition DN
  • Verify secondary type rejects custom partition with a clear error
  • Verify check_mode correctly simulates the change
  • Verify zone deletion still works for zones in custom partitions

Closes #901

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Docs Build 📝

Thank you for contribution!✨

The docsite for this PR is available for download as an artifact from this run:
https://github.com/ansible-collections/ansible.windows/actions/runs/27760098894

You can compare to the docs for the main branch here:
https://ansible-collections.github.io/ansible.windows/branch/main

File changes:

  • M collections/ansible/windows/win_dns_zone_module.html
Click to see the diff comparison.

NOTE: only file modifications are shown here. New and deleted files are excluded.
See the file list and check the published docs to see those files.

diff --git a/home/runner/work/ansible.windows/ansible.windows/docsbuild/base/collections/ansible/windows/win_dns_zone_module.html b/home/runner/work/ansible.windows/ansible.windows/docsbuild/head/collections/ansible/windows/win_dns_zone_module.html
index 46e643c..6e0ce55 100644
--- a/home/runner/work/ansible.windows/ansible.windows/docsbuild/base/collections/ansible/windows/win_dns_zone_module.html
+++ b/home/runner/work/ansible.windows/ansible.windows/docsbuild/head/collections/ansible/windows/win_dns_zone_module.html
@@ -175,6 +175,16 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 </thead>
 <tbody>
 <tr class="row-even"><td><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-directory_partition"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-directory-partition"><strong>directory_partition</strong></p>
+<a class="ansibleOptionLink" href="#parameter-directory_partition" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-cell"><p>Specifies the name of an existing custom Active Directory application directory partition where the zone should be stored.</p>
+<p>When specified, the zone will use a custom replication scope instead of the standard domain or forest partitions.</p>
+<p>This is mutually exclusive with <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-ansible-windows-win-dns-zone-module-parameter-replication"><span class="std std-ref"><span class="pre">replication</span></span></a></strong></code> values of <code class="docutils literal notranslate"><span class="pre">forest</span></code>, <code class="docutils literal notranslate"><span class="pre">domain</span></code>, and <code class="docutils literal notranslate"><span class="pre">legacy</span></code>.</p>
+<p>Requires the target directory partition to already exist.</p>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-dns_servers"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-dns-servers"><strong>dns_servers</strong></p>
 <a class="ansibleOptionLink" href="#parameter-dns_servers" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">list</span> / <span class="ansible-option-elements">elements=string</span></p>
 </div></td>
@@ -184,7 +194,7 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 <p>At least one server is required.</p>
 </div></td>
 </tr>
-<tr class="row-odd"><td><div class="ansible-option-cell">
+<tr class="row-even"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-dynamic_update"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-dynamic-update"><strong>dynamic_update</strong></p>
 <a class="ansibleOptionLink" href="#parameter-dynamic_update" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
 </div></td>
@@ -199,7 +209,7 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 </ul>
 </div></td>
 </tr>
-<tr class="row-even"><td><div class="ansible-option-cell">
+<tr class="row-odd"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-forwarder_timeout"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-forwarder-timeout"><strong>forwarder_timeout</strong></p>
 <a class="ansibleOptionLink" href="#parameter-forwarder_timeout" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">integer</span></p>
 </div></td>
@@ -208,20 +218,21 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 <p>If the provided value is not valid, it will be omitted and a warning will be issued.</p>
 </div></td>
 </tr>
-<tr class="row-odd"><td><div class="ansible-option-cell">
+<tr class="row-even"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-name"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-name"><strong>name</strong></p>
 <a class="ansibleOptionLink" href="#parameter-name" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span> / <span class="ansible-option-required">required</span></p>
 </div></td>
 <td><div class="ansible-option-cell"><p>Fully qualified name of the DNS zone.</p>
 </div></td>
 </tr>
-<tr class="row-even"><td><div class="ansible-option-cell">
+<tr class="row-odd"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-replication"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-replication"><strong>replication</strong></p>
 <a class="ansibleOptionLink" href="#parameter-replication" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
 </div></td>
 <td><div class="ansible-option-cell"><p>Specifies the replication scope for the DNS zone.</p>
 <p>l(replication=forest) will replicate the DNS zone to all domain controllers in the Active Directory forest.</p>
 <p>l(replication=domain) will replicate the DNS zone to all domain controllers in the Active Directory domain.</p>
+<p>l(replication=legacy) will replicate the DNS zone to all domain controllers in the Active Directory domain and to legacy clients.</p>
 <p>l(replication=none) disables Active Directory integration and creates a local file with the name of the zone.</p>
 <p>This is the equivalent of selecting l(store the zone in Active Directory) in the GUI.</p>
 <p class="ansible-option-line"><strong class="ansible-option-choices">Choices:</strong></p>
@@ -233,7 +244,7 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 </ul>
 </div></td>
 </tr>
-<tr class="row-odd"><td><div class="ansible-option-cell">
+<tr class="row-even"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-state"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-state"><strong>state</strong></p>
 <a class="ansibleOptionLink" href="#parameter-state" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
 </div></td>
@@ -247,7 +258,7 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 </ul>
 </div></td>
 </tr>
-<tr class="row-even"><td><div class="ansible-option-cell">
+<tr class="row-odd"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-type"></div><p class="ansible-option-title" id="ansible-collections-ansible-windows-win-dns-zone-module-parameter-type"><strong>type</strong></p>
 <a class="ansibleOptionLink" href="#parameter-type" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
 </div></td>
@@ -335,6 +346,20 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 <span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">marshallb.euc.vmware.com</span>
 <span class="w">    </span><span class="nt">state</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">absent</span>
 
+<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Ensure primary zone is stored in a custom directory partition</span>
+<span class="w">  </span><span class="nt">ansible.windows.win_dns_zone</span><span class="p">:</span>
+<span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">custom.euc.vmware.com</span>
+<span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">primary</span>
+<span class="w">    </span><span class="nt">directory_partition</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">CustomDNSPartition</span>
+
+<span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Ensure forwarder zone is stored in a custom directory partition</span>
+<span class="w">  </span><span class="nt">ansible.windows.win_dns_zone</span><span class="p">:</span>
+<span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">forwarder.euc.vmware.com</span>
+<span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">forwarder</span>
+<span class="w">    </span><span class="nt">directory_partition</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">CustomDNSPartition</span>
+<span class="w">    </span><span class="nt">dns_servers</span><span class="p">:</span>
+<span class="w">      </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">10.245.51.100</span>
+
 <span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Ensure DNS zones are absent</span>
 <span class="w">  </span><span class="nt">ansible.windows.win_dns_zone</span><span class="p">:</span>
 <span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">item</span> <span class="cp">}}</span><span class="s">&quot;</span>
@@ -364,7 +389,7 @@ see <a class="reference internal" href="#ansible-collections-ansible-windows-win
 </div></td>
 <td><div class="ansible-option-cell"><p>New/Updated DNS zone parameters</p>
 <p class="ansible-option-line"><strong class="ansible-option-returned-bold">Returned:</strong> When l(state=present)</p>
-<p class="ansible-option-line ansible-option-sample"><strong class="ansible-option-sample-bold">Sample:</strong> <code class="ansible-option-sample docutils literal notranslate"><span class="pre">{&quot;dns_servers&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;dynamic_update&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;forwarder_timeout&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;name&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;paused&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;replication&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;reverse_lookup&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;shutdown&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;type&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;zone_file&quot;:</span> <span class="pre">null}</span></code></p>
+<p class="ansible-option-line ansible-option-sample"><strong class="ansible-option-sample-bold">Sample:</strong> <code class="ansible-option-sample docutils literal notranslate"><span class="pre">{&quot;directory_partition&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;dns_servers&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;dynamic_update&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;forwarder_timeout&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;name&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;paused&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;replication&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;reverse_lookup&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;shutdown&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;type&quot;:</span> <span class="pre">null,</span> <span class="pre">&quot;zone_file&quot;:</span> <span class="pre">null}</span></code></p>
 </div></td>
 </tr>
 </tbody>

@stevefulme1 stevefulme1 requested a review from Yaish25491 June 10, 2026 21:33
stevefulme1 pushed a commit to stevefulme1/ansible.windows that referenced this pull request Jun 10, 2026
…partitions (ansible-collections#908)

Add directory_partition parameter to win_dns_zone module, allowing zones
to be stored in custom Active Directory application directory partitions
instead of the standard forest/domain/legacy scopes. The parameter is
mutually exclusive with replication values of forest, domain, and legacy.

Includes integration tests for primary, forwarder, and stub zone types
with custom directory partitions, idempotence checks, mutual exclusivity
validation, and check mode tests.
@stevefulme1 stevefulme1 force-pushed the feat/win-dns-zone-directory-partition branch from 347e487 to bbbbb55 Compare June 10, 2026 22:41
@stevefulme1

Copy link
Copy Markdown
Collaborator Author

Reworked implementation — fixes CI failures and design issues

This push replaces the original approach with a cleaner design. Key changes:

Design change: separate directory_partition parameter

Instead of overloading the replication parameter to accept arbitrary DN strings (which removed input validation and broke CI), this implementation adds a dedicated directory_partition parameter. This:

  • Preserves choices validation on replication — typos in forest/domain/legacy/none are caught at input validation, not silently passed through
  • Clearer semantics — a directory partition name is not a replication scope; mixing them in one parameter is confusing for users
  • Mutually exclusivedirectory_partition fails fast if combined with replication values of forest, domain, or legacy

What's included

  • plugins/modules/win_dns_zone.pydirectory_partition parameter docs, examples, and RETURN block
  • plugins/modules/win_dns_zone.ps1 — full implementation for all zone types (primary, stub, forwarder), including:
    • Argument spec with directory_partition
    • Mutual exclusivity validation
    • Get-DnsZoneObject returns directory_partition and replication: custom for custom-partition zones
    • Compare-DnsZone includes DirectoryPartitionName in change detection
    • Create and update paths for primary, stub, and forwarder zones pass -DirectoryPartitionName to the appropriate cmdlets
    • Fast-fail checks updated to handle custom partition → file-backed conversion attempts
  • Integration tests — 10 new test tasks in activedirectory.yml:
    • Creates custom application directory partition
    • Tests primary, forwarder, and stub zone creation with custom partition + idempotence checks
    • Asserts directory_partition and replication: custom in return values
    • Validates mutual exclusivity error
    • Check mode test
    • Cleanup tasks
  • Standalone test — mutual exclusivity validation (no AD required)
  • Changelog fragment — references issue Extend win_dns_zone to Support AD Integrated Application Partitions #901

Usage example

- name: Store zone in custom directory partition
  ansible.windows.win_dns_zone:
    name: example.com
    type: primary
    directory_partition: CustomDNSPartition

Secondary zones are excluded from custom partition support (they are always file-backed).

Test User added 2 commits June 18, 2026 08:40
…partitions (ansible-collections#908)

Add directory_partition parameter to win_dns_zone module, allowing zones
to be stored in custom Active Directory application directory partitions
instead of the standard forest/domain/legacy scopes. The parameter is
mutually exclusive with replication values of forest, domain, and legacy.

Includes integration tests for primary, forwarder, and stub zone types
with custom directory partitions, idempotence checks, mutual exclusivity
validation, and check mode tests.
@stevefulme1 stevefulme1 force-pushed the feat/win-dns-zone-directory-partition branch from bbbbb55 to 4df15f7 Compare June 18, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend win_dns_zone to Support AD Integrated Application Partitions

1 participant