Coping with Renamed user Accounts in sharepoint

Yesterday I received a strange error report from a person trying to create a new SharePoint site collection.  Our front line guy went to investigate and found that she was getting a “User cannot be found” error out of SharePoint when attempting to complete the self-service site creation process.  This person reported that her last name changed recently, along with her user ID, yet SharePoint will still showing her as logged in under her old name.

Linking the “Correlation ID” up to the diagnostic logs was of no great help.  The diagnostic logs simply reported “User cannot be found” when executing the method “Microsoft.SharePoint.SPSite.SelfServiceCreateSite”.  We are able to see that “ownerLogin”, “ownerEmail”, and “ownerName” strings were being passed to this function, but not what the values of those strings were.  I guessed that the web form was passing the person’s old account login name to the function, and that since this data was no longer valid, an error was getting displayed.  But how to fix this?

SharePoint 2010 (and WSS 3.0 before it) keeps a list of Site Users that can be accessed using the SharePoint Web “SiteUsers” property. This list is updated every time a new user logs in to the site.  The list entries contain username, login identity, email address, and security ID (SID) data.  It also appears that Site User data is not updated when user data changes in Active Directory (as long as the SID stays the same, that is).  Additional user account data is stored in XML data in the SharePoint databases, and can be accessed using the SharePoint Web “SiteUserInfoList” property.  All of this data needs to be purged from the root web site so that our hapless user can once again pass valid data to the SelfServiceCreateSite method.

Presumably the Site Management tools could be forced to get the job done, but the default views under SharePoint 2010 are hiding all site users from me, even when I log in as a site administrator.  Let’s try PowerShell instead:

add-pssnapin microsoft.sharepoint.powershell 
$root = get-spweb -identity "https://sharepoint.uvm.edu/" 

# "Old ID" below should be all or part of the user's original login name: 
$oldAcc = $root.SiteUsers | ? {$_.userLogin -match "oldID"} 
#Let's see if we found something: 
$oldAcc.LoginName 

#Remove the user from the web's SiteUsers list: 
$root.SiteUsers.Remove($oldAcc.LoginName) 
$root.Update() 
#Let's see if it worked: 
$id = $oldAcc.ID 
$root = get-spweb -identity "https://sharepoint.uvm.edu/" 
$root.SiteUsers.GetByID($id) 
# (This should return a "User cannot be found" error.) 

#Now to see what is in SiteUserInfoList: 
$root.SiteUserInfoList.GetItemById($id) 
# (This data can be cleaned up in the browser by visiting:
# " /_catalogs/users/simple.aspx" 
# from your site collection page.)

Dell XPS 12 – The Windows 8 Flagship?

Regular readers of my blog (all two of you) may recall the “series” I started this fall on Windows 8 launch devices (concerning the HP Envy X2 and the Samsung SmartPC Pro 700t). These devices both had strengths, but failed in other ways that made them difficult or impossible to support in an enterprise environment. This month, I got my hands on a device that breaks though that barrier and satisfies in a big way. The new Dell XPS 12 finally arrived on our campus about two weeks ago. We immediately were taken with its light weight (3 lbs.), sleek styling, and novel materials (full carbon fiber base, carbon fiber and aluminum lid, and that unique flip-over touch screen). The 8-second boot time is another impressive feature. A longer battery life would have been appreciated, but I can live with it. Other helpful enhancements would be the inclusion of an active stylus. I also would appreciate slightly more resistance in the keyboard.

Others have weighed in on the appearance, performance, and usability of this fancy Ultrabook, though, so I will forgo further commentary on those aspects of the XPS 12. What most concerned us was the ability to support OS redeployment, BitLocker encryption, and hardware servicing on our Campus.

We unboxed and re-deployed the computer with Windows 8 Enterprise within one day. There were a few deployment hiccoughs, but in general re-deployment was what we have come to expect from Dell. All required drivers for the XPS 12 were made available in a single downloadable CAB file. We extracted this CAB to our MDT/LiteTouch Deployment Share, rebuilt our boot media, and initiated a LiteTouch deployment. There was a brief problem getting LiteTouch to start… we needed to disable the “Safe Boot” option in EFI/BIOS, and we needed to set the EFI boot mode to “Legacy” to allow our boot media to operate. Once those changes were made, the XPS 12 booted to our USB WinPE media without complaint. Upon completion of deployment, all devices in the device manager reported as functioning. There were no “poorly-behaved” drivers that required un-scripted installation. We did find that the track-pad was behaving strangely. Investigation revealed that the PnP process had grabbed a Windows 7 track-pad driver from our deployment share. We corrected this manually, then separated our Windows 8 drivers from our Windows 7 drivers in the Deployment Workbench… this should prevent the problem from recurring in future deployments.

BitLocker was easy to implement. The TPM chip readily was recognized by the OS, and TPM-with-PIN encryption was accomplished in minutes. I spent half a day trying to encrypt an older Dell Latitude E6500 a few months back. This was a breeze by comparison.

On the servicing front, we have good news. Dell now is allowing on-site servicing for all XPS models, with full reimbursement for parts and labor for qualified technicians. Physical serviceability is a big concern for newer Ultrabooks. A troubling trend in tablet and notebook design is the use of solder on drive mounts and glue to hold batteries in place (the latest “Retina” MacBooks and the MS Surface tablets suffer from these problems). Fortunately, it appears that all major components of the XPS 12 can be removed and replaced without the need to re-solder or remove glue. The most frequently swapped components such as the battery, mSATA drive, and memory chips look pretty easy to access. The keyboard is a bit of a pain to get to, but at least it can be serviced.

If only more Windows 8 launch products had been this good… I hope we see more products of this quality coming from Dell (and other vendors) in the near future.

Update:  2013-11-1

Five months into using the XPS 12, I started to have trouble with the trackpad.  It would not click anymore!  Since we are working with an evaluation unit, I do not have warranty coverage, so I figured I had no warranty to void by attempting to repair it on my own.

Some digging in the Dell support site revealed that the so-called XPS 12 “User Manual” is actually a service manual!  The readily available PDF document illustrates step-by step how to remove the carbon fiber base plate and the battery in order to get to the track pad.  (The only challenging part was locating a #5 Torx screwdriver to take off the base plate.)  Within 15 minutes I had removed the click pad, and cleaned the trapped grit out from under it.  (Within a half hour I had the unit re-assembled.  In another 15 minutes I had taken the base plate back off, reconnected the battery power connector, and re-attached the base plate, again.)  The unit powered back on as normal, with the track pad working like new.

At a time when consumer devices are moving towards non-serviceable designs (think MacBook Retina), it is nice to see a device that is thin and light while still maintaining serviceability.  Perhaps the track pad on the MacBook Retina is less prone to trapping grit, but imagine if it did?  With all the components glued together, you might be out $2000 because of a bit of sand.  I really have to hand it to Dell.  These XPS Ultrabooks are really nicely engineered.

 

Prepare SCCM Clients for Cloning

Annoying task… configuring VMware View desktops for use in an environment that utilizes VMware View.  Some say, don’t put the management agent on the View desktop, just rebuild your desktops every time there is a security patch.  I say, even if recomposing your pools is fast and easy, I still do not want to do it with every patch release.

Best practice for preparing a reference computer for deployment in an SCCM environment is to not include the SCCM client.  However, SCCM client installation is SLOW, so I would like to save time and CPU load by including the software.  Documentation how to do this is sketchy.  Also, I really want a script fired off by the QuickPrep process to do the prep work, so that someone does not forget that it needs to be done.

Here is my first pass at the script… again, not too pretty, but functional:

'==========================================================================
'
'  NAME:    sccmClientPrep.vbs
'
'  AUTHOR:  J. Greg Mackinnon
'  DATE:    2013-02-01
'
'  COMMENT: prepares SCCM client for cloning
'           Requires:
'             - Certutil.exe in %systemroot%\system32 (included with Win7)
'           Returns:
'           1 - CCMEXEC service stop failure
'           2 - Machine Certificate Store deletion failure
'           4 - SMS Certificate Store deletion failure
'           8 - SMSCFG.INI deltion failure
'==========================================================================
option explicit

'=-=-=-=-=-=-=-=-=-=-=-=-=
'        CONSTANTS
const MACH_STORE = "My"
const SMS_STORE = "SMS"
const SVCNAME = "ccmexec"
const TIMEOUT = "120"

'=-=-=-=-=-=-=-=-=-=-=-=-=
'        OBJECTS
dim oShell
set oShell = CreateObject("WScript.Shell")

'=-=-=-=-=-=-=-=-=-=-=-=-=
'        VARIABLES
dim sSysRoot,sCUPath,sINIPath
dim iRet, iExit

sSysRoot = oShell.ExpandEnvironmentStrings("%SystemRoot%")
sCUPath = sSysRoot & "\system32\certutil.exe"
sINIPath = sSysRoot & "\SMSCFG.INI"
iExit = 0

'=-=-=-=-=-=-=-=-=-=-=-=-=
'   FUNCTIONS AND SUBS
function stopSvc(sSvcName,iTimeout)
' Stops the Windows service with name matching input string "sSvcName".
' Times out in iTimeout seconds.
' Needs routine to verify that sSvcName is a valid service name.
	'Variables:
	dim bDone 
	dim iSecs 
	bDone = False
	iSecs = 0

	'Objects and Collections:
	dim cSvcs
	dim oWMI, oSvc
	Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
	Set cSvcs = oWMI.ExecQuery("Select * from Win32_Service Where Name = '" & sSvcName & "'")

	'Stop the service if it is running, exit if it is not running
	For Each oSvc In cSvcs
		if oSvc.State = "Running" then
			oSvc.StopService
		else
			stopSvc = 0
			exit function
		end if
		exit for 'Only on service in collection
	Next

	'Check on the service until stopped.  Timeout in iTimeout seconds.
	Do while bDone = False
		Set cSvcs = oWMI.ExecQuery("Select * from Win32_Service Where Name = '" & sSvcName & "'")
		bDone = True
		For Each oSvc In cSvcs
			If oSvc.State  "Stopped" Then
				bDone = False
				WScript.Sleep 1000
				Exit For
			End If
		Next
		iSecs = iSecs + 1
		If iSecs >= iTimeout Then
			stopSvc = 1
			exit function
		End If
	Loop 
	stopSvc = 0
end function

function delCert(sStore,sSerial)
'Deletes certificate in certificate store "sStore" with serial number "sSerial"
'Returns: The ExitCode from certutil.exe
'Requires: 
'   - WScript.Shell object named "oShell"
'   - Defined path to "certutil.exe" named "sCUPath"
'   - Presence of certutil.exe on the system
	dim oExec, oStdOut
	dim sLine
	
	'wscript.echo "About to execute: " & sCUPath & " -delstore " & sStore & " " & sSerial
	set oExec = oShell.Exec(sCUPath & " -delstore " & sStore & " " & sSerial)
	Set oStdOut = oExec.StdOut
	Do While oExec.Status = 0
		WScript.Sleep 100
	Loop
	'Uncomment the next four lines to debug certutil:
	'Do Until oStdOut.AtEndOfStream
	'	sLine = oStdOut.ReadLine
	'	wscript.echo sLine
	'Loop
	delCert = oExec.ExitCode
end function

function getCert(sStore)
'Gets the serial numbers of certificates in the machine store specified by "sStore"
'Sends the captured serial numbers to the "delCert" function for deletion.
'Returns: 1 - If cert deletion files, 0 - If no errors are detected.
'Requires: 
'   - WScript.Shell object named "oShell"
'   - Defined path to "certutil.exe" named "sCUPath"
'   - Presence of certutil.exe on the system
	dim oExec, oStdOut
	dim bFail
	dim i, iRet
	dim sLine, sSerial
	
	bFail=False
	Set oExec = oShell.Exec(sCUPath & " -store " & sStore)
	Set oStdOut = oExec.StdOut
	
	Do Until oStdOut.AtEndOfStream
		sLine = oStdOut.ReadLine
		if InStr(1,sLine,"Serial Number",1) then
			i = CInt(InStr(1,sLine,":",1) + 2)
			sSerial = Mid(sLine,i)
			iRet = delCert(sStore,sSerial)
			if (iRet  0) then
				wscript.echo "Certificate deletion failed"
				bFail = True
			end if
		end if
	Loop
	
	if bFail = True then
		getCert = 1
	else
		getCert = 0
	end if
end function

function delFile(sFile)
'Deletes the file specified by "sFile"
'Requires existing Wscript.Shell object named "oShell"
	dim oFSO, oFile
	set oFSO = CreateObject("Scripting.FileSystemObject") 

	'wscript.echo "About to delete file: " & sFile
	if oFSO.FileExists(sFile) then
		'Delete method will force a WSH quit if it fails, so we need to disable exit-on-error:
		Err.Clear
		On Error Resume Next
		
		set oFile = oFSO.GetFile(sFile)
		delFile = oFile.Delete(True)
		if Err.Number = 0 then
			delFile = 0
		else
			delFile = Err.Number
		end if
	else
		'Exit code for the function could be changed here if you are concerned about
		' the requested file to delete not being present on the system.
		'wscript.echo "File " & sFile & " does not exist."
		delFile = 0
	end if
end function

'=-=-=-=-=-=-=-=-=-=-=-=-=
'          MAIN
iRet = stopSvc(SVCNAME, TIMEOUT)
'wscript.echo "Return from stopSvc: " & iRet
if iRet  0 then
	iExit = iExit + 1
end if

iRet = getCert(MACH_STORE)
'wscript.echo "Return from cert deletion for store " & MACH_STORE & ": " & iRet
if iRet  0 then
	iExit = iExit + 2
end if

iRet = getCert(SMS_STORE)
'wscript.echo "Return from cert deletion for store " & SMS_STORE & ": " & iRet
if iRet  0 then
	iExit = iExit + 4
end if

iRet = delFile(sINIPath)
'wscript.echo "Return from file deletion: " & iRet
if iRet  0 then
	iExit = iExit + 8
end if

WScript.Quit iExit

Scriptomatic Access to the Start Menu and Taskbar

As promised in my previous post, here is my current VBScript for configuring the Windows 7 Start Menu and Taskbar. Not beautiful, but certainly functional. My thanks to JuliusPIV and cogumel0 for doing the heavy lifting that made this script possible.

Note that you really will need to set the Group Policy option to turn off the Start Menu program history if you want Start Menu pinning to be at all effective in streamlining the Windows 7 “first time” GUI.

'==========================================================================
'
' NAME: Pin & Unpin items to/from Start Menu & Taskbar
'
' AUTHOR: J. Greg Mackinnon
' DATE  : 2013-01-31
'
' COMMENT: Derived from code by JuliusPIV found here:
'   http://social.technet.microsoft.com/Forums/en/w7itproinstall/thread/73eb1c0a-fc78-4ae7-ba6d-356d9a9a5328
'
'   To add items to Start Menu or Taskbar, add a variable defining the 
'   path to the original link in the variables section, then add that 
'   variable to the "aPinSM", "aPinTB", or "aUnpinTB" arrays.
'
'   Note that not all links (such as filesystem shortcuts) can be pinned.
'
'   Uncomment "debugecho" lines to troubleshoot.
'
'==========================================================================
option explicit

'=-=-=-=-=-=-=-=-=-=-=-=-=
'        CONSTANTS
'=-=-=-=-=-=-=-=-=-=-=-=-=
'List of "Shell Special Folder Constants" used in the script.  See:
'http://msdn.microsoft.com/en-us/library/windows/desktop/bb774096(v=vs.85).aspx
const ssfAPPDATA = &H1a
const ssfCOMMONPROGRAMS = &H17
const ssfPROGRAMFILESx86 = &H30
const ssfPROGRAMS = &H2 
const ssfSYSTEM = &H25
const ssfWINDOWS = &H24

'=-=-=-=-=-=-=-=-=-=-=-=-=
'         OBJECTS
'=-=-=-=-=-=-=-=-=-=-=-=-=
dim fso, oShell, oShortcut
set fso = CreateObject("Scripting.FileSystemObject")
set oShell = CreateObject("Shell.Application") 

'=-=-=-=-=-=-=-=-=-=-=-=-=
'        VARIABLES
'=-=-=-=-=-=-=-=-=-=-=-=-=
dim aPinSM, aPinTB, aUnpinTB
dim bEchoOut, bPinItem
dim sAUP, sUP, sRAD, sPFx86, sSys32, sItem, sScriptHost, sFileName
dim sGC, sMOW, sMOE, sMOPP, sMOON, sMOO, sOC, sFZ, sPT, sProj, sCalc, sSnip, sPDN, sMag, sKey, sWMP

'Configure variables for well known folders:
sRAD = oShell.NameSpace(ssfAPPDATA).Self.Path & "\"            'Roaming AppData
sAUP = oShell.NameSpace(ssfCOMMONPROGRAMS).Self.Path & "\"     'Start Menu Programs - All Users
sUP = oShell.NameSpace(ssfPROGRAMS).Self.Path & "\"            'Start Menu Programs - Current User
'sPFx86 = oShell.NameSpace(ssfPROGRAMFILESx86).Self.Path & "\" 'Program Files (x86)
'sSys32 = oShell.NameSpace(ssfSYSTEM).Self.Path & "\"          '%WinDir%\system32

'List of links to be added to the Start Menu or Taskbar, relative to:
' C:\ProgramData\Microsoft\Windows\Start Menu\Programs
sGC = sAUP & "Google Chrome\Google Chrome.lnk"
sMOW = sAUP & "Microsoft Office 2013\Word 2013.lnk"
sMOE = sAUP & "Microsoft Office 2013\Excel 2013.lnk"
sMOPP = sAUP & "Microsoft Office 2013\PowerPoint 2013.lnk"
sMOON = sAUP & "Microsoft Office 2013\Onenote 2013.lnk"
sMOO = sAUP & "Microsoft Office 2013\Outlook 2013.lnk"
sOC = sAUP & "Oracle Calendar\Oracle Calendar.lnk"
sFZ = sAUP & "FileZilla FTP Client\FileZilla.lnk"
sPT = sAUP & "PuTTY\PuTTY.lnk"
sProj = sAUP & "Accessories\displayswitch.lnk"
sCalc = sAUP & "Accessories\Calculator.lnk"
sSnip = sAUP & "Accessories\Snipping Tool.lnk"
sPDN = sAUP & "Paint.NET.lnk"
sMag = sUP & "Accessories\Accessibility\Magnify.lnk"
sKey = sUP & "Accessories\Accessibility\On-Screen Keyboard.lnk"
sWMP = sRAD & "Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Windows Media Player.lnk"

'Arrays containing links to be added to StartMenu or Taskbar, or to be removed from the Taskbar:
aPinSM = Array(sOC,sFZ,sPT,sPDN,sSnip,sCalc,sProj,sMag,sKey)
aPinTB = Array(sGC,sMOW,sMOE,sMOPP,sMOON)
aUnpinTB = Array(sWMP,sMOW,sMOPP)

'=-=-=-=-=-=-=-=-=-=-=-=-=
'   FUNCTIONS AND SUBS
'=-=-=-=-=-=-=-=-=-=-=-=-=
function PinSM(shortcut)
	dim oFolder, oFolderItem
	dim sFolder, sFile
	dim colVerbs
	dim itemverb
	
	sFolder = fso.GetParentFolderName(shortcut)
	sFile = fso.GetFileName(shortcut)

	'debugecho "Pinning " & sFolder & "\" & sFile & " to Start Menu."
	Err.Clear
					
	set oFolder = oShell.NameSpace(sFolder)
	set oFolderItem = oFolder.ParseName(sFile)
	set colVerbs = oFolderItem.Verbs

	for each itemverb in oFolderItem.Verbs
		if Replace(itemverb.name, "&", "") = "Pin to Start Menu" then itemverb.DoIt
	next
end function

function PinTB(shortcut)
	dim sFolder, sFile
	dim oFolder, oFolderItem
	dim colVerbs, itemverb
	
	sFolder = fso.GetParentFolderName(shortcut)
	sFile = fso.GetFileName(shortcut)

	'debugecho "Pinning " & sFolder & "\" & sFile & " to Taskbar."
	Err.Clear
					
	set oFolder = oShell.NameSpace(sFolder)
	set oFolderItem = oFolder.ParseName(sFile)
	set colVerbs = oFolderItem.Verbs
	
	for each itemverb in oFolderItem.Verbs
		if Replace(itemverb.name, "&", "") = "Pin to Taskbar" then itemverb.DoIt
	next
end function

function UnpinTB(shortcut)
	dim sFolder, sFile
	dim oFolder, oFolderItem
	dim colVerbs, itemverb
	
	sFolder = fso.GetParentFolderName(shortcut)
	sFile = fso.GetFileName(shortcut)

	'debugecho "Unpinning " & sFolder & "\" & sFile & " from Taskbar."
	Err.Clear
					
	set oFolder = oShell.NameSpace(sFolder)
	set oFolderItem = oFolder.ParseName(sFile)
	set colVerbs = oFolderItem.Verbs
	
	for each itemverb in oFolderItem.Verbs
		if Replace(itemverb.name, "&", "") = "Unpin from Taskbar" then itemverb.DoIt
	next
end function

function debugecho(msg)
	if bEchoOut then
		wscript.echo msg
	end if
end function

sub Main
	for each sItem in aUnpinTB
		if not fso.FileExists(sItem) then
			bPinItem = false
			'debugecho "File, " & sItem & ", to unpin does not exist."
			'debugecho "Please check the input and try again."
		else
			UnpinTB(sItem)
		end if
	next
	for each sItem in aPinSM
		if not fso.FileExists(sItem) then
			bPinItem = false
			'debugecho "File, " & sItem & ", to pin does not exist."
			'debugecho "Please check the input and try again."
		else
			PinSM(sItem)
		end if
	next
	for each sItem in aPinTB
		if not fso.FileExists(sItem) then
			bPinItem = false
			'debugecho "File, " & sItem & ", to pin does not exist."
			'debugecho "Please check the input and try again."
		else
			PinTB(sItem)
		end if
	next
end sub

'=-=-=-=-=-=-=-=-=-=-=-=-=
'        MAIN BODY
'=-=-=-=-=-=-=-=-=-=-=-=-=
'Suppress echo if we are in WScript:
sScriptHost = LCase(Wscript.FullName)
if Right(sScriptHost, 11) = "wscript.exe" then
    bEchoOut = false
else
    bEchoOut = true
end if

call Main

View Desktop Template Building Notes – Nothing is Easy

This week I have been working on updating our VMware View template for our public terminals (those are kiosk and lab systems).  As always, simple things have become difficult, and time has disappeared like cookies at a preschool party. Here are some resources that were useful to me in cleaning up the reference system:

Default User Profile Settings:

Why must this be so hard?  Under XP, you used to be able to tweak the Default User profile to within an inch of its life, then copy it.  Easy!  But MS maintains that this “caused problems” of some vague nature, and so the process is now not possible.  Instead, we are supposed to use the “CopyProfile” action in the unattend.xml file used by sysprep to trigger copying of the “Administrator” profile to “Default”.  However, this operation does not copy all settings… aargh!  And the settings that are excluded are not documented… double aargh!  And if you have any profile traces left over on your system for any user other than “Administrator”, sysprep will fail miserably… triple aargh!

Fortunately, there is help available:
http://blogs.technet.com/b/deploymentguys/archive/2009/10/29/configuring-default-user-settings-full-update-for-windows-7-and-windows-server-2008-r2.aspx

I found especially helpful the following post on managing Windows 7 Taskbar links:
http://sites.uci.edu/itsdeployment/blog/2010/09/01/mdt-pinning-to-taskbar-in-windows-7/

Ultimately, I used a variation on this script to pin items to the Win 7 taskbar:
http://www.msfn.org/board/topic/142521-i-solved-default-user-all-user-start-menu-task-bar-customizat/
(I’ll post the final script I developed separately.  I could not find a fully functional script on the internet.  I had to do some significant mods to the scripts in the above post to get something that does everything that I want.)

Hey, would it not be nice if there were an Group Policy Preference for “shortcuts” to have “taskbar” as a target location?  Yes… yes it would.

Configuration Manager Client Preparation:

I understand you are not supposed to clone a system that has the SCCM client installed on it.  MS documents steps that should be taken to remove the fingerprints of an existing SCCM client prior to cloning… what, so script?

I did some digging and found that the OS Deployment Task Sequences include a step that is supposed to do just this.  After more digging, I determined that the tool for this step is embedded in the SCCM OSD Capture Media.  I generated an OSD Capture Media set, extracted the contents, and found a small executable named “OsdPrepareSmsClient.exe”.  Perhaps this is what I was looking for?  I will test it and find out.

User Profile Cleanup:

Some of our lab admins used to use the profile cleaner utility from MS to erase user profiles from Win2000/XP on logout.  That utility is gone, but you can now use Group Policy to force deletion of all non-Administrator profiles on a schedule.  Unfortunately, this only happens on system restart.

I have settled on “DelProf2.exe” as a replacement:
http://helgeklein.com/free-tools/delprof2-user-profile-deletion-tool/#download
This excellent freeware utility by Helge Klein will delete all profiles that are not in use, and will clean up the ProfileList registry entries as well.  You can exclude specific named profiles as well.

Java Update… make it stop! :

http://www.myitkb.ch/index.php?option=com_k2&view=item&id=207:disable-java-updates-for-all-users&Itemid=60&tmpl=component&print=1

Unofficial Mozilla Builds for Windows:

https://code.google.com/p/htguardmozilla/
(Includes “BlueGriffon” installers that do not embed the obnoxious “iminent toolbar”.  Kudos to Glazman for this excellent basic HTML editor.  Boo to the invasive toolbar installer.)

VMware View – Implementing Idle User Auto-Logout

We are going live with out first public VMware View terminals this week (Wyse P25 “zero-clients”… nice units).  I had what I expected would be a easy list of “little jobs” to be completed before going live. Famous last words…

One item on the list was implementing an “idle user logout” process.  This process would detect when a View session had gone idle, and would disconnect the session automatically (preferably after prompting the user).  This disconnected session then would be logged out by View Manager after a fixed amount of time.

This proved rather more difficult than I had predicted.  I tried several solutions before arriving at one that worked.  Among the failed solutions:

  • Using Group Policy to configure Remote Desktop Session Manager idle session limits.  The View configuration documents imply that this should work, but it does not.  I expect that the policies would be effective if you were connecting to your View desktops using RDP, but PCoIP sessions just will not disconnect automatically (at least, they would not for me).
  • Using the Windows Task Scheduler to configure a disconnect script that will trigger on idle.  This did not work for two reasons.  First, the Task Scheduler only evaluates for idle conditions every 15 minutes.  Second, for the Task Scheduler, “idle” means not only that the user is not directing mouse and keyboard to the computer, but that the CPU also is not doing anything.  As a result, we could not get consistent auto-logout times.

The solution that we settled on involved the use of a custom screensaver developed by the “Grim Admin”.  “ScreenSaver Operations”:

http://www.grimadmin.com/staticpages/index.php/ss-operations

This is a great little utility that accomplishes what the “WinExit” screensaver used to for Win XP.  (WinExit cannot easily be used on Win7, and is a bit hostile to 64-bit Windows).  Screensaver Operations has a well-written README describing the use of registry entries to control the screensaver globally (i.e. for all users on the computer).  I set these registry operations as Group Policy Preferences, and we are in business.

Two slight complications… since the screensaver is 32-bit, you need to use the “sysnative” filesystem redirector if your want the screensaver to trigger 64-bit executables.  In our case, I wanted the screensaver to launch “tsdiscon.exe” (to disconnect the View session), so I had to use the path:
%windir%\sysnative\tsdiscon.exe
Additionally, you will need to specify the full path to the screensaver in the Group Policy dialogs (i.e. %SystemRoot%\SysWOW64\Screensaver Operations.scr).  If you fail to do so, the screensaver will appear to be configured in the Control Panel, and you will be able to preview it by clicking the “preview” button, but the screensaver WILL NEVER START.

Ashamedly I will admit that this little challenge too much longer to accomplish than it should have.  No wonder lab managers burn out so easily.

Evaluating Windows 8 Tablets – Samsung ATIV SmartPC Pro (700T)

The journey continues…

The boss approved purchase of a Samsung ATIV SmartPC Pro (the “700T” model).  I wassoooexcited… this was the tablet PC I had been waiting for.  Thin, light, and fully convertible from Ultrabook to slate.  Stylus included, 1080 high-definition display, full Intel i5 processor.  So much to love…

First impressions were really positive.  The build quality seemed really high… solid magnesium case, good keyboard response, fast boot, very responsive Wacom digitizer stylus.  As a tablet, this thing is awesome. And while it is expensize compared to an iPad, it is very cheap compared to the Tablet PCs of yesteryear.

However, I quickly ran into trouble.  When typing with the SmartPC on my lap, the keyboard would frequently disconnect from the display.  It would not fall off, but the tablet component would lose electrical connection to the keyboard, causing typing input to stop.  Sometimes this would happen as often as five times in a single line of text.  Awful!

There were other problems as well.  Like the HP Envy X2, the screen does not tilt back far enough to allow comfortable use of the keyboard on a countertop.  The 1080p display, which is very crisp and bright, is inconvenient to use for remote desktop connections to Server 2008 R2 and earlier hosts (the fonts do not scale for remote desktop sessions, leading to comically tiny print size and rediculiously small buttons and window controls).  The system did not include a TPM chip (that is only available on the models that ship with Win8 Pro… something that was not clear when ordering the device).  And finally, Samsung does not bundle drivers for the SmartPC in any way that is convenient for business deployment.  Re-imaging the systems would be a pain.

It also is worth noting that Microsoft decided that in-place upgrades of retail versions of Win 8 to volume license editions woudl not be supported.  If you want simply to install Win 8 Enterprise over the factory-shipped consumer edition of Win8, you are out of luck.  I also experienced this problem with the HP Envy X2.  For corporate users, volume license installs are strictly a nuke-and-repave operation.  Booooooo!  This is not Samsung’s fault, but the lack of support for business deployment (i.e. driver bundles or driver repository building tools) is a killer for the SmartPC in the enterprise.

I really wanted to love this device, but I really just have to return it.  Consumers seeking a top-performance tablet may love it, but it does not work for this sysadmin.  I am hoping that the Lenovo ThinkPad Helix will work out better.

Evaluating Windows 8 Tablets in the Enterprise – HP Envy X2

In desperation over our inability to tell University employees what they should be looking for in a Windows 8 tablet, I asked the boss if we could get our hands on one of the new Intel “Atom” processor-based Windows 8 tablets (these are the “Clover Trail” Atom processors, designed to compete with ARM-based devices).  I had been wanting to eval a Samsung ATIV Smart PC 500T, but these have been hard to get locally.  Instead, I bought a hot-off-the-shelf HP Envy X2.  This device boasts a well-engineered all-metal shell, full size keyboard dock with full-sized HDMI and SD card readers, and an extra battery in the dock for a claimed 15-hour run life.  It also claims to support an optional digitizer stylus.

I only have just started putting the machine though its paces.  My first impression is that it performs surprisingly well as a standard notebook, but that there will be significant challenges in supporting these types of devices at the same level as our existing business-model Dell systems.  I am not going to bother “reviewing” this tablet… others in the trade can handle that.  Rather, this blog post is going to address the challenges of supporting a consumer tablet in a business environment.

  • Processor:  The new “Clover Trail” Atom processors are 32-bit only.  Surprise!  I though the industry was leaving 32-bt behind, but it appears to be alive and well.  We had made the initial decision to support only 64-bit Windows 8, and have developed only 64-bit baseline images.  I see that this choice will need to be reconsidered.
  • EFI/UEFI:  These new systems boot using EFI, with emulated BIOS, with the “SafeBoot” option enabled.  Out of the box, you cannot boot to USB because the SafeBoot prohibits this.  You need to load your OS to change EFI options.  EFI is not identical between systems, so navigating the process of booting to deployment/maintenance media will be a tough challenge for technicians to work through.  I actually was completely unable to boot the Envy X2 to an USB flash drive, running either WinPE (MDT boot media) or the FreeDOS-based(?) CloneZilla  live CD.  Bummer.
  • Drivers:  Most new tablets are aimed at the consumer market.  As a result, the vendors make little effort to package drivers in a way that is convenient for local IT staff to integrate into on-premise Windows deployment tools.  The Envy X2 is no exception.  A small handful of one-off driver installers are available, including a big bundle of Intel Chipset drivers.  The chipset drivers were critical in getting a freshly installed Windows 8 Enterprise OS working with the hardware.
  • Windows Editions:  This tablet shipped with “Windows 8″.  Not “Home”, not “Professional”, not “Ultimate”.  I tried performing an in-place SKU upgrade to Windows 8 Enterprise, but setup.exe said that this was not supported, so I needed to do a full OS install.  This process worked, but it was seriously aggravating to have to boot to the OEM OS, start the Enterprise OS install, re-install all of the required drivers, then clean up the original OS install.  Our users will not want to have to deal with this, and it will make our IT support staff very tired.
  • Hardware:  No Ethernet.  Unfortunately, our MDT/LTI deployment tools are designed to run over Ethernet, not Wi-Fi.  The LTI scripts actually will terminate if a Wi-Fi connection is detected.  Of course, application-only LTI task sequences really should run just fine over wireless, but the scripts still will not run over wireless.  We either will have to comment out the Wi-Fi checks, or require that the person launching LTI have a USB Ethernet dongle handy.

So… a lot of challenges.  More details as time permits.

Oh, one small “review style” note.  I decided to evaluate this tablet because HP claims that it supports an optional stylus.  However, the stylus for this device is not actually available for sale at this time.  Further, the device does not use the common Wacom or N-Trig digitizers, so buying a spare “Bamboo” stylus will not help you here.  HP has chosen to use the new “Atmel” integrated touch/pen sensors, and as such an Atmel-compatible stylus is required.  I cannot find these on the market anywhere.  As a result I cannot make any recommendation for or against the purchase of this device for Tablet PC enthusiasts.  I don’t even know if the stylus will be available for sale before the return period for this device expires.

UPDATE:

I returned this tablet.  Why?  It was not the screen resolution, which I though would be a problem but was not.  There were three primary reasons:

  1. It was not possible to determine the quality of the digitizer within the return period of the tablet.  I was unwilling to accept the risk of having a low-quality stylus for note taking.
  2. Keyboard dock quality was low.  The keyboard itself was reasonably good, but the trackpad was very annoying.  The texture was awful, and it was overly sensitive to the slightest palm brushes.  Given the small size of the keyboard deck, it was impossible to avoid brushing the trackpad, too.  Also, the screen did not tip back far enough for comfort when used on a countertop or other waist-height surface.
  3. Business deployment essentially was unsupportable.  HP support could not assist me with initialization of the TPM chip for BitLocker.  It appeared that a TPM was present, but there was no option in BIOS to reset the TPM, and the OS could not get ownership of the chip.  Also, the total lack of driver bundles would make deployment using MDT very difficult.
  4. The graphics card could not drive my external display at native resolution.  It maxed out at 1080p.

I did quite like this tablet, though.  Consumers seeking an additional computer for the road may really enjoy using it.  For fussy power users like me, it was close butnot quite there.

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:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" `
	| ? {$_.name -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
	).ProfileImagePath.tostring()
	
	#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_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
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 = "https://sharepoint2010.uvm.edu"
[string] $SmtpServer = "smtp.uvm.edu"
[string] $From = "saa-ad@uvm.edu"

$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) }
	}
	$site.Dispose()
}

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

foreach ($admin in $allAdmins) {
	[string] $to = $admin[0] + "@uvm.edu"
	[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
}