Friday, May 6, 2011

Migrating from Windows XP to Windows 7 with USMT and Powershell!

If you've ever had to migrate a users (or perhaps your own) files/folders from one system to another you know how annoying and tedious this process is. To help with this Microsoft release a very useful tool called USMT (User state migration tool) with Windows XP many years ago that many people still use today. Today Windows 7 still supports USMT but just a later version; version 4 specifically.

When using the utility you need to pass parameters to either 'ScanState.exe' when scanning the system and 'LoadState.exe' when loading the data from your scanstate scan. If you've seen the parameters from Scanstate and Loadstate you know the options can be a bit confusing. To help with this I've created a script that makes this a bit easier.

Call the script with the 'mode' parameter first with ScanState. After your scan your old system run the script again on the new computer with the 'mode' parameter of LoadState.

You can only migrate from an XP system to Windows 7. Or a Win7 to another Win7. You can't migrate a system from XP to XP. If you need to do this then you will have to use a previous version of USMT.

<#
.SYNOPSIS
Runs USMT 4.0 to migrate app/user/OS settings from XP to Win7
.DESCRIPTION
Runs USMT 4.0 to migrate app/user/OS settings from XP to Win7
USMT 4 will only migrate from XP to Vista OS and later. XP to XP is not supported.
Run as administrator
.PARAMETER Mode
Pass either ScanState or LoadState to this parameter. Any other parameter passed will throw an error.
ScanState will scan the local machine and upload data to a network share.
LoadState will load the uploaded data that was created from ScanState and apply the settings on the local machine.
.EXAMPLE
./usmt.ps1 -mode scanstate

Scan and upload the local machine settings to a network location. This is for the logged on user.
This must be ran first before 'loadstate' mode in order to create the migration store.
.EXAMPLE
./usmt.ps1 -mode loadstate

Download and apply the settings from the data that was gathered using ScanState mode
.EXAMPLE
./usmt.ps1 -mode scanstate -VerboseOff
./usmt.ps1 -mode loadstate -VerboseOff

Turns off Scanstate or Loadstate Verbose mode.

#>

param(
[Parameter(Mandatory=$true)]
[ValidateSet("scanstate","loadstate")]
[string]
$Mode,

[Parameter(Mandatory=$false)]
[switch]
$VerboseOff = $false
)

if ($verboseOff) {$VerbosePreference="SilentlyContinue"} else {$VerbosePreference="Continue"}


$srcFolder = "\\myServer\users\usmt4"
$usmtFolder = "c:\usmt"
$storePath = "\\myServer\users\MigStore_$env:username"


function scanstate {
# If USMT folder exist, do not copy from network location
if (!(Test-Path $usmtFolder)) {
Write-Verbose "Copying data from $srcFolder to $usmtFolder"
copy $srcFolder $usmtFolder -Recurse
}

Set-Location $usmtFolder

# sets UMST Scanstate to migrate Docs,Apps and User settings. Also sets logging at the highest level and specifies to only
# migrate any users who are currently logged on. LocalOnly parameter is specified to omit removeable or network drive data.
$ScanStateCmd = ".\scanstate.exe $storePath /i:migdocs.xml /i:migapp.xml /i:miguser.xml /localonly /o /v:13 /uel:0 /c /l:scanstate.log"
Write-Verbose "Running command: $scanStateCmd"
Invoke-Expression $scanStateCmd
}

function loadstate {
# If USMT folder exist, do not copy from network location
if (!(Test-Path $usmtFolder)) {
Write-Verbose "Copying data from $srcFolder to $usmtFolder"
copy $srcFolder $usmtFolder -Recurse
}

Set-Location $usmtFolder

# Loads the user store from the network location
$LoadStateCmd = ".\loadstate.exe $storePath /i:migdocs.xml /i:migapp.xml /i:miguser.xml /v:13 /uel:0 /c /l:loadstate.log"
Write-Verbose "Running command: $LoadStateCmd"
Invoke-expression $LoadStateCmd
}

function Select-Item
{
<# .Synopsis Allows the user to select simple items, returns a number to indicate the selected item. .Description Produces a list on the screen with a caption followed by a message, the options are then displayed one after the other, and the user can one. Note that help text is not supported in this version. .Example PS> select-item -Caption "Configuring RemoteDesktop" -Message "Do you want to: " -choice "&Disable Remote Desktop",
"&Enable Remote Desktop","&Cancel" -default 1
Will display the following
Configuring RemoteDesktop
Do you want to:
[D] Disable Remote Desktop [E] Enable Remote Desktop [C] Cancel [?] Help (default is "E"):
.Parameter Choicelist
An array of strings, each one is possible choice. The hot key in each choice must be prefixed with an & sign
.Parameter Default
The zero based item in the array which will be the default choice if the user hits enter.
.Parameter Caption
The First line of text displayed
.Parameter Message
The Second line of text displayed
#>

param( [String[]]$choiceList,
[String]$Caption = "Please make a selection",
[String]$Message = "Choices are presented below",
[int]$default = 0
)
$choicedesc = New-Object System.Collections.ObjectModel.Collection[System.Management.Automation.Host.ChoiceDescription]
$choiceList | foreach { $choicedesc.Add((New-Object "System.Management.Automation.Host.ChoiceDescription" -ArgumentList $_))}
$Host.ui.PromptForChoice($caption, $message, $choicedesc, $default)
}

switch ($mode) {
"scanstate" {
if (Test-Path $storePath) {
$choice = [bool](Select-Item -Caption "The path $storePath exist! Do you want to overwrite?" `
-Message "Press 'Y' or 'N' ?" -choiceList "&No","&Yes" -default 0 )

if ($choice) {
Write-Verbose "Overwriting folder: $storePath"
scanstate
} else {
Write-warning "Overwrite skipped. Script exiting..."
Write-Host
break
}
} else {
scanstate
}
}
"loadstate" {
if (Test-Path $storePath) {
loadstate
} else {
Write-warning "User store for $env:username cannot be found at $storePath `nRun script in 'ScanState' mode first."
Write-warning "Script exiting..."
Write-Host
break
}
}
}

This script will create a migration store with the logged on user to a network location that you specify in the $storepath variable. USMT 4 supports hardlink migrations to speed up the transfer and reduce the migration size. I've not included this in this script.

Credit for the Select-Item function goes to James O'Neill.

4 comments:

Joe Pereira said...

will this work on detecting the architecture og the source machine so the right USMT architecture is used?

Danny said...

Thanks for the Great script! I wonder if its possible to run this against remote machines and have it choose a specfic user.

Unknown said...

This is a good piece of code to add

$osArch = Get-WmiObject -Class Win32_Processor
switch ($osArch.AddressWidth)
{
"32" {$srcFolder = "\\x86 path}
"64" {$srcFolder = "\\amd64 path"}
}

Unknown said...

Thanks for sharing this great and informative post about the windows migration.