Skip to content

Instantly share code, notes, and snippets.

@cobbr
Created April 2, 2024 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cobbr/69a7b92429fa38ea86faa88e07e2c9b2 to your computer and use it in GitHub Desktop.
Save cobbr/69a7b92429fa38ea86faa88e07e2c9b2 to your computer and use it in GitHub Desktop.
win_domain_child
#!powershell
# Copyright: (c) 2022 Jordan Borean (@jborean93) jborean93@gmail.com
# Copyright: (c) 2023, Ryan Cobb <ryancobb65@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CsharpUtil Ansible.Basic
# win_domain_child module
$spec = @{
options = @{
dns_domain_name = @{ type = 'str' }
domain_admin_password = @{ type = 'str'; required = $true; no_log = $true }
domain_admin_username = @{ type = 'str'; required = $true }
domain_mode = @{ type = 'str'; choices = 'Win2008', 'Win2008R2', 'Win2012', 'Win2012R2', 'WinThreshold', 'Default' }
safe_mode_password = @{ type = 'str'; required = $true; no_log = $true }
site_name = @{ type = 'str' }
}
required_together = @(
, @("domain_admin_username", "domain_admin_password")
)
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$dns_domain_name = $module.Params.dns_domain_name
$domain_admin_password = $module.Params.domain_admin_password
$domain_admin_username = $module.Params.domain_admin_username
$domain_mode = $module.Params.domain_mode
$safe_mode_password = $module.Params.safe_mode_password
$site_name = $module.Params.site_name
$module.Result.reboot_required = $false
$system_role = Get-CimInstance -ClassName Win32_ComputerSystem -Property Domain, DomainRole
if ($system_role.DomainRole -in @(4, 5))
{
if ($system_role.Domain -ne $dns_domain_name)
{
$module.FailJson("Host is already a domain controller in another domain $($system_role.Domain)")
}
$module.ExitJson()
}
$new_domain_name, $parent_domain_name = $dns_domain_name.Split(".", 2)
$domain_admin_credential = New-Object -TypeName PSCredential -ArgumentList @(
$domain_admin_username,
(ConvertTo-SecureString -AsPlainText -Force -String $domain_admin_password)
)
$install_params = @{
NewDomainName = $new_domain_name
ParentDomainName = $parent_domain_name
DomainType = 'ChildDomain'
NoRebootOnCompletion = $true
SafeModeAdministratorPassword = (ConvertTo-SecureString -AsPlainText -Force -String $safe_mode_password)
Force = $true
Credential = $domain_admin_credential
WhatIf = $module.CheckMode
ErrorAction = 'Stop'
}
if ($domain_mode)
{
$install_params.DomainMode = $domain_mode
}
if ($site_name)
{
$install_params.SiteName = $site_name
}
$install_domain = $null
try {
$install_domain = Install-ADDSDomain @install_params
}
catch {
# DCPromo exit codes details can be found at
# https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/deploy/troubleshooting-domain-controller-deployment
# ExitCode 1 == 'Exit, success'
# ExitCode 2 == 'Exit, success, need to reboot'
# ExitCode 3 == 'Exit, success, with a noncritical failure'
# ExitCode 4 == 'Exit, success, with a noncritical failure, need to reboot'
if ($_.Exception.ErrorCode -in @(1, 2, 3, 4)) {
$module.Result.changed = $true
$module.Result.reboot_required = $true
}
# ExitCode 15 == 'Role change is in progress or needs reboot'
# ExitCode 19 == 'Name change pending, needs reboot'
# ExitCode 53 == 'The promotion/demotion failed, machine must be rebooted to clean up'
# ExitCode 56 == 'The promotion/demotion was canceled by the user, machine must be rebooted to clean up'
elseif ($_.Exception.ErrorCode -in @(15, 19, 53, 56)) {
$module.Result.changed = $false
$module.Result.reboot_required = $true
}
# ExitCode 45 == 'A forest with the specified name already exists'
# ExitCode 47 == 'A tree with the specified name already exists'
elseif ($_.Exception.ErrorCode -in @(45, 47))
{
$module.Result.changed = $false
}
else
{
$module.FailJson("Failed to install ADDSDomain, DCPromo exited with $($_.Exception.ExitCode)", $_)
}
}
if ($install_domain)
{
$module.Result.changed = $true
$module.Result.reboot_required = $true
if (-not $module.CheckMode)
{
# The Netlogon service is set to auto start but is not started. This is
# required for Ansible to connect back to the host and reboot in a
# later task. Even if this fails Ansible can still connect but only
# with ansible_winrm_transport=basic so we just display a warning if
# this fails.
try {
$started = Start-Service -Name Netlogon
}
catch {
$msg = -join @(
"Failed to start the Netlogon service after promoting the host, "
"Ansible may be unable to connect until the host is manually rebooted: $($_.Exception.Message)"
)
$module.Warn($msg)
}
}
}
$module.ExitJson()
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2022 Jordan Borean (@jborean93) jborean93@gmail.com
# Copyright: (c) 2023, Ryan Cobb <ryancobb65@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = r'''
---
module: win_domain_child
short_description: Ensure the existence of a Windows child domain within a forest.
description:
- Ensure the existence of a Windows child domain within a forest.
- This module may require subsequent use of the M(ansible.windows.win_reboot) action if changes are made.
options:
dns_domain_name:
description:
- The DNS name of the domain which should exist and be reachable or reside on the target Windows host.
type: str
required: yes
domain_admin_username:
description:
- The username of a domain admin used to authenticate with the remote domain.
type: str
required: yes
domain_admin_password:
description:
- The password of the domain admin used to authenticate with the remote domain.
type: str
required: yes
domain_mode:
description:
- Specifies the domain functional level of the first domain in the creation of a new forest.
type: str
choices: [ Win2008, Win2008R2, Win2012, Win2012R2, WinThreshold, Default ]
required: no
safe_mode_password:
description:
- Specifies the password for the administrator account when the computer is started in Safe Mode.
type: str
required: yes
site_name:
description:
- Specifies the name of an existing site where you can place the new domain controller.
type: str
required: no
author:
- Ryan Cobb (@cobbr)
'''
EXAMPLES = r'''
- name: Create a domain child within a forest
win_domain_child:
name: child.domain.local
domain_admin_username: Administrator
domain_admin_password: Password123!
safe_mode_password: Password123!
register: child_domain
'''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment