I can’t wait for the day that every client in my domain has powershell installed on it. Then everyone can be set to the following .vbs
set objShell = CreateObject( “Wscript.Shell” )
objShell.run( “powershell.exe -noexit c:\scripts\logon.ps1” )
So I have no choice, I must do something I’ve avoided for a long time. I must wade into the quirks, oddities, and idiosyncrasies of VB Script. Among them I had to brush up on:
But while I wait for the ubiquity of windows 7 (thus powershell 2.0) being installed on all client machines; migrations must be done so I’m faced the following requirements:
- Allow for “click-style admins” – those who to control the execution of a logon script
- Script must execute once, and only once
- Script must delete the MAPI profile so that users may use Autodiscover to setup their new Outlook Anywhere profiles on our multitenancy system.
- (bonus) Lock out user access to the user’s current Exchange mailbox on their own system
First: allowing for execution (or not) of the script though an easy to administer way to control the execution of the script. I chose to use group membership. This lets the helpdesk personnel to simply add the users to be migrated to an AD group. Easy schmeezy for them. On the backend, it means that the logon script must check for membership in the group so:
groupDN = "CN=lescript,OU=lescript,OU=Department,DC=FABRICAM,DC=DOM"
Set objSysInfo = CreateObject("ADSystemInfo")
Set user = getObject("LDAP://" & objSysInfo.UserName)
on error resume next
memberOf = user.getex("memberOf")
If (Err.Number <> 0) then
'you are here if the user is a member of no groups.
'msgbox("you are member of no groups")
On Error GoTo 0
else
For Each objGroup in memberOf
'msgbox("objgroup,groupDN is: " & vbcr & objGroup & vbcr &groupDN)
If objGroup = groupDN Then
'Checks completed. "Work" code goes here.
End If
Next
End If
On line 0 I hard code the DN of the group I want to test membership in. Lines 1-2 create the LDAP object for connecting to AD. Line 3 changes the error handling so I can capture errors and handle them. This is needed because if the user is a member of no groups, an error will be thrown and needs to be handled. Because I’m only enabling error handling here, any _other_ ADSI problems should cause execution to stop due to the default error handling. The use of getex() over get() is important here because it is guaranteed to return an array, which can then be iterated through with For Each. Finally on line 12 we pass the last If statement and have completed this requirement.
Assigning a logon script to a user or group.
Using AD groups from VBScript.
Getting the current AD user information from a logon script.
Enumerating AD Group membership from vbs.
A more familiar way to .NET programmers using DirectoryServices.DirectoryEntry (I found this after I had a working script
)
Next: Execution should be once, and only once. We don’t want to be deleting the Outlook profile every time a user logs on.
Set wmiLocator = CreateObject("WbemScripting.SWbemLocator") 'to get to stdRegProv
Set wshNetwork = CreateObject("WScript.Network") 'used to determine local machine name
'msgbox("name: " & WshNetwork.ComputerName"
set wmiNS = wmiLocator.ConnectServer(wshNetwork.ComputerName,"root\default") 'WMI namespace
set objReg = wmiNS.Get("StdRegProv")
In this snippet, I go through the steps required to get a connection to the local registry. More on working with the registry and keys from vbs. The comments are pretty self-explanatory, and the good news is that because I’m only looking at the HKCU hive, I’m do not need to use impersonation.
if objGroup = groupDN then
set wsh = createobject("wscript.shell")
on error resume next
key = wsh.RegRead("HKCU\ProfileDeleted")
'when err = (hex)80070002, key does not exist
if err.number <> 0 then
if hex(err.number) = 80070002 then
'msgBox("Script set to run: deleting profile and adding reg. key")
'wsh.sleep 14000
'old way: lRC = DeleteRegEntry(HKEY_CURRENT_USER, profile)
wsh.RegWrite"HKCU\ProfileDeleted", "True", "REG_SZ"
wsh.run("regDelete.bat")
End If
Else
if key = "True" then
'msgBox("Profile has already been deleted, nothing will be done.")
ElseIf key = "False" then
'external editing of the key is the only way to get here.
'Either the key does not exist, or it's set to true by the script.
msgBox("Please report this message to support" & vbcr & _
"""Profile deletion key was set to False, no action was taken.""" & vbcr & _
"HKCU\ProfileDeleted")
Else
msgbox("Please report this error to support" & vbcr & _
"""Key set to something other than True or False""" & _
vbcr & "HKCU\ProfileDeleted" & vbcr & "Key value: " & key)
End If
End If
In this section (executed after the group membership tests above) I check for registry key named “HKCU\ProfileDeleted”. If it does NOT exist (Line 6 checks for this read error), then I add it, set it to “True”, and run the batch file “regDelete.bat”. The rest of the snippet contains a do-nothing check if they key has already been set (lines 14-15) and error handling should the key be set to “False” (16-21) or some other value (22-25). Since these states should never happen, I’m only kicking an error to a message box, and asking the user to report it.
Now the “meat”: Deleting the MAPI profile. Because the profile is nothing more than a stack of registry keys, it should be simple to delete right? Wrong. VBS apparently has no way to recursively delete a registry key without writing the recursion yourself. If that wasn’t enough, for some reason I’ve been unable to completely isolate, when I tried to recursively delete keys (line 9) with the sample function (recursion handled via vbs logic) found here kb279847, some keys remain. I tried the standard programmer “wait 10″ solution in case something was accessing the MAPI profile and adding keys as I tried to delete them (line 8 ) but that yielded only slightly better results. Less subkeys remained, but full recursive deletion still failed.
So what to do? Well there’s Wscript.shell.Run(foo.exe) and that gives us the ability to run a file. So why not take the easy way out and just do a “reg /delete”?
rem reg delete /va /f "HKCU\Software\Microsoft\WIndows NT\CurrentVersion\Windows Messaging Subsystem\"
regedit.exe /s \FABRICAM.DOM\SYSVOL\LCS.CORP\scripts\profileDelete.reg
Line 0 fails. So I’m just moving on, I’m really tired of jumping through hoops at this point, but trial and error proves that line 1 (using regedit to load a .reg file) works out just fine. I saved the “profileDelete.reg” to the System Volume and tested successfully.
Yes, it seems like this should have been easier, but soon enough it won’t matter and I’ll be able to use powershell
remove-item -path "HKCU\Software\Microsoft\WIndows NT\CurrentVersion\Windows Messaging Subsystem\"
Until then the .reg file looks like this:
Windows Registry Editor Version 5.00
[-HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem]
Forget it, I’ll use a .reg file
Removing the Outlook profile registry keys using regedit
More recursive registry vbs (.reg too)
The “bonus” requirement of locking users out of their mailboxes I’ll handle in my next post. Until then, you need an updated token– any ACL changes won’t take effect until you log out/in. Remember mailbox ACLs are held in the LDAP property “msExchMailboxSecurityDescriptor”, and you’d need to go through the painful process of decrypting that.