<# ============================================================================================================================================== CleanupDCs.ps1 V1.51: Performs automatic cleanup of DCs removed from a forest Usage: .\CleanupDCs -action ====== Parameters: =========== -action can be set to: ListDCs: Creates a file called DClist.txt conatining al the DCs in the forest MetadataCleanup: Performs a metadata cleanup of the DCs of DCList.txt DNSCleanup: Deletes all DNS records registered by the DCs in DCList.txt AccountCleanup: Deletes the computer account of the DCs of DCLIST.txt ResetTrustPwd: Resets the password of every trust relationship of the domain Logs the output in CleanupDCs.log in the current directory How to use for ADRES: ===================== - On the first DC restored (the DC of the forest root domain), logon with the root domain administrator account (rid 500): - Run .\CleanupDcs.ps1 -action ListDCs to create file DCList.txt containing all the DCs of the forest - Edit DCList.txt and remove the DCs you want to keep. - Run .\CleanupDcs.ps1 -action MetadataCleanup to perform a metadata Cleanup of the root domain DCs present in DCList.txt - Run .\CleanupDcs.ps1 -action DNSCleanup to delete the DNS records registered by the root domain DCs present in DCList.txt - Run .\CleanupDcs.ps1 -action ResetTrustPwd to restore the password of every trust relationship of the root domain - On the first DC restored of each child domain, logon with the child domain administrator account (rid 500): - Copy DCList.txt from the Root DC. - Run .\CleanupDcs.ps1 -action AccountCleanup to delete the computer account of the child domain DCs present in DCList.txt - Run .\CleanupDcs.ps1 -action DNSCleanup to delete the DNS records of the child domain DCs present in DCList.txt - Run .\CleanupDcs.ps1 -action ResetTrustPwd to restore the password of every trust relationship of the child domain Disclaimer: This sample script is not supported under any Microsoft standard support program or service. The sample script is provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages ======================================================================================================================================================== #> param ([parameter(Mandatory=$true)]$action) $debugPreference = "inquire" # Function ListKeptDCs # Displays the DCs that will be kept and returns their number # --------------------------------------------------------------- function ListKeptDCs { $keptDCs=0 $DCList = get-content DCList.txt write-host "The following DCs will be kept:" $ldapQuery = "(&(objectCategory=nTDSDSA))" $ObjAD = new-object System.DirectoryServices.DirectoryEntry $ADSearcher = new-object system.directoryservices.directorysearcher –argumentlist $ObjAD,$ldapQuery $Root = New-Object DirectoryServices.DirectoryEntry "LDAP://CN=Sites,CN=Configuration,$ForestRootDomain" $ADSearcher.SearchRoot = $Root try { $QueryResult = $ADSearcher.findall() $QueryResult | foreach { $ldapObj=$_.Path.replace("LDAP://CN=NTDS Settings,","") if (!($DCList | where {$_ -eq $ldapObj})) { $keptDCs++ write-host " $ldapObj" } } } catch { write-host $_ -fore red break } return $KeptDCs } # Initialization # ---------------- start-transcript .\CleanupDCs.Log $listedDCs=0 $removedDCs=0 $DNSCleanupDCs=0 $ComputerAccountCleanupDCs=0 $TrustPwdReset=0 $rep="" $Info = "CleanupDC.ps1 V1.5 " $usage = @" Usage: .\CleanupDCs -action Valid actions are: ListDCs: Creates a file called DClist.txt containing all DCs in the forest MetadataCleanup: Performs a Metadata cleanup of all the DCs of DCList.txt DNSCleanup: Deletes all DNS records registered by the DCs in DCList.txt AccountCleanup: Deletes the computer account a Metadata cleanup of all the DCs of DCList.txt ResetTrustPwd: Resets the password of every trust relationship of the domain Example: .\CleanupDCs -action ListDCs "@ cls switch ($Action) { "ListDCs" {$Info = $Info + "List forest DCs in DCList.txt"} "DNSCleanup" {$Info = $Info + "Cleanup DNS records registered by DCs of DCList.txt"} "MetadataCleanup" {$Info = $Info + "Perform a MetadataCleanup of all DCs of DCList.txt"} "AccountCleanup" {$Info = $Info + "Deletes the computer account of all DCs of DCList.txt"} "ResetTrustPwd" {$Info = $Info + "Resets the password of every trust relationship oof the domain"} default { write-host $info write-host $usage stop-transcript exit } } Write-host $Info `n try { $RootDSE=([ADSI]"LDAP://RootDSE") $ForestRootDomain=$RootDSE.rootDomainNamingContext $CurrentDomain=$RootDSE.Get("defaultNamingContext") } catch { write-host $_ -fore red break } # Action: List DCs # ------------------- if ($action -eq "ListDCs") { if (test-path .\DCList.txt) { remove-item .\DCList.txt } $ldapQuery = "(&(objectClass=nTDSDSA))" $ObjAD = new-object System.DirectoryServices.DirectoryEntry $ADSearcher = new-object system.directoryservices.directorysearcher –argumentlist $ObjAD,$ldapQuery $Root = New-Object DirectoryServices.DirectoryEntry "LDAP://CN=Sites,CN=Configuration,$ForestRootDomain" $ADSearcher.SearchRoot = $Root write-host "`nThe following DCs will be added to DCList.txt:" try { $QueryResult = $ADSearcher.findall() $QueryResult | foreach { $ldapObj=$_.Path.replace("LDAP://CN=NTDS Settings,","") add-content -path .\DCList.txt -value $ldapObj write-host " $ldapObj" $ListedDCs++ } } catch { write-host $_ -fore red break } $KeptDCs="na" $TrustPwdReset="na" } # Action: MetadataCleanup # ----------------------- if ($action -eq "MetadataCleanup") { # Check DCList.txt is present if(!(test-path .\DCList.txt)) { write-host "File DCList.txt not found, please select action ListDCs to create the file`n" -fore red stop-transcript exit } # Lists the DCs that will be kept and make sure not all of them will be removed $KeptDCs=ListKeptDCs if (!($KeptDCs)) { write-host "None`n`n" write-host "The procedure would cleanup ALL DCs of the forest, as a result it will not be performed" -fore red write-host "Pleasea make sure you removed the DCs you want to keep from DCList.txt`n" -fore red stop-transcript exit } # Prompts the user to proceed write-host "`nWe are now ready to remove ALL other DCs of the forest." write-host " !!! This operation should be done ONLY in a test envrironment" -fore red write-host " !!! In a production environment, it will lead to a major outage highly impacting production" -fore red write-host " !!! Only run in production in case of a real disaster recovery" -fore red $rep="" while (!($rep -eq "Metadata Cleanup DCs")) { if ($rep -eq "q") { stop-transcript exit } else { $rep=read-host ("`nPlease enter `"Metadata Cleanup DCs`" to perform a metadata cleanup of all other DCs or `"q`" to quit") } } # Perform the metdata cleanup of the DCs $DCList = get-content DCList.txt write-host "`nPerforming a metadata cleanup of the following DCs:" foreach ($DCName in $DCList) { $NTDSobj=[ADSI]"LDAP://CN=NTDS Settings,$dcName" if ($NTDSObj.name) # makes sure object NTDS setting exists { write-host " $DCName" -fore yellow # Uncomment the following line to perform the metadata cleanup ntdsutil.exe “metadata cleanup” “remove selected server $DCName” quit quit $removedDCs++ } else { write-host " $DCName has no NTDS Settings object" -fore red } } write-host "`n`n" $TrustPwdReset="na" } # Action: DNSCleanup # ------------------ if ($action -eq "DNSCleanup") { if(!(test-path .\DCList.txt)) { write-host "File DCList.txt not found, please select action ListDCs to create the file`n" -fore red stop-transcript exit } # Lists the DCs that will be kept and make sure not all of them will be removed $KeptDCs=ListKeptDCs if (!($KeptDCs)) { write-host "The procedure would delete DNS records of ALL DCs of the forest, as a result it will not be performed" -fore red write-host "Please make sure you removed the DCs you want to keep from DCList.txt`n" -fore red stop-transcript exit } # Prompts the user to proceed write-host "`nWe are now ready to remove DNS records from ALL other DCs of the forest." write-host " !!! This operation should be done ONLY in a test envrironment" -fore red write-host " !!! In a production environment, it will lead to a major outage highly impacting production" -fore red write-host " !!! Only run in production in case of a real disaster recovery" -fore red $rep="" while (!($rep -eq "DNS Cleanup")) { if ($rep -eq "q") { stop-transcript exit } else { $rep=read-host("`nPlease enter `"DNS Cleanup`" to delete DNS records of other DCs or `"q`" to quit") } } # Deletes the DNS records of the DCs $DCList = get-content DCList.txt write-host "`nDeleting DNS records of the following DCs:" try { foreach ($DCName in $DCList) { $DCShortName = ($DCName.split(",")[0]).split("cn=")[1] (Get-WmiObject -Namespace "root\MicrosoftDNS" -Class "MicrosoftDNS_AType" |where {$_.TextRepresentation -like "*$DCShortName*"}) | foreach { $DCIPAddr = $_.IPAddress # Deletes the A record with the IP address (Get-WmiObject -Namespace "root\MicrosoftDNS" -Class "MicrosoftDNS_AType" |where {$_.IPAddress -like "*$DCIPAddr"}) | remove-wmiObject # Deletes the A records with the DC name $_ | remove-wmiObject } # Deletes the NS records (Get-WmiObject -Namespace "root\MicrosoftDNS" -Class "MicrosoftDNS_NSType" |where {$_.TextRepresentation -like "*$DCShortName*"}) | remove-wmiObject # Deletes the SRV DNS records (Get-WmiObject -Namespace "root\MicrosoftDNS" -Class "MicrosoftDNS_SRVType" |where {$_.TextRepresentation -like "*$DCShortName*"}) | remove-wmiObject # Deletes the CNAME DNS records (Get-WmiObject -Namespace "root\MicrosoftDNS" -Class "MicrosoftDNS_CNAMEType" |where {$_.RecordData -like "*$DCShortName*"}) |remove-wmiObject write-host " $DCShortName" -fore yellow $DNSCleanupDCs++ } } catch { write-host $_ -fore red break } write-host "`n`n" $TrustPwdReset="na" } # Action: Accountcleanup # ---------------------- if ($action -eq "AccountCleanup") { if(!(test-path .\DCList.txt)) { write-host "File DCList.txt not found, please select action ListDCs to create the file`n" -fore red stop-transcript exit } # Lists the DCs that will be kept and make sure not all of them will be removed $KeptDCs=ListKeptDCs if (!($KeptDCs)) { write-host "The procedure would delete the computer account of ALL DCs of the forest, as a result it will not be performed" -fore red write-host "Please make sure you removed the DCs you want to keep from DCList.txt`n" -fore red stop-transcript exit } # Prompts the user to proceed write-host "`nWe are now ready to delete the computer account of ALL other DCs of the forest." write-host " !!! This operation should be done ONLY in a test envrironment" -fore red write-host " !!! In a production environment, it will lead to a major outage highly impacting production" -fore red write-host " !!! Only run in production in case of a real disaster recovery" -fore red $rep="" while (!($rep -eq "Account Cleanup")) { if ($rep -eq "q") { stop-transcript exit } else { $rep=read-host("`nPlease enter `"Account Cleanup`" to delete the computer account of other DCs or `"q`" to quit") } } # Deletes the computer accounts and their child objects $DCList = get-content DCList.txt write-host "`nDeleting computer account of the following DCs:" foreach ($DCName in $DCList) { $DCShortName = ($DCName.split(",")[0]).split("cn=")[1] $ldapQuery = "(&(cn=$DCShortName))" # We need to perform a LDAP query since the computer account might be in a sub OU $ObjAD = new-object System.DirectoryServices.DirectoryEntry $ADSearcher = new-object system.directoryservices.directorysearcher –argumentlist $ObjAD,$ldapQuery $Root = New-Object DirectoryServices.DirectoryEntry "LDAP://OU=Domain Controllers,$CurrentDomain" $ADSearcher.SearchRoot = $Root try { $QueryResult = $ADSearcher.findall() if ($QueryResult.count -ne 0) { [ADSI]$computerAccount = $QueryResult[0].path $ComputerAccount.psbase.deleteTree() $ComputerAccount.psbase.commitChanges() write-host " $DCShortName deleted" -fore yellow $ComputerAccountCleanupDCs++ } else { write-host " $DCShortName not present in current domain" -fore yellow } } catch { write-host $_ -fore red break } } write-host "`n`n" $TrustPwdReset="na" } # Action: ResetTrustPwd # ---------------------- if ($action -eq "ResetTrustPwd") { # Prompts the user to proceed write-host "`nWe are now ready to reset the password of all trusts relationships of domain $CurrentDomain" write-host " !!! This operation should be done ONLY in a test envrironment" -fore red write-host " !!! In a production environment, it will lead to a major outage highly impacting production" -fore red write-host " !!! Only run in production in case of a real disaster recovery" -fore red $rep="" while (!($rep -eq "Reset Trusts Password")) { if ($rep -eq "q") { stop-transcript exit } else { $rep=read-host("`nPlease enter `"Reset Trusts Password`" to reset the domain's trust relationships password or `"q`" to quit") } } $TrustPass=read-host "Please enter the trust password" -asSecureString $currentDomainFQDN = (Get-Item env:USERDNSDOMAIN).value try { $trustCollection=[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().GetAllTrustRelationships() $trustCollection | foreach { write-host "Resetting password of trust with domain $($_.targetName)" -fore yellow netdom trust $currentDomainFQDN /domain:$($_.targetName) /resetOneSide /passwordT:$TrustPass $TrustPwdReset++ } } catch { write-host $_ -fore red break } write-host "`n`n" $KeptDCs="na" } # Statistics # -------------- Write-Host "`n==================================" Write-Host " Statistics:" -fore green Write-Host " DCs Listed: $listedDCs" -fore green Write-Host " DCs metadata cleanup: $removedDCs" -fore green Write-Host " DCs computer account deleted: $ComputerAccountCleanupDCs" -fore green Write-Host " DCs Removed from DNS: $DNSCleanupDCs" -fore green Write-Host " DCs Kept: $KeptDCs" -fore green Write-Host " Trusts password reset: $TrustPwdReset" -fore green Write-Host "==================================" stop-transcript