diff --git a/Src/DSInternals.PowerShell/ADDBRestoreFromMediaScriptTemplate.ps1 b/Src/DSInternals.PowerShell/ADDBRestoreFromMediaScriptTemplate.ps1 index 811642f..9b8b4e6 100644 --- a/Src/DSInternals.PowerShell/ADDBRestoreFromMediaScriptTemplate.ps1 +++ b/Src/DSInternals.PowerShell/ADDBRestoreFromMediaScriptTemplate.ps1 @@ -6,8 +6,9 @@ Restores the {DCName} domain controller from ntds.dit. This script should only be executed on a freshly installed {OSName}. Use at your own risk The DSInternals PowerShell module must be installed for all users on the target server. -.AUTHOR -Michael Grafnetter + +Author: Michael Grafnetter + #> #Requires -Version 3 -Modules DSInternals -RunAsAdministrator @@ -17,80 +18,97 @@ $vssResult = ([wmiclass] 'Win32_ShadowCopy').Create("$env:SystemDrive\", 'Client # All the other operations will be executed by a restartable workflow running in SYSTEM context. $initTask = Register-ScheduledJob -Name DSInternals-RFM-Initializer -ScriptBlock { - workflow Restore-DomainController - { - if ($env:COMPUTERNAME -ne '{DCName}') - { - # A server rename operation is required. - Rename-Computer -NewName '{DCName}' -Force - - # We explicitly suspend the workflow as Restart-Computer with the -Wait parameter does not survive local reboots. - shutdown.exe /r /t 5 - Suspend-Workflow -Label 'Waiting for reboot' - } + workflow Restore-DomainController + { + if ($env:COMPUTERNAME -ne '{DCName}') + { + # A server rename operation is required. + Rename-Computer -NewName '{DCName}' -Force + + # We explicitly suspend the workflow as Restart-Computer with the -Wait parameter does not survive local reboots. + shutdown.exe /r /t 5 + Suspend-Workflow -Label 'Waiting for reboot' + } - if ((Get-Service NTDS -ErrorAction SilentlyContinue) -eq $null) - { - # A DC promotion is required. - # Note: In order to mainstain compatibility with Windows Server 2008 R2, the ADDSDeployment module is not used. - # Advice: It is recommenced to change the DSRM password after DC promotion. - dcpromo.exe /unattend /ReplicaOrNewDomain:Domain /NewDomain:Forest /NewDomainDNSName:"{DomainName}" /DomainNetBiosName:"{NetBIOSDomainName}" /DomainLevel:{DomainMode} /ForestLevel:{ForestMode} '/SafeModeAdminPassword:"{DSRMPassword}"' /DatabasePath:"{TargetDBDirPath}" /LogPath:"{TargetLogDirPath}" /SysVolPath:"{TargetSysvolPath}" /AllowDomainReinstall:Yes /CreateDNSDelegation:No /DNSOnNetwork:No /InstallDNS:Yes /RebootOnCompletion:No - Set-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ServerManager\Roles\10 -Name ConfigurationStatus -Value 2 -Force - } - - # Reboot the computer into the Directory Services Restore Mode. - bcdedit.exe /set safeboot dsrepair - shutdown.exe /r /t 5 - Suspend-Workflow -Label 'Waiting for reboot' + if ((Get-Service NTDS -ErrorAction SilentlyContinue) -eq $null) + { + # A DC promotion is required. + # Note: In order to mainstain compatibility with Windows Server 2008 R2, the ADDSDeployment module is not used. + # Advice: It is recommenced to change the DSRM password after DC promotion. + dcpromo.exe /unattend /ReplicaOrNewDomain:Domain /NewDomain:Forest /NewDomainDNSName:"{DomainName}" /DomainNetBiosName:"{NetBIOSDomainName}" /DomainLevel:{DomainMode} /ForestLevel:{ForestMode} '/SafeModeAdminPassword:"{DSRMPassword}"' /DatabasePath:"{TargetDBDirPath}" /LogPath:"{TargetLogDirPath}" /SysVolPath:"{TargetSysvolPath}" /AllowDomainReinstall:Yes /CreateDNSDelegation:No /DNSOnNetwork:No /InstallDNS:Yes /RebootOnCompletion:No + Set-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ServerManager\Roles\10 -Name ConfigurationStatus -Value 2 -Force - # Re-encrypt the DB with the new boot key. - $currentBootKey = Get-BootKey -Online - Set-ADDBBootKey -DBPath '{SourceDBPath}' -LogPath '{SourceLogDirPath}' -OldBootKey {OldBootKey} -NewBootKey $currentBootKey + <# Alternative approach for Winows Server 2012+ + Install-WindowsFeature -Name AD-Domain-Services + Install-ADDSForest -DomainName '{DomainName}' ` + -DomainNetbiosName '{NetBIOSDomainName}' ` + -ForestMode {ForestModeString} ` + -DomainMode {DomainModeString} ` + -DatabasePath "{TargetDBDirPath}" ` + -LogPath "{TargetLogDirPath}" ` + -SysvolPath "{TargetSysvolPath}" ` + -InstallDns ` + -CreateDnsDelegation:$false ` + -NoDnsOnNetwork ` + -SafeModeAdministratorPassword (ConvertTo-SecureString -String '{DSRMPassword}' -AsPlainText -Force)` + -NoRebootOnCompletion ` + -Force + #> + } - # Clone the DC account password. - $ntdsParams = Get-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters - InlineScript { - # Note: SupplementalCredentials do not get serialized properly without using the InlineScript activity. - $dcAccount = Get-ADDBAccount -SamAccountName '{DCName}$' -DBPath $using:ntdsParams.'DSA Database file' -LogPath $using:ntdsParams.'Database log files path' -BootKey $using:currentBootKey - Set-ADDBAccountPasswordHash -ObjectGuid {DCGuid} -NTHash $dcAccount.NTHash -SupplementalCredentials $dcAccount.SupplementalCredentials -DBPath '{SourceDBPath}' -LogPath '{SourceLogDirPath}' -BootKey $using:currentBootKey - } + # Reboot the computer into the Directory Services Restore Mode. + bcdedit.exe /set safeboot dsrepair + shutdown.exe /r /t 5 + Suspend-Workflow -Label 'Waiting for reboot' - # Replace the database and transaction logs. - robocopy.exe '{SourceDBDirPath}' $ntdsParams.'DSA Working Directory' *.dit *.edb *.chk *.jfm /MIR /NP /NDL /NJS - robocopy.exe '{SourceLogDirPath}' $ntdsParams.'Database log files path' *.log *.jrs /MIR /NP /NDL /NJS + # Re-encrypt the DB with the new boot key. + $currentBootKey = Get-BootKey -Online + Set-ADDBBootKey -DBPath '{SourceDBPath}' -LogPath '{SourceLogDirPath}' -OldBootKey {OldBootKey} -NewBootKey $currentBootKey - # Replace SYSVOL. - $netlogonParams = Get-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters -Name SysVol - robocopy.exe '{SourceSysvolPath}' (Join-Path -Path $netlogonParams.SysVol -ChildPath '{DomainName}') /MIR /XD DfsrPrivate /XJ /COPYALL /DCOPY:DAT /SECFIX /TIMFIX /NP /NDL + # Clone the DC account password. + $ntdsParams = Get-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters + InlineScript { + # Note: SupplementalCredentials do not get serialized properly without using the InlineScript activity. + $dcAccount = Get-ADDBAccount -SamAccountName '{DCName}$' -DBPath $using:ntdsParams.'DSA Database file' -LogPath $using:ntdsParams.'Database log files path' -BootKey $using:currentBootKey + Set-ADDBAccountPasswordHash -ObjectGuid {DCGuid} -NTHash $dcAccount.NTHash -SupplementalCredentials $dcAccount.SupplementalCredentials -DBPath '{SourceDBPath}' -LogPath '{SourceLogDirPath}' -BootKey $using:currentBootKey + } - # Reconfigure LSA policies. We would get into a BSOD loop if they do not match the corresponding values in the database. - Set-LsaPolicyInformation -DomainName '{NetBIOSDomainName}' -DnsDomainName '{DomainName}' -DnsForestName '{ForestName}' -DomainGuid {DomainGuid} -DomainSid {DomainSid} + # Replace the database and transaction logs. + robocopy.exe '{SourceDBDirPath}' $ntdsParams.'DSA Working Directory' *.dit *.edb *.chk *.jfm /MIR /NP /NDL /NJS + robocopy.exe '{SourceLogDirPath}' $ntdsParams.'Database log files path' *.log *.jrs /MIR /NP /NDL /NJS - # Tell the DC that its DB has intentionally been restored. A new InvocationID will be generated as soon as the service starts. - Set-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters -Name 'Database restored from backup' -Value 1 -Force - Remove-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters -Name 'DSA Database Epoch' -Force + # Replace SYSVOL. + $netlogonParams = Get-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters -Name SysVol + robocopy.exe '{SourceSysvolPath}' (Join-Path -Path $netlogonParams.SysVol -ChildPath '{DomainName}') /MIR /XD DfsrPrivate /XJ /COPYALL /DCOPY:DAT /SECFIX /TIMFIX /NP /NDL - # Disable DSRM and do one last reboot. - bcdedit.exe /deletevalue safeboot - shutdown.exe /r /t 5 - Suspend-Workflow -Label 'Waiting for reboot' - } + # Reconfigure LSA policies. We would get into a BSOD loop if they do not match the corresponding values in the database. + Set-LsaPolicyInformation -DomainName '{NetBIOSDomainName}' -DnsDomainName '{DomainName}' -DnsForestName '{ForestName}' -DomainGuid {DomainGuid} -DomainSid {DomainSid} - # Delete any pre-existing workflows with the same name before starting a new one. - Remove-Job -Name DSInternals-RFM-Workflow -Force -ErrorAction SilentlyContinue + # Tell the DC that its DB has intentionally been restored. A new InvocationID will be generated as soon as the service starts. + Set-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters -Name 'Database restored from backup' -Value 1 -Force + Remove-ItemProperty -Path registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters -Name 'DSA Database Epoch' -Force - # Start the workflow. - Restore-DomainController -JobName DSInternals-RFM-Workflow + # Disable DSRM and do one last reboot. + bcdedit.exe /deletevalue safeboot + shutdown.exe /r /t 5 + Suspend-Workflow -Label 'Waiting for reboot' + } + + # Delete any pre-existing workflows with the same name before starting a new one. + Remove-Job -Name DSInternals-RFM-Workflow -Force -ErrorAction SilentlyContinue + + # Start the workflow. + Restore-DomainController -JobName DSInternals-RFM-Workflow } $resumeTask = Register-ScheduledJob -Name DSInternals-RFM-Resumer -Trigger (New-JobTrigger -AtStartup) -ScriptBlock { - # Resume the workflow after the computer is rebooted. - Resume-Job -Name DSInternals-RFM-Workflow -Wait | Wait-Job | where State -In Completed,Failed,Stopped | foreach { - # Perform cleanup when finished. - Remove-Job -Job $PSItem -Force - Unregister-ScheduledJob -Name DSInternals-RFM-Initializer -Force - Unregister-ScheduledJob -Name DSInternals-RFM-Resumer -Force - } + # Resume the workflow after the computer is rebooted. + Resume-Job -Name DSInternals-RFM-Workflow -Wait | Wait-Job | where State -In Completed,Failed,Stopped | foreach { + # Perform cleanup when finished. + Remove-Job -Job $PSItem -Force + Unregister-ScheduledJob -Name DSInternals-RFM-Initializer -Force + Unregister-ScheduledJob -Name DSInternals-RFM-Resumer -Force + } } # Configure the scheduled tasks to run under the SYSTEM account. diff --git a/Src/DSInternals.PowerShell/Commands/Datastore/NewADDBRestoreFromMediaScriptCommand.cs b/Src/DSInternals.PowerShell/Commands/Datastore/NewADDBRestoreFromMediaScriptCommand.cs index e722ddd..3ec7f33 100644 --- a/Src/DSInternals.PowerShell/Commands/Datastore/NewADDBRestoreFromMediaScriptCommand.cs +++ b/Src/DSInternals.PowerShell/Commands/Datastore/NewADDBRestoreFromMediaScriptCommand.cs @@ -94,6 +94,8 @@ Replace("{DomainSid}", dc.DomainSid.ToString()). Replace("{DomainMode}", ((int)dc.DomainMode).ToString()). Replace("{ForestMode}", ((int)dc.ForestMode).ToString()). + Replace("{DomainModeString}", (dc.DomainMode).ToString()). + Replace("{ForestModeString}", (dc.ForestMode).ToString()). Replace("{OSName}", dc.OSName). Replace("{OldBootKey}", this.BootKey.ToHex()). Replace("{SourceDBPath}", this.DirectoryContext.DSADatabaseFile). @@ -126,4 +128,4 @@ } } } -} \ No newline at end of file +}