Client Drive Mappings in a View Desktop
A customer recently came to me with a very specific
request. They wanted all of the drive
mappings that each user had created on their local workstation (their View
Client) to be created on their VDI Desktop.
Without roaming their profile, I created a simple logon script for the
VDI Desktop to execute (VMware’s Profile Migration does not capture Drive
Mappings, at least not when converting from XP to Win 7). As you’ll see below, I really just found 3
other smarter people’s scripts and mashed them together (with some minor adjustments to work in the
VDI scenario) in order to get them to do what I needed. Since the results are highly specific (and a
bit interesting), I figured that I’d go ahead and post the script. Be aware that it abuses the fact that, in
this environment, everyone is a local administrator on their physical desktop,
so I expect that some of the client registry queries might fail under other
circumstances.
Just so that everyone knows, the customer is considering a
few solutions to accomplish their needs.
A logon script is not the most desirable solution, and we’d certainly
want to find a real scripting guy to at least clean this up before going live
with it. That said, it seems to do what
it’s supposed to do in the test scenarios that I’ve subjected it to so far
(which is not nearly as in depth as I’d like).
This script was an interesting challenge because it had to accomplish a
few distinct tasks:
1) It had to identify the Client machine
2) It had to check for mapped network drives on the Client machine
3) It had to ensure that it wasn’t conflicting with existing drives in the VDI Desktop
4) It had to map any drives that weren’t already in use
1) It had to identify the Client machine
2) It had to check for mapped network drives on the Client machine
3) It had to ensure that it wasn’t conflicting with existing drives in the VDI Desktop
4) It had to map any drives that weren’t already in use
The dictionary procedure that I used for goal #3 could be adapted to find the next available drive
letter (and in fact, that’s what the source had originally written it to
accomplish), but I elected to just skip that drive if it already exists
(otherwise you’d have to start checking mapping destinations as well as the
existence of drive letters, which would get onerous).
Identifying the client’s mapped network drives (goal #2)
proved to be more challenging than expected.
Finding the network mapped drives from the local system is a trivial
procedure (accomplished with the WMI Query: “Select * from Win32_LogicalDisk
Where DriveType = 4”), however a remote system is querying the computer’s WMI,
whereas mapped network drives are associated with a user. The long and short of it is that that method
simply doesn’t work from a remote system (in this case, the virtual desktop).
The procedure that I found for discovering a remotecomputer’s network drive mappings involved going through that computer’s registry and looking at the current
user’s hive. The original code that I
found to do this just looked at the owner of the Explorer.exe process in order
to determine who is currently logged in, however our desktops are not starting
the Explorer shell (and are instead booting directly to the View Client), so
that option was out (and I want something flexible enough that it will run
regardless of the shell that Windows starts).
Fortunately, Guy Thomas posted a very easy to use bit of code that used WMI’s Win32_ComputerSystem
class to fetch that same information.
So, with elements of those three scripts combined with a
little bit (a very little bit) of my own VDI Registry knowledge, I put together
the following script. Please feel free
to use it (or bits of it), and hopefully you’ll be able to improve on my mess…
but always make sure that those aforementioned smart people get credit where
it’s due. As always, please be wary of
unintended line breaks due to blog width and this is provided as-is, use at your own risk, etc.
'===========================================
' Dynamic Drive Mappings Based on Client Computer Mappings
' Author(s): Ginolard / ScriptingGuy1 / Guy Thomas / Jason
Coleman
' Source(s): a lot of duct tape.
' This script is designed to be run on a VMware View Virtual
Desktop that is being accessed from a Windows Client.
' It looks at all of the drives that the current user has
mapped on their Windows Client, then creates those drive mappings on the
Virtual Desktop.
' It does not attempt to map a network drive if that drive
letter is already in use on the system.
' It also maps the client's "CD" share in order to
support local CD-Rom access.
'===========================================
on error resume next
'Define variables, constants and objects
Dim objShell
Dim sUser, strRemComputer, strRemotePath
Set objShell = CreateObject("WScript.Shell")
Set objNetwork = CreateObject("Wscript.Network")
Set objDictionary =
CreateObject("Scripting.Dictionary")
strComputer = "."
'Defines the remote computer by looking in the local
computer's registry for the View Client info
strRemComputer = objShell.RegRead("HKEY_CURRENT_USER\Volatile
Environment\ViewClient_Machine_Name")
Const HKEY_USERS = &H80000003
Set objWbem = GetObject("winmgmts:")
Set objRegistry = GetObject("winmgmts://" &
strRemComputer & "/root/default:StdRegProv")
Set objRemWMIService = GetObject("winmgmts:"
& "{impersonationLevel=impersonate}!\\" & strRemComputer
& "\root\cimv2")
Set objWMIService = GetObject("winmgmts:\\" &
strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery("Select * from
Win32_LogicalDisk")
'Create a Dictionary of all local disks, to easily check
against later when mapping
For Each objDisk in colDisks
objDictionary.Add objDisk.DeviceID,
objDisk.DeviceID
Next
'Uses Guy Thomas' code to discover the currently logged on
user of the Client system
Set colComputer = objRemWMIService.ExecQuery ("Select *
from Win32_ComputerSystem")
For Each objComputer in colComputer
'Note: if objComputer.UserName is null,
this generates a script error. objComputer.UserName is null when no one
is logged in.
sUser =
Split(objComputer.UserName,"\")
sUser(1) = LCase(sUser(1))
Next
WScript.sleep 3000
WriteFile sUser(1) & " is logged on at " &
strRemComputer, false
objNetwork.RemoveNetworkDrive "E:"
'Quit out of the script if the Client is not responding to
WMI and is therefore assumed to not be Windows
If sUser(1) = Chr(0) Then
WriteFile "Unable to discover logged on client user, client does not
appear to be Windows", true
wscript.quit
End If
'Loop through the HKEY_USERS hive until (ignoring the
.DEFAULT and _CLASSES trees) we find the tree that corresponds to the currently
logged on user.
lngRtn = objRegistry.EnumKey(HKEY_USERS, "",
arrRegKeys)
For Each strKey In arrRegKeys
If UCase(strKey) =
".DEFAULT" Or UCase(Right(strKey, 8)) = "_CLASSES" Then
Else
'Gets the user name that corresponds with the SID that owns the registry key
Set objSID =
objWbem.Get("Win32_SID.SID='" & strKey & "'")
'If the account name of the current sid we're checking matches the username
that is logged into the Client, then enumerate the Network subtree
If
objSID.accountname = sUser(1) Then
regpath2enumerate
= strkey & "\Network" 'strkey is the SID
objRegistry.enumkey
hkey_users, regpath2enumerate, arrkeynames
'If the array has elements, go and get the drive mapping info from the registry
If
Not (IsEmpty(arrkeynames)) Then
For
Each subkey In arrkeynames
regpath
= strkey & "\Network\" & subkey
regentry
= "RemotePath"
objRegistry.getstringvalue
hkey_users, regpath, regentry, dapath
WriteFile
subkey & ":" & vbTab & dapath, true
If objDictionary.Exists(subkey & ":") Then
WriteFile subkey & ": drive already exists", true
Else
WriteFile "Mapping " & subkey & ": to " &
dapath, true
objNetwork.MapNetworkDrive subkey & ":", dapath, true
End If
Next
End
If
End If
End If
Next
'Map the Client's CD Share to the E Drive
'creates the path to the client's CD Share based on the
registry
WriteFile "Mapping E: to \\" & strRemComputer
& "\CD", true
objNetwork.MapNetworkDrive "E:", "\\"
& strRemComputer & "\CD", true
Function WriteFile(sText, bAppend)
strFile = "C:\Temp\Drives-Log.txt"
Const ForAppending = 8
set objFSO = CreateObject("Scripting.FileSystemObject")
if bAppend then
set objFile = objFSO.OpenTextFile(strFile, ForAppending, true)
else
Set objFile = objFSO.CreateTextFile(strFile, True)
end if
objFile.WriteLine(Now & ": " & sText)
objFile.Close
End Function
Comments
Post a Comment
Sorry guys, I've been getting a lot of spam recently, so I've had to turn on comment moderation. I'll do my best to moderate them swiftly after they're submitted,