· 6 years ago · May 17, 2019, 07:34 AM
1<#
2#>
3
4### FUNCTION: Logging Data To The Log File
5Function Logging($dataToLog, $lineType) {
6 $datetimeLogLine = "[" + $(Get-Date -format "yyyy-MM-dd HH:mm:ss") + "] : "
7 Out-File -filepath "$logFilePath" -append -inputObject "$datetimeLogLine$dataToLog"
8 #Write-Output($datetimeLogLine + $dataToLog)
9 If ($lineType -eq $NULL) {
10 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Yellow
11 }
12 If ($lineType -eq "SUCCESS") {
13 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Green
14 }
15 If ($lineType -eq "ERROR") {
16 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Red
17 }
18 If ($lineType -eq "WARNING") {
19 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Red
20 }
21 If ($lineType -eq "MAINHEADER") {
22 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Magenta
23 }
24 If ($lineType -eq "HEADER") {
25 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor DarkCyan
26 }
27 If ($lineType -eq "REMARK") {
28 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Cyan
29 }
30 If ($lineType -eq "REMARK-IMPORTANT") {
31 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Green
32 }
33 If ($lineType -eq "REMARK-MORE-IMPORTANT") {
34 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Yellow
35 }
36 If ($lineType -eq "REMARK-MOST-IMPORTANT") {
37 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Red
38 }
39 If ($lineType -eq "ACTION") {
40 Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor White
41 }
42 If ($lineType -eq "ACTION-NO-NEW-LINE") {
43 Write-Host "$datetimeLogLine$dataToLog" -NoNewline -ForeGroundColor White
44 }
45}
46
47### FUNCTION: Test The Port Connection
48Function portConnectionCheck($fqdnServer,$port,$timeOut) {
49 $tcpPortSocket = $null
50 $portConnect = $null
51 $tcpPortWait = $null
52 $tcpPortSocket = New-Object System.Net.Sockets.TcpClient
53 $portConnect = $tcpPortSocket.BeginConnect($fqdnServer,$port,$null,$null)
54 $tcpPortWait = $portConnect.AsyncWaitHandle.WaitOne($timeOut,$false)
55 If(!$tcpPortWait) {
56 $tcpPortSocket.Close()
57 Return "ERROR"
58 } Else {
59 #$error.Clear()
60 $ErrorActionPreference = "SilentlyContinue"
61 $tcpPortSocket.EndConnect($portConnect) | Out-Null
62 If (!$?) {
63 Return "ERROR"
64 } Else {
65 Return "SUCCESS"
66 }
67 $tcpPortSocket.Close()
68 $ErrorActionPreference = "Continue"
69 }
70}
71
72### FUNCTION: Load Required PowerShell Modules
73Function loadPoSHModules($PoSHModule) {
74 $retValue = $null
75 If(@(Get-Module | ?{$_.Name -eq $PoSHModule}).count -eq 0) {
76 If(@(Get-Module -ListAvailable | ?{$_.Name -eq $PoSHModule} ).count -ne 0) {
77 Import-Module $PoSHModule
78 Logging "PoSH Module '$PoSHModule' Has Been Loaded..." "SUCCESS"
79 $retValue = "HasBeenLoaded"
80 } Else {
81 Logging "PoSH Module '$PoSHModule' Is Not Available To Load..." "ERROR"
82 Logging "Aborting Script..." "ERROR"
83 $retValue = "NotAvailable"
84 }
85 } Else {
86 Logging "PoSH Module '$PoSHModule' Already Loaded..." "SUCCESS"
87 $retValue = "AlreadyLoaded"
88 }
89 Return $retValue
90}
91
92### FUNCTION: Test Credentials For Specific Admin Role
93Function testAdminRole($adminRole) {
94 # Determine Current User
95 $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
96 # Check The Current User Is In The Specified Admin Role
97 (New-Object Security.Principal.WindowsPrincipal $currentUser).IsInRole($adminRole)
98}
99
100### FUNCTION: Create Temporary Canary Object
101Function createTempCanaryObject($targetedADdomainRWDC, $krbTgtSamAccountName, $execDateTimeCustom1) {
102 # Determine The DN Of The Default NC Of The Targeted Domain
103 $targetedADdomainDefaultNC = $null
104 $targetedADdomainDefaultNC = (Get-ADRootDSE -Server $targetedADdomainRWDC).defaultNamingContext
105 # Determine The DN Of The Users Container Of The Targeted Domain
106 $containerForTempCanaryObject = $null
107 $containerForTempCanaryObject = "CN=Users," + $targetedADdomainDefaultNC
108 # Generate The Name Of The Temporary Canary Object
109 $targetObjectToCheckName = $null
110 $targetObjectToCheckName = "_adReplTempObject_" + $krbTgtSamAccountName + "_" + $execDateTimeCustom1
111 # Specify The Description Of The Temporary Canary Object
112 $targetObjectToCheckDescription = "...!!!.TEMP OBJECT TO CHECK AD REPLICATION IMPACT.!!!..."
113 # Generate The DN Of The Temporary Canary Object
114 $targetObjectToCheckDN = $null
115 $targetObjectToCheckDN = "CN=" + $targetObjectToCheckName + "," + $containerForTempCanaryObject
116 Logging " --> RWDC To Create Object On..............: '$targetedADdomainRWDC'"
117 Logging " --> Full Name Temp Canary Object..........: '$targetObjectToCheckName'"
118 Logging " --> Description...........................: '$targetObjectToCheckDescription'"
119 Logging " --> Container For Temp Canary Object......: '$containerForTempCanaryObject'"
120 Logging ""
121 # Try To Create The Canary Object In The AD Domain And If Not Successfull Throw Error
122 Try {
123 New-ADObject -Type contact -Name $targetObjectToCheckName -Path $containerForTempCanaryObject -DisplayName $targetObjectToCheckName -Description $targetObjectToCheckDescription -Server $targetedADdomainRWDC
124 } Catch {
125 Logging " --> Temp Canary Object [$targetObjectToCheckDN] FAILED TO BE CREATED on RWDC [$targetedADdomainRWDC]!..." "ERROR"
126 Logging "" "ERROR"
127 }
128 # Check The Temporary Canary Object Exists And Was created In AD
129 $targetObjectToCheck = $null
130 $targetObjectToCheck = Get-ADObject -LDAPFilter "(&(objectClass=contact)(name=$targetObjectToCheckName))" -Server $targetedADdomainRWDC
131 If ($targetObjectToCheck) {
132 $targetObjectToCheckDN = $null
133 $targetObjectToCheckDN = $targetObjectToCheck.DistinguishedName
134 Logging " --> Temp Canary Object [$targetObjectToCheckDN] CREATED on RWDC [$targetedADdomainRWDC]!..." "REMARK"
135 }
136 Return $targetObjectToCheckDN
137}
138
139### FUNCTION: Confirm Generated Password Meets Complexity Requirements
140# Source: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/password-must-meet-complexity-requirements
141Function confirmPasswordIsComplex($pwd) {
142 Process {
143 $criteriaMet = 0
144 # Upper Case Characters (A through Z, with diacritic marks, Greek and Cyrillic characters)
145 If ($pwd -cmatch '[A-Z]') {$criteriaMet++}
146 # Lower Case Characters (a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters)
147 If ($pwd -cmatch '[a-z]') {$criteriaMet++}
148 # Numeric Characters (0 through 9)
149 If ($pwd -match '\d') {$criteriaMet++}
150 # Special Chracters (Non-alphanumeric characters, currency symbols such as the Euro or British Pound are not counted as special characters for this policy setting)
151 If ($pwd -match '[\^~!@#$%^&*_+=`|\\(){}\[\]:;"''<>,.?/]') {$criteriaMet++}
152 # Check If It Matches Default Windows Complexity Requirements
153 If ($criteriaMet -lt 3) {Return $false}
154 If ($pwd.Length -lt 8) {Return $false}
155 Return $true
156 }
157}
158
159### FUNCTION: Generate New Complex Password
160Function generateNewComplexPassword([int]$passwordNrChars) {
161 Process {
162 $iterations = 0
163 Do {
164 If ($iterations -ge 20) {
165 Logging " --> Complex password generation failed after '$iterations' iterations..." "ERROR"
166 Logging "" "ERROR"
167 EXIT
168 }
169 $iterations++
170 $pwdBytes = @()
171 $rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
172 Do {
173 [byte[]]$byte = [byte]1
174 $rng.GetBytes($byte)
175 If ($byte[0] -lt 33 -or $byte[0] -gt 126) {
176 CONTINUE
177 }
178 $pwdBytes += $byte[0]
179 }
180 While ($pwdBytes.Count -lt $passwordNrChars)
181 $pwd = ([char[]]$pwdBytes) -join ''
182 }
183 Until (confirmPasswordIsComplex $pwd)
184 Return $pwd
185 }
186}
187
188### FUNCTION: Reset Password Of AD Account
189Function setPasswordOfADAccount($targetedADdomainRWDC, $krbTgtSamAccountName) {
190 # Retrieve The KrgTgt Object In The AD Domain BEFORE THE PASSWORD SET
191 $krbTgtObjectBefore = $null
192 $krbTgtObjectBefore = Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Properties * -Server $targetedADdomainRWDC
193 # Get The DN Of The KrgTgt Object In The AD Domain BEFORE THE PASSWORD SET
194 $krbTgtObjectBeforeDN = $null
195 $krbTgtObjectBeforeDN = $krbTgtObjectBefore.DistinguishedName
196 # Get The Password Last Set Value From The KrgTgt Object In The AD Domain BEFORE THE PASSWORD SET
197 $krbTgtObjectBeforePwdLastSet = $null
198 $krbTgtObjectBeforePwdLastSet = Get-Date $([datetime]::fromfiletime($krbTgtObjectBefore.pwdLastSet)) -f "yyyy-MM-dd HH:mm:ss"
199 # Get The Metadata Of The Object, And More Specific Of The pwdLastSet Attribute Of That Object BEFORE THE PASSWORD SET
200 $metadataObjectBefore = $null
201 $metadataObjectBefore = Get-ADReplicationAttributeMetadata $krbTgtObjectBeforeDN -Server $targetedADdomainRWDC
202 $metadataObjectBeforeAttribPwdLastSet = $null
203 $metadataObjectBeforeAttribPwdLastSet = $metadataObjectBefore | ?{$_.AttributeName -eq "pwdLastSet"}
204 $orgRWDCNTDSSettingsObjectDNBefore = $null
205 $orgRWDCNTDSSettingsObjectDNBefore = $metadataObjectBeforeAttribPwdLastSet.LastOriginatingChangeDirectoryServerIdentity
206 $metadataObjectBeforeAttribPwdLastSetOrgRWDCFQDN = $null
207 If ($orgRWDCNTDSSettingsObjectDNBefore) {
208 # Strip "CN=NTDS Settings," To End Up With The Server Object DN
209 $orgRWDCServerObjectDNBefore = $null
210 $orgRWDCServerObjectDNBefore = $orgRWDCNTDSSettingsObjectDNBefore.SubString(("CN=NTDS Settings,").Length)
211 # Connect To The Server Object DN
212 $orgRWDCServerObjectObjBefore = $null
213 $orgRWDCServerObjectObjBefore = ([ADSI]"LDAP://$targetedADdomainRWDC/$orgRWDCServerObjectDNBefore")
214 $metadataObjectBeforeAttribPwdLastSetOrgRWDCFQDN = $orgRWDCServerObjectObjBefore.dnshostname[0]
215 } Else {
216 $metadataObjectBeforeAttribPwdLastSetOrgRWDCFQDN = "RWDC Demoted"
217 }
218 $metadataObjectBeforeAttribPwdLastSetOrgTime = $null
219 $metadataObjectBeforeAttribPwdLastSetOrgTime = Get-Date $($metadataObjectBeforeAttribPwdLastSet.LastOriginatingChangeTime) -f "yyyy-MM-dd HH:mm:ss"
220 $metadataObjectBeforeAttribPwdLastSetVersion = $null
221 $metadataObjectBeforeAttribPwdLastSetVersion = $metadataObjectBeforeAttribPwdLastSet.Version
222
223 Logging " --> RWDC To Reset Password On.............: '$targetedADdomainRWDC'"
224 Logging " --> sAMAccountName Of KrbTgt Account......: '$krbTgtSamAccountName'"
225 Logging " --> Distinguished Name Of KrbTgt Account..: '$krbTgtObjectBeforeDN'"
226
227 # Specify The Number Of Characters The Generate Password Should Contain
228 $passwordNrChars = 64
229 Logging " --> Number Of Chars For Pwd Generation....: '$passwordNrChars'"
230 # Generate A New Password With The Specified Length (Text)
231 $newKrbTgtPassword = $null
232 $newKrbTgtPassword = (generateNewComplexPassword $passwordNrChars).ToString()
233 # Convert The Text Based Version Of The New Password To A Secure String
234 $newKrbTgtPasswordSecure = $null
235 $newKrbTgtPasswordSecure = ConvertTo-SecureString $newKrbTgtPassword -AsPlainText -Force
236 # Try To Set The New Password On The Targeted KrbTgt Account And If Not Successfull Throw Error
237 Try {
238 Set-ADAccountPassword -Identity $krbTgtObjectBeforeDN -Server $targetedADdomainRWDC -Reset -NewPassword $newKrbTgtPasswordSecure
239 } Catch {
240 Logging ""
241 Logging " --> Setting the new password for [$krbTgtObjectBeforeDN] FAILED on RWDC [$targetedADdomainRWDC]!..." "ERROR"
242 Logging "" "ERROR"
243 }
244
245 # Retrieve The KrgTgt Object In The AD Domain AFTER THE PASSWORD SET
246 $krbTgtObjectAfter = $null
247 $krbTgtObjectAfter = Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Properties * -Server $targetedADdomainRWDC
248 # Get The DN Of The KrgTgt Object In The AD Domain AFTER THE PASSWORD SET
249 $krbTgtObjectAfterDN = $null
250 $krbTgtObjectAfterDN = $krbTgtObjectAfter.DistinguishedName
251 # Get The Password Last Set Value From The KrgTgt Object In The AD Domain AFTER THE PASSWORD SET
252 $krbTgtObjectAfterPwdLastSet = $null
253 $krbTgtObjectAfterPwdLastSet = Get-Date $([datetime]::fromfiletime($krbTgtObjectAfter.pwdLastSet)) -f "yyyy-MM-dd HH:mm:ss"
254 # Get The Metadata Of The Object, And More Specific Of The pwdLastSet Attribute Of That Object AFTER THE PASSWORD SET
255 $metadataObjectAfter = $null
256 $metadataObjectAfter = Get-ADReplicationAttributeMetadata $krbTgtObjectAfterDN -Server $targetedADdomainRWDC
257 $metadataObjectAfterAttribPwdLastSet = $null
258 $metadataObjectAfterAttribPwdLastSet = $metadataObjectAfter | ?{$_.AttributeName -eq "pwdLastSet"}
259 $orgRWDCNTDSSettingsObjectDNAfter = $null
260 $orgRWDCNTDSSettingsObjectDNAfter = $metadataObjectAfterAttribPwdLastSet.LastOriginatingChangeDirectoryServerIdentity
261 $metadataObjectAfterAttribPwdLastSetOrgRWDCFQDN = $null
262 If ($orgRWDCNTDSSettingsObjectDNAfter) {
263 # Strip "CN=NTDS Settings," To End Up With The Server Object DN
264 $orgRWDCServerObjectDNAfter = $null
265 $orgRWDCServerObjectDNAfter = $orgRWDCNTDSSettingsObjectDNAfter.SubString(("CN=NTDS Settings,").Length)
266 # Connect To The Server Object DN
267 $orgRWDCServerObjectObjAfter = $null
268 $orgRWDCServerObjectObjAfter = ([ADSI]"LDAP://$targetedADdomainRWDC/$orgRWDCServerObjectDNAfter")
269 $metadataObjectAfterAttribPwdLastSetOrgRWDCFQDN = $orgRWDCServerObjectObjAfter.dnshostname[0]
270 } Else {
271 $metadataObjectAfterAttribPwdLastSetOrgRWDCFQDN = "RWDC Demoted"
272 }
273 $metadataObjectAfterAttribPwdLastSetOrgTime = $null
274 $metadataObjectAfterAttribPwdLastSetOrgTime = Get-Date $($metadataObjectAfterAttribPwdLastSet.LastOriginatingChangeTime) -f "yyyy-MM-dd HH:mm:ss"
275 $metadataObjectAfterAttribPwdLastSetVersion = $null
276 $metadataObjectAfterAttribPwdLastSetVersion = $metadataObjectAfterAttribPwdLastSet.Version
277 Logging ""
278 Logging " --> Previous Password Set Date/Time.......: '$krbTgtObjectBeforePwdLastSet'"
279 If ($krbTgtObjectAfterPwdLastSet -ne $krbTgtObjectBeforePwdLastSet) {
280 Logging " --> New Password Set Date/Time............: '$krbTgtObjectAfterPwdLastSet'"
281 }
282 Logging ""
283 Logging " --> Previous Originating RWDC.............: '$metadataObjectBeforeAttribPwdLastSetOrgRWDCFQDN'"
284 If ($krbTgtObjectAfterPwdLastSet -ne $krbTgtObjectBeforePwdLastSet) {
285 Logging " --> New Originating RWDC..................: '$metadataObjectAfterAttribPwdLastSetOrgRWDCFQDN'"
286 }
287 Logging ""
288 Logging " --> Previous Originating Time.............: '$metadataObjectBeforeAttribPwdLastSetOrgTime'"
289 If ($krbTgtObjectAfterPwdLastSet -ne $krbTgtObjectBeforePwdLastSet) {
290 Logging " --> New Originating Time..................: '$metadataObjectAfterAttribPwdLastSetOrgTime'"
291 }
292 Logging ""
293 Logging " --> Previous Version Of Attribute Value...: '$metadataObjectBeforeAttribPwdLastSetVersion'"
294 If ($krbTgtObjectAfterPwdLastSet -ne $krbTgtObjectBeforePwdLastSet) {
295 Logging " --> New Version Of Attribute Value........: '$metadataObjectAfterAttribPwdLastSetVersion'"
296 }
297
298 # Check And Confirm If The Password Value Has Been Updated By Comparing The Password Last Set Before And After The Reset
299 If ($krbTgtObjectAfterPwdLastSet -ne $krbTgtObjectBeforePwdLastSet) {
300 Logging ""
301 Logging " --> The new password for [$krbTgtObjectAfterDN] HAS BEEN SET on RWDC [$targetedADdomainRWDC]!..." "REMARK"
302 Logging "" "REMARK"
303 }
304}
305
306### FUNCTION: Replicate Single AD Object
307# INFO: https://msdn.microsoft.com/en-us/library/cc223306.aspx
308Function replicateSingleADObject($sourceDCNTDSSettingsObjectDN, $targetDCFQDN, $objectDN, $contentScope) {
309 # Define And Target The root DSE Context
310 $rootDSE = $null
311 $rootDSE = [ADSI]"LDAP://$targetDCFQDN/rootDSE"
312 # Perform A Replicate Single Object For The Complete Object
313 If ($contentScope -eq "Full") {
314 $rootDSE.Put("replicateSingleObject",$sourceDCNTDSSettingsObjectDN+":"+$objectDN)
315 }
316 # Perform A Replicate Single Object For Obnly The Secrets Of The Object
317 If ($contentScope -eq "Secrets") {
318 $rootDSE.Put("replicateSingleObject",$sourceDCNTDSSettingsObjectDN+":"+$objectDN+":SECRETS_ONLY")
319 }
320 # Commit The Change To The Operational Attribute
321 $rootDSE.SetInfo()
322}
323
324### FUNCTION: Delete/Cleanup Temporary Canary Object
325Function deleteTempCanaryObject($targetedADdomainRWDC, $targetObjectToCheckDN) {
326 # Try To Delete The Canary Object In The AD Domain And If Not Successfull Throw Error
327 Try {
328 Remove-ADObject -Identity $targetObjectToCheckDN -Server $targetedADdomainRWDC -Confirm:$false
329 } Catch {
330 Logging " --> Temp Canary Object [$targetObjectToCheckDN] FAILED TO BE DELETED on RWDC [$targetedADdomainRWDC]!..." "ERROR"
331 Logging " --> Manually delete the Temp Canary Object [$targetObjectToCheckDN] on RWDC [$targetedADdomainRWDC]!..." "ERROR"
332 Logging "" "ERROR"
333 }
334 # Retrieve The Temporary Canary Object From The AD Domain And If It Does Not Exist It Was Deleted Successfully
335 $targetObjectToCheck = $null
336 $targetObjectToCheck = Get-ADObject -LDAPFilter "(distinguishedName=$targetObjectToCheckDN)" -Server $targetedADdomainRWDC
337 If (!$targetObjectToCheck) {
338 Logging " --> Temp Canary Object [$targetObjectToCheckDN] DELETED on RWDC [$targetedADdomainRWDC]!..." "REMARK"
339 Logging "" "REMARK"
340 }
341}
342
343### FUNCTION: Check AD Replication Convergence
344Function checkADReplicationConvergence($targetedADdomainFQDN, $targetedADdomainSourceRWDCFQDN, $targetObjectToCheckDN, $listOfDCsToCheckObjectOnStart, $listOfDCsToCheckObjectOnEnd, $modeOfOperationNr) {
345 # Determine The Starting Time
346 $startDateTime = Get-Date
347 # Counter
348 $c = 0
349 # Boolean To Use In The While Condition
350 $continue = $true
351 # The Delay In Seconds Before The Next Check Iteration
352 $delay = 0.1
353
354 While($continue) {
355 $c++
356 $oldpos = $host.UI.RawUI.CursorPosition
357 Logging ""
358 Logging " =================================================================== CHECK $c ==================================================================="
359 Logging ""
360 # Wait For The Duration Of The Configured Delay Before Trying Again
361 Start-Sleep $delay
362 # Variable Specifying The Object Is In Sync
363 $replicated = $true
364
365 # For Each DC To Check On The Starting List With All DCs To Check Execute The Following...
366 ForEach ($dcToCheck in $listOfDCsToCheckObjectOnStart) {
367 # HostName Of The DC To Check
368 $dcToCheckHostName = $null
369 $dcToCheckHostName = $dcToCheck."Host Name"
370 # Is The DC To Check Also The PDC?
371 $dcToCheckIsPDC = $null
372 $dcToCheckIsPDC = $dcToCheck.PDC
373 # SiteName Of The DC To Check
374 $dcToCheckSiteName = $null
375 $dcToCheckSiteName = $dcToCheck."Site Name"
376 # Type (RWDC Or RODC) Of The DC To Check
377 $dcToCheckDSType = $null
378 $dcToCheckDSType = $dcToCheck."DS Type"
379 # IP Address Of The DC To Check
380 $dcToCheckIPAddress = $null
381 $dcToCheckIPAddress = $dcToCheck."IP Address"
382 # Reachability Of The DC To Check
383 $dcToCheckReachability = $null
384 $dcToCheckReachability = $dcToCheck.Reachable
385 # HostName Of The Source RWDC Of The DC To Check
386 $dcToCheckSourceRWDCFQDN = $null
387 $dcToCheckSourceRWDCFQDN = $dcToCheck."Source RWDC FQDN"
388 # DSA DN Of The Source RWDC Of The DC To Check
389 $dcToCheckSourceRWDCNTDSSettingsObjectDN = $null
390 $dcToCheckSourceRWDCNTDSSettingsObjectDN = $dcToCheck."Source RWDC DSA"
391
392 # When Running Mode 3 (Using TEST/BOGUS KrbTgt Accounts) Or Mode 4 (Using PROD/REAL KrbTgt Accounts)
393 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
394 # Retrieve The Object From The Source Originating RWDC
395 $objectOnSourceOrgRWDC = $null
396 $objectOnSourceOrgRWDC = Get-ADObject -Identity $targetObjectToCheckDN -Properties * -Server $targetedADdomainSourceRWDCFQDN
397 # Retrieve The Password Last Set Of The Object On The Source Originating RWDC
398 $objectOnSourceOrgRWDCPwdLastSet = $null
399 $objectOnSourceOrgRWDCPwdLastSet = Get-Date $([datetime]::fromfiletime($objectOnSourceOrgRWDC.pwdLastSet)) -f "yyyy-MM-dd HH:mm:ss"
400 }
401
402 # When The DC To Check Is Also The Source (Originating) RWDC
403 If ($dcToCheckHostName -eq $targetedADdomainSourceRWDCFQDN) {
404 Logging " - Contacting DC in AD domain ...[$($dcToCheckHostName.ToUpper())]...(SOURCE RWDC)"
405 Logging " * DC is Reachable..." "SUCCESS"
406 # For Mode 2 Only
407 If ($modeOfOperationNr -eq 2) {
408 Logging " * Object [$targetObjectToCheckDN] exists in the AD database" "SUCCESS"
409 }
410 # For Mode 3 Or 4 Only
411 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
412 Logging " * The new password for Object [$targetObjectToCheckDN] exists in the AD database" "SUCCESS"
413 }
414 Logging ""
415 CONTINUE
416 }
417
418 Logging " - Contacting DC in AD domain ...[$($dcToCheckHostName.ToUpper())]..."
419 If ($dcToCheckReachability) {
420 # When The DC To Check Is Reachable
421
422 Logging " * DC is Reachable..." "SUCCESS"
423 # When The DC To Check Is Not The Source (Originating) RWDC
424 If ($dcToCheckHostName -ne $targetedADdomainSourceRWDCFQDN) {
425 # As The DSA DN Used The DSA DN Of The Source (Originating) RWDC Of The DC Being Checked
426 $sourceDCNTDSSettingsObjectDN = $dcToCheckSourceRWDCNTDSSettingsObjectDN
427 # For Mode 2 Perform A Full Replicate Single Object
428 If ($modeOfOperationNr -eq 2) {
429 $contentScope = "Full"
430 }
431 # For Mode 3 Or 4
432 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
433 # If The DC Being Checked Is An RWDC Perform A Full Replicate Single Object
434 If ($dcToCheckDSType -eq "Read/Write") {
435 $contentScope = "Full"
436 }
437 # If The DC Being Checked Is An RODC Perform A Partial Replicate Single Object (Secrets Only)
438 If ($dcToCheckDSType -eq "Read-Only") {
439 $contentScope = "Secrets"
440 }
441 }
442 # Execute The Replicate Single Object Function For The Targeted Object To Check
443 replicateSingleADObject $sourceDCNTDSSettingsObjectDN $dcToCheckHostName $targetObjectToCheckDN $contentScope
444 }
445 # For Mode 2 From The DC to Check Retrieve The AD Object Of The Temporary Canary Object That Was Created On The Source (Originating) RWDC
446 If ($modeOfOperationNr -eq 2) {
447 $targetObjectToCheck = $null
448 $targetObjectToCheck = Get-ADObject -LDAPFilter "(distinguishedName=$targetObjectToCheckDN)" -Server $dcToCheckHostName
449 }
450 # For Mode 3 Or 4 From The DC to Check Retrieve The AD Object Of The Targeted KrbTgt Account (And Its Password Last Set) That Had Its Password Reset On The Source (Originating) RWDC
451 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
452 # Retrieve The Object From The Target DC
453 $objectOnTargetDC = $null
454 $objectOnTargetDC = Get-ADObject -Identity $targetObjectToCheckDN -Properties * -Server $dcToCheckHostName
455 # Retrieve The Password Last Set Of The Object On The Target DC
456 $objectOnTargetDCPwdLastSet = $null
457 $objectOnTargetDCPwdLastSet = Get-Date $([datetime]::fromfiletime($objectOnTargetDC.pwdLastSet)) -f "yyyy-MM-dd HH:mm:ss"
458 }
459 } Else {
460 # When The DC To Check Is Not Reachable
461
462 Logging " * DC is NOT reachable..." "ERROR"
463 }
464
465 If ($dcToCheckReachability) {
466 # When The DC To Check Is Reachable
467
468 If ($targetObjectToCheck -Or $objectOnTargetDCPwdLastSet -eq $objectOnSourceOrgRWDCPwdLastSet) {
469 # If The Target Object To Check Does Exist Or Its Password Last Set Does Match With The Password Last Set Of The Object On The Source (Originating) RWDC
470
471 # For Mode 2 Only
472 If ($modeOfOperationNr -eq 2) {
473 Logging " * Object [$targetObjectToCheckDN] now does exist in the AD database" "SUCCESS"
474 }
475 # For Mode 3 Or 4 Only
476 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
477 Logging " * The new password for Object [$targetObjectToCheckDN] now does exist in the AD database" "SUCCESS"
478 }
479 Logging "" "SUCCESS"
480 # If The DC To Check Does Not Yet Exist On The Ending List With All DCs That Were Checked, Then Add It To The Ending List
481 If (!($listOfDCsToCheckObjectOnEnd | ?{$_."Host Name" -eq $dcToCheckHostName})) {
482 # Define The Columns For This DC To Be Filled In
483 $listOfDCsToCheckObjectOnEndObj = "" | Select "Host Name",PDC,"Site Name","DS Type","IP Address",Reachable,"Source RWDC FQDN",Time
484 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
485 $listOfDCsToCheckObjectOnEndObj."Host Name" = $null
486 $listOfDCsToCheckObjectOnEndObj."Host Name" = $dcToCheckHostName
487 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
488 $listOfDCsToCheckObjectOnEndObj.PDC = $null
489 $listOfDCsToCheckObjectOnEndObj.PDC = $dcToCheckIsPDC
490 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
491 $listOfDCsToCheckObjectOnEndObj."Site Name" = $null
492 $listOfDCsToCheckObjectOnEndObj."Site Name" = $dcToCheckSiteName
493 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
494 $listOfDCsToCheckObjectOnEndObj."DS Type" = $null
495 $listOfDCsToCheckObjectOnEndObj."DS Type" = $dcToCheckDSType
496 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
497 $listOfDCsToCheckObjectOnEndObj."IP Address" = $null
498 $listOfDCsToCheckObjectOnEndObj."IP Address" = $dcToCheckIPAddress
499 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
500 $listOfDCsToCheckObjectOnEndObj.Reachable = $null
501 $listOfDCsToCheckObjectOnEndObj.Reachable = $dcToCheckReachability
502 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
503 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $null
504 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $targetedADdomainSourceRWDCFQDN
505 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
506 $listOfDCsToCheckObjectOnEndObj.Time = ("{0:n2}" -f ((Get-Date) - $startDateTime).TotalSeconds)
507 # Add The Row For The DC To The Table
508 $listOfDCsToCheckObjectOnEnd += $listOfDCsToCheckObjectOnEndObj
509 }
510 } Else {
511 # If The Target Object To Check Does Not Exist Or Its Password Last Set Does Not Match (Yet) With The Password Last Set Of The Object On The Source (Originating) RWDC
512
513 # For Mode 2 Only
514 If ($modeOfOperationNr -eq 2) {
515 Logging " * Object [$targetObjectToCheckDN] does NOT exist yet in the AD database" "WARNING"
516 }
517 # For Mode 3 Or 4 Only
518 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
519 Logging " * The new password for Object [$targetObjectToCheckDN] does NOT exist yet in the AD database" "WARNING"
520 }
521 Logging "" "WARNING"
522 # Variable Specifying The Object Is Not In Sync
523 $replicated = $false
524 }
525 } Else {
526 # When The DC To Check Is Not Reachable
527 Logging " * Unable to connect to DC and check for the Temp Canary Object..." "ERROR"
528 Logging "" "WARNING"
529 # If The DC To Check Does Not Yet Exist On The Ending List With All DCs That Were Checked, Then Add It To The Ending List
530 If (!($listOfDCsToCheckObjectOnEnd | ?{$_."Host Name" -eq $dcToCheckHostName})) {
531 # Define The Columns For This DC To Be Filled In
532 $listOfDCsToCheckObjectOnEndObj = "" | Select "Host Name",PDC,"Site Name","DS Type","IP Address",Reachable,"Source RWDC FQDN",Time
533 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
534 $listOfDCsToCheckObjectOnEndObj."Host Name" = $null
535 $listOfDCsToCheckObjectOnEndObj."Host Name" = $dcToCheckHostName
536 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
537 $listOfDCsToCheckObjectOnEndObj.PDC = $null
538 $listOfDCsToCheckObjectOnEndObj.PDC = $dcToCheckIsPDC
539 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
540 $listOfDCsToCheckObjectOnEndObj."Site Name" = $null
541 $listOfDCsToCheckObjectOnEndObj."Site Name" = $dcToCheckSiteName
542 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
543 $listOfDCsToCheckObjectOnEndObj."DS Type" = $null
544 $listOfDCsToCheckObjectOnEndObj."DS Type" = $dcToCheckDSType
545 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
546 $listOfDCsToCheckObjectOnEndObj."IP Address" = $null
547 $listOfDCsToCheckObjectOnEndObj."IP Address" = $dcToCheckIPAddress
548 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
549 $listOfDCsToCheckObjectOnEndObj.Reachable = $null
550 $listOfDCsToCheckObjectOnEndObj.Reachable = $dcToCheckReachability
551 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
552 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $null
553 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $targetedADdomainSourceRWDCFQDN
554 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
555 $listOfDCsToCheckObjectOnEndObj.Time = "<Fail>"
556 # Add The Row For The DC To The Table
557 $listOfDCsToCheckObjectOnEnd += $listOfDCsToCheckObjectOnEndObj
558 }
559 }
560 }
561
562 # If The Object Is In Sync
563 If ($replicated) {
564 # Do Not Continue For The DC That Is Being Checked
565 $continue = $false
566 } Else {
567 # Do Continue For The DC That Is Being Checked And Move The Cursor Back To The Initial Position
568 $host.UI.RawUI.CursorPosition = $oldpos
569 }
570 }
571
572 # Determine The Ending Time
573 $endDateTime = Get-Date
574 # Calculate The Duration
575 $duration = "{0:n2}" -f ($endDateTime.Subtract($startDateTime).TotalSeconds)
576 Logging ""
577 Logging " --> Start Time......: $(Get-Date $startDateTime -format 'yyyy-MM-dd HH:mm:ss')"
578 Logging " --> End Time........: $(Get-Date $endDateTime -format 'yyyy-MM-dd HH:mm:ss')"
579 Logging " --> Duration........: $duration Seconds"
580 Logging ""
581
582 # If Mode 2 Was Being Executed, Then Delete The Temp Canary Object On The Source (Originating) RWDC
583 If ($modeOfOperationNr -eq 2) {
584 # Retrieve The Temp Canary Object From The Source (Originating) RWDC
585 $targetObjectToCheck = $null
586 $targetObjectToCheck = Get-ADObject -LDAPFilter "(distinguishedName=$targetObjectToCheckDN)" -Server $targetedADdomainSourceRWDCFQDN
587 # If The Temp Canary Object Exists On The Source (Originating) RWDC, Then Delete It
588 If ($targetObjectToCheck) {
589 # Execute The Deletion Of The Temp Canary Object On The Source (Originating) RWDC
590 deleteTempCanaryObject $targetedADdomainSourceRWDCFQDN $targetObjectToCheckDN
591 }
592 }
593
594 # Sort The Ending List With All DCs That Were Checked
595 $listOfDCsToCheckObjectOnEnd = $listOfDCsToCheckObjectOnEnd | Sort-Object -Property @{Expression = "Time"; Descending = $False} | FT -Autosize
596 Logging ""
597 Logging "List Of DCs In AD Domain '$targetedADdomainFQDN' And Their Timing..."
598 Logging ""
599 Logging "$($listOfDCsToCheckObjectOnEnd | Out-String)"
600 Logging ""
601}
602
603### FUNCTION: Create Test Krbtgt Accounts
604Function createTestKrbTgtADAccount($targetedADdomainRWDC, $krbTgtSamAccountName, $krbTgtUse) {
605 # Determine The DN Of The Default NC Of The Targeted Domain
606 $targetedADdomainDefaultNC = $null
607 $targetedADdomainDefaultNC = (Get-ADRootDSE -Server $targetedADdomainRWDC).defaultNamingContext
608 # Determine The DN Of The Users Container Of The Targeted Domain
609 $containerForTestKrbTgtAccount = $null
610 $containerForTestKrbTgtAccount = "CN=Users," + $targetedADdomainDefaultNC
611 # Set The SamAccountName For The Test/Bogus KrbTgt Account
612 $testKrbTgtObjectSamAccountName = $null
613 $testKrbTgtObjectSamAccountName = $krbTgtSamAccountName
614 # Set The Name For The Test/Bogus KrbTgt Account
615 $testKrbTgtObjectName = $null
616 $testKrbTgtObjectName = $testKrbTgtObjectSamAccountName
617 # Set The Description For The Test/Bogus KrbTgt Account
618 $testKrbTgtObjectDescription = $null
619 # Set The Description For The Test/Bogus KrbTgt Account For RWDCs
620 If ($krbTgtUse -eq "RWDC") {
621 $testKrbTgtObjectDescription = "Test Copy Representing '$($krbTgtSamAccountName.SubString(0,$krbTgtSamAccountName.IndexOf('_TEST')))' - Key Distribution Center Service Account"
622 }
623 # Set The Description For The Test/Bogus KrbTgt Account For RODCs
624 If ($krbTgtUse -eq "RODC") {
625 $testKrbTgtObjectDescription = "Test Copy Representing '$($krbTgtSamAccountName.SubString(0,$krbTgtSamAccountName.IndexOf('_TEST')))' - Key Distribution Center service account for read-only domain controller"
626 }
627 # Generate The DN Of The Test KrbTgt Object
628 $testKrbTgtObjectDN = $null
629 $testKrbTgtObjectDN = "CN=" + $testKrbTgtObjectName + "," + $containerForTestKrbTgtAccount
630 Logging " --> RWDC To Create Object On..............: '$targetedADdomainRWDC'"
631 Logging " --> Full Name Test KrbTgt Account.........: '$testKrbTgtObjectName'"
632 Logging " --> Description...........................: '$testKrbTgtObjectDescription'"
633 Logging " --> Container Test KrbTgt Account.........: '$containerForTestKrbTgtAccount'"
634 # If The Test/Bogus KrbTgt Account Is Used By RWDCs
635 If ($krbTgtUse -eq "RWDC") {
636 Logging " --> Made Member Of RODC PRP Group.........: 'Denied RODC Password Replication Group'"
637 }
638 # If The Test/Bogus KrbTgt Account Is Used By RODCs
639 If ($krbTgtUse -eq "RODC") {
640 Logging " --> Made Member Of RODC PRP Group.........: 'Allowed RODC Password Replication Group'"
641 }
642 Logging ""
643 # Check If The Test/Bogus KrbTgt Account Already Exists In AD
644 $testKrbTgtObject = $null
645 $testKrbTgtObject = Get-ADUser -LDAPFilter "(distinguishedName=$testKrbTgtObjectDN)" -Server $targetedADdomainRWDC
646 If ($testKrbTgtObject) {
647 # If The Test/Bogus KrbTgt Account Already Exists In AD
648 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] ALREADY EXISTS on RWDC [$targetedADdomainRWDC]!..." "REMARK"
649 Logging "" "REMARK"
650 } Else {
651 # If The Test/Bogus KrbTgt Account Does Not Exist Yet In AD
652
653 # Specify The Number Of Characters The Generate Password Should Contain
654 $passwordNrChars = 64
655 # Generate A New Password With The Specified Length (Text)
656 $krbTgtPassword = $null
657 $krbTgtPassword = (generateNewComplexPassword $passwordNrChars).ToString()
658 # Convert The Text Based Version Of The New Password To A Secure String
659 $krbTgtPasswordSecure = $null
660 $krbTgtPasswordSecure = ConvertTo-SecureString $krbTgtPassword -AsPlainText -Force
661 # Try To Create The Test/Bogus KrbTgt Account In The AD Domain And If Not Successfull Throw Error
662 Try {
663 New-ADUser -SamAccountName $testKrbTgtObjectSamAccountName -Name $testKrbTgtObjectName -DisplayName $testKrbTgtObjectName -Path $containerForTestKrbTgtAccount -AccountPassword $krbTgtPasswordSecure -Enabled $False -description $testKrbTgtObjectDescription -Server $targetedADdomainRWDC
664 } Catch {
665 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] FAILED TO BE CREATED on RWDC [$targetedADdomainRWDC]!..." "ERROR"
666 Logging "" "ERROR"
667 }
668 # Check The The Test/Bogus KrbTgt Account Exists And Was created In AD
669 $testKrbTgtObject = $null
670 $testKrbTgtObject = Get-ADObject -LDAPFilter "(&(objectClass=user)(name=$testKrbTgtObjectName))" -Server $targetedADdomainRWDC
671 If ($testKrbTgtObject) {
672 $testKrbTgtObjectDN = $null
673 $testKrbTgtObjectDN = $testKrbTgtObject.DistinguishedName
674 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] CREATED on RWDC [$targetedADdomainRWDC]!..." "REMARK"
675 Logging "" "REMARK"
676 }
677 }
678 If ($testKrbTgtObject) {
679 # If The Test/Bogus KrbTgt Account Already Exists In AD
680
681 # If The Test/Bogus KrbTgt Account Is Used By RWDCs
682 If ($krbTgtUse -eq "RWDC") {
683 # Check If The Test/Bogus KrbTgt Account Is Already A Member Of The Specified AD Group On The Targeted Originating RWDC
684 $membershipDeniedPRPGroup = $null
685 $membershipDeniedPRPGroup = Get-ADGroupMember -Identity 'Denied RODC Password Replication Group' -Server $targetedADdomainRWDC | ?{$_.distinguishedName -eq $testKrbTgtObjectDN}
686 If ($membershipDeniedPRPGroup) {
687 # If The Test/Bogus KrbTgt Account Is Already A Member Of The Specified AD Group On The Targeted Originating RWDC
688
689 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] ALREADY MEMBER OF [Denied RODC Password Replication Group]!..." "REMARK"
690 Logging "" "REMARK"
691 } Else {
692 # If The Test/Bogus KrbTgt Account Is Not Yet A Member Of The Specified AD Group On The Targeted Originating RWDC, Then Add It As A Member
693
694 Add-ADGroupMember -Identity 'Denied RODC Password Replication Group' -Members $testKrbTgtObjectDN -Server $targetedADdomainRWDC
695 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] ADDED AS MEMBER OF [Denied RODC Password Replication Group]!..." "REMARK"
696 Logging "" "REMARK"
697 }
698 }
699 # If The Test/Bogus KrbTgt Account Is Used By RODCs
700 If ($krbTgtUse -eq "RODC") {
701 # Check If The Test/Bogus KrbTgt Account Is Already A Member Of The Specified AD Group On The Targeted Originating RWDC
702 $membershipAllowedPRPGroup = $null
703 $membershipAllowedPRPGroup = Get-ADGroupMember -Identity 'Allowed RODC Password Replication Group' -Server $targetedADdomainRWDC | ?{$_.distinguishedName -eq $testKrbTgtObjectDN}
704 If ($membershipAllowedPRPGroup) {
705 # If The Test/Bogus KrbTgt Account Is Already A Member Of The Specified AD Group On The Targeted Originating RWDC
706
707 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] ALREADY MEMBER OF [Allowed RODC Password Replication Group]!..." "REMARK"
708 Logging "" "REMARK"
709 } Else {
710 # If The Test/Bogus KrbTgt Account Is Not Yet A Member Of The Specified AD Group On The Targeted Originating RWDC, Then Add It As A Member
711
712 Add-ADGroupMember -Identity 'Allowed RODC Password Replication Group' -Members $testKrbTgtObjectDN -Server $targetedADdomainRWDC
713 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] ADDED AS MEMBER OF [Allowed RODC Password Replication Group]!..." "REMARK"
714 Logging "" "REMARK"
715 }
716 }
717 }
718}
719
720### FUNCTION: Delete Test Krbtgt Accounts
721Function deleteTestKrbTgtADAccount($targetedADdomainRWDC, $krbTgtSamAccountName) {
722 # Check If The Test/Bogus KrbTgt Account Exists In AD
723 $testKrbTgtObject = $null
724 $testKrbTgtObject = Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Server $targetedADdomainRWDC
725 If ($testKrbTgtObject) {
726 # If It Does Exist In AD
727 $testKrbTgtObjectDN = $null
728 $testKrbTgtObjectDN = $testKrbTgtObject.DistinguishedName
729 Logging " --> RWDC To Delete Object On..............: '$targetedADdomainRWDC'"
730 Logging " --> Test KrbTgt Account DN................: '$testKrbTgtObjectDN'"
731 Logging ""
732 Remove-ADUser -Identity $testKrbTgtObjectDN -Server $targetedADdomainRWDC -Confirm:$false
733 $testKrbTgtObject = $null
734 $testKrbTgtObject = Get-ADUser -LDAPFilter "(distinguishedName=$testKrbTgtObjectDN)" -Server $targetedADdomainRWDC
735 If (!$testKrbTgtObject) {
736 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] DELETED on RWDC [$targetedADdomainRWDC]!..." "REMARK"
737 Logging "" "REMARK"
738 } Else {
739 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] FAILED TO BE DELETED on RWDC [$targetedADdomainRWDC]!..." "ERROR"
740 Logging " --> Manually delete the Test KrbTgt Account [$testKrbTgtObjectDN] on RWDC [$targetedADdomainRWDC]!..." "ERROR"
741 Logging "" "ERROR"
742 }
743 } Else {
744 # If It Does Not Exist In AD
745
746 Logging " --> Test KrbTgt Account [$testKrbTgtObjectDN] DOES NOT EXIST on RWDC [$targetedADdomainRWDC]!..." "WARNING"
747 Logging "" "WARNING"
748 }
749}
750
751### Clear The Screen
752Clear-Host
753
754### Configure The Appropriate Screen And Buffer Size To Make Sure Everything Fits Nicely
755$uiConfig = (Get-Host).UI.RawUI
756$uiConfig.WindowTitle = "+++ RESET KRBTGT ACCOUNT PASSWORD FOR RWDCs/RODCs +++"
757$uiConfig.ForegroundColor = "Yellow"
758$uiConfigBufferSize = $uiConfig.BufferSize
759$uiConfigBufferSize.Width = 240
760$uiConfigBufferSize.Height = 9999
761$uiConfigScreenSizeMax = $uiConfig.MaxPhysicalWindowSize
762$uiConfigScreenSizeMaxWidth = $uiConfigScreenSizeMax.Width
763$uiConfigScreenSizeMaxHeight = $uiConfigScreenSizeMax.Height
764$uiConfigScreenSize = $uiConfig.WindowSize
765If ($uiConfigScreenSizeMaxWidth -lt 240) {
766 $uiConfigScreenSize.Width = $uiConfigScreenSizeMaxWidth
767} Else {
768 $uiConfigScreenSize.Width = 240
769}
770If ($uiConfigScreenSizeMaxHeight -lt 75) {
771 $uiConfigScreenSize.Height = $uiConfigScreenSizeMaxHeight - 5
772} Else {
773 $uiConfigScreenSize.Height = 75
774}
775$uiConfig.BufferSize = $uiConfigBufferSize
776$uiConfig.WindowSize = $uiConfigScreenSize
777
778### Definition Of Some Constants
779$execDateTime = Get-Date
780$execDateTimeYEAR = $execDateTime.Year
781$execDateTimeMONTH = $execDateTime.Month
782$execDateTimeDAY = $execDateTime.Day
783$execDateTimeHOUR = $execDateTime.Hour
784$execDateTimeMINUTE = $execDateTime.Minute
785$execDateTimeSECOND = $execDateTime.Second
786$execDateTimeCustom = [STRING]$execDateTimeYEAR + "-" + $("{0:D2}" -f $execDateTimeMONTH) + "-" + $("{0:D2}" -f $execDateTimeDAY) + "_" + $("{0:D2}" -f $execDateTimeHOUR) + "." + $("{0:D2}" -f $execDateTimeMINUTE) + "." + $("{0:D2}" -f $execDateTimeSECOND)
787$execDateTimeCustom1 = [STRING]$execDateTimeYEAR + $("{0:D2}" -f $execDateTimeMONTH) + $("{0:D2}" -f $execDateTimeDAY) + $("{0:D2}" -f $execDateTimeHOUR) + $("{0:D2}" -f $execDateTimeMINUTE) + $("{0:D2}" -f $execDateTimeSECOND)
788$adRunningUserAccount = $ENV:USERDOMAIN + "\" + $ENV:USERNAME
789$scriptFullPath = $MyInvocation.MyCommand.Definition
790$currentScriptFolderPath = Split-Path $scriptFullPath
791$localComputerName = $(Get-WmiObject -Class Win32_ComputerSystem).Name
792[string]$logFilePath = Join-Path $currentScriptFolderPath $($execDateTimeCustom + "_" + $localComputerName + "_Reset-KrbTgt-Password-For-RWDCs-And-RODCs.log")
793
794### Providing Information About What The Script Is Capable Of And How The Script Works
795Logging ""
796Logging "Do you want to read information about the script, its functions, its behavior and the impact? [YES | NO]: " "ACTION-NO-NEW-LINE"
797$yesOrNo = $null
798$yesOrNo = "YES"
799If ($yesOrNo.ToUpper() -ne "NO") {
800 $yesOrNo = "YES"
801}
802Logging ""
803Logging " --> Chosen: $yesOrNo" "REMARK"
804Logging ""
805### Loading Required PowerShell Modules
806Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
807Logging "LOADING REQUIRED POWERSHELL MODULES..." "HEADER"
808Logging ""
809# Try To Load The Required PowerShell Module. Abort Script If Not Available
810$poshModuleAD = loadPoSHModules ActiveDirectory
811If ($poshModuleAD -eq "NotAvailable") {
812 Logging ""
813 EXIT
814}
815Logging ""
816# Try To Load The Required PowerShell Module. Abort Script If Not Available
817$poshModuleGPO = loadPoSHModules GroupPolicy
818If ($poshModuleGPO -eq "NotAvailable") {
819 Logging ""
820 EXIT
821}
822Logging ""
823
824### Display And Selecting The Mode Of Operation
825Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
826Logging "SELECT THE MODE OF OPERATION..." "HEADER"
827Logging ""
828Logging "Which mode of operation do you want to execute?"
829Logging " - 4 - Real Reset Mode - Use KrbTgt PROD/REAL Accounts (Password Will Be Reset Once!)"
830Logging "Please specify the mode of operation: " "ACTION-NO-NEW-LINE"
831$modeOfOperationNr = 4
832Logging ""
833# If Anything Else Than The Allowed/Available Non-Zero Modes, Abort The Script
834If (($modeOfOperationNr -ne 1 -And $modeOfOperationNr -ne 2 -And $modeOfOperationNr -ne 3 -And $modeOfOperationNr -ne 4 -And $modeOfOperationNr -ne 8 -And $modeOfOperationNr -ne 9) -Or $modeOfOperationNr -notmatch "^[\d\.]+$") {
835 Logging " --> Chosen mode: Mode 0 - Exit Script..." "REMARK"
836 Logging ""
837
838 EXIT
839}
840# If Mode 1
841If ($modeOfOperationNr -eq 1) {
842 Logging " --> Chosen Mode: Mode 1 - Informational Mode (No Changes At All)..." "REMARK"
843 Logging ""
844}
845# If Mode 2
846If ($modeOfOperationNr -eq 2) {
847 Logging " --> Chosen Mode: Mode 2 - Simulation Mode (Temporary Canary Object Created, No Password Reset!)..." "REMARK"
848 Logging ""
849}
850# If Mode 3
851If ($modeOfOperationNr -eq 3) {
852 Logging " --> Chosen Mode: Mode 3 - Simulation Mode - Use KrbTgt TEST/BOGUS Accounts (Password Will Be Reset Once!)..." "REMARK"
853 Logging ""
854}
855# If Mode 4
856If ($modeOfOperationNr -eq 4) {
857 Logging " --> Chosen Mode: Mode 4 - Real Reset Mode - Use KrbTgt PROD/REAL Accounts (Password Will Be Reset Once!)..." "REMARK"
858 Logging ""
859}
860# If Mode 8
861If ($modeOfOperationNr -eq 8) {
862 Logging " --> Chosen Mode: Mode 8 - Create TEST KrbTgt Accounts..." "REMARK"
863 Logging ""
864}
865# If Mode 9
866If ($modeOfOperationNr -eq 9) {
867 Logging " --> Chosen Mode: Mode 9 - Cleanup TEST KrbTgt Accounts..." "REMARK"
868 Logging ""
869}
870
871### All Modes - Selecting The Target AD Domain
872Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
873Logging "SELECT THE TARGET AD DOMAIN..." "HEADER"
874Logging ""
875# Retrieve The AD Domain Of The Computer Where The Script Is Executed
876$currentADDomainOfLocalComputer = $null
877$currentADDomainOfLocalComputer = $(Get-WmiObject -Class Win32_ComputerSystem).Domain
878# Retrieve Information About The AD Forest
879$thisADForest = $null
880$thisADForest = Get-ADForest
881# Retrieve Root AD Domain Of The AD Forest
882$rootADDomainInADForest = $null
883$rootADDomainInADForest = $thisADForest.RootDomain
884# Retrieve All The AD Domains In The AD Forest
885$listOfADDomainsInADForest = $null
886$listOfADDomainsInADForest = $thisADForest.Domains
887# Retrieve The DN Of The Partitions Container In The AD Forest
888$partitionsContainerDN = $null
889$partitionsContainerDN = $thisADForest.PartitionsContainer
890# Retrieve The Mode/Functional Level Of The AD Forest
891$adForestMode = $null
892$adForestMode = $thisADForest.ForestMode
893# Define An Empty List/Table That Will Contain All AD Domains In The AD Forest And Related Information
894$tableOfADDomainsInADForest = @()
895Logging "Forest Mode/Level...: $adForestMode"
896# Set The Counter To Zero
897$nrOfDomainsInForest = 0
898# Execute For All AD Domains In The AD Forest
899$listOfADDomainsInADForest | %{
900 # Increase The Counter
901 $nrOfDomainsInForest += 1
902 # Get The FQDN Of The AD Domain
903 $domainFQDN = $null
904 $domainFQDN = $_
905 # Retrieve The Object Of The AD Domain From AD
906 $domainObj = $null
907 Try {
908 $domainObj = Get-ADDomain $domainFQDN
909 } Catch {
910 $domainObj = $null
911 }
912 # Define The Columns For This AD Domain To Be Filled In
913 $tableOfADDomainsInADForestObj = "" | Select Name,DomainSID,IsRootDomain,DomainMode,IsCurrentDomain,IsAvailable
914 # Set The Corresponding Value Of The AD Domain In The Correct Column Of The Table
915 $tableOfADDomainsInADForestObj.Name = $null
916 $tableOfADDomainsInADForestObj.Name = $domainFQDN
917 # Set The Corresponding Value Of The AD Domain In The Correct Column Of The Table
918 $tableOfADDomainsInADForestObj.DomainSID = $null
919 $tableOfADDomainsInADForestObj.DomainSID = $domainObj.DomainSID.Value
920 # Set The Corresponding Value Of The AD Domain In The Correct Column Of The Table
921 $tableOfADDomainsInADForestObj.IsRootDomain = $null
922 If ($rootADDomainInADForest -eq $domainFQDN) {
923 $tableOfADDomainsInADForestObj.IsRootDomain = "TRUE"
924 } Else {
925 $tableOfADDomainsInADForestObj.IsRootDomain = "FALSE"
926 }
927 # Set The Corresponding Value Of The AD Domain In The Correct Column Of The Table
928 $tableOfADDomainsInADForestObj.DomainMode = $null
929 If ($domainObj) {
930 $tableOfADDomainsInADForestObj.DomainMode = $domainObj.DomainMode
931 } Else {
932 $tableOfADDomainsInADForestObj.DomainMode = "AD Domain Is Not Available"
933 }
934 # Set The Corresponding Value Of The AD Domain In The Correct Column Of The Table
935 $tableOfADDomainsInADForestObj.IsCurrentDomain = $null
936 If ($domainFQDN -eq $currentADDomainOfLocalComputer) {
937 $tableOfADDomainsInADForestObj.IsCurrentDomain = "TRUE"
938 } Else {
939 $tableOfADDomainsInADForestObj.IsCurrentDomain = "FALSE"
940 }
941 # Set The Corresponding Value Of The AD Domain In The Correct Column Of The Table
942 $tableOfADDomainsInADForestObj.IsAvailable = $null
943 If ($domainObj) {
944 $tableOfADDomainsInADForestObj.IsAvailable = "TRUE"
945 } Else {
946 $tableOfADDomainsInADForestObj.IsAvailable = "FALSE"
947 }
948 # Add The Row For The AD Domain To The Table
949 $tableOfADDomainsInADForest += $tableOfADDomainsInADForestObj
950}
951# Display The List And Amount Of AD Domains
952Logging ""
953Logging "List Of AD Domains In AD Forest '$rootADDomainInADForest'..."
954Logging ""
955Logging "$($tableOfADDomainsInADForest | FT | Out-String)"
956Logging " --> Found [$nrOfDomainsInForest] AD Domain(s) In The AD Forest..." "REMARK"
957Logging ""
958# Ask Which AD Domain To Target From The Previously Presented List
959Logging "For the AD domain to be targeted, please provide the FQDN or press [ENTER] for the current AD domain: " "ACTION-NO-NEW-LINE"
960$targetedADdomainFQDN = $null
961$targetedADdomainFQDN = ""
962# If No FQDN Of An AD Domain Is Specified, Then Use The AD Domain Of The Local Computer
963If ($targetedADdomainFQDN -eq "" -Or $targetedADdomainFQDN -eq $null) {
964 $targetedADdomainFQDN = $currentADDomainOfLocalComputer
965}
966Logging ""
967Logging " --> Selected AD Domain: '$targetedADdomainFQDN'..." "REMARK"
968# Validate The Chosen AD Domain Against The List Of Available AD Domains To See If It Does Exist In The AD Forest
969$adDomainValidity = $false
970$listOfADDomainsInADForest | %{
971 $domainFQDN = $null
972 $domainFQDN = $_
973 If ($domainFQDN -eq $targetedADdomainFQDN) {
974 $adDomainValidity = $true
975 }
976}
977Logging ""
978Logging "Checking existence of the specified AD domain '$targetedADdomainFQDN' in the AD forest..."
979If ($adDomainValidity) {
980 # If The AD Domain Is Valid And Therefore Exists, Continue
981
982 Logging "" "SUCCESS"
983 Logging "The specified AD domain '$targetedADdomainFQDN' exists in the AD forest!" "SUCCESS"
984 Logging "" "SUCCESS"
985 Logging "Continuing Script..." "SUCCESS"
986 Logging "" "SUCCESS"
987} Else {
988 # If The AD Domain Is Not Valid And Therefore Does Not Exist, Abort
989
990 Logging "" "ERROR"
991 Logging "The specified AD domain '$targetedADdomainFQDN' DOES NOT exist in the AD forest!" "ERROR"
992 Logging "" "ERROR"
993 Logging "Please re-run the script and provide the FQDN of an AD domain that does exist in the AD forest..." "ERROR"
994 Logging "" "ERROR"
995 Logging "Aborting Script..." "ERROR"
996 Logging "" "ERROR"
997
998 EXIT
999}
1000
1001### All Modes - Testing If Required Permissions Are Available (Domain/Enterprise Admin Credentials)
1002Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
1003Logging "TESTING IF REQUIRED PERMISSIONS ARE AVAILABLE (DOMAIN/ENTERPRISE ADMIN CREDENTIALS)..." "HEADER"
1004Logging ""
1005
1006# Validate The User Account Running This Script Is A Member Of The Domain Admins Group Of The Targeted AD Domain
1007$targetedDomainObjectSID = ($tableOfADDomainsInADForest | ?{$_.Name -eq $targetedADdomainFQDN}).DomainSID
1008$domainAdminRID = "512"
1009$domainAdminRole = (New-Object System.Security.Principal.SecurityIdentifier($targetedDomainObjectSID + "-" + $domainAdminRID)).Translate([System.Security.Principal.NTAccount]).Value
1010$userIsDomainAdmin = $null
1011$userIsDomainAdmin = testAdminRole $domainAdminRole
1012If (!$userIsDomainAdmin) {
1013 # The User Account Running This Script Has Been Validated Not Being A Member Of The Domain Admins Group Of The Targeted AD Domain
1014
1015 # Validate The User Account Running This Script Is A Member Of The Enterprise Admins Group Of The AD Forest
1016 $forestRootDomainObjectSID = ($tableOfADDomainsInADForest | ?{$_.IsRootDomain -eq "TRUE"}).DomainSID
1017 $enterpriseAdminRID = "519"
1018 $enterpriseAdminRole = (New-Object System.Security.Principal.SecurityIdentifier($forestRootDomainObjectSID + "-" + $enterpriseAdminRID)).Translate([System.Security.Principal.NTAccount]).Value
1019 $userIsEnterpriseAdmin = $null
1020 $userIsEnterpriseAdmin = testAdminRole $enterpriseAdminRole
1021 If (!$userIsEnterpriseAdmin) {
1022 # The User Account Running This Script Has Been Validated Not Being A Member Of The Enterprise Admins Group Of The AD Forest
1023
1024 Logging "The user account IS NOT running with Domain/Enterprise Administrator equivalent permissions in the AD Domain '$targetedADdomainFQDN'!..." "ERROR"
1025 Logging "The user account IS NOT a member of '$domainAdminRole' and NOT a member of '$enterpriseAdminRole'!..." "ERROR"
1026 Logging "" "ERROR"
1027 Logging "For this script to run successfully, Domain/Enterprise Administrator equivalent permissions are required..." "ERROR"
1028 Logging "" "ERROR"
1029 Logging "Aborting Script..." "ERROR"
1030 Logging "" "ERROR"
1031
1032 EXIT
1033 } Else {
1034 # The User Account Running This Script Has Been Validated To Be A Member Of The Enterprise Admins Group Of The AD Forest
1035
1036 Logging "The user account is running with Enterprise Administrator equivalent permissions in the AD Domain '$targetedADdomainFQDN'!..." "SUCCESS"
1037 Logging "The user account is a member of '$enterpriseAdminRole'!..." "SUCCESS"
1038 Logging "" "SUCCESS"
1039 Logging "Continuing Script..." "SUCCESS"
1040 Logging "" "SUCCESS"
1041 }
1042} Else {
1043 # The User Account Running This Script Has Been Validated To Be A Member Of The Domain Admins Group Of The Targeted AD Domain
1044
1045 Logging "The user account is running with Domain Administrator equivalent permissions in the AD Domain '$targetedADdomainFQDN'!..." "SUCCESS"
1046 Logging "The user account is a member of '$domainAdminRole'!..." "SUCCESS"
1047 Logging "" "SUCCESS"
1048 Logging "Continuing Script..." "SUCCESS"
1049 Logging "" "SUCCESS"
1050}
1051
1052### All Modes - Gathering AD Domain Information
1053Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
1054Logging "GATHERING TARGETED AD DOMAIN INFORMATION..." "HEADER"
1055Logging ""
1056# Retrieve Information For The AD Domain That Was Chosen
1057$thisADDomain = $null
1058Try {
1059 $thisADDomain = Get-ADDomain $targetedADdomainFQDN
1060} Catch {
1061 $thisADDomain = $null
1062}
1063If ($thisADDomain) {
1064 # Retrieve The HostName Of RWDC In The AD Domain That Hosts The PDC FSMO Role
1065 $targetedADdomainRWDCWithPDCFSMOFQDN = $null
1066 $targetedADdomainRWDCWithPDCFSMOFQDN = $thisADDomain.PDCEmulator
1067 # Retrieve The DSA DN Of RWDC In The AD Domain That Hosts The PDC FSMO Role
1068 $targetedADdomainRWDCWithPDCFSMONTDSSettingsObjectDN = $null
1069 $targetedADdomainRWDCWithPDCFSMONTDSSettingsObjectDN = (Get-ADDomainController $targetedADdomainRWDCWithPDCFSMOFQDN -Server $targetedADdomainRWDCWithPDCFSMOFQDN).NTDSSettingsObjectDN
1070 # Retrieve Domain Functional Level/Mode Of The AD Domain
1071 $targetedADdomainDomainFunctionalMode = $null
1072 $targetedADdomainDomainFunctionalMode = $thisADDomain.DomainMode
1073 $targetedADdomainDomainFunctionalModeLevel = $null
1074 $targetedADdomainDomainFunctionalModeLevel = (Get-ADObject -LDAPFilter "(&(objectClass=crossRef)(nCName=$('DC=' + $targetedADdomainFQDN.replace('.',',DC='))))" -SearchBase $partitionsContainerDN -Properties "msDS-Behavior-Version" -Server $targetedADdomainRWDCWithPDCFSMOFQDN)."msDS-Behavior-Version"
1075 # Determine The Max Tgt Lifetime In Hours And The Max Clock Skew In Minutes
1076 Try {
1077 $gpoObjXML = $null
1078 [xml]$gpoObjXML = Get-GPOReport -Domain $targetedADdomainFQDN -Guid '{31B2F340-016D-11D2-945F-00C04FB984F9}' -ReportType Xml
1079 $targetedADdomainMaxTgtLifetimeHrs = $null
1080 $targetedADdomainMaxTgtLifetimeHrs = (($gpoObjXML.gpo.Computer.ExtensionData | ?{$_.name -eq 'Security'}).Extension.ChildNodes | ?{$_.Name -eq 'MaxTicketAge'}).SettingNumber
1081 $targetedADdomainMaxClockSkewMins = $null
1082 $targetedADdomainMaxClockSkewMins = (($gpoObjXML.gpo.Computer.ExtensionData | ?{$_.name -eq 'Security'}).Extension.ChildNodes | ?{$_.Name -eq 'MaxClockSkew'}).SettingNumber
1083 $sourceInfoFrom = "Default Domain GPO"
1084 } Catch {
1085 Logging "Could not lookup 'MaxTicketAge' (default 10 hours) and 'MaxClockSkew' (default 5 minutes) from the 'Default Domain Policy' GPO, so default values will be assumed." "WARNING"
1086 $targetedADdomainMaxTgtLifetimeHrs = 10
1087 $targetedADdomainMaxClockSkewMins = 5
1088 $sourceInfoFrom = "Assumed"
1089 }
1090} Else {
1091 $targetedADdomainRWDCWithPDCFSMOFQDN = "Unavailable"
1092 $targetedADdomainRWDCWithPDCFSMONTDSSettingsObjectDN = "Unavailable"
1093 $targetedADdomainDomainFunctionalMode = "Unavailable"
1094 $targetedADdomainDomainFunctionalModeLevel = "Unavailable"
1095 $targetedADdomainMaxTgtLifetimeHrs = "Unavailable"
1096 $targetedADdomainMaxClockSkewMins = "Unavailable"
1097 $sourceInfoFrom = "Unavailable"
1098}
1099
1100# Present The Information
1101Logging "Domain FQDN...........................: '$targetedADdomainFQDN'"
1102Logging "Domain Functional Mode................: '$targetedADdomainDomainFunctionalMode'"
1103Logging "Domain Functional Mode Level..........: '$targetedADdomainDomainFunctionalModeLevel'"
1104Logging "FQDN RWDC With PDC FSMO...............: '$targetedADdomainRWDCWithPDCFSMOFQDN'"
1105Logging "DSA RWDC With PDC FSMO................: '$targetedADdomainRWDCWithPDCFSMONTDSSettingsObjectDN'"
1106Logging "Max TGT Lifetime (Hours)..............: '$targetedADdomainMaxTgtLifetimeHrs'"
1107Logging "Max Clock Skew (Minutes)..............: '$targetedADdomainMaxClockSkewMins'"
1108Logging "TGT Lifetime/Clock Skew Sourced From..: '$sourceInfoFrom'"
1109Logging ""
1110Logging "Checking Domain Functional Mode of targeted AD domain '$targetedADdomainFQDN' is high enough..."
1111# Check If The Domain Functional Level/Mode Of The AD Domain Is High Enough To Continue
1112If ($targetedADdomainDomainFunctionalModeLevel -ne "Unavailable" -And $targetedADdomainDomainFunctionalModeLevel -ge 3) {
1113 # If The Domain Functional Level/Mode Of The AD Domain Is Equal Or Higher Than Windows Server 2008 (3), Then Continue
1114
1115 Logging "" "SUCCESS"
1116 Logging "The specified AD domain '$targetedADdomainFQDN' has a Domain Functional Mode of 'Windows2008Domain (3)' or higher!..." "SUCCESS"
1117 Logging "" "SUCCESS"
1118 Logging "Continuing Script..." "SUCCESS"
1119 Logging "" "SUCCESS"
1120} Else {
1121 # If The Domain Functional Level/Mode Of The AD Domain Is Lower Than Windows Server 2008 (3) Or It Cannot Be Determined, Then Abort
1122
1123 Logging "" "ERROR"
1124 Logging "It CANNOT be determined the specified AD domain '$targetedADdomainFQDN' has a Domain Functional Mode of 'Windows2008Domain (3)' or higher!..." "ERROR"
1125 Logging "" "ERROR"
1126 Logging "AD domains with Windows Server 2000/2003 DCs CANNOT do KDC PAC validation using the previous (N-1) KrbTgt Account Password" "ERROR"
1127 Logging "like Windows Server 2008 and higher DCs are able to. Windows Server 2000/2003 DCs will only attempt it with the current (N)" "ERROR"
1128 Logging "KrbTgt Account Password. That means that in the subset of KRB AP exchanges where KDC PAC validation is performed," "ERROR"
1129 Logging "authentication issues could be experience because the target server gets a PAC validation error when asking the KDC (DC)" "ERROR"
1130 Logging "to validate the KDC signature of the PAC that is inside the service ticket that was presented by the client to the server." "ERROR"
1131 Logging "This problem would potentially persist for the lifetime of the service ticket(s). And by the way... for Windows Server" "ERROR"
1132 Logging "2000/2003 support already ended years ago. Time to upgrade to higher version dude!" "ERROR"
1133 Logging "Be aware though, when increasing the DFL from Windows Server 2003 to any higher level, the password of the KrbTgt Account" "ERROR"
1134 Logging "will be reset automatically due to the introduction of AES encryption for Kerberos and the requirement to regenerate new" "ERROR"
1135 Logging "keys for DES, RC4, AES128, AES256!" "ERROR"
1136 Logging "" "ERROR"
1137 Logging "Aborting Script..." "ERROR"
1138 Logging "" "ERROR"
1139
1140 EXIT
1141}
1142
1143### All Modes - Gathering Domain Controller Information And Testing Connectivity
1144Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
1145Logging "GATHERING DOMAIN CONTROLLER INFORMATION AND TESTING CONNECTIVITY..." "HEADER"
1146Logging ""
1147
1148# Define An Empty List/Table That Will Contain All DCs In The AD Domain And Related Information
1149$tableOfDCsInADDomain = @()
1150# Retrieve All The RWDCs In The AD Domain
1151$listOfRWDCsInADDomain = $null
1152$listOfRWDCsInADDomain = $thisADDomain.ReplicaDirectoryServers
1153# Set The Counters To Zero
1154$nrOfRWDCs = 0
1155$nrOfReachableRWDCs = 0
1156$nrOfUnReachableRWDCs = 0
1157# Execute For All RWDCs In The AD Domain If Any
1158If ($listOfRWDCsInADDomain) {
1159 $listOfRWDCsInADDomain | %{
1160 # Get The FQDN Of The RWDC
1161 $rwdcFQDN = $null
1162 $rwdcFQDN = $_
1163 # Retrieve The Object Of The RWDC From AD
1164 $rwdcObj = $null
1165 $rwdcObj = Get-ADDomainController $rwdcFQDN -Server $targetedADdomainRWDCWithPDCFSMOFQDN
1166 # Define The Columns For The RWDCs In The AD Domain To Be Filled In
1167 $tableOfDCsInADDomainObj = "" | Select "Host Name",PDC,"Site Name","DS Type","Krb Tgt","Pwd Last Set","Org RWDC","Org Time","Ver","IP Address","OS Version",Reachable,"Source RWDC FQDN","Source RWDC DSA"
1168 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1169 $tableOfDCsInADDomainObj."Host Name" = $null
1170 $tableOfDCsInADDomainObj."Host Name" = $rwdcFQDN
1171 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1172 $tableOfDCsInADDomainObj.PDC = $null
1173 If ($rwdcObj.OperationMasterRoles -contains "PDCEmulator") {
1174 $tableOfDCsInADDomainObj.PDC = $True
1175 } Else {
1176 $tableOfDCsInADDomainObj.PDC = $False
1177 }
1178 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1179 $tableOfDCsInADDomainObj."Site Name" = $null
1180 $tableOfDCsInADDomainObj."Site Name" = $rwdcObj.Site
1181 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1182 $tableOfDCsInADDomainObj."DS Type" = $null
1183 $tableOfDCsInADDomainObj."DS Type" = "Read/Write"
1184 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1185 $rwdcKrbTgtSamAccountName = $null
1186 If ($modeOfOperationNr -eq 1 -Or $modeOfOperationNr -eq 2 -Or $modeOfOperationNr -eq 4) {
1187 # Use The PROD/REAL KrbTgt Account Of The RWDC
1188
1189 $rwdcKrbTgtSamAccountName = "krbtgt"
1190 }
1191 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 8 -Or $modeOfOperationNr -eq 9) {
1192 # Use The TEST/BOGUS KrbTgt Account Of The RWDC
1193
1194 $rwdcKrbTgtSamAccountName = "krbtgt_TEST"
1195 }
1196 $tableOfDCsInADDomainObj."Krb Tgt" = $rwdcKrbTgtSamAccountName
1197 # Retrieve The Object Of The KrbTgt Account
1198 $rwdcKrbTgtObject = $null
1199 $rwdcKrbTgtObject = Get-ADUser -LDAPFilter "(sAMAccountName=$rwdcKrbTgtSamAccountName)" -Properties * -Server $targetedADdomainRWDCWithPDCFSMOFQDN
1200 $tableOfDCsInADDomainObj."Pwd Last Set" = $null
1201 $tableOfDCsInADDomainObj."Org RWDC" = $null
1202 $tableOfDCsInADDomainObj."Org Time" = $null
1203 $tableOfDCsInADDomainObj."Ver" = $null
1204 If ($rwdcKrbTgtObject) {
1205 # If The Object Of The KrbTgt Account Exists
1206
1207 # Retrieve The DN OF The Object
1208 $rwdcKrbTgtObjectDN = $null
1209 $rwdcKrbTgtObjectDN = $rwdcKrbTgtObject.DistinguishedName
1210
1211 # Retrieve The Password Last Set Value Of The KrbTgt Account
1212 $rwdcKrbTgtPwdLastSet = $null
1213 $rwdcKrbTgtPwdLastSet = Get-Date $([datetime]::fromfiletime($rwdcKrbTgtObject.pwdLastSet)) -f "yyyy-MM-dd HH:mm:ss"
1214 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1215 $tableOfDCsInADDomainObj."Pwd Last Set" = $rwdcKrbTgtPwdLastSet
1216
1217 # Retrieve The Metadata Of The Object, And More Specific Of The pwdLastSet Attribute Of That Object
1218 $metadataObject = $null
1219 $metadataObject = Get-ADReplicationAttributeMetadata $rwdcKrbTgtObjectDN -Server $targetedADdomainRWDCWithPDCFSMOFQDN
1220 $metadataObjectAttribPwdLastSet = $null
1221 $metadataObjectAttribPwdLastSet = $metadataObject | ?{$_.AttributeName -eq "pwdLastSet"}
1222 $orgRWDCNTDSSettingsObjectDN = $null
1223 $orgRWDCNTDSSettingsObjectDN = $metadataObjectAttribPwdLastSet.LastOriginatingChangeDirectoryServerIdentity
1224 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $null
1225 If ($orgRWDCNTDSSettingsObjectDN) {
1226 # Strip "CN=NTDS Settings," To End Up With The Server Object DN
1227 $orgRWDCServerObjectDN = $null
1228 $orgRWDCServerObjectDN = $orgRWDCNTDSSettingsObjectDN.SubString(("CN=NTDS Settings,").Length)
1229 # Connect To The Server Object DN
1230 $orgRWDCServerObjectObj = $null
1231 $orgRWDCServerObjectObj = ([ADSI]"LDAP://$targetedADdomainRWDCWithPDCFSMOFQDN/$orgRWDCServerObjectDN")
1232 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $orgRWDCServerObjectObj.dnshostname[0]
1233 } Else {
1234 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = "RWDC Demoted"
1235 }
1236 $metadataObjectAttribPwdLastSetOrgTime = $null
1237 $metadataObjectAttribPwdLastSetOrgTime = Get-Date $($metadataObjectAttribPwdLastSet.LastOriginatingChangeTime) -f "yyyy-MM-dd HH:mm:ss"
1238 $metadataObjectAttribPwdLastSetVersion = $null
1239 $metadataObjectAttribPwdLastSetVersion = $metadataObjectAttribPwdLastSet.Version
1240 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1241 $tableOfDCsInADDomainObj."Org RWDC" = $metadataObjectAttribPwdLastSetOrgRWDCFQDN
1242 $tableOfDCsInADDomainObj."Org Time" = $metadataObjectAttribPwdLastSetOrgTime
1243 $tableOfDCsInADDomainObj."Ver" = $metadataObjectAttribPwdLastSetVersion
1244 } Else {
1245 # If The Object Of The KrbTgt Account Does Not Exist
1246
1247 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1248 $tableOfDCsInADDomainObj."Pwd Last Set" = "No Such Object"
1249 $tableOfDCsInADDomainObj."Org RWDC" = "No Such Object"
1250 $tableOfDCsInADDomainObj."Org Time" = "No Such Object"
1251 $tableOfDCsInADDomainObj."Ver" = "No Such Object"
1252 }
1253 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1254 $tableOfDCsInADDomainObj."IP Address" = $null
1255 $tableOfDCsInADDomainObj."IP Address" = $rwdcObj.IPv4Address
1256 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1257 $tableOfDCsInADDomainObj."OS Version" = $null
1258 $tableOfDCsInADDomainObj."OS Version" = $rwdcObj.OperatingSystem
1259 # Define The Ports To Check Against
1260 $ports = 135,389 # RPC Endpoint Mapper, LDAP
1261 # Define The Connection Check To Be True Initially
1262 $connectionCheckOK = $true
1263 # For Every Defined Port Check The Connection And Report
1264 $ports | %{
1265 # Set The Port To Check Against
1266 $port = $null
1267 $port = $_
1268 # Test The Connection To The Server Using The Port
1269 $connectionResult = $null
1270 $connectionResult = portConnectionCheck $rwdcFQDN $port 500
1271 If ($connectionResult -eq "ERROR") {
1272 $connectionCheckOK = $false
1273 }
1274 }
1275 If ($connectionCheckOK) {
1276 # If The Connection Check Is OK
1277
1278 # Connect To The RootDSE Of The RWDC
1279 $rwdcRootDSEObj = $null
1280 $rwdcRootDSEObj = [ADSI]"LDAP://$rwdcFQDN/rootDSE"
1281 If ($rwdcRootDSEObj.Path -eq $null) {
1282 # If It Throws An Error Then The RWDC Is Not Available/Reachable And Increase The Counter Of Unreachable RWDCs
1283
1284 $tableOfDCsInADDomainObj.Reachable = $False
1285 $nrOfUnReachableRWDCs += 1
1286
1287 } Else {
1288 # If It Does Not Throw An Error Then The RWDC Is Available/Reachable And Increase The Counter Of Reachable RWDCs
1289
1290 $tableOfDCsInADDomainObj.Reachable = $True
1291 $nrOfReachableRWDCs += 1
1292 }
1293 } Else {
1294 # If The Connection Check Is Not OK Then The RWDC Is Not Available/Reachable And Increase The Counter Of Unreachable RWDCs
1295
1296 $tableOfDCsInADDomainObj.Reachable = $False
1297 $nrOfUnReachableRWDCs += 1
1298 }
1299 If ($rwdcObj.OperationMasterRoles -contains "PDCEmulator") {
1300 # If The RWDC Is The RWDC With The PDC FSMO, Then Do Not Specify A Source RWDC As The RWDC With The PDC FSMO Is The Source Originating RWDC
1301
1302 $tableOfDCsInADDomainObj."Source RWDC FQDN" = "N.A."
1303 $tableOfDCsInADDomainObj."Source RWDC DSA" = "N.A."
1304 } Else {
1305 # If The RWDC Is Not The RWDC With The PDC FSMO, Then Specify A Source RWDC Being The RWDC With The PDC FSMO As The Source Originating RWDC
1306
1307 $tableOfDCsInADDomainObj."Source RWDC FQDN" = $targetedADdomainRWDCWithPDCFSMOFQDN
1308 $tableOfDCsInADDomainObj."Source RWDC DSA" = $targetedADdomainRWDCWithPDCFSMONTDSSettingsObjectDN
1309 }
1310
1311 # Increase The Counter For The Number Of RWDCs
1312 $nrOfRWDCs += 1
1313 # Add The Row For The RWDC To The Table
1314 $tableOfDCsInADDomain += $tableOfDCsInADDomainObj
1315 }
1316}
1317
1318# Retrieve All The RODCs In The AD Domain
1319$listOfRODCsInADDomain = $null
1320$listOfRODCsInADDomain = $thisADDomain.ReadOnlyReplicaDirectoryServers
1321# Set The Counters To Zero
1322$nrOfRODCs = 0
1323$nrOfReachableRODCs = 0
1324$nrOfUnReachableRODCs = 0
1325$nrOfUnDetermined = 0
1326# Execute For All RODCs In The AD Domain
1327If ($listOfRODCsInADDomain) {
1328 $listOfRODCsInADDomain | %{
1329 # Get The FQDN Of The RODC
1330 $rodcFQDN = $null
1331 $rodcFQDN = $_
1332 # Get The FQDN Of The RODC
1333 $rodcObj = $null
1334 $rodcObj = Get-ADDomainController $rodcFQDN -Server $targetedADdomainRWDCWithPDCFSMOFQDN
1335 # Define The Columns For The RODCs In The AD Domain To Be Filled In
1336 $tableOfDCsInADDomainObj = "" | Select "Host Name",PDC,"Site Name","DS Type","Krb Tgt","Pwd Last Set","Org RWDC","Org Time","Ver","IP Address","OS Version",Reachable,"Source RWDC FQDN","Source RWDC DSA"
1337 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1338 $tableOfDCsInADDomainObj."Host Name" = $null
1339 $tableOfDCsInADDomainObj."Host Name" = $rodcFQDN
1340 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1341 $tableOfDCsInADDomainObj.PDC = $null
1342 $tableOfDCsInADDomainObj.PDC = $False
1343 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1344 $tableOfDCsInADDomainObj."Site Name" = $null
1345 If ($rodcObj.OperatingSystem) {
1346 $tableOfDCsInADDomainObj."Site Name" = $rodcObj.Site
1347 } Else {
1348 $tableOfDCsInADDomainObj."Site Name" = "Unknown"
1349 }
1350 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1351 $tableOfDCsInADDomainObj."DS Type" = $null
1352 $tableOfDCsInADDomainObj."DS Type" = "Read-Only"
1353 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1354 $rodcKrbTgtSamAccountName = $null
1355 If ($modeOfOperationNr -eq 1 -Or $modeOfOperationNr -eq 2 -Or $modeOfOperationNr -eq 4) {
1356 # Use The PROD/REAL KrbTgt Account Of The RODC
1357
1358 $rodcKrbTgtSamAccountName = ((Get-ADObject $($rodcObj.ComputerObjectDN) -properties msDS-KrbTgtLink -Server $targetedADdomainRWDCWithPDCFSMOFQDN)."msDS-KrbTgtLink" | Get-ADObject -Server $targetedADdomainRWDCWithPDCFSMOFQDN).Name
1359 }
1360 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 8 -Or $modeOfOperationNr -eq 9) {
1361 # Use The TEST/BOGUS KrbTgt Account Of The RODC
1362
1363 $rodcKrbTgtSamAccountName = $(((Get-ADObject $($rodcObj.ComputerObjectDN) -properties msDS-KrbTgtLink -Server $targetedADdomainRWDCWithPDCFSMOFQDN)."msDS-KrbTgtLink" | Get-ADObject -Server $targetedADdomainRWDCWithPDCFSMOFQDN).Name) + "_TEST"
1364 }
1365 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1366 $tableOfDCsInADDomainObj."Krb Tgt" = $null
1367 $tableOfDCsInADDomainObj."Krb Tgt" = $rodcKrbTgtSamAccountName
1368 # Retrieve The Object Of The KrbTgt Account
1369 $rodcKrbTgtObject = $null
1370 $rodcKrbTgtObject = Get-ADUser -LDAPFilter "(sAMAccountName=$rodcKrbTgtSamAccountName)" -Properties * -Server $targetedADdomainRWDCWithPDCFSMOFQDN
1371 $tableOfDCsInADDomainObj."Pwd Last Set" = $null
1372 $tableOfDCsInADDomainObj."Org RWDC" = $null
1373 $tableOfDCsInADDomainObj."Org Time" = $null
1374 $tableOfDCsInADDomainObj."Ver" = $null
1375 If ($rodcKrbTgtObject) {
1376 # If The Object Of The KrbTgt Account Exists
1377
1378 # Retrieve The DN OF The Object
1379 $rodcKrbTgtObjectDN = $null
1380 $rodcKrbTgtObjectDN = $rodcKrbTgtObject.DistinguishedName
1381
1382 # Retrieve The Password Last Set Value Of The KrbTgt Account
1383 $rodcKrbTgtPwdLastSet = $null
1384 $rodcKrbTgtPwdLastSet = Get-Date $([datetime]::fromfiletime($rodcKrbTgtObject.pwdLastSet)) -f "yyyy-MM-dd HH:mm:ss"
1385 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1386 $tableOfDCsInADDomainObj."Pwd Last Set" = $rodcKrbTgtPwdLastSet
1387
1388 # Retrieve The Metadata Of The Object, And More Specific Of The pwdLastSet Attribute Of That Object
1389 $metadataObject = $null
1390 $metadataObject = Get-ADReplicationAttributeMetadata $rodcKrbTgtObjectDN -Server $targetedADdomainRWDCWithPDCFSMOFQDN
1391 $metadataObjectAttribPwdLastSet = $null
1392 $metadataObjectAttribPwdLastSet = $metadataObject | ?{$_.AttributeName -eq "pwdLastSet"}
1393 $orgRWDCNTDSSettingsObjectDN = $null
1394 $orgRWDCNTDSSettingsObjectDN = $metadataObjectAttribPwdLastSet.LastOriginatingChangeDirectoryServerIdentity
1395 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $null
1396 If ($orgRWDCNTDSSettingsObjectDN) {
1397 # Strip "CN=NTDS Settings," To End Up With The Server Object DN
1398 $orgRWDCServerObjectDN = $null
1399 $orgRWDCServerObjectDN = $orgRWDCNTDSSettingsObjectDN.SubString(("CN=NTDS Settings,").Length)
1400 # Connect To The Server Object DN
1401 $orgRWDCServerObjectObj = $null
1402 $orgRWDCServerObjectObj = ([ADSI]"LDAP://$targetedADdomainRWDCWithPDCFSMOFQDN/$orgRWDCServerObjectDN")
1403 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $orgRWDCServerObjectObj.dnshostname[0]
1404 } Else {
1405 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = "RWDC Demoted"
1406 }
1407 $metadataObjectAttribPwdLastSetOrgTime = $null
1408 $metadataObjectAttribPwdLastSetOrgTime = Get-Date $($metadataObjectAttribPwdLastSet.LastOriginatingChangeTime) -f "yyyy-MM-dd HH:mm:ss"
1409 $metadataObjectAttribPwdLastSetVersion = $null
1410 $metadataObjectAttribPwdLastSetVersion = $metadataObjectAttribPwdLastSet.Version
1411 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1412 $tableOfDCsInADDomainObj."Org RWDC" = $metadataObjectAttribPwdLastSetOrgRWDCFQDN
1413 $tableOfDCsInADDomainObj."Org Time" = $metadataObjectAttribPwdLastSetOrgTime
1414 $tableOfDCsInADDomainObj."Ver" = $metadataObjectAttribPwdLastSetVersion
1415 } Else {
1416 # If The Object Of The KrbTgt Account Does Not Exist
1417
1418 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1419 $tableOfDCsInADDomainObj."Pwd Last Set" = "No Such Object"
1420 $tableOfDCsInADDomainObj."Org RWDC" = "No Such Object"
1421 $tableOfDCsInADDomainObj."Org Time" = "No Such Object"
1422 $tableOfDCsInADDomainObj."Ver" = "No Such Object"
1423 }
1424 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1425 $tableOfDCsInADDomainObj."IP Address" = $null
1426 If ($rodcObj.OperatingSystem) {
1427 $tableOfDCsInADDomainObj."IP Address" = $rodcObj.IPv4Address
1428 } Else {
1429 $tableOfDCsInADDomainObj."IP Address" = "Unknown"
1430 }
1431 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1432 $tableOfDCsInADDomainObj."OS Version" = $null
1433 If ($rodcObj.OperatingSystem) {
1434 $tableOfDCsInADDomainObj."OS Version" = $rodcObj.OperatingSystem
1435 } Else {
1436 $tableOfDCsInADDomainObj."OS Version" = "Unknown"
1437 }
1438 # Define The Ports To Check Against
1439 $ports = 135,389 # RPC Endpoint Mapper, LDAP
1440 # Define The Connection Check To Be True Initially
1441 $connectionCheckOK = $true
1442 # For Every Defined Port Check The Connection And Report
1443 $ports | %{
1444 # Set The Port To Check Against
1445 $port = $null
1446 $port = $_
1447 # Test The Connection To The Server Using The Port
1448 $connectionResult = $null
1449 $connectionResult = portConnectionCheck $rodcFQDN $port 500
1450 If ($connectionResult -eq "ERROR") {
1451 $connectionCheckOK = $false
1452 }
1453 }
1454 If ($connectionCheckOK) {
1455 # If The Connection Check Is OK
1456
1457 # Connect To The RootDSE Of The RODC
1458 $rodcRootDSEObj = $null
1459 $rodcRootDSEObj = [ADSI]"LDAP://$rodcFQDN/rootDSE"
1460 If ($rodcRootDSEObj.Path -eq $null) {
1461 # If It Throws An Error Then The RODC Is Not Available/Reachable And Increase The Counter Of Unreachable RODCs
1462
1463 $tableOfDCsInADDomainObj.Reachable = $False
1464 $nrOfUnReachableRODCs += 1
1465 } Else {
1466 # If It Does Not Throw An Error Then The RWDC Is Available/Reachable And Increase The Counter Of Reachable RODCs
1467
1468 $tableOfDCsInADDomainObj.Reachable = $True
1469 $nrOfReachableRODCs += 1
1470 }
1471 } Else {
1472 # If The Connection Check Is Not OK Then The RWDC Is Not Available/Reachable And Increase The Counter Of Unreachable RODCs
1473
1474 $tableOfDCsInADDomainObj.Reachable = $False
1475 $nrOfUnReachableRODCs += 1
1476 }
1477 If ($rodcObj.OperatingSystem) {
1478 # If The RODC Has An Operating System Specified, Then It Is Most Likely A Windows RODC
1479
1480 If ($tableOfDCsInADDomainObj.Reachable -eq $True) {
1481 # If The RODC Is Available/Reachable
1482
1483 # Get The DSA DN Of The RODC
1484 $rodcNTDSSettingsObjectDN = $null
1485 $rodcNTDSSettingsObjectDN = $rodcObj.NTDSSettingsObjectDN
1486 # Define An LDAP Query With A Search Base And A Filter To Determine The DSA DN Of The Source RWDC Of The RODC
1487 $dsDirSearcher = $null
1488 $dsDirSearcher = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
1489 $dsDirSearcher.SearchRoot = $null
1490 $dsDirSearcher.SearchRoot = "LDAP://$rodcFQDN/$rodcNTDSSettingsObjectDN"
1491 $dsDirSearcher.Filter = $null
1492 $dsDirSearcher.Filter = "(&(objectClass=nTDSConnection)(ms-DS-ReplicatesNCReason=*))"
1493 $sourceRWDCsNTDSSettingsObjectDN = $null
1494 $sourceRWDCsNTDSSettingsObjectDN = $dsDirSearcher.FindAll().Properties.fromserver
1495 # For Every DSA DN Of The Source RWDC Retrieved
1496 $sourceRWDCsNTDSSettingsObjectDN | %{
1497 $sourceRWDCNTDSSettingsObjectDN = $null
1498 $sourceRWDCNTDSSettingsObjectDN = $_
1499 # Strip "CN=NTDS Settings," To End Up With The Server Object DN
1500 $sourceRWDCServerObjectDN = $null
1501 $sourceRWDCServerObjectDN = $sourceRWDCNTDSSettingsObjectDN.SubString(("CN=NTDS Settings,").Length)
1502 # Connect To The Server Object DN
1503 $sourceRWDCServerObjectObj = ([ADSI]"LDAP://$targetedADdomainRWDCWithPDCFSMOFQDN/$sourceRWDCServerObjectDN")
1504 # If The Domain Of The Source RWDC Matches The Domain Of The RODC, Then That's The One We Need
1505 If (($sourceRWDCServerObjectObj.dnshostname).SubString($sourceRWDCServerObjectObj.name.Length + 1) -eq $rodcObj.Domain) {
1506 # The HostName Of Source RWDC Used By The RODC - Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1507 $tableOfDCsInADDomainObj."Source RWDC FQDN" = $sourceRWDCServerObjectObj.dnshostname[0]
1508 # The DSA DN Of Source RWDC Used By The RODC - Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1509 $tableOfDCsInADDomainObj."Source RWDC DSA" = $sourceRWDCsNTDSSettingsObjectDN[0]
1510 }
1511 }
1512 } Else {
1513 # If The RODC Is Available/Reachable
1514
1515 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1516 $tableOfDCsInADDomainObj."Source RWDC FQDN" = "RODC Unreachable"
1517 $tableOfDCsInADDomainObj."Source RWDC DSA" = "RODC Unreachable"
1518 }
1519 } Else {
1520 # If The RODC Does Not Have An Operating System Specified, Then It Is Most Likely Not A Windows RODC
1521
1522 # Set The Corresponding Value Of The RODC In The Correct Column Of The Table
1523 $tableOfDCsInADDomainObj."Source RWDC FQDN" = "Unknown"
1524 $tableOfDCsInADDomainObj."Source RWDC DSA" = "Unknown"
1525 }
1526 If ($rodcObj.OperatingSystem) {
1527 # If The RODC Has An Operating System Specified, Then It Is Most Likely A Windows RODC, Therefore Increase The Counter For Real RODCs
1528
1529 $nrOfRODCs += 1
1530 } Else {
1531 # If The RODC Does Not Have An Operating System Specified, Then It Is Most Likely Not A Windows RODC, Therefore Increase The Counter For Unknown RODCs
1532
1533 $nrOfUnDetermined += 1
1534 }
1535 # Add The Row For The RODC To The Table
1536 $tableOfDCsInADDomain += $tableOfDCsInADDomainObj
1537 }
1538}
1539
1540# Sort The Table With DCs In The AD Domain In The Order "DS Type" (Read/Write At The Top), Then If It Is The PDC Or Not (PDC At The Top), Then If It Is Reachable Or Not (Reachable At the Top)
1541$tableOfDCsInADDomain = $tableOfDCsInADDomain | Sort-Object -Property @{Expression = "DS Type"; Descending = $False}, @{Expression = "PDC"; Descending = $True}, @{Expression = "Reachable"; Descending = $True}
1542
1543# Determine The Number Of DCs Based Upon The Number Of RWDCs And The Number Of RODCs
1544$nrOfDCs = $nrOfRWDCs + $nrOfRODCs
1545# Display The Information
1546Logging "" "REMARK"
1547Logging "List Of Domain Controllers In AD Domains '$targetedADdomainFQDN'..."
1548Logging "" "REMARK"
1549Logging "$($tableOfDCsInADDomain | FT * -Autosize | Out-String)"
1550Logging "" "REMARK"
1551Logging "REMARKS:" "REMARK"
1552Logging " - 'N.A.' in the columns 'Source RWDC FQDN' and 'Source RWDC DSA' means the RWDC is considered as the master for this script." "REMARK"
1553Logging " - 'RODC Unreachable' in the columns 'Source RWDC FQDN' and 'Source RWDC DSA' means the RODC cannot be reached to determine its replicating source" "REMARK"
1554Logging " RWDC/DSA. The unavailability can be due to firewalls/networking or the RODC actually being down." "REMARK"
1555Logging " - 'Unknown' in various columns means that an RODC was found that may not be a true Windows Server RODC. It may be an appliance acting as an RODC." "REMARK"
1556Logging " - 'RWDC Demoted' in the column 'Org RWDC' means the RWDC existed once, but it does not exist anymore as it has been decommissioned in the past." "REMARK"
1557Logging " This is normal." "REMARK"
1558Logging " - 'No Such Object' in the columns 'Pwd Last Set', 'Org RWDC', 'Org Time' or 'Ver' means the targeted object was not found in the AD domain." "REMARK"
1559Logging " Although this is possible for any targeted object, this is most likely the case when targeting the KrbTgt TEST/BOGUS accounts and if those" "REMARK"
1560Logging " do not exist yet. This may also occur for an appliance acting as an RODC as in that case no KrbTgt TEST/BOGUS account is created." "REMARK"
1561Logging "" "REMARK"
1562Logging "" "REMARK"
1563Logging "" "REMARK"
1564Logging " --> Found [$nrOfDCs] Real DC(s) In AD Domain..." "REMARK"
1565Logging "" "REMARK"
1566Logging " --> Found [$nrOfRWDCs] RWDC(s) In AD Domain..." "REMARK"
1567Logging " --> Found [$nrOfReachableRWDCs] Reachable RWDC(s) In AD Domain..." "REMARK"
1568Logging " --> Found [$nrOfUnReachableRWDCs] UnReachable RWDC(s) In AD Domain..." "REMARK"
1569Logging "" "REMARK"
1570Logging " --> Found [$nrOfRODCs] RODC(s) In AD Domain..." "REMARK"
1571Logging " --> Found [$nrOfReachableRODCs] Reachable RODC(s) In AD Domain..." "REMARK"
1572Logging " --> Found [$nrOfUnReachableRODCs] UnReachable RODC(s) In AD Domain..." "REMARK"
1573Logging " --> Found [$nrOfUnDetermined] Undetermined RODC(s) In AD Domain..." "REMARK"
1574Logging "" "REMARK"
1575
1576### Mode 2 And 3 And 4 Only - Selecting The KrbTgt Account To Target And Scope If Applicable (Only Applicable To RODCs)
1577If ($modeOfOperationNr -eq 2 -Or $modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
1578 Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
1579 Logging "SELECT THE SCOPE OF THE KRBTGT ACCOUNT(S) TO TARGET..." "HEADER"
1580 Logging ""
1581 Logging "Which KrbTgt account do you want to target?"
1582 Logging ""
1583 Logging " - 1 - Scope of KrbTgt in use by all RWDCs in the AD Domain"
1584 Logging ""
1585 Logging " - 2 - Scope of KrbTgt in use by specific RODC - Single RODC in the AD Domain"
1586 Logging ""
1587 Logging " - 3 - Scope of KrbTgt in use by specific RODC - Multiple RODCs in the AD Domain"
1588 Logging ""
1589 Logging " - 4 - Scope of KrbTgt in use by specific RODC - All RODCs in the AD Domain"
1590 Logging ""
1591 Logging ""
1592 Logging " - 0 - Exit Script"
1593 Logging ""
1594 Logging "Please specify the scope of KrbTgt Account to target: " "ACTION-NO-NEW-LINE"
1595 $targetKrbTgtAccountNr = 1
1596 Logging ""
1597 # If Anything Else Than The Allowed/Available Non-Zero KrbTgt Accounts, Abort The Script
1598 If (($targetKrbTgtAccountNr -ne 1 -And $targetKrbTgtAccountNr -ne 2 -And $targetKrbTgtAccountNr -ne 3 -And $targetKrbTgtAccountNr -ne 4) -Or $targetKrbTgtAccountNr -notmatch "^[\d\.]+$") {
1599 Logging " --> Chosen Scope KrbTgt Account Target: 0 - Exit Script..." "REMARK"
1600 Logging ""
1601
1602 EXIT
1603 }
1604 # If KrbTgt Account Scope 1
1605 If ($targetKrbTgtAccountNr -eq 1) {
1606 $targetKrbTgtAccountDescription = "1 - Scope of KrbTgt in use by all RWDCs in the AD Domain..."
1607 }
1608 # If KrbTgt Account Scope 2
1609 If ($targetKrbTgtAccountNr -eq 2) {
1610 $targetKrbTgtAccountDescription = "2 - Scope of KrbTgt in use by specific RODC - Single RODC in the AD Domain..."
1611 }
1612 # If KrbTgt Account Scope 3
1613 If ($targetKrbTgtAccountNr -eq 3) {
1614 $targetKrbTgtAccountDescription = "3 - Scope of KrbTgt in use by specific RODC - Multiple RODCs in the AD Domain..."
1615 }
1616 # If KrbTgt Account Scope 4
1617 If ($targetKrbTgtAccountNr -eq 4) {
1618 $targetKrbTgtAccountDescription = "4 - Scope of KrbTgt in use by specific RODC - All RODCs in the AD Domain..."
1619 }
1620 Logging " --> Chosen Scope KrbTgt Account Target: $targetKrbTgtAccountDescription" "REMARK"
1621 Logging ""
1622 # If KrbTgt Account 2 Specify The FQDN Of A Single RODC To Target
1623 If ($targetKrbTgtAccountNr -eq 2) {
1624 Logging "Specify the FQDN of single RODC for which the KrbTgt Account Password must be reset: " "ACTION-NO-NEW-LINE"
1625 $targetRODCFQDNList = Read-Host
1626 Logging ""
1627 Logging " --> Specified RODC:" "REMARK"
1628 Logging " * $targetRODCFQDNList" "REMARK"
1629 Logging ""
1630 }
1631 # If KrbTgt Account 3 Specify A Comma Separated List Of FQDNs Of RODCs To Target
1632 If ($targetKrbTgtAccountNr -eq 3) {
1633 Logging "Specify a comma-separated list of FQDNs of RODCs for which the KrbTgt Account Password must be reset: " "ACTION-NO-NEW-LINE"
1634 $targetRODCFQDNList = Read-Host
1635 $targetRODCFQDNList = $targetRODCFQDNList.Split(",")
1636 Logging ""
1637 Logging " --> Specified RODCs:" "REMARK"
1638 $targetRODCFQDNList | %{
1639 Logging " * $($_)" "REMARK"
1640 }
1641 Logging ""
1642 }
1643}
1644
1645### Mode 2/3 - Simulation Mode AND Mode 4 - Real Reset Mode
1646If ($modeOfOperationNr -eq 2 -Or $modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
1647 # Mode 2 - Simulation Mode
1648 If ($modeOfOperationNr -eq 2) {
1649 Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
1650 Logging "SIMULATION MODE (MODE $modeOfOperationNr) - CREATING/REPLICATING TEMPORARY CANARY OBJECT ($targetKrbTgtAccountDescription)" "HEADER"
1651 Logging ""
1652 }
1653 # Mode 3 - Simulation Mode AND Mode 4 - Real Reset Mode
1654 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
1655 Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
1656 Logging "REAL RESET MODE (MODE $modeOfOperationNr) - RESETTING PASSWORD OF SCOPED KRBTGT ACCOUNT(S) ($targetKrbTgtAccountDescription)" "HEADER"
1657 Logging ""
1658 }
1659
1660 # Asking Confirmation To Continue Or Not
1661 Logging "Do you really want to continue and execute 'Mode $modeOfOperationNr'? [CONTINUE | STOP]: " "ACTION-NO-NEW-LINE"
1662 $continueOrStop = $null
1663 $continueOrStop = "CONTINUE"
1664 # Any Confirmation Not Equal To CONTINUE Will Be Equal To STOP
1665 If ($continueOrStop.ToUpper() -ne "CONTINUE") {
1666 $continueOrStop = "STOP"
1667 }
1668 Logging ""
1669 Logging " --> Chosen: $continueOrStop" "REMARK"
1670 Logging ""
1671 # Any Confirmation Not Equal To CONTINUE Will Abort The Script
1672 If ($continueOrStop.ToUpper() -ne "CONTINUE") {
1673 EXIT
1674 }
1675
1676 # KrbTgt in use by all RWDCs in the AD Domain
1677 If ($targetKrbTgtAccountNr -eq 1) {
1678 # Retrieve The KrbTgt Account Listed For The RWDC With The PDC FSMO
1679 $krbTgtSamAccountName = $null
1680 $krbTgtSamAccountName = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Krb Tgt"
1681 # Retrieve The Hosted Listed For The RWDC With The PDC FSMO
1682 $targetedADdomainSourceRWDCFQDN = $null
1683 $targetedADdomainSourceRWDCFQDN = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Host Name"
1684 # Retrieve The DN Of The KrbTgt Account
1685 $krbTgtDN = $null
1686 $krbTgtDN = (Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Server $targetedADdomainSourceRWDCFQDN).DistinguishedName
1687 Logging "+++++" "REMARK"
1688 Logging "+++ Processing KrbTgt Account....: '$krbTgtSamAccountName' | '$krbTgtDN' +++" "REMARK"
1689 Logging "+++ Used By RWDC.................: 'All RWDCs' +++" "REMARK"
1690 Logging "+++++" "REMARK"
1691 Logging "" "REMARK"
1692
1693 # Retrieve The Status Of Hosting The PDC FSMO From The RWDC Hosting The PDC FSMO (Duh!)
1694 $targetedADdomainSourceRWDCIsPDC = $null
1695 $targetedADdomainSourceRWDCIsPDC = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True}).PDC
1696 # Retrieve The SiteName Listed For The RWDC With The PDC FSMO
1697 $targetedADdomainSourceRWDCSiteName = $null
1698 $targetedADdomainSourceRWDCSiteName = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Site Name"
1699 # Retrieve The DS Type Listed For The RWDC With The PDC FSMO
1700 $targetedADdomainSourceRWDCDSType = $null
1701 $targetedADdomainSourceRWDCDSType = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."DS Type"
1702 # Retrieve The IP Address Listed For The RWDC With The PDC FSMO
1703 $targetedADdomainSourceRWDCIPAddress = $null
1704 $targetedADdomainSourceRWDCIPAddress = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."IP Address"
1705 # Retrieve The Reachability Listed For The RWDC With The PDC FSMO
1706 $targetedADdomainRWDCReachability = $null
1707 $targetedADdomainRWDCReachability = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True}).Reachable
1708 # Retrieve The FQDN Of The Source RWDC Listed For The RWDC With The PDC FSMO
1709 $targetedADdomainRWDCSourceRWDCFQDN = "N.A."
1710 # Set The Start Time For The RWDC With The PDC FSMO
1711 $targetedADdomainRWDCTime = 0.00
1712
1713 If ($targetedADdomainRWDCReachability) {
1714 # If The RWDC With The PDC FSMO Is Reachable
1715
1716 # If Mode 2
1717 If ($modeOfOperationNr -eq 2) {
1718 # Execute The Creation Of the Temporary Canary Object, And Abort The Script If That Fails
1719 $targetObjectToCheckDN = $null
1720 $targetObjectToCheckDN = createTempCanaryObject $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName $execDateTimeCustom1
1721 If (!$targetObjectToCheckDN) {
1722 EXIT
1723 }
1724 }
1725 # If Mode 3 Or 4
1726 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
1727 # Retrieve The KrbTgt Account Object
1728 $targetObjectToCheck = $null
1729 $targetObjectToCheck = Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Properties * -Server $targetedADdomainSourceRWDCFQDN
1730 If ($targetObjectToCheck) {
1731 # If The KrbTgt Account Object Exists (You're In Deep Sh!t If The Account Does Not Exist! :-))
1732
1733 # Retrieve The DN Of The KrbTgt Account Object
1734 $targetObjectToCheckDN = $null
1735 $targetObjectToCheckDN = $targetObjectToCheck.DistinguishedName
1736 # Retrieve The Password Last Set Of The KrbTgt Account Object
1737 $targetObjectToCheckPwdLastSet = $null
1738 $targetObjectToCheckPwdLastSet = Get-Date $([datetime]::fromfiletime($targetObjectToCheck.pwdLastSet))
1739
1740 # Calculate The Expiration Date/Time Of N-1 Kerberos Tickets
1741 $expirationTimeForNMinusOneKerbTickets = $null
1742 $expirationTimeForNMinusOneKerbTickets = (($targetObjectToCheckPwdLastSet.AddHours($targetedADdomainMaxTgtLifetimeHrs)).AddMinutes($targetedADdomainMaxClockSkewMins)).AddMinutes($targetedADdomainMaxClockSkewMins)
1743
1744 # Retrieve The Metadata Of The Object, And More Specific Of The pwdLastSet Attribute Of That Object
1745 $metadataObject = $null
1746 $metadataObject = Get-ADReplicationAttributeMetadata $targetObjectToCheckDN -Server $targetedADdomainSourceRWDCFQDN
1747 $metadataObjectAttribPwdLastSet = $null
1748 $metadataObjectAttribPwdLastSet = $metadataObject | ?{$_.AttributeName -eq "pwdLastSet"}
1749 $orgRWDCNTDSSettingsObjectDN = $null
1750 $orgRWDCNTDSSettingsObjectDN = $metadataObjectAttribPwdLastSet.LastOriginatingChangeDirectoryServerIdentity
1751 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $null
1752 If ($orgRWDCNTDSSettingsObjectDN) {
1753 # Strip "CN=NTDS Settings," To End Up With The Server Object DN
1754 $orgRWDCServerObjectDN = $null
1755 $orgRWDCServerObjectDN = $orgRWDCNTDSSettingsObjectDN.SubString(("CN=NTDS Settings,").Length)
1756 # Connect To The Server Object DN
1757 $orgRWDCServerObjectObj = $null
1758 $orgRWDCServerObjectObj = ([ADSI]"LDAP://$targetedADdomainRWDCWithPDCFSMOFQDN/$orgRWDCServerObjectDN")
1759 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $orgRWDCServerObjectObj.dnshostname[0]
1760 } Else {
1761 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = "RWDC Demoted"
1762 }
1763 $metadataObjectAttribPwdLastSetOrgTime = $null
1764 $metadataObjectAttribPwdLastSetOrgTime = Get-Date $($metadataObjectAttribPwdLastSet.LastOriginatingChangeTime) -f "yyyy-MM-dd HH:mm:ss"
1765 $metadataObjectAttribPwdLastSetVersion = $null
1766 $metadataObjectAttribPwdLastSetVersion = $metadataObjectAttribPwdLastSet.Version
1767
1768 $okToReset = $null
1769 If ($expirationTimeForNMinusOneKerbTickets -lt [DateTime]::Now) {
1770 # Allow The Password Reset To Occur Without Questions If The Expiration Date/Time Of N-1 Kerberos Tickets Is Earlier Than The Current Time
1771
1772 $okToReset = $True
1773 } Else {
1774 # Allow The Password Reset To Occur After Confirnation Only If The Expiration Date/Time Of N-1 Kerberos Tickets Is Equal Or Later Than The Current Time
1775
1776 Logging " --> According To RWDC.....................: '$targetedADdomainSourceRWDCFQDN'"
1777 Logging " --> Previous Password Set Date/Time.......: '$(Get-Date $targetObjectToCheckPwdLastSet -f 'yyyy-MM-dd HH:mm:ss')'"
1778 Logging " --> Date/Time N-1 Kerberos Tickets........: '$(Get-Date $expirationTimeForNMinusOneKerbTickets -f 'yyyy-MM-dd HH:mm:ss')'"
1779 Logging " --> Date/Time Now.........................: '$(Get-Date $([DateTime]::Now) -f 'yyyy-MM-dd HH:mm:ss')'"
1780 Logging " --> Max TGT Lifetime (Hours)..............: '$targetedADdomainMaxTgtLifetimeHrs'"
1781 Logging " --> Max Clock Skew (Minutes)..............: '$targetedADdomainMaxClockSkewMins'"
1782 Logging " --> Originating RWDC Previous Change......: '$metadataObjectAttribPwdLastSetOrgRWDCFQDN'"
1783 Logging " --> Originating Time Previous Change......: '$metadataObjectAttribPwdLastSetOrgTime'"
1784 Logging " --> Current Version Of Attribute Value....: '$metadataObjectAttribPwdLastSetVersion'"
1785 Logging ""
1786 Logging " --> Resetting KrbTgt Accnt Password Means.: 'MAJOR DOMAIN WIDE IMPACT'" "WARNING"
1787 Logging "" "WARNING"
1788 Logging "What do you want to do? [CONTINUE | STOP]: " "ACTION-NO-NEW-LINE"
1789 $continueOrStop = $null
1790 $continueOrStop = "CONTINUE"
1791 # Any Confirmation Not Equal To CONTINUE Will Be Equal To STOP
1792 If ($continueOrStop.ToUpper() -ne "CONTINUE") {
1793 $continueOrStop = "STOP"
1794 }
1795 Logging ""
1796 If ($continueOrStop.ToUpper() -eq "CONTINUE") {
1797 # If The Confirmation Equals CONTINUE Allow The Password Reset To Continue
1798
1799 $okToReset = $True
1800 } Else {
1801 # If The Confirmation Does Not Equal CONTINUE Do Not Allow The Password Reset To Continue. Abort
1802
1803 $okToReset = $False
1804 }
1805 Logging " --> Chosen: $continueOrStop" "REMARK"
1806 Logging ""
1807 }
1808 If ($okToReset) {
1809 # If OK To Reset Then Execute The Password Reset Of The KrbTgt Account
1810
1811 setPasswordOfADAccount $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName
1812 } Else {
1813 # If Not OK To Reset Then Abort
1814
1815 EXIT
1816 }
1817 } Else {
1818 # If The KrbTgt Account Object Does Not Exist (You're In Deep Sh!t If The Account Does Not Exist! :-))
1819
1820 Logging " --> KrbTgt Account With sAMAccountName '$krbTgtSamAccountName' Does NOT Exist! Skipping..." "ERROR"
1821 Logging "" "ERROR"
1822 }
1823 }
1824 } Else {
1825 # If The RWDC With The PDC FSMO Is NOT Reachable
1826
1827 Logging ""
1828 Logging "The RWDC '$targetedADdomainSourceRWDCFQDN' to make the change on is not reachable/available..." "ERROR"
1829 Logging ""
1830 }
1831 # If The DN Of The Target Object To Check (Temp Canary Object Or KrbTgt Account, Depends On The Mode Chosen) Was Determined/Found
1832 If ($targetObjectToCheckDN) {
1833 # Retrieve/Define The Starting List With RWDCs
1834 $listOfDCsToCheckObjectOnStart = $null
1835 $listOfDCsToCheckObjectOnStart = ($tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read/Write"})
1836 # Define An Empty List/Table For At The End That Will Contain All DCs In The AD Domain And Related Information
1837 $listOfDCsToCheckObjectOnEnd = @()
1838 # Define The Columns For The RWDCs In The AD Domain To Be Filled In
1839 $listOfDCsToCheckObjectOnEndObj = "" | Select "Host Name",PDC,"Site Name","DS Type","IP Address",Reachable,"Source RWDC FQDN",Time
1840 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1841 $listOfDCsToCheckObjectOnEndObj."Host Name" = $null
1842 $listOfDCsToCheckObjectOnEndObj."Host Name" = $targetedADdomainSourceRWDCFQDN
1843 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1844 $listOfDCsToCheckObjectOnEndObj.PDC = $null
1845 $listOfDCsToCheckObjectOnEndObj.PDC = $targetedADdomainSourceRWDCIsPDC
1846 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1847 $listOfDCsToCheckObjectOnEndObj."Site Name" = $null
1848 $listOfDCsToCheckObjectOnEndObj."Site Name" = $targetedADdomainSourceRWDCSiteName
1849 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1850 $listOfDCsToCheckObjectOnEndObj."DS Type" = $null
1851 $listOfDCsToCheckObjectOnEndObj."DS Type" = $targetedADdomainSourceRWDCDSType
1852 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1853 $listOfDCsToCheckObjectOnEndObj."IP Address" = $null
1854 $listOfDCsToCheckObjectOnEndObj."IP Address" = $targetedADdomainSourceRWDCIPAddress
1855 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1856 $listOfDCsToCheckObjectOnEndObj.Reachable = $null
1857 $listOfDCsToCheckObjectOnEndObj.Reachable = $targetedADdomainRWDCReachability
1858 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1859 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $null
1860 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $targetedADdomainRWDCSourceRWDCFQDN
1861 # Set The Corresponding Value Of The RWDC In The Correct Column Of The Table
1862 $listOfDCsToCheckObjectOnEndObj.Time = $null
1863 $listOfDCsToCheckObjectOnEndObj.Time = $targetedADdomainRWDCTime
1864 # Add The Row For The RWDC To The Table
1865 $listOfDCsToCheckObjectOnEnd += $listOfDCsToCheckObjectOnEndObj
1866
1867 # Execute The Check AD Replication Convergence Function For The Targeted Object To Check
1868 checkADReplicationConvergence $targetedADdomainFQDN $targetedADdomainSourceRWDCFQDN $targetObjectToCheckDN $listOfDCsToCheckObjectOnStart $listOfDCsToCheckObjectOnEnd $modeOfOperationNr
1869 }
1870 }
1871
1872 # KrbTgt in use by specific RODC - Single RODC in the AD Domain Or KrbTgt in use by specific RODC - Multiple RODCs in the AD Domain, but not all
1873 If ($targetKrbTgtAccountNr -eq 2 -Or $targetKrbTgtAccountNr -eq 3) {
1874 # Collection Of Reachable RODCs Specified To Be Targeted
1875 $collectionOfReachableRODCsToProcess = $null
1876 $collectionOfReachableRODCsToProcess = $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -ne "Unknown" -And $_."Source RWDC FQDN" -ne "RODC Unreachable" -And $targetRODCFQDNList -contains $_."Host Name"}
1877 # Collection Of UnReachable RODCs Specified To Be Targeted
1878 $collectionOfUnReachableRODCsToProcess = $null
1879 $collectionOfUnReachableRODCsToProcess = $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -eq "RODC Unreachable" -And $targetRODCFQDNList -contains $_."Host Name"}
1880 # Collection Of Unknown RODCs Specified To Be Targeted
1881 $collectionOfUnknownRODCsToProcess = $null
1882 $collectionOfUnknownRODCsToProcess = $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -eq "Unknown" -And $targetRODCFQDNList -contains $_."Host Name"}
1883 }
1884
1885 # KrbTgt in use by specific RODC - All RODCs in the AD Domain
1886 If ($targetKrbTgtAccountNr -eq 4) {
1887 # Collection Of Reachable RODCs Specified To Be Targeted
1888 $collectionOfReachableRODCsToProcess = $null
1889 $collectionOfReachableRODCsToProcess = $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -ne "Unknown" -And $_."Source RWDC FQDN" -ne "RODC Unreachable"}
1890 # Collection Of UnReachable RODCs Specified To Be Targeted
1891 $collectionOfUnReachableRODCsToProcess = $null
1892 $collectionOfUnReachableRODCsToProcess = $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -eq "RODC Unreachable"}
1893 # Collection Of Unknown RODCs Specified To Be Targeted
1894 $collectionOfUnknownRODCsToProcess = $null
1895 $collectionOfUnknownRODCsToProcess = $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -eq "Unknown"}
1896 }
1897 # KrbTgt in use by specific RODC - Single RODC in the AD Domain OR KrbTgt in use by specific RODC - Multiple RODCs in the AD Domain, but not all OR KrbTgt in use by specific RODC - All RODCs in the AD Domain
1898 If ($targetKrbTgtAccountNr -eq 2 -Or $targetKrbTgtAccountNr -eq 3 -Or $targetKrbTgtAccountNr -eq 4) {
1899 # If Any RODC Object Exists In The Reachable RODC List
1900 If ($collectionOfReachableRODCsToProcess) {
1901 $collectionOfReachableRODCsToProcess | %{
1902 # Retrieve The RODC Object In The List
1903 $rodcToProcess = $null
1904 $rodcToProcess = $_
1905
1906 # Retrieve The sAMAccountName Of The KrbTgt Account In Use By The RODC
1907 $krbTgtSamAccountName = $null
1908 $krbTgtSamAccountName = $rodcToProcess."Krb Tgt"
1909 # Retrieve The HostName Of The RODC
1910 $rodcFQDNTarget = $null
1911 $rodcFQDNTarget = $rodcToProcess."Host Name"
1912 # Retrieve The SiteName Of The RODC
1913 $rodcSiteTarget = $null
1914 $rodcSiteTarget = $rodcToProcess."Site Name"
1915 # Retrieve The HostName Source RWDC Used By The RODC
1916 $targetedADdomainSourceRWDCFQDN = $null
1917 $targetedADdomainSourceRWDCFQDN = $rodcToProcess."Source RWDC FQDN"
1918
1919 # Retrieve The KrbTgt Account Object
1920 $krbTgtObject = $null
1921 $krbTgtObject = Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Server $targetedADdomainSourceRWDCFQDN
1922 # Retrieve The DN Of The KrbTgt Account Object
1923 $krbTgtObjectDN = $null
1924 $krbTgtObjectDN = $krbTgtObject.DistinguishedName
1925 Logging "+++++" "REMARK"
1926 Logging "+++ Processing KrbTgt Account....: '$krbTgtSamAccountName' | '$krbTgtObjectDN' +++" "REMARK"
1927 Logging "+++ Used By RODC.................: '$rodcFQDNTarget' (Site: $rodcSiteTarget) +++" "REMARK"
1928 Logging "+++++" "REMARK"
1929 Logging "" "REMARK"
1930
1931 # Retrieve The Object Of The Source RWDC Specified For The RODC
1932 $targetedADdomainSourceRWDCObj = $null
1933 $targetedADdomainSourceRWDCObj = Get-ADDomainController $targetedADdomainSourceRWDCFQDN -Server $targetedADdomainRWDCWithPDCFSMOFQDN
1934 # Retrieve If The Source RWDC Specified For The RODC Is The RWDC With The PDC FSMO
1935 $targetedADdomainSourceRWDCIsPDC = $null
1936 If ($targetedADdomainSourceRWDCFQDN -eq $targetedADdomainRWDCWithPDCFSMOFQDN) {
1937 $targetedADdomainSourceRWDCIsPDC = $True
1938 } Else {
1939 $targetedADdomainSourceRWDCIsPDC = $False
1940 }
1941 # Retrieve The SiteName Of The Source RWDC Specified For The RODC
1942 $targetedADdomainSourceRWDCSiteName = $null
1943 $targetedADdomainSourceRWDCSiteName = $targetedADdomainSourceRWDCObj.Site
1944 # Retrieve The DS Type Of The Source RWDC Specified For The RODC
1945 $targetedADdomainSourceRWDCDSType = "Read/Write"
1946 # Retrieve The IP Address Of The Source RWDC Specified For The RODC
1947 $targetedADdomainSourceRWDCIPAddress = $null
1948 $targetedADdomainSourceRWDCIPAddress = $targetedADdomainSourceRWDCObj.IPv4Address
1949 # Retrieve The Reachability Of The Source RWDC Specified For The RODC
1950 $targetedADdomainSourceRWDCReachability = $null
1951 $targetedADdomainSourceRWDCReachability = ($tableOfDCsInADDomain | ?{$_."Host Name" -eq $targetedADdomainSourceRWDCFQDN}).Reachable
1952 # Set The Start Time For The Source RWDC Specified For The RODC
1953 $targetedADdomainSourceRWDCTime = 0.00
1954
1955 # Define An Empty List/Table That Will Contain All DCs In The AD Domain And Related Information
1956 # Define An Empty Starting List With DCs
1957 $listOfDCsToCheckObjectOnStart = @()
1958 # Add The Row For The DC To The Table
1959 $listOfDCsToCheckObjectOnStart += $rodcToProcess
1960 # Define The Columns For This DC To Be Filled In
1961 $listOfDCsToCheckObjectOnStartObj = "" | Select "Host Name",PDC,"Site Name","DS Type","IP Address",Reachable,"Source RWDC FQDN","Source RWDC DSA"
1962 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
1963 $listOfDCsToCheckObjectOnStartObj."Host Name" = $null
1964 $listOfDCsToCheckObjectOnStartObj."Host Name" = $targetedADdomainSourceRWDCFQDN
1965 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
1966 $listOfDCsToCheckObjectOnStartObj.PDC = $null
1967 $listOfDCsToCheckObjectOnStartObj.PDC = $targetedADdomainSourceRWDCIsPDC
1968 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
1969 $listOfDCsToCheckObjectOnStartObj."Site Name" = $null
1970 $listOfDCsToCheckObjectOnStartObj."Site Name" = $targetedADdomainSourceRWDCSiteName
1971 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
1972 $listOfDCsToCheckObjectOnStartObj."DS Type" = $null
1973 $listOfDCsToCheckObjectOnStartObj."DS Type" = $targetedADdomainSourceRWDCDSType
1974 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
1975 $listOfDCsToCheckObjectOnStartObj."IP Address" = $null
1976 $listOfDCsToCheckObjectOnStartObj."IP Address" = $targetedADdomainSourceRWDCIPAddress
1977 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
1978 $listOfDCsToCheckObjectOnStartObj.Reachable = $null
1979 $listOfDCsToCheckObjectOnStartObj.Reachable = $targetedADdomainSourceRWDCReachability
1980 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
1981 $listOfDCsToCheckObjectOnStartObj."Source RWDC FQDN" = "N.A."
1982 $listOfDCsToCheckObjectOnStartObj."Source RWDC DSA" = "N.A."
1983 # Add The Row For The DC To The Table
1984 $listOfDCsToCheckObjectOnStart += $listOfDCsToCheckObjectOnStartObj
1985 # Sort The List Of DCs Based Upon DS Type
1986 $listOfDCsToCheckObjectOnStart = $listOfDCsToCheckObjectOnStart | Sort-Object -Property @{Expression = "DS Type"; Descending = $False}
1987
1988 If ($targetedADdomainSourceRWDCReachability) {
1989 # If The Source RWDC Is Available/Reachable
1990
1991 # If Mode 2
1992 If ($modeOfOperationNr -eq 2) {
1993 # Execute The Creation Of the Temporary Canary Object, And Abort The Script If That Fails
1994 $targetObjectToCheckDN = $null
1995 $targetObjectToCheckDN = createTempCanaryObject $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName $execDateTimeCustom1
1996 If (!$targetObjectToCheckDN) {
1997 EXIT
1998 }
1999 }
2000 # If Mode 3 Or 4
2001 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
2002 # Retrieve The KrbTgt Account Object
2003 $targetObjectToCheck = $null
2004 $targetObjectToCheck = Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Properties * -Server $targetedADdomainSourceRWDCFQDN
2005 If ($targetObjectToCheck) {
2006 # If The KrbTgt Account Object Exists (You're In Deep Sh!t If The Account Does Not Exist! :-))
2007
2008 # Retrieve The DN Of The KrbTgt Account Object
2009 $targetObjectToCheckDN = $null
2010 $targetObjectToCheckDN = $targetObjectToCheck.DistinguishedName
2011
2012 # Retrieve The Password Last Set Of The KrbTgt Account Object
2013 $targetObjectToCheckPwdLastSet = $null
2014 $targetObjectToCheckPwdLastSet = Get-Date $([datetime]::fromfiletime($targetObjectToCheck.pwdLastSet))
2015
2016 # Calculate The Expiration Date/Time Of N-1 Kerberos Tickets
2017 $expirationTimeForNMinusOneKerbTickets = $null
2018 $expirationTimeForNMinusOneKerbTickets = (($targetObjectToCheckPwdLastSet.AddHours($targetedADdomainMaxTgtLifetimeHrs)).AddMinutes($targetedADdomainMaxClockSkewMins)).AddMinutes($targetedADdomainMaxClockSkewMins)
2019
2020 # Retrieve The Metadata Of The Object, And More Specific Of The pwdLastSet Attribute Of That Object
2021 $metadataObject = $null
2022 $metadataObject = Get-ADReplicationAttributeMetadata $targetObjectToCheckDN -Server $targetedADdomainSourceRWDCFQDN
2023 $metadataObjectAttribPwdLastSet = $null
2024 $metadataObjectAttribPwdLastSet = $metadataObject | ?{$_.AttributeName -eq "pwdLastSet"}
2025 $orgRWDCNTDSSettingsObjectDN = $null
2026 $orgRWDCNTDSSettingsObjectDN = $metadataObjectAttribPwdLastSet.LastOriginatingChangeDirectoryServerIdentity
2027 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $null
2028 If ($orgRWDCNTDSSettingsObjectDN) {
2029 # Strip "CN=NTDS Settings," To End Up With The Server Object DN
2030 $orgRWDCServerObjectDN = $null
2031 $orgRWDCServerObjectDN = $orgRWDCNTDSSettingsObjectDN.SubString(("CN=NTDS Settings,").Length)
2032 # Connect To The Server Object DN
2033 $orgRWDCServerObjectObj = $null
2034 $orgRWDCServerObjectObj = ([ADSI]"LDAP://$targetedADdomainRWDCWithPDCFSMOFQDN/$orgRWDCServerObjectDN")
2035 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = $orgRWDCServerObjectObj.dnshostname[0]
2036 } Else {
2037 $metadataObjectAttribPwdLastSetOrgRWDCFQDN = "RWDC Demoted"
2038 }
2039 $metadataObjectAttribPwdLastSetOrgTime = $null
2040 $metadataObjectAttribPwdLastSetOrgTime = Get-Date $($metadataObjectAttribPwdLastSet.LastOriginatingChangeTime) -f "yyyy-MM-dd HH:mm:ss"
2041 $metadataObjectAttribPwdLastSetVersion = $null
2042 $metadataObjectAttribPwdLastSetVersion = $metadataObjectAttribPwdLastSet.Version
2043
2044 $okToReset = $null
2045 If ($expirationTimeForNMinusOneKerbTickets -lt [DateTime]::Now) {
2046 # Allow The Password Reset To Occur Without Questions If The Expiration Date/Time Of N-1 Kerberos Tickets Is Earlier Than The Current Time
2047
2048 $okToReset = $True
2049 } Else {
2050 # Allow The Password Reset To Occur After Confirnation Only If The Expiration Date/Time Of N-1 Kerberos Tickets Is Equal Or Later Than The Current Time
2051
2052 Logging " --> According To RWDC.....................: '$targetedADdomainSourceRWDCFQDN'"
2053 Logging " --> Previous Password Set Date/Time.......: '$(Get-Date $targetObjectToCheckPwdLastSet -f 'yyyy-MM-dd HH:mm:ss')'"
2054 Logging " --> Date/Time N-1 Kerberos Tickets........: '$(Get-Date $expirationTimeForNMinusOneKerbTickets -f 'yyyy-MM-dd HH:mm:ss')'"
2055 Logging " --> Date/Time Now.........................: '$(Get-Date $([DateTime]::Now) -f 'yyyy-MM-dd HH:mm:ss')'"
2056 Logging " --> Max TGT Lifetime (Hours)..............: '$targetedADdomainMaxTgtLifetimeHrs'"
2057 Logging " --> Max Clock Skew (Minutes)..............: '$targetedADdomainMaxClockSkewMins'"
2058 Logging " --> Originating RWDC Previous Change......: '$metadataObjectAttribPwdLastSetOrgRWDCFQDN'"
2059 Logging " --> Originating Time Previous Change......: '$metadataObjectAttribPwdLastSetOrgTime'"
2060 Logging " --> Current Version Of Attribute Value....: '$metadataObjectAttribPwdLastSetVersion'"
2061 Logging ""
2062 Logging " --> Resetting KrbTgt Accnt Password Means.: 'MAJOR IMPACT FOR RESOURCES SERVICED BY $rodcFQDNTarget' (Site: $rodcSiteTarget)" "WARNING"
2063 Logging "" "WARNING"
2064 Logging "What do you want to do? [CONTINUE | SKIP | STOP]: " "ACTION-NO-NEW-LINE"
2065 $continueOrStop = $null
2066 $continueOrStop = "CONTINUE"
2067 # Any Confirmation Not Equal To CONTINUE And Not Euqla To SKIP And Not Equal To STOP Will Be Equal To STOP
2068 If ($continueOrStop.ToUpper() -ne "CONTINUE" -And $continueOrStop.ToUpper() -ne "SKIP" -And $continueOrStop.ToUpper() -ne "STOP") {
2069 $continueOrStop = "STOP"
2070 }
2071 Logging ""
2072 If ($continueOrStop.ToUpper() -eq "CONTINUE") {
2073 # If The Confirmation Equals CONTINUE Allow The Password Reset To Continue
2074
2075 $okToReset = $True
2076 } Else {
2077 # If The Confirmation Does Not Equal CONTINUE Do Not Allow The Password Reset To Continue. Abort
2078
2079 $okToReset = $False
2080 }
2081 Logging " --> Chosen: $continueOrStop" "REMARK"
2082 Logging ""
2083 }
2084 If ($okToReset) {
2085 # If OK To Reset Then Execute The Password Reset Of The KrbTgt Account
2086
2087 setPasswordOfADAccount $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName
2088 } Else {
2089 # If Not OK To Reset Then Abort, Except When SKIP Was Specified
2090
2091 If ($continueOrStop.ToUpper() -eq "SKIP") {
2092 # JUST A PLACEHOLDER, Does Not Do Anything
2093 } ElseIf ($continueOrStop.ToUpper() -eq "STOP") {
2094 EXIT
2095 } Else {
2096 EXIT
2097 }
2098 }
2099 } Else {
2100 # If The KrbTgt Account Object Does Not Exist (You're In Deep Sh!t If The Account Does Not Exist! :-))
2101
2102 Logging " --> KrbTgt Account With sAMAccountName '$krbTgtSamAccountName' Does NOT Exist! Skipping..." "ERROR"
2103 Logging "" "ERROR"
2104 }
2105 }
2106 } Else {
2107 # If The Source RWDC Is Not Available/Reachable
2108
2109 Logging ""
2110 Logging "The RWDC '$targetedADdomainSourceRWDCFQDN' to make the change on is not reachable/available..." "ERROR"
2111 Logging ""
2112 }
2113
2114 # If The DN Of The Target Object To Check (Temp Canary Object Or KrbTgt Account, Depends On The Mode Chosen) Was Determined/Found
2115 If ($targetObjectToCheckDN) {
2116 # If The Confirmation Equals CONTINUE
2117 If ($continueOrStop.ToUpper() -eq "CONTINUE") {
2118 # Define An Empty List/Table For At The End That Will Contain The DCs In The AD Domain And Related Information
2119 $listOfDCsToCheckObjectOnEnd = @()
2120 # Define The Columns For The DCs In The AD Domain To Be Filled In
2121 $listOfDCsToCheckObjectOnEndObj = "" | Select "Host Name",PDC,"Site Name","DS Type","IP Address",Reachable,"Source RWDC FQDN",Time
2122 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2123 $listOfDCsToCheckObjectOnEndObj."Host Name" = $null
2124 $listOfDCsToCheckObjectOnEndObj."Host Name" = $targetedADdomainSourceRWDCFQDN
2125 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2126 $listOfDCsToCheckObjectOnEndObj.PDC = $null
2127 $listOfDCsToCheckObjectOnEndObj.PDC = $targetedADdomainSourceRWDCIsPDC
2128 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2129 $listOfDCsToCheckObjectOnEndObj."Site Name" = $null
2130 $listOfDCsToCheckObjectOnEndObj."Site Name" = $targetedADdomainSourceRWDCSiteName
2131 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2132 $listOfDCsToCheckObjectOnEndObj."DS Type" = $null
2133 $listOfDCsToCheckObjectOnEndObj."DS Type" = $targetedADdomainSourceRWDCDSType
2134 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2135 $listOfDCsToCheckObjectOnEndObj."IP Address" = $null
2136 $listOfDCsToCheckObjectOnEndObj."IP Address" = $targetedADdomainSourceRWDCIPAddress
2137 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2138 $listOfDCsToCheckObjectOnEndObj.Reachable = $null
2139 $listOfDCsToCheckObjectOnEndObj.Reachable = $targetedADdomainSourceRWDCReachability
2140 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2141 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = "N.A."
2142 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2143 $listOfDCsToCheckObjectOnEndObj.Time = $null
2144 $listOfDCsToCheckObjectOnEndObj.Time = $targetedADdomainSourceRWDCTime
2145 # Add The Row For The DC To The Table
2146 $listOfDCsToCheckObjectOnEnd += $listOfDCsToCheckObjectOnEndObj
2147
2148 # Execute The Check AD Replication Convergence Function For The Targeted Object To Check
2149 checkADReplicationConvergence $targetedADdomainFQDN $targetedADdomainSourceRWDCFQDN $targetObjectToCheckDN $listOfDCsToCheckObjectOnStart $listOfDCsToCheckObjectOnEnd $modeOfOperationNr
2150 }
2151 }
2152 }
2153 } Else {
2154 #XXX
2155 }
2156 # If Any RODC Object Exists In The UnReachable RODC List
2157 If ($collectionOfUnReachableRODCsToProcess) {
2158 $collectionOfUnReachableRODCsToProcess | %{
2159 # Retrieve The RODC Object In The List
2160 $rodcToProcess = $null
2161 $rodcToProcess = $_
2162
2163 # Retrieve The sAMAccountName Of The KrbTgt Account In Use By The RODC
2164 $krbTgtSamAccountName = $null
2165 $krbTgtSamAccountName = $rodcToProcess."Krb Tgt"
2166 # Retrieve The HostName Of The RODC
2167 $rodcFQDNTarget = $null
2168 $rodcFQDNTarget = $rodcToProcess."Host Name"
2169 # Retrieve The SiteName Of The RODC
2170 $rodcSiteTarget = $null
2171 $rodcSiteTarget = $rodcToProcess."Site Name"
2172 # Retrieve The HostName Source RWDC Used By The RODC
2173 $targetedADdomainSourceRWDCFQDN = $null
2174 $targetedADdomainSourceRWDCFQDN = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Host Name"
2175 # Retrieve The DN Of The KrbTgt Account Object
2176 $krbTgtDN = $null
2177 $krbTgtDN = (Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Server $targetedADdomainSourceRWDCFQDN).DistinguishedName
2178 Logging "+++++" "REMARK"
2179 Logging "+++ Processing KrbTgt Account....: '$krbTgtSamAccountName' | '$krbTgtDN' +++" "REMARK"
2180 Logging "+++ Used By RODC.................: '$rodcFQDNTarget' (Site: $rodcSiteTarget) +++" "REMARK"
2181 Logging "+++++" "REMARK"
2182 Logging "" "REMARK"
2183
2184 # Retrieve The Status Of Hosting The PDC FSMO From The RWDC Hosting The PDC FSMO (Duh!)
2185 $targetedADdomainSourceRWDCIsPDC = $null
2186 $targetedADdomainSourceRWDCIsPDC = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True}).PDC
2187 # Retrieve The SiteName Listed For The RWDC With The PDC FSMO
2188 $targetedADdomainSourceRWDCSiteName = $null
2189 $targetedADdomainSourceRWDCSiteName = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Site Name"
2190 # Retrieve The DS Type Listed For The RWDC With The PDC FSMO
2191 $targetedADdomainSourceRWDCDSType = $null
2192 $targetedADdomainSourceRWDCDSType = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."DS Type"
2193 # Retrieve The IP Address Listed For The RWDC With The PDC FSMO
2194 $targetedADdomainSourceRWDCIPAddress = $null
2195 $targetedADdomainSourceRWDCIPAddress = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."IP Address"
2196 # Retrieve The Reachability Listed For The RWDC With The PDC FSMO
2197 $targetedADdomainRWDCReachability = $null
2198 $targetedADdomainRWDCReachability = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True}).Reachable
2199 # Retrieve The FQDN Of The Source RWDC Listed For The RWDC With The PDC FSMO
2200 $targetedADdomainRWDCSourceRWDCFQDN = "N.A."
2201 # Set The Start Time For The RWDC With The PDC FSMO
2202 $targetedADdomainRWDCTime = 0.00
2203
2204 If ($targetedADdomainRWDCReachability) {
2205 # If The RWDC With The PDC FSMO Is Reachable
2206
2207 # If Mode 2
2208 If ($modeOfOperationNr -eq 2) {
2209 # Execute The Creation Of the Temporary Canary Object, And Abort The Script If That Fails
2210 $targetObjectToCheckDN = $null
2211 $targetObjectToCheckDN = createTempCanaryObject $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName $execDateTimeCustom1
2212 If (!$targetObjectToCheckDN) {
2213 EXIT
2214 }
2215 }
2216 # If Mode 3 Or 4
2217 If ($modeOfOperationNr -eq 3 -Or $modeOfOperationNr -eq 4) {
2218 # Retrieve The KrbTgt Account Object
2219 $targetObjectToCheck = $null
2220 $targetObjectToCheck = Get-ADUser -LDAPFilter "(sAMAccountName=$krbTgtSamAccountName)" -Properties * -Server $targetedADdomainSourceRWDCFQDN
2221 If ($targetObjectToCheck) {
2222 # If The KrbTgt Account Object Exists (You're In Deep Sh!t If The Account Does Not Exist! :-))
2223
2224 # Retrieve The DN Of The KrbTgt Account Object
2225 $targetObjectToCheckDN = $null
2226 $targetObjectToCheckDN = $targetObjectToCheck.DistinguishedName
2227 # Retrieve The Password Last Set Of The KrbTgt Account Object
2228 $targetObjectToCheckPwdLastSet = $null
2229 $targetObjectToCheckPwdLastSet = Get-Date $([datetime]::fromfiletime($targetObjectToCheck.pwdLastSet))
2230 # Calculate The Expiration Date/Time Of N-1 Kerberos Tickets
2231 $expirationTimeForNMinusOneKerbTickets = $null
2232 $expirationTimeForNMinusOneKerbTickets = (($targetObjectToCheckPwdLastSet.AddHours($targetedADdomainMaxTgtLifetimeHrs)).AddMinutes($targetedADdomainMaxClockSkewMins)).AddMinutes($targetedADdomainMaxClockSkewMins)
2233 $okToReset = $null
2234 If ($expirationTimeForNMinusOneKerbTickets -lt [DateTime]::Now) {
2235 # Allow The Password Reset To Occur Without Questions If The Expiration Date/Time Of N-1 Kerberos Tickets Is Earlier Than The Current Time
2236
2237 $okToReset = $True
2238 } Else {
2239 # Allow The Password Reset To Occur After Confirnation Only If The Expiration Date/Time Of N-1 Kerberos Tickets Is Equal Or Later Than The Current Time
2240
2241 Logging " --> According To RWDC.....................: '$targetedADdomainSourceRWDCFQDN'"
2242 Logging " --> Previous Password Set Date/Time.......: '$(Get-Date $targetObjectToCheckPwdLastSet -f 'yyyy-MM-dd HH:mm:ss')'"
2243 Logging " --> Date/Time N-1 Kerberos Tickets........: '$(Get-Date $expirationTimeForNMinusOneKerbTickets -f 'yyyy-MM-dd HH:mm:ss')'"
2244 Logging " --> Date/Time Now.........................: '$(Get-Date $([DateTime]::Now) -f 'yyyy-MM-dd HH:mm:ss')'"
2245 Logging " --> Max TGT Lifetime (Hours)..............: '$targetedADdomainMaxTgtLifetimeHrs'"
2246 Logging " --> Max Clock Skew (Minutes)..............: '$targetedADdomainMaxClockSkewMins'"
2247 Logging ""
2248 Logging " --> Resetting KrbTgt Accnt Password Means.: 'MAJOR IMPACT FOR RESOURCES SERVICED BY $rodcFQDNTarget' (Site: $rodcSiteTarget)" "WARNING"
2249 Logging "" "WARNING"
2250 Logging "What do you want to do? [CONTINUE | SKIP | STOP]: " "ACTION-NO-NEW-LINE"
2251 $continueOrStop = $null
2252 $continueOrStop = "CONTINUE"
2253 # Any Confirmation Not Equal To CONTINUE And Not Equal To SKIP And Not Equal STOP Will Be Equal To STOP
2254 If ($continueOrStop.ToUpper() -ne "CONTINUE" -And $continueOrStop.ToUpper() -ne "SKIP" -And $continueOrStop.ToUpper() -ne "STOP") {
2255 $continueOrStop = "STOP"
2256 }
2257 Logging ""
2258 If ($continueOrStop.ToUpper() -eq "CONTINUE") {
2259 # If The Confirmation Equals CONTINUE Allow The Password Reset To Continue
2260
2261 $okToReset = $True
2262 } Else {
2263 # If The Confirmation Does Not Equal CONTINUE Do Not Allow The Password Reset To Continue. Abort
2264
2265 $okToReset = $False
2266 }
2267 Logging " --> Chosen: $continueOrStop" "REMARK"
2268 Logging ""
2269 }
2270 If ($okToReset) {
2271 # If OK To Reset Then Execute The Password Reset Of The KrbTgt Account
2272
2273 setPasswordOfADAccount $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName
2274 } Else {
2275 # If Not OK To Reset Then Abort, Except When SKIP Was Specified
2276
2277 If ($continueOrStop.ToUpper() -eq "SKIP") {
2278 # JUST A PLACEHOLDER, Does Not Do Anything
2279 } ElseIf ($continueOrStop.ToUpper() -eq "STOP") {
2280 EXIT
2281 } Else {
2282 EXIT
2283 }
2284 }
2285 } Else {
2286 # If The KrbTgt Account Object Does Not Exist (You're In Deep Sh!t If The Account Does Not Exist! :-))
2287
2288 Logging " --> KrbTgt Account With sAMAccountName '$krbTgtSamAccountName' Does NOT Exist! Skipping..." "ERROR"
2289 Logging "" "ERROR"
2290 }
2291 }
2292 } Else {
2293 # If The Source RWDC With The PDC FSMO Is NOT Reachable
2294
2295 Logging ""
2296 Logging "The RWDC '$targetedADdomainSourceRWDCFQDN' to make the change on is not reachable/available..." "ERROR"
2297 Logging ""
2298 }
2299
2300 # If The Confirmation Equals CONTINUE
2301 If ($continueOrStop.ToUpper() -eq "CONTINUE") {
2302 $listOfDCsToCheckObjectOnStart = ($tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read/Write"})
2303 # Define An Empty List/Table For At The End That Will Contain The DCs In The AD Domain And Related Information
2304 $listOfDCsToCheckObjectOnEnd = @()
2305 # Define The Columns For The DCs In The AD Domain To Be Filled In
2306 $listOfDCsToCheckObjectOnEndObj = "" | Select "Host Name",PDC,"Site Name","DS Type","IP Address",Reachable,"Source RWDC FQDN",Time
2307 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2308 $listOfDCsToCheckObjectOnEndObj."Host Name" = $null
2309 $listOfDCsToCheckObjectOnEndObj."Host Name" = $targetedADdomainSourceRWDCFQDN
2310 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2311 $listOfDCsToCheckObjectOnEndObj.PDC = $null
2312 $listOfDCsToCheckObjectOnEndObj.PDC = $targetedADdomainSourceRWDCIsPDC
2313 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2314 $listOfDCsToCheckObjectOnEndObj."Site Name" = $null
2315 $listOfDCsToCheckObjectOnEndObj."Site Name" = $targetedADdomainSourceRWDCSiteName
2316 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2317 $listOfDCsToCheckObjectOnEndObj."DS Type" = $null
2318 $listOfDCsToCheckObjectOnEndObj."DS Type" = $targetedADdomainSourceRWDCDSType
2319 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2320 $listOfDCsToCheckObjectOnEndObj."IP Address" = $null
2321 $listOfDCsToCheckObjectOnEndObj."IP Address" = $targetedADdomainSourceRWDCIPAddress
2322 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2323 $listOfDCsToCheckObjectOnEndObj.Reachable = $null
2324 $listOfDCsToCheckObjectOnEndObj.Reachable = $targetedADdomainRWDCReachability
2325 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2326 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $null
2327 $listOfDCsToCheckObjectOnEndObj."Source RWDC FQDN" = $targetedADdomainRWDCSourceRWDCFQDN
2328 # Set The Corresponding Value Of The DC In The Correct Column Of The Table
2329 $listOfDCsToCheckObjectOnEndObj.Time = $targetedADdomainRWDCTime
2330 # Add The Row For The DC To The Table
2331 $listOfDCsToCheckObjectOnEnd += $listOfDCsToCheckObjectOnEndObj
2332
2333 # Execute The Check AD Replication Convergence Function For The Targeted Object To Check
2334 checkADReplicationConvergence $targetedADdomainFQDN $targetedADdomainSourceRWDCFQDN $targetObjectToCheckDN $listOfDCsToCheckObjectOnStart $listOfDCsToCheckObjectOnEnd $modeOfOperationNr
2335 }
2336 }
2337 } Else {
2338 #XXX
2339 }
2340 # If Any RODC Object Exists In The Unknown RODC List
2341 If ($collectionOfUnknownRODCsToProcess) {
2342 Logging "+++++" "REMARK"
2343 Logging "+++ The Following Look Like RODCs, But May Not Be Real RODCs..." "REMARK"
2344 Logging "+++++" "REMARK"
2345 Logging "" "REMARK"
2346 # For Every Unknown RODC
2347 $collectionOfUnknownRODCsToProcess | %{
2348 $rodcToProcess = $null
2349 $rodcToProcess = $_
2350 Logging "$($rodcToProcess | FT * | Out-String)"
2351 Logging ""
2352 }
2353 Logging ""
2354 } Else {
2355 #XXX
2356 }
2357 }
2358}
2359
2360### Mode 8 - Create TEST KrbTgt Accounts
2361If ($modeOfOperationNr -eq 8) {
2362 Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
2363 Logging "CREATE TEST KRBTGT ACCOUNTS (MODE 8)..." "HEADER"
2364 Logging ""
2365
2366 # Asking Confirmation To Continue Or Not
2367 Logging "Do you really want to continue and execute 'Mode $modeOfOperationNr'? [CONTINUE | STOP]: " "ACTION-NO-NEW-LINE"
2368 $continueOrStop = $null
2369 $continueOrStop = "CONTINUE"
2370 # Any Confirmation Not Equal To CONTINUE Will Be Equal To STOP
2371 If ($continueOrStop.ToUpper() -ne "CONTINUE") {
2372 $continueOrStop = "STOP"
2373 }
2374 Logging ""
2375 Logging " --> Chosen: $continueOrStop" "REMARK"
2376 Logging ""
2377 # Any Confirmation Not Equal To CONTINUE Will Abort The Script
2378 If ($continueOrStop.ToUpper() -ne "CONTINUE") {
2379 EXIT
2380 }
2381
2382 # Retrieve The FQDN Of The RWDC With The PDC FSMO To Create The TEST/BOGUS KrbTgt Account Objects
2383 $targetedADdomainSourceRWDCFQDN = $null
2384 $targetedADdomainSourceRWDCFQDN = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Host Name"
2385
2386 # Determine The KrbTgt Account In Use By The RWDC with The PDC FSMO (Representative For All RWDCs In The AD Domain)
2387 $krbTgtSamAccountName = $null
2388 $krbTgtSamAccountName = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Krb Tgt"
2389 Logging "+++++" "REMARK"
2390 Logging "+++ Create Test KrbTgt Account...: '$krbTgtSamAccountName' +++" "REMARK"
2391 Logging "+++ Used By RWDC.................: 'All RWDCs' +++" "REMARK"
2392 Logging "+++++" "REMARK"
2393 Logging "" "REMARK"
2394
2395 # Execute The Create Test KrbTgt Accounts Function To Create The TEST/BOGUS KrbTgt Account For RWDCs
2396 createTestKrbTgtADAccount $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName "RWDC"
2397
2398 # For All RODCs In The AD Domain That Do Not Have An Unknown RWDC Specfied
2399 $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -ne "Unknown"} | %{
2400 # Retrieve The RODC Object In The List
2401 $rodcToProcess = $null
2402 $rodcToProcess = $_
2403 # Retrieve The sAMAccountName Of The KrbTgt Account In Use By The RODC
2404 $krbTgtSamAccountName = $null
2405 $krbTgtSamAccountName = $rodcToProcess."Krb Tgt"
2406 # Retrieve The HostName Of The RODC
2407 $rodcFQDNTarget = $null
2408 $rodcFQDNTarget = $rodcToProcess."Host Name"
2409 # Retrieve The SiteName Of The RODC
2410 $rodcSiteTarget = $null
2411 $rodcSiteTarget = $rodcToProcess."Site Name"
2412 Logging "+++++" "REMARK"
2413 Logging "+++ Create Test KrbTgt Account...: '$krbTgtSamAccountName' +++" "REMARK"
2414 Logging "+++ Used By RODC.................: '$rodcFQDNTarget' (Site: $rodcSiteTarget) +++" "REMARK"
2415 Logging "+++++" "REMARK"
2416 Logging "" "REMARK"
2417
2418 # Execute The Create Test KrbTgt Accounts Function To Create The TEST/BOGUS KrbTgt Account For Each RODC
2419 createTestKrbTgtADAccount $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName "RODC"
2420 }
2421}
2422
2423### Mode 9 - Cleanup TEST KrbTgt Accounts
2424If ($modeOfOperationNr -eq 9) {
2425 Logging "------------------------------------------------------------------------------------------------------------------------------------------------------" "HEADER"
2426 Logging "CLEANUP TEST KRBTGT ACCOUNTS (MODE 9)..." "HEADER"
2427 Logging ""
2428
2429 # Asking Confirmation To Continue Or Not
2430 Logging "Do you really want to continue and execute 'Mode $modeOfOperationNr'? [CONTINUE | STOP]: " "ACTION-NO-NEW-LINE"
2431 $continueOrStop = $null
2432 $continueOrStop = "CONTINUE"
2433 # Any Confirmation Not Equal To CONTINUE Will Be Equal To STOP
2434 If ($continueOrStop.ToUpper() -ne "CONTINUE") {
2435 $continueOrStop = "STOP"
2436 }
2437 Logging ""
2438 Logging " --> Chosen: $continueOrStop" "REMARK"
2439 Logging ""
2440 # Any Confirmation Not Equal To CONTINUE Will Abort The Script
2441 If ($continueOrStop.ToUpper() -ne "CONTINUE") {
2442 EXIT
2443 }
2444
2445 # Retrieve The FQDN Of The RWDC With The PDC FSMO To Delete The TEST/BOGUS KrbTgt Account Objects
2446 $targetedADdomainSourceRWDCFQDN = $null
2447 $targetedADdomainSourceRWDCFQDN = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Host Name"
2448
2449 # Determine The KrbTgt Account In Use By The RWDC with The PDC FSMO (Representative For All RWDCs In The AD Domain)
2450 $krbTgtSamAccountName = $null
2451 $krbTgtSamAccountName = ($tableOfDCsInADDomain | ?{$_.PDC -eq $True})."Krb Tgt"
2452 Logging "+++++" "REMARK"
2453 Logging "+++ Delete Test KrbTgt Account...: '$krbTgtSamAccountName' +++" "REMARK"
2454 Logging "+++ Used By RWDC.................: 'All RWDCs' +++" "REMARK"
2455 Logging "+++++" "REMARK"
2456 Logging "" "REMARK"
2457
2458 # Execute The Delete Test KrbTgt Accounts Function To Delete The TEST/BOGUS KrbTgt Account For RWDCs
2459 deleteTestKrbTgtADAccount $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName
2460
2461 # For All RODCs In The AD Domain That Do Not Have An Unknown RWDC Specfied
2462 $tableOfDCsInADDomain | ?{$_."DS Type" -eq "Read-Only" -And $_."Source RWDC FQDN" -ne "Unknown"} | %{
2463 # Retrieve The RODC Object In The List
2464 $rodcToProcess = $null
2465 $rodcToProcess = $_
2466 # Retrieve The sAMAccountName Of The KrbTgt Account In Use By The RODC
2467 $krbTgtSamAccountName = $null
2468 $krbTgtSamAccountName = $rodcToProcess."Krb Tgt"
2469 # Retrieve The HostName Of The RODC
2470 $rodcFQDNTarget = $null
2471 $rodcFQDNTarget = $rodcToProcess."Host Name"
2472 # Retrieve The SiteName Of The RODC
2473 $rodcSiteTarget = $null
2474 $rodcSiteTarget = $rodcToProcess."Site Name"
2475 Logging "+++++" "REMARK"
2476 Logging "+++ Delete Test KrbTgt Account...: '$krbTgtSamAccountName' +++" "REMARK"
2477 Logging "+++ Used By RODC.................: '$rodcFQDNTarget' (Site: $rodcSiteTarget) +++" "REMARK"
2478 Logging "+++++" "REMARK"
2479 Logging "" "REMARK"
2480
2481 # Execute The Delete Test KrbTgt Accounts Function To Delete The TEST/BOGUS KrbTgt Account For Each RODC
2482 deleteTestKrbTgtADAccount $targetedADdomainSourceRWDCFQDN $krbTgtSamAccountName
2483 }
2484}
2485
2486# Display The Full Path To The Log File
2487Logging ""
2488Logging "Log File Path...: $logFilePath" "REMARK"
2489Logging ""