Tuesday, May 20, 2008

PowerShell - Converting a script

As a further exercise with PowerShell, I've converted the 'resetLastLogin' script (http://www.microsoft.com/technet/scriptcenter/csc/scripts/desktop/settings/cscds075.mspx) from VBScript to PowerShell. The script resets the 'default user name' on the specified computer. Basically, if you as an admin logged in to someone's computer, it may show your name as the last to login, confusing the regular user of the computer. This script blanks it out, or sets it to the given text. The script can be ran against a remote computer as well, so as to reset the last login from a remote desktop session as well.

Analysis



The first batch of lines in the VBScript handle the arguments, with appropriate defaults:

Dim sComputer
Dim sNewUserName

if WScript.arguments.count > 0 then
sComputer = WScript.arguments(0)
if WScript.arguments.count > 1 then
sNewUserName = WScript.arguments(1)
else
'default the user name to .
sNewUserName = ""
end if
else
'default computer name to local
sComputer = "."
sNewUserName = ""
end if


This was converted using the parameter definition

param ([string]$MachineName, [string]$DefaultUserName)

and applying some defaults

if ($MachineName -eq "") {$MachineName = $env:COMPUTERNAME}
if ($DefaultUserName -eq "") {$DefaultUserName = ''}

while the overall code is much cleaner, had to make non-intuitive use of the "-eq" operator for comparison, vs. just an '=' sign. I could have left the "[string]" definition off of the parameters, the script worked fine, but when I later went to echo those values back, they were rendering as blank.

VBScript registry access was similar in that we first get a registry object:

'get the registry object
Set oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
sComputer & "\root\default:StdRegProv")

get the old value

oReg.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

and set the new value

oReg.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, sNewUserName


In PowerShell, the O-O pattern means that we follow a pattern of getting an object representing the registry:

$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $MachineName)

, get an object representing the subkey

$regKey = $reg.OpenSubKey($keyPath,$true)

then get the old value (only to display later) and set the new value

$oldValue = $regKey.GetValue($valueName)
$regKey.SetValue($valueName,$DefaultUserName)


The last step simply echoes something back so the user can see what happened. In VBScript

Wscript.echo "Changed DefaultUserName from '" & strValue & "' to '" & sNewUserName & "'"

and in PowerShell

Write-Host "Changed DefaultUserName from $oldValue to $DefaultUserName on $MachineName"


Full Source Code



The VBScript (without introductory comments) is 43 lines:

Dim sComputer
Dim sNewUserName

if WScript.arguments.count > 0 then
sComputer = WScript.arguments(0)
if WScript.arguments.count > 1 then
sNewUserName = WScript.arguments(1)
else
'default the user name to .
sNewUserName = ""
end if
else
'default computer name to local
sComputer = "."
sNewUserName = ""
end if

Dim sNewValue
sNewValue = ""

'constants for registry access
const HKEY_CURRENT_USER = &H80000001
const HKEY_LOCAL_MACHINE = &H80000002

'standard output
Set StdOut = WScript.StdOut

'get the registry object
Set oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
sComputer & "\root\default:StdRegProv")

'set the key to grab
strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
strValueName = "DefaultUserName"

'get the value
oReg.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

'set the new value
oReg.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, sNewUserName

'display it
Wscript.echo "Changed DefaultUserName from '" & strValue & "' to '" & sNewUserName & "'"


The PowerShell version is 14 lines:

param ([string]$MachineName, [string]$DefaultUserName)
if ($MachineName -eq "") {$MachineName = $env:COMPUTERNAME}
if ($DefaultUserName -eq "") {$DefaultUserName = ''}

$keyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
$valueName = "DefaultUserName"

$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $MachineName)
$regKey = $reg.OpenSubKey($keyPath,$true)

$oldValue = $regKey.GetValue($valueName)
$regKey.SetValue($valueName,$DefaultUserName)

Write-Host "Changed DefaultUserName from $oldValue to $DefaultUserName on $MachineName"