Tuesday, August 29, 2006
VBScript and %windir%\Registration
I loathe the typical and all to frequent support response to weird desktop issues: reimage it! If your company has an SLA established where by issues that cannot be resolved within a certain time period, yadda, yadda, yadda; but I see it too often where these SLAs don't exist, and folks just jump to reimaging the system when a solution isn't easily available.
Case in point: User logs onto their workstation one morning and receives the error:
Little could be found via Google on this error. I tried a few things (re-register vbscript.dll, reinstall IE6 SP1, patch) - no dice. I was able to replicate the error when logged on as an admin by using
The fix:
Case in point: User logs onto their workstation one morning and receives the error:
Can't find script engine "VBScript" for script \\server\sysvol\domain.com\Policies\{1E6FB511-C1A1-49AB-8E4C-BC34076F683B}\User\Scripts\Logon\Drivemapping.vbs
They click OK and then nothing happens. Explorer doesn't load; you can't even manually start it. Yet when you logon as an administrator, all is well.Little could be found via Google on this error. I tried a few things (re-register vbscript.dll, reinstall IE6 SP1, patch) - no dice. I was able to replicate the error when logged on as an admin by using
runas /user:domain\username "wscript myscript.vbs"
which caused the same error. I assumed it was a permissions issue, so I whipped out the handy regmon and filemon tools. Nothing in the regmon capture log (filtered to just wscript) indicated a problem, but filemon quickly showed the source of the problem: access denied to C:\WINNT\Registration\R000000000006.clb
. I checked the privs - System & Admins Full Control, Everyone special Read (everything but execute). The permissions were unique to the folder, not inherited from the system root.The fix:
cacls %windir%\Registration /e /g Users:R
It took me about an hour of total work to come up with a single command-line fix, versus the hours of toil and trouble to backup the user data, reimage the system, replace the data, etc. Of course that's another task for me for a rainy day: automate that process with SMS OSD.... :)Sunday, August 27, 2006
CSG/CWI Interaction
For someone who has been working with Citrix for years, this is probably a very simple concept, but it just "clicked" for me the other day. (You know when a concept is just out of reach but then one day it just clicks and suddenly makes sense? I love it when that happens!)
I'm talking about the interaction between Citrix Secure Gateway (CSG) and Citrix Web Interface (CWI). In some environments, these roles are separated out onto multiple servers so that this interaction makes much more sense, but in my case they are both on the same physical server.
I was having an issue where after I thought I had it all configured and it should be working, the clients were receiving an IIS "Bad Request (Invalid Hostname)" error. It turns out this was because I had defined a host header value on the IIS Default Web Site (used for the Citrix Web Interface).
The SSL certificate for the URL is primarily used by CSG. When the client hits the server on 443, it's talking with CSG. Then CSG internally hits the CWI site at
Here's a diagram that I whipped up to show the interaction. I'm very much a visual learner, so I often create things like to this to help me visualize what's happening. The diagram also gives which ports are needed through the firewalls; once setup this is all visible using
I'm talking about the interaction between Citrix Secure Gateway (CSG) and Citrix Web Interface (CWI). In some environments, these roles are separated out onto multiple servers so that this interaction makes much more sense, but in my case they are both on the same physical server.
I was having an issue where after I thought I had it all configured and it should be working, the clients were receiving an IIS "Bad Request (Invalid Hostname)" error. It turns out this was because I had defined a host header value on the IIS Default Web Site (used for the Citrix Web Interface).
The SSL certificate for the URL is primarily used by CSG. When the client hits the server on 443, it's talking with CSG. Then CSG internally hits the CWI site at
http://localhost
, not the URL. I found a post on Brian Madden's forums that suggested for additional security you should configure the IIS website to only listen on 127.0.0.1 instead of a specific external IP or All Unassigned. So removing the host header value from the IIS site fixed my problem, but I also set it to only listen on the local IP and all is still good.Here's a diagram that I whipped up to show the interaction. I'm very much a visual learner, so I often create things like to this to help me visualize what's happening. The diagram also gives which ports are needed through the firewalls; once setup this is all visible using
netstat
on each system.Monday, August 14, 2006
CLI Tab-Completion for Win2k
[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"CompletionChar"=dword:00000009
It exists by default in XP. How to Enable Automatic Complete for the Command Prompt implies that it did exist in NT.
I wish I would have found this months ago, since all I have at work (other than my laptop) is Win2k.
Wednesday, August 02, 2006
CreatePrinters.vbs
As previously mentioned, I wrote a script to loop through creation of multiple printers.
It makes a lot of assumptions, but does the trick. There are probably better ways to do some of the steps of this script, but again: it works.
Sample Input File,
The script dumps out four log files (one for each task) for each printer, e.g., PRINTER_NAME-1.log, PRINTER_NAME-2.log, PRINTER_NAME-3.log, PRINTER_NAME-4.log. So if you have a lot of printers in the input file, it will create a LOT of log files. Fortunately they are extremely small in size.
Sample Log Files
If any task fails, the error(s) will be in the log file for that task. If any one step fails, all later tasks for that printer will also fail.
It makes a lot of assumptions, but does the trick. There are probably better ways to do some of the steps of this script, but again: it works.
CreatePrinters.vbs
' Creates printers on local system from input file
' Input file:
' - Must be plain text
' - No header or whitespace
' - Tab-delimited columns: Printer Name, IP Address, Driver Name, Location, Comment
' Assumptions:
' - The Standard TCP/IP Port will be named IP_%IPADDRESS% where %IPADDRESS% is the input value and it will use the RAW protocol on port 9100
' - The driver will be for Windows 2000/XP/2003 only and must be included with Windows source
' - The share name is the same as the printer name
' - The share will be published
' - The printer will use bi-directional support when available
' - Everything else is the default value
' The resulting printers are as if manually created accepting default values.
' create shell object (used later)
set objWsh = WScript.CreateObject ( "WScript.Shell" )
' create FileSystemObject
set objFSO = WScript.CreateObject ( "Scripting.FileSystemObject" )
' open file, create TextStream Object
set objTSO = objFSO.OpenTextFile ( "printers.txt" )
' loop through the file
Do Until objTSO.AtEndofStream
' read a line
strLine = objTSO.ReadLine
' split the line into an array
strArray = Split ( strLine, vbTab )
' build command lines
strTask1 = "cmd /c cscript %windir%\system32\prnport.vbs -a -r IP_" & strArray(1) & " -h " & strArray(1) & " -o raw -n 9100 > c:\" & strArray(0) & "-1.log"
strTask2 = "cmd /c cscript %windir%\system32\prndrvr.vbs -a -m """ & strArray(2) & """ -v 3 -e ""Windows NT x86"" > c:\" & strArray(0) & "-2.log"
strTask3 = "cmd /c cscript %windir%\system32\prnmngr.vbs -a -p """ & strArray(0) & """ -m """ & strArray(2) & """ -r IP_" & strArray(1) & " > c:\" & strArray(0) & "-3.log"
strTask4 = "cmd /c cscript %windir%\system32\prncnfg.vbs -t -p """ & strArray(0) & """ -l """ & strArray(3) & """ -m """ & strArray(4) & """ -h """ & strArray(0) & """ +shared +published +enablebidi > c:\" & strArray(0) & "-4.log"
' run the commands
objWsh.Run strTask1, 1, 1
objWsh.Run strTask2, 1, 1
objWsh.Run strTask3, 1, 1
objWsh.Run strTask4, 1, 1
Loop
' cleanup
objTSO.Close
set objFSO = Nothing
Sample Input File,
printers.txt
Test1 10.10.1.5 HP LaserJet 4100 Series PCL Test Lab Test PrinterUsing this sample file, Test1 and Test2 are created, Test3 is not because the driver is not in the Windows source.
Test2 10.10.1.6 HP LaserJet 8150 Series PCL Engineering Rick's printer
Test3 10.10.1.7 HP Business Inkjet 2280 PCL 5C Operations Dave's printer
The script dumps out four log files (one for each task) for each printer, e.g., PRINTER_NAME-1.log, PRINTER_NAME-2.log, PRINTER_NAME-3.log, PRINTER_NAME-4.log. So if you have a lot of printers in the input file, it will create a LOT of log files. Fortunately they are extremely small in size.
Sample Log Files
C:\Test1-1.log
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.
Created/updated port IP_10.10.1.5
C:\Test1-2.log
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.
Added printer driver HP LaserJet 4100 Series PCL
C:\Test1-3.log
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.
Added printer Test1
C:\Test1-4.log
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.
Configured printer Test1
If any task fails, the error(s) will be in the log file for that task. If any one step fails, all later tasks for that printer will also fail.
Password Change Errors
I changed my password today. And then promptly forgot it. It was obivously too complicated, because after a meeting, I could not unlock my Win2k workstation. So I asked a fellow admin to reset my password; he did not set "must change password at next logon." So When I went to manually change my password, I received the "your password is not complex" message. I tried a variety of complex passwords, including
(Prohe, can you figure out the source of this?)
4 of 4 by 9, no part of my name, not similar to a previously used password. WTF?
It turns out, policy has a two day password maximum. SO WHY DIDN'T THE ERROR MESSAGE SAY SO! Comma DAMNIT!
<grumble>
Prohe, here's a hint: WuiAccountCreator :)
RiadBsc-0
(Prohe, can you figure out the source of this?)
4 of 4 by 9, no part of my name, not similar to a previously used password. WTF?
It turns out, policy has a two day password maximum. SO WHY DIDN'T THE ERROR MESSAGE SAY SO! Comma DAMNIT!
<grumble>
Prohe, here's a hint: WuiAccountCreator :)
Scripting Printer Creation
Windows XP and Windows 2003 include the following scripts in %windir%\system32 for CLI printer management
Prncnfg.vbs
Prndrvr.vbs
Prnjobs.vbs
Prnmngr.vbs
Prnport.vbs
Prnqctl.vbs
This is (IMO) much improved over the printui.dll hackery.
While there's some basic usage info out there on these scripts, I couldn't find anything that gives any good practical advice; aka "notes from the field."
Scenario: you have to create 50 printers on a print server. You have the following data: Name, Location, IP, Driver Name. Some additional assumptions: you only have Win2k/XP clients, you want to share each printer consistently, and default permissions (Everyone: Print) are sufficient.
There are four steps you need to do:
1. Create the port
2. Install the driver
3. Create the printer
4. Configure the printer
A sample script:
Line 1 creates the port named "IP_10.10.1.5" with the IP address 10.10.1.5, using RAW protocol on port 9100.
Line 2 installs the HP LJ 4100 PCL driver for Windows 2000/XP clients.
Line 3 creates the printer named "Test1" using the previously created port and driver.
Line 4 sets the location "Test Lab", comment "Comment", shares and publishes it as "Test1" and enables bi-directional support (if available).
The result is a shared printer identical to as if you had done it manually accepting the default settings.
I haven't yet written a script to loop through these commands with the input data for all 50 printers, but that will come shortly (and depending upon how well I do it I may or may not post it).
Prncnfg.vbs
Prndrvr.vbs
Prnjobs.vbs
Prnmngr.vbs
Prnport.vbs
Prnqctl.vbs
This is (IMO) much improved over the printui.dll hackery.
While there's some basic usage info out there on these scripts, I couldn't find anything that gives any good practical advice; aka "notes from the field."
Scenario: you have to create 50 printers on a print server. You have the following data: Name, Location, IP, Driver Name. Some additional assumptions: you only have Win2k/XP clients, you want to share each printer consistently, and default permissions (Everyone: Print) are sufficient.
There are four steps you need to do:
1. Create the port
2. Install the driver
3. Create the printer
4. Configure the printer
A sample script:
cscript C:\WINDOWS\system32\prnport.vbs -a -r IP_10.10.1.5 -h 10.10.1.5 -o raw -n 9100
cscript c:\WINDOWS\system32\prndrvr.vbs -a -m "HP LaserJet 4100 Series PCL" -v 3 -e "Windows NT x86"
cscript c:\windows\system32\prnmngr.vbs -a -p "Test1" -m "HP LaserJet 4100 Series PCL" -r IP_10.10.1.5
cscript C:\WINDOWS\system32\prncnfg.vbs -t -p "Test1" -l "Test Lab" -m "Comment" -h Test1 +shared +published +enablebidi
Line 1 creates the port named "IP_10.10.1.5" with the IP address 10.10.1.5, using RAW protocol on port 9100.
Line 2 installs the HP LJ 4100 PCL driver for Windows 2000/XP clients.
Line 3 creates the printer named "Test1" using the previously created port and driver.
Line 4 sets the location "Test Lab", comment "Comment", shares and publishes it as "Test1" and enables bi-directional support (if available).
The result is a shared printer identical to as if you had done it manually accepting the default settings.
I haven't yet written a script to loop through these commands with the input data for all 50 printers, but that will come shortly (and depending upon how well I do it I may or may not post it).