Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADDomainController - Added support to demote domain controller - Fixes #251 #634

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ For older change log history see the [historic changelog](HISTORIC_CHANGELOG.md)
- ADFineGrainedPasswordPolicy
- New resource for creating and updating Fine Grained Password Policies for AD principal subjects.
([issue #584](https://github.com/dsccommunity/ActiveDirectoryDsc/issues/584)).
- ADDomainController
- Added support to demote domain controller when `ensure` is set to `Absent`.
([issue #251](https://github.com/dsccommunity/ActiveDirectoryDsc/issues/251))

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ function Get-TargetResource

$allowedPasswordReplicationAccountName = (
Get-ADDomainControllerPasswordReplicationPolicy -Allowed -Identity $domainControllerObject |
ForEach-Object -MemberName sAMAccountName)
ForEach-Object -MemberName sAMAccountName)
$deniedPasswordReplicationAccountName = (
Get-ADDomainControllerPasswordReplicationPolicy -Denied -Identity $domainControllerObject |
ForEach-Object -MemberName sAMAccountName)
ForEach-Object -MemberName sAMAccountName)
$serviceNTDS = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters'
$serviceNETLOGON = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters'
$installDns = [System.Boolean](Get-Service -Name dns -ErrorAction SilentlyContinue)
Expand Down Expand Up @@ -146,6 +146,9 @@ function Get-TargetResource
.PARAMETER SafemodeAdministratorPassword
Provide a password that will be used to set the DSRM password. This is a PSCredential.

.PARAMETER Ensure
Specifies if the node will be configured as a domain controller

.PARAMETER DatabasePath
Provide the path where the NTDS.dit will be created and stored.

Expand Down Expand Up @@ -191,6 +194,8 @@ function Get-TargetResource
Name | Module
---------------------------------------------------|--------------------------
Install-ADDSDomainController | ActiveDirectory
Test-ADDSDomainControllerUninstallation | ActiveDirectory
Uninstall-ADDSDomainController | ActiveDirectory
Get-ADDomain | ActiveDirectory
Get-ADForest | ActiveDirectory
Set-ADObject | ActiveDirectory
Expand Down Expand Up @@ -226,6 +231,11 @@ function Set-TargetResource
[System.Management.Automation.PSCredential]
$SafemodeAdministratorPassword,

[Parameter()]
[ValidateSet('Absent', 'Present')]
[System.String]
$Ensure = 'Present',

[Parameter()]
[System.String]
$DatabasePath,
Expand Down Expand Up @@ -280,7 +290,59 @@ function Set-TargetResource

$targetResource = Get-TargetResource @getTargetResourceParameters

if ($targetResource.Ensure -eq $false)
if ($targetResource.Ensure)
{
$ensureValue = 'Present'
}
else
{
$ensureValue = 'Absent'
}


if ($Ensure -eq 'Absent')
{
if ($targetResource.Ensure -eq $false)
{
break
}

# Test to make sure the domain controller can be removed from the domain
$ADDSDomainUninstallationParameters = @{
LocalAdministratorPassword = $SafemodeAdministratorPassword.Password
Credential = $Credential
NoRebootOnCompletion = $true
Force = $true
}
$testStatus = Test-ADDSDomainControllerUninstallation @ADDSDomainUninstallationParameters

if ($testStatus.Status -eq 'Error')
{
New-InvalidOperationException -Message ($script:localizedData.TestDemoteStatus -f $testStatus.Status, $testStatus.Message)
}
elseif ($testStatus.Status -eq 'Success')
{
# No issues found that will cause issues demoting the domain controller
try
{
Uninstall-ADDSDomainController @ADDSDomainUninstallationParameters -ErrorAction Stop
Write-Verbose -Message ($script:localizedData.Demoted -f $env:COMPUTERNAME)
}
catch
{
$errorMessage = $script:localizedData.FailedToDemote
New-InvalidResultException -Message $errorMessage -ErrorRecord $_
}
<#
Signal to the LCM to reboot the node to compensate for the one we
suppressed from Uninstall-ADDSDomainController
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '',
Justification = 'Set LCM DSCMachineStatus to indicate reboot required')]
$global:DSCMachineStatus = 1
}
}
elseif ($targetResource.Ensure -eq $false)
{
Write-Verbose -Message ($script:localizedData.Promoting -f $env:COMPUTERNAME, $DomainName)

Expand Down Expand Up @@ -560,6 +622,9 @@ function Set-TargetResource
.PARAMETER SafemodeAdministratorPassword
Provide a password that will be used to set the DSRM password. This is a PSCredential.

.PARAMETER Ensure
Specifies if the node will be configured as a domain controller

.PARAMETER DatabasePath
Provide the path where the NTDS.dit will be created and stored.

Expand Down Expand Up @@ -632,6 +697,11 @@ function Test-TargetResource
[System.Management.Automation.PSCredential]
$SafemodeAdministratorPassword,

[Parameter()]
[ValidateSet('Absent', 'Present')]
[System.String]
$Ensure = 'Present',

[Parameter()]
[System.String]
$DatabasePath,
Expand Down Expand Up @@ -707,6 +777,22 @@ function Test-TargetResource

$testTargetResourceReturnValue = $existingResource.Ensure

if ($testTargetResourceReturnValue)
{
$ensureValue = 'Present'
}
else
{
$ensureValue = 'Absent'
}


if ($ensureValue -ne $Ensure)
{
Write-Verbose -Message ($script:localizedData.EnsureMismatch -f $ensureValue, $Ensure)
$testTargetResourceReturnValue = $false
}

if ($PSBoundParameters.ContainsKey('ReadOnlyReplica') -and $ReadOnlyReplica)
{
if ($testTargetResourceReturnValue -and -not $existingResource.ReadOnlyReplica)
Expand Down Expand Up @@ -795,6 +881,15 @@ function Test-TargetResource
}
}

<#
If the node is not a domain controller and ensure is set to Absent we need to return
True as it will fail if other options are set on the resource.
#>
if ($ensureValue -eq 'Absent' -and $Ensure -eq 'Absent')
{
$testTargetResourceReturnValue = $true
}

return $testTargetResourceReturnValue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MSFT_ADDomainController : OMI_BaseResource
[Write, Description("The name of the site this Domain Controller will be added to.")] String SiteName;
[Write, Description("The path of the media you want to use install the Domain Controller.")] String InstallationMediaPath;
[Write, Description("Specifies if the domain controller will be a Global Catalog (GC).")] Boolean IsGlobalCatalog;
[Read, Description("Returns the state of the Domain Controller.")] String Ensure;
[Write, Description("Specifies the state of the Domain Controller."), ValueMap{"Absent", "Present"}, Values{"Absent", "Present"}] String Ensure;
[Write, Description("Indicates that the cmdlet installs the domain controller as an Read-Only Domain Controller (RODC) for an existing domain.")] Boolean ReadOnlyReplica;
[Write, Description("Specifies an array of names of user accounts, group accounts, and computer accounts whose passwords can be replicated to this Read-Only Domain Controller (RODC).")] String AllowPasswordReplicationAccountName[];
[Write, Description("Specifies the names of user accounts, group accounts, and computer accounts whose passwords are not to be replicated to this Read-Only Domain Controller (RODC).")] String DenyPasswordReplicationAccountName[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ ConvertFrom-StringData @'
CannotConvertToRODC = Cannot convert a existing domain controller to a Read-Only Domain Controller (RODC). (ADDC0023)
NotOwnerOfFlexibleSingleMasterOperationRole = The domain controller was expected to be the owner of the Flexible Single Master Operation (FSMO) role '{0}', but it is not. (ADDC0024)
MovingFlexibleSingleMasterOperationRole = The Flexible Single Master Operation (FSMO) role '{0}' is being moved from domain controller '{1}' to this domain controller. (ADDC0025)
EnsureMismatch = The current domain controller ensure does not match. Got {0}, expected was {1}. (ADDC0026)
TestDemoteStatus = Received status: {0} with message {0}. (ADDC0027)
Demoted = The current node '{0}' has been demoted from a domain controller to a member server. (ADDC0028)
FailedToDemote = Failed to demote the domain controller. (ADDC0029)
'@
Original file line number Diff line number Diff line change
Expand Up @@ -454,4 +454,62 @@ Configuration ADDomainController_AddDomainControllerUsingInstallDns_Config
}
}

.EXAMPLE 7

This configuration will demote an exiting domain controller from the domain contoso.com.

Configuration ADDomainController_DemoteDomainController_Config
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Credential,

[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$SafeModePassword
)

Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName ActiveDirectoryDsc

node localhost
{
WindowsFeature 'InstallADDomainServicesFeature'
{
Ensure = 'Present'
Name = 'AD-Domain-Services'
}

WindowsFeature 'RSATADPowerShell'
{
Ensure = 'Present'
Name = 'RSAT-AD-PowerShell'

DependsOn = '[WindowsFeature]InstallADDomainServicesFeature'
}

WaitForADDomain 'WaitForestAvailability'
{
DomainName = 'contoso.com'
Credential = $Credential

DependsOn = '[WindowsFeature]RSATADPowerShell'
}

ADDomainController 'DemoteDomainController'
{
Ensure = 'Absent'
DomainName = 'contoso.com'
Credential = $Credential
SafeModeAdministratorPassword = $SafeModePassword

DependsOn = '[WaitForADDomain]WaitForestAvailability'
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<#PSScriptInfo
.VERSION 1.0.1
.GUID 50ee1ed8-2e39-4194-93f5-ceff18443e13
.AUTHOR DSC Community
.COMPANYNAME DSC Community
.COPYRIGHT DSC Community contributors. All rights reserved.
.TAGS DSCConfiguration
.LICENSEURI https://github.com/dsccommunity/ActiveDirectoryDsc/blob/master/LICENSE
.PROJECTURI https://github.com/dsccommunity/ActiveDirectoryDsc
.ICONURI https://dsccommunity.org/images/DSC_Logo_300p.png
.RELEASENOTES
Updated author, copyright notice, and URLs.
#>

#Requires -Module ActiveDirectoryDsc

<#
.DESCRIPTION
This configuration will demote an exiting domain controller from the domain contoso.com.
#>


Configuration ADDomainController_DemoteDomainController_Config
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Credential,

[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$SafeModePassword
)

Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName ActiveDirectoryDsc

node localhost
{
WindowsFeature 'InstallADDomainServicesFeature'
{
Ensure = 'Present'
Name = 'AD-Domain-Services'
}

WindowsFeature 'RSATADPowerShell'
{
Ensure = 'Present'
Name = 'RSAT-AD-PowerShell'

DependsOn = '[WindowsFeature]InstallADDomainServicesFeature'
}

WaitForADDomain 'WaitForestAvailability'
{
DomainName = 'contoso.com'
Credential = $Credential

DependsOn = '[WindowsFeature]RSATADPowerShell'
}

ADDomainController 'DemoteDomainController'
{
Ensure = 'Absent'
DomainName = 'contoso.com'
Credential = $Credential
SafeModeAdministratorPassword = $SafeModePassword

DependsOn = '[WaitForADDomain]WaitForestAvailability'
}
}
}
Loading