Active Directory Migrations: DNS Suffixes & Reboot

Part 2 of the continuing series of how to migrate users and computers in a domain migration.

This next script shouldn’t be necessary, if you’re managing your DNS suffixes correctly, using WINS, or don’t have a ton of DNS suffixes out there.

In this migration we’re turning off WINS as part of the migration. There literally dozens of possible DNS Suffixes users can have in their search order, and none of it is centrally managed. The preferred way to do this is to manager your suffix search order thru either GPO or DHCP options, but that wasn’t possible here. We also wanted to make sure that all of our user workstations were online prior to the migration and didn’t have any lingering issues that would prevent them from migrating, so wanted to be able to reboot the workstations first, before we did anything.

This script is actually doing a lot of stuff, and calls another script (pasted in below). Comments inline

# FileName: AppendToRegistry.ps1
# Purpose: To append a string value to a registry entry locally or remotely
# Hive valid values: "ClassesRoot","CurrentConfig","CurrentUser","DynData","LocalMachine","PerformanceData","Users"

## Set the date format and output files for our transcript
## Start transcript to log everything that happens on the screen.

$date=get-date -format "yyyyMMdd"
$Tranoutput="d:\migration\Outputs\" + $date + "DNSSuffix.txt"
start-transcript -path $Tranoutput

## DNS Suffixes we want to ensure get added to workstations
## This doesn't overwrite existing suffixes, but checks to see if these exist. If they do, cool.
## If they don't, add them

$domains = @("DOM1.COM","DOM2.com","DOM3.com")

## Include file generated previously that all the computer names we need to hit.
## Set array variables for our ping script so that we don't have timeouts connecting to machines that aren't up
$RegistryFile = "D:\Migration\admtincludes\CompIncludes.csv"
$NoPing=@()
$YesPing=@()

## Import our CSV file and for each item in the list do a test-connection (i.e. ping)
## If a machine can't be pinged (pung?), add it to the array
## Output the array to the screen in bright colors so that we know we can't connect to them.
## We need to know this so that we don't try to migrate that user or desktop

$NoPing=import-csv -path $Registryfile|where-object {-not (Test-Connection -count 1 -computername $_.computer -erroraction silentlycontinue)}
clear-host
if ($noping){foreach ($item in $NoPing){write-host "The following computers are not responding:" $item.computer -backgroundcolor "red"}}

## Set a new variable and try to ping them all again.
## This sets the array that we use later for actually setting registry and reboot

$YesPing=import-csv -path $Registryfile|where-object {(Test-Connection -count 1 -computername $_.computer -erroraction silentlycontinue)}

##Using the array of up machines, set variables for computers and reg keys

$YesPing| ForEach-Object {
 $computer = $_.Computer
 $hive = "LocalMachine"
 $key = "System\CurrentcontrolSet\services\tcpip\parameters"
 $property = "SearchList"

##Call the other powershell file Get-RemoteRegistry.ps1 and pass variables to it.

[string]$currentValue=./Get-RemoteRegistry -hive $hive -key $key -name $property -computername $computer
 $modified = $false
 $newvalue = [string]::Empty

## If there is no value, set the value to the three default domains
## I.e. if there was no DNS Suffixes set

if ([string]::IsNullOrEmpty($currentValue))  {
$newvalue = $domains -join ','
$modified = $true}
 else {
## if there suffixes set
$domains | ForEach-Object {
## convert everything to lowercase
## set new values comma delimited, set modified variable to true
            if ($currentValue.ToLower().IndexOf($_) -lt 0) {
$newvalue = $newvalue + "," + $_
$modified = $true}
}

## Add current value and new value together, comma delimited
if ($modified){$newvalue = $currentValue + $newvalue}
 }
 # Check to see if the value was modified
# Call set-remoteregistry.ps1 and pass variables
 if ($modified)
 {
 ./Set-RemoteRegistry -key $key -name $property -value $newvalue -type string -computername $computer –force
 write-host "DNS Registry set on: " $computer
 }

## Reboot workstation
 restart-computer $computer -force
 write-host $Computer " is being rebooted"

}
stop-transcript

Get-RemoteRegistry.ps1:


[CmdletBinding(SupportsShouldProcess=$true)]
param
(
 [Parameter(Position=0, Mandatory=$false)]
 [System.String]
 $ComputerName = $Env:COMPUTERNAME,
 [Parameter(Position=1, Mandatory=$false)]
 [ValidateSet("ClassesRoot","CurrentConfig","CurrentUser","DynData","LocalMachine","PerformanceData","Users")]
 [System.String]
 $Hive = "LocalMachine",
 [Parameter(Position=2, Mandatory=$true, HelpMessage="Enter Registry key in format System\CurrentControlSet\Services")]
 [ValidateNotNullOrEmpty()]
 [System.String]
 $Key,
 [Parameter(Position=3, Mandatory=$true)]
 [ValidateNotNullOrEmpty()]
 [System.String]
 $Name
)

#Open remote registry
try
{
 $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $ComputerName)

}
catch
{

Write-Error "The computer $ComputerName is inaccessible. Please check computer name. Please ensure remote registry service is running and you have administrative access to $ComputerName."
 Return
}

$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $ComputerName)
$regkey = $reg.OpenSubkey($Key)
return $regkey.GetValue($Name)

Set-RemoteRegistry.ps1:


<#
 .SYNOPSIS
 Set-RemoteRegistry allows user to set any given registry key/value pair.

.DESCRIPTION
 Set-RemoteRegistry allows user to change registry on remote computer using remote registry access.

.PARAMETER ComputerName
 Computer name where registry change is desired. If not specified, defaults to computer where script is run.

.PARAMETER Hive
 Registry hive where the desired key exists. If no value is specified, LocalMachine is used as default value. Valid values are: ClassesRoot,CurrentConfig,CurrentUser,DynData,LocalMachine,PerformanceData and Users.

.PARAMETER Key
 Key where item value needs to be created/changed. Specify Key in the following format: System\CurrentControlSet\Services.

.PARAMETER Name
 Name of the item that needs to be created/changed.

 .PARAMETER Value
 Value of item that needs to be created/changed. Value must be of correct type (as specified by -Type).

 .PARAMETER Type
 Type of item being created/changed. Valid values for type are: String,ExpandString,Binary,DWord,MultiString and QWord.

 .PARAMETER Force
 Allows user to bypass confirmation prompts.

 .EXAMPLE
 PS C:\> .\Set-RemoteRegistry.ps1 -Key SYSTEM\CurrentControlSet\services\AudioSrv\Parameters -Name ServiceDllUnloadOnStop -Value 1 -Type DWord

.EXAMPLE
 PS C:\> .\Set-RemoteRegistry.ps1 -ComputerName ServerA -Key SYSTEM\CurrentControlSet\services\AudioSrv\Parameters -Name ServiceDllUnloadOnStop -Value 0 -Type DWord -Force

.INPUTS
 System.String

.OUTPUTS
 System.String

.NOTES
 Created and maintainted by Bhargav Shukla (MSFT). Please report errors through contact form at http://blogs.technet.com/b/bshukla/contact.aspx. Do not remove original author credits or reference.

.LINK
 http://blogs.technet.com/bshukla
#>
 [CmdletBinding(SupportsShouldProcess=$true)]
 param
 (
 [Parameter(Position=0, Mandatory=$false)]
 [System.String]
 $ComputerName = $Env:COMPUTERNAME,
 [Parameter(Position=1, Mandatory=$false)]
 [ValidateSet("ClassesRoot","CurrentConfig","CurrentUser","DynData","LocalMachine","PerformanceData","Users")]
 [System.String]
 $Hive = "LocalMachine",
 [Parameter(Position=2, Mandatory=$true, HelpMessage="Enter Registry key in format System\CurrentControlSet\Services")]
 [ValidateNotNullOrEmpty()]
 [System.String]
 $Key,
 [Parameter(Position=3, Mandatory=$true)]
 [ValidateNotNullOrEmpty()]
 [System.String]
 $Name,
 [Parameter(Position=4, Mandatory=$true)]
 [ValidateNotNullOrEmpty()]
 [System.String]
 $Value,
 [Parameter(Position=5, Mandatory=$true)]
 [ValidateSet("String","ExpandString","Binary","DWord","MultiString","QWord")]
 [System.String]
 $Type,
 [Parameter(Position=6, Mandatory=$false)]
 [Switch]
 $Force
 )

 If ($pscmdlet.ShouldProcess($ComputerName, "Open registry $Hive"))
 {
 #Open remote registry
 try
 {
 $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $ComputerName)

 }
 catch
 {

Write-Error "The computer $ComputerName is inaccessible. Please check computer name. Please ensure remote registry service is running and you have administrative access to $ComputerName."
 Return
 }
 }

If ($pscmdlet.ShouldProcess($ComputerName, "Check existense of $Key"))
 {
 #Open the targeted remote registry key/subkey as read/write
 $regKey = $reg.OpenSubKey($Key,$true)

 #Since trying to open a regkey doesn't error for non-existent key, let's sanity check
 #Create subkey if parent exists. If not, exit.
 If ($regkey -eq $null)
 {
 Write-Warning "Specified key $Key does not exist in $Hive."
 $Key -match ".*\x5C" | Out-Null
 $parentKey = $matches[0]
 $Key -match ".*\x5C(\w*\z)" | Out-Null
 $childKey = $matches[1]

try
 {
 $regtemp = $reg.OpenSubKey($parentKey,$true)
 }
 catch
 {
 Write-Error "$parentKey doesn't exist in $Hive or you don't have access to it. Exiting."
 Return
 }
 If ($regtemp -ne $null)
 {
 Write-Output "$parentKey exists. Creating $childKey in $parentKey."
 try
 {
 $regtemp.CreateSubKey($childKey) | Out-Null
 }
 catch
 {
 Write-Error "Could not create $childKey in $parentKey. You may not have permission. Exiting."
 Return
 }

$regKey = $reg.OpenSubKey($Key,$true)
 }
 else
 {
 Write-Error "$parentKey doesn't exist. Exiting."
 Return
 }
 }

 #Cleanup temp operations
 try
 {
 $regtemp.close()
 Remove-Variable $regtemp,$parentKey,$childKey
 }
 catch
 {
 #Nothing to do here. Just suppressing the error if $regtemp was null
 }
 }

 #If we got this far, we have the key, create or update values
 If ($Force)
 {
 If ($pscmdlet.ShouldProcess($ComputerName, "Create or change $Name's value to $Value in $Key. Since -Force is in use, no confirmation needed from user"))
 {
 $regKey.Setvalue("$Name", "$Value", "$Type")
 }
 }
 else
 {
 If ($pscmdlet.ShouldProcess($ComputerName, "Create or change $Name's value to $Value in $Key. No -Force specified, user will be asked for confirmation"))
 {
 $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes",""
 $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No",""
 $choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no)
 $caption = "Warning!"
 $message = "Value of $Name will be set to $Value. Current value `(If any`) will be replaced. Do you want to proceed?"
 Switch ($result = $Host.UI.PromptForChoice($caption,$message,$choices,0))
 {
 1
 {
 Return
 }
 0
 {
 $regKey.Setvalue("$Name", "$Value", "$Type")
 }
 }
 }
 }

 #Cleanup all variables
 try
 {
 $regKey.close()
 Remove-Variable $ComputerName,$Hive,$Key,$Name,$Value,$Force,$reg,$regKey,$yes,$no,$caption,$message,$result
 }
 catch
 {
 #Nothing to do here. Just suppressing the error if any variable is null
 }

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s