Posts Tagged ‘VBS’

Printer Migration Client Script

November 3rd, 2009

If you have ever had to migrate all the printers from one Windows print server to another, the server side part is pretty easy.  Windows 2008 includes the Printer Migration Wizard which can migrate the queues.  Prior to Windows 2008 there was an outstanding utility called Print Migrator (PrintMig) which made it a snap.

However the client side is a bit more tricky.  Since there is a different server name in the printer share path the clients will be broken after the migration.

Printer share  path on old server: \\OldPrnSrv\Printer1

Printer share path on new server: \\NewPrnSrv\Printer1

This problem can be further complicated if during the migration you took the opportunity to standardize Printer/Printer Share names.  I had this exact problem at a client.  I solved it with a GPO-based logon script and input file.  Before I go into how to implement this, first a little about the script.

  • The script first checks for the existence of a “check” file.  If the check file exists then we assume the script already ran and we exit.
  • Then the script records the user’s default printer as it will need this info later to ensure the same device is set as default after the printer reconnect.
  • Next the script walks through the input file and attempts to disconnect each “old” printer connection and reconnect to the new.  If the printer share in the input file is not defined on the person’s machine then it skips that line and moves on to the next line/printer.
  • Once all the input file has been traversed the script resets the person’s default printer and creates the “check” file.

Here is the script code.

On Error Resume Next
Const ForReading = 1

Set WshNetwork = CreateObject("WScript.Network")
Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FileExists("c:\PrinterReconnectFinished.txt") then
Set f1 = fso.OpenTextFile(GetPath() & "input.txt", ForReading)

varDefault = GetDefaultPrinter()

tmpArr = Split(f1.readall, vbcrlf)
for i = 0 to UBOUND(tmpArr)
    If instr(1, tmpArr(i), varDefault, 1) then x = i
    err.clear
    tmpVar = split(tmpArr(i), "~")
    WshNetwork.RemovePrinterConnection tmpVar(0)
    If err.number = 0 then WshNetwork.AddWindowsPrinterConnection tmpVar(1)
next

var1 = split(tmpArr(x), "~")
ConfigDefaultPrinter(var1(1))
    Set f2 = fso.CreateTextFile("c:\PrinterReconnectFinished.txt", True)
    Set f3 = fso.Getfile("c:\PrinterReconnectFinished.txt")
    f3.attributes = f3.attributes + 2
End If

'*************************************************************************************************
Sub ConfigDefaultPrinter(name)
        '*****************************************************************************************
        '*****************************************************************************************
        ' Purpose:  This sub routine defines a default printer based on the printer name passed to
        ' it.
        ' Version 1.0
        ' Arguements:  None
        '*****************************************************************************************
        '*****************************************************************************************
    strComputer = "."
    Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")
    Set colInstalledPrinters =  objWMIService.ExecQuery _
    ("Select * from Win32_Printer Where Name = 'name'")
    For Each objPrinter in colInstalledPrinters
            objPrinter.SetDefaultPrinter()
    Next
End Sub
'*************************************************************************************************

'*************************************************************************************************
Function GetDefaultPrinter()
        '*****************************************************************************************
        '*****************************************************************************************
        ' Purpose:  This Function returns the default printer as configured on the client.
        ' Version 1.0
        ' Arguements:  None
        ' Returns:  Text string
        '*****************************************************************************************
        '*****************************************************************************************
    strComputer = "."
    Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")
    Set colInstalledPrinters =  objWMIService.ExecQuery("Select * from Win32_Printer")
    For Each objPrinter in colInstalledPrinters
        If objPrinter.Default then
            GetDefaultPrinter = objPrinter.Name
        End If
    Next
End Function
'*************************************************************************************************

'*************************************************************************************************
Function GetPath()
    '*********************************************************************************************
    '*********************************************************************************************
    ' Purpose:  This Function returns the path where the script is currently being executed.
    ' Version 1.0
    ' Arguements:  None
    ' Returns:  Text string
    '*********************************************************************************************
    '*********************************************************************************************
    Dim path
    path = WScript.ScriptFullName
    GetPath = Left(path, InStrRev(path, "\"))
End Function
'*************************************************************************************************

Here’s how you can implement this solution.

1. Create an input file and put each old printer share path and new share path on a new line separated by a “~”.  It should look like this:

\\OldPrnSrv1\Printer1~\\NewPrnSrv1\Printer1
\\OldPrnSrv1\Printer2~\\NewPrnSrv1\Printer1
etc...

2. When you have all the printer share paths entered in your input file save it as input.txt.

3. Next download the printer migration script here.  Strip the “.txt” off of the end of the name.

4. Save the input.txt and script files in the domain scripts share (\\mydomain.com\SYSVOL\mydomain.com\scripts).

5. Create a new Group Policy Object (GPO) called Printer Reconnect. NOTEDon’t link it to any container yet.

6. Edit the GPO under User Configuration>Windows Settings>Scripts double-click Logon.

7. Click the Add button, then Browse.  Locate/select the PrinterReconnect.vbs script in the path you saved it to previously.

8. Click OK and make sure the script is displayed on the Logon Properties screen.  Click OK.

Once all printers are migrated to the new server and well tested, you should be ready to have the clients reconnect to the migrated queues on the new server.  ONLY when ready to have clients reconnect, go to the next step.

9. Link the Printer Reconnect GPO to an appropriate container to apply the logon script to targeted users.

As always, use this at your own risk.  Let me know if you have any issues and I will help out as I can.

Re-Use Your (or someone else’s) VBScript Code

June 20th, 2009

How many of us have searched incessantly for “that script” we wrote/borrowed/stole 2 years back?  OK, I still do that sometimes but I have a method that drastically reduces this usually fruitless but always frustrating script-hunt.  This method leverages Windows Script Host’s capability to reference another script at execution time, simlar to the way programming languages allow you to reference an “include” file.  Basically there are 2 steps to start taking advantage of this sanity-saving technique.

Step 1 – The “Include” File

Create your “include file”.  This is simply a VBS file that has all of the reusable Functions and Subs you have found helpful over the years.  I call mine “Global.vbs”.  You can download the one I use here (rename it with a VBS extension) as a starting point, but be sure to add your additional functions/subs as needed.  Whenever you find or write a cool function/sub, make sure it is well tested then add it to your include file.  If you downloaded mine note the comments at the top which describe the functions/subs contained.  I recommend keeping this comment section up-to-date so that it is easy to see what goodness the file contains.  Once the include file is complete place it in a location that is accessible from anywhere on your network.  If you use Active Directory I suggest placing this in the sysvol SCRIPTS directory (the old NETLOGON share).  That way it will be replicated and accessible from all your domain controllers, can be updated from one place, and when called, the most “local” version will be used (thanks to AD Sites).

Step 2 – The “Main” Script

In step 1 you created an awesome repository of tried and true code.  Now to put it to use.  Create a text file called EXAMPLE.WSF and paste the following in it:

<job>
<script language="VBScript">
	option explicit
	Dim TotalRunTime
	TotalRunTime = Timer()
</script>
<script language="VBScript" Src="\\SERVER\SHARE\global.vbs"/>
<script language="VBScript">
'***************************************************************************************
'***************************************************************************************
'This is a blank script file that includes global.vbs.
'
' Version 0.1
' Author
' Arguements:
'***************************************************************************************
'************************************Insert user code below*****************************

'************************************Insert user code above*****************************
wscript.echo "Total Run Time = " & timer() - TotalRunTime & " Seconds"
</script>
</job>

Keep this EXAMPLE.WSF handy so you can use it as a template to create your scripts.  Everytime you need to write a script just make a copy of EXAMPLE.WSF and write your code between the “Insert code Here” lines.  As you write it and need to reference a function/sub that is listed in the “include” file, just reference it as though the function/sub was already in the script you are writing.  Remember that all of the reusable code from your “include” file is read in during execution time so it is all available to call.

As implied in the title of the blog entry, the main benefit of this approach to scripting is that you reuse your code…no more re-writing the same script you wrote (and lost) several times in the past.  And when you don’t have to re-write code you don’t have to re-debug all the same mistakes you made in the last several times you wrote the script.  Additional benefits include smaller scripts (you don’t have the functions/subs in your main script), and easier scripting for the VBS-uninitiated (all the hard stuff is already written…just call it).

Disclaimer - I did not invent this “Mac Daddy” process but I have used the heck out of it.  All thanks go to Tommy Mills, one of my many scripting mentors…Thanks Tommy!

OpsMgr Event Correlation – Branch Site Monitoring

December 11th, 2008

I am a big fan of System Center Operations Manager 2007, but in it’s current version it has a limitation.  If your management server is at your primary datacenter and it monitors servers in a remote site, what happens if the WAN link between the two locations goes down?  Ideally your monitoring solution is smart enough to know that the branch site is unavailable and due to this condition it will cease trying to monitor the end points at that branch until the site is available again.  This capability is known as event correlation.  However I have found no way to configure this functionality in OpsMgr.  I have even posed the question on OpsMgr forums and asked OpsMgr MVPs, all to no avail.  So, necessity being the mother of invention (or creative re-utilization) I have pieced together a solution.

Assumptions:

  1. You have computer groups set up in OpsMgr for each branch site.  In my case, my branch sites are also set up in Active Directory as AD sites.  If this is true for you as well be sure to check out Cameron Fuller’s blog about setting up dynamic computer groups based on AD sites.
  2. You know the IP address of the branch site’s router.  We will be ping’ing this to check the availability of the site as a whole.
  3. You have Boris Yanushpolsky’s “rock-on awesome” script to place an entire computer group in maintenance mode (get it here).  Put his script in a folder on your management server, such as “C:\Scripts\GroupMM”.
  4. Your management server has Powershell and the OpsMgr Powershell console component installed.

How Does It Work

The heavy lifting is done by Boris’ maintenance mode script.  All this solution does is determine whether the branch site router is reachable (via our friend “Ping”).  If it is then we record that in the results file (auto-created).  If the router is not reachable then put the computer group in maintenance mode and create a tracking file with the site name (so when we run later we don’t try to put it in maintenance mode again).  The condition and action taken are recorded in the results file.  Later when the router becomes reachable again we remove the computer group from maintenance mode, delete the tracking file and record the condition and action taken in the results file.

To Implement the Solution:

  1. Create a directory on your management server for the branch availabilty monitoring script, such as “C:\Scripts\BranchSiteMonitoring”.
  2. Edit and save the BranchSiteMonitoring script (at the end of this blog entry) in the directory you just created.
  3. Create an input file called “input.txt” and place in the directory you created in step 1. This input file should list the IP address of the branch site router and the OpsMgr computer group name that corresponds to that site.  Separate these two bits of information with a “~”.  Each new IP address and computer group name should be on a new line.  Ensure there is not a blank line at the end!  The input file should look like this:

192.168.1.254~Houston Branch Site

192.168.2.254~Austin Branch Site

etc…

4.    Schedule a task on the management server to run the BranchSiteMonitoring script as often as you like.  Keep in mind the idea is to “catch” the site outage before OpsMgr does so the script can place the end points at the unavailable site in maintenance mode BEFORE getting a bunch of unneeded alerts.  I would start out with running it once every 5 minutes.

Cowardly Disclaimer:

Use this solution at your own risk.  I’ll take responsibility for my actions (like running scripts on my corporate network written by people I don’t know) and you can take responsibility for yours <g>.

The Script:

Big thanks to dm_4ever for his WMI ping function (you thought I was kidding about “re-utilization!).  If the WMI version gives you grief you can try his non-WMI version instead.

 Script Download