Moving User Profiles with PowerShell

Something that comes up with some frequency on Terminal Servers (or “Remote Desktop Servers”), but perhaps sometimes in VDI, is “How to I move a user profile from one drive to another”. The traditional answers include the use of the user profile management GUI, or some expensive piece of software. But what if you need to automate the job? Or if you don’t have any money for the project?

Answer? PowerShell, of course… and robocopy.

Below is a code snippet that will set existing user profiles to load from “C:Users” to “E:Users”:
#Collect profile reg keys for regular users ("S-1-5-21" excludes local admin, network service, and system)
$profiles = gci -LiteralPath "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionProfileList" `
| ? {$ -match "S-1-5-21-"}

foreach ($profile in $profiles) {
#Set the registry path in a format that can be used by the annoyingly demanding "get-itemproperty" cmdlet:
$regPath = $(
$($profile.pspath.tostring().split("::") | Select-Object -Last 1).Replace("HKEY_LOCAL_MACHINE","HKLM:")

#Get the current filesystem path for the user profile, using get-ItemProperty"
$oldPath = $(
Get-ItemProperty -LiteralPath $regPath -name ProfileImagePath

#Set a varialble for the new profile filesystem path:
$newPath = $oldPath.Replace("C:","E:")

#Set the new profile path using "set-itemproperty"
Set-ItemProperty -LiteralPath $regPath -Name ProfileImagePath -Value $newPath

#Now copy the profile filesystem directories using "robocopy".

But this code will not actually move the data. For that, we need robocopy. Make sure that your users are logged off before performing this operation, otherwise “NTUSER.DAT” will not get moved, and your users will get a new TEMP profile on next login:
robocopy /e /copyall /r:0 /mt:4 /b /nfl /xj /xjd /xjf C:users e:Users

Finally, be sure to set the default location for new profiles and the “Public” directory to your new drive as well. For that, run “Regedit”, then go to:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionProfileList
and set new paths for the registry strings “ProfilesDirectory” and “Public”. Moving the default user profile is optional.

Oh yeah… you might want to purge the old Recycle Bin cruft for your moved users as well:
rmdir /s /q C:$Recycle.Bin


SharePoint 2010 – Email Alerts to Site Administrators

We are in the final stages of preparation for the long-overdue upgrade to SharePoint 2010.  I have set up a preview site with a copy of the production SharePoint content database, and I want to notify all site owners that they should check out their sites for major problems.  How to do?  PowerShell?  Absolutely!


Set-PSDebug -Strict
Add-PSSnapin -Name microsoft.SharePoint.PowerShell

[string] $waUrl = ""
[string] $SmtpServer = ""
[string] $From = ""

$allAdmins = @()

[string] $subjTemplate = ‘Pending Upgrade for your site -siteURL-‘
[string] $bodyTemplate = @"
Message Body Goes Here.
Use the string -siteURL- in the body where you want the user’s site address to appear.

$wa = Get-SPWebApplication -Identity $waUrl

foreach ($site in $wa.sites) {
#Write-Host "Working with site: " + $site.url
$siteAdmins = @()
$siteAdmins = $site.RootWeb.SiteAdministrators
ForEach ($admin in $siteAdmins) {
#Write-Host "Adding Admin: " + $admin.UserLogin
[string]$a = $($admin.UserLogin).Replace("CAMPUS","")
[string]$a = $a.replace(".adm","")
[string]$a = $a.replace("-admin","")
[string]$a = $a.replace("admin-","")
if ($a -notmatch "sa_|\system") { $allAdmins += , @($a; [string]$site.Url) }

$allAdmins = $allAdmins | Sort-Object -Unique
#$allAdmins = $allAdmins | ? {$_[0] -match "jgm"} | Select-Object -Last 4

foreach ($admin in $allAdmins) {
[string] $to = $admin[0] + ""
[string] $siteUrl = $admin[1]
[string] $subj = $subjTemplate.Replace("-siteURL-",$siteUrl)
[string] $body = $bodyTemplate.Replace("-siteURL-",$siteUrl)
Send-MailMessage -To $to -From $From -SmtpServer $SmtpServer -Subject $subj -BodyAsHtml $body


How to disable Internet Explorer Automatic Proxy Configuration

You would think this one would be easy…

Some of our users were noticing that it was taking over 30 seconds to launch IE to a web site that was configured at the command line (i.e. we run “iexplore.exe”).  While page loading was indeed slow, at least 20 seconds of the delay was seen before IE even stated to load content from the web site in question (determined by using WireShark).  Instead what we saw was a lot of CLDAP chatter and the ever-revealing DNS lookup attempt for hosts starting with the RDN “WPAD”.  Looks like Internet Explorer Web Proxy Auto Detection is wasting our time again.

Traditionally, the solution was to use the “Internet Explorer Maintenance” settings in Group Policy to disable auto proxy config.  However, it appears that with the release of Windows 8, this branch of Group Policy is being deprecated.  So what it the “right” way to set this policy now?  I am using GP Preferences.

Using SysInternals ProcMon, we are able to see that the following registry value is modified when we manually disable automatic proxy detection:
HKCUSoftwareMicrosoftWindowsCurrentVersionInternet SettingsConnectionsDefaultConnectionSettings
this is a RegBinary setting, which makes intuitive understanding of the meaning of the value impossible.

Though experimentation, I was able to determine that when the ninth pair of digits is altered from “09” to “01”, proxy auto configuration is disabled.  The parallel “SavedLegacySettings” value also gets modified, but this value has no effect on actual IE settings.  The fifth pair of settings also gets modified each time you reconfigure the IE connection settings.  I expect these digits represent some sort of change sequence number, as the setting different values here does not seem to affect a behavior change in IE.

So whoopee… another GP Preference setting, and I have saved my colleagues 20 seconds of time on each startup of IE.  If they all use the affected systems once a day for the next 80 days, this whole activity will have been worthwhile.