I am very excited as I finally got my home lab complete! Check it out and tell me what you think.
Archive for the ‘Technology’ category
Home Lab…Complete!
July 30th, 2010Windows 2008 R2 – File Management Tasks
March 10th, 2010There is a really neat post on Adi Oltean’s Blog concerning the use of Windows 2008 R2′s File Management Tasks (FMTs) to archive data. He uses a “Custom” task type and refers to a batch script which moves files and leaves a symbolic link. This is sort of a “poor man’s” HSM solution, admittedly, but still pretty cool and instructive as to what can be accomplished with FMTs (anything you can script!). His script is below:
if exist "c:\protected\%~pnx1" @echo Target file already exists! & goto :EOF md "c:\HSM\%~p1" move %1 "c:\protected\%~p1" mklink %1 "c:\protected\%~pnx1"
If you are wondering (like I was) what the heck “%~p1″ or “%~pnx1″ means, check out this site.
I set up a similar FMT in my test environment to learn about this technology, but I had problems with Adi’s batch script. It was hard to debug because it has no logging, so I wrote my own in VBS with lots of logging. You can download my script here. Here is how I set up my FMT.
1. On the General tab enter a task name and the Scope. The scope is where the FMT run against.
2. On the Action tab choose “Custom” for the Type. For the Executable section enter the path to cscript.exe. In the Arguments section enter the path to the script (download above), and choose the [Source File Path] variable to insert. Command Security should be set to Local System.
3. On the Condition tab place a check mark inside the Days since file was last accessed box. Note the property conditions section of this tab. Here you can add file classifications you have previously set up. For a great read on Microsoft’s File Classification Infrastructure read the Storage Team’s blog about it. Note, if you want to use “Last Accessed” time for your FMT as I did you need to enable the tracking of this attribute. This is done by setting HKLM\System\CurrentControlSet\Control\FileSystem\NtfsDisableLastAccessUpdate to 0 (this value is REG_DWORD). Note that this could impact disk IO performance on busy file servers!
4. On the Schedule tab enter whatever schedule/frequency is desired.
Finally, use a Windows “Touch” utility (download one here) to alter the Last Accessed date of a file that falls within the scope of the FMT you created. Then manually run your FMT (Right-click > Run Now) and if all is well the file you altered should be moved and a symbolic link left in it’s place.
System Center Operations Manager 2007 R2 – How to Install a Gateway Server
March 10th, 2010Installing a Gateway server in your existing OpsMgr environment is a fairly challenging task. When accomplishing this task for a customer I found many resources which helped, but I could not find one that was complete. The process I found which was the most helpful was Brad Hearn’s blog. Much of my documented process comes from his document. Honestly I would never have gotten my gateways operational without Brad’s excellent post so many thanks!
OpsMgr 2007 DMZ-Based Agents Fail to Report to Gateway with Event ID 20070
February 19th, 2010We recently noticed most of our DMZ-based OpsMgr agents were not connecting to their gateway server. On the agent we saw the following event:
Event Type: Error
Event Source: OpsMgr Connector
Event Category: None
Event ID: 20070
Computer: <Computer>
Description: The OpsMgr Connector connected to <domain>, but the connection was closed immediately after authentication occurred. The most likely cause of this error is that the agent is not authorized to communicate with the server, or the server has not received configuration. Check the event log on the server for the presence of 20000 events, indicating that agents which are not approved are attempting to connect.
On the gateway server the following event was being logged:
- Is TCP 5723 open to the gateway server?
- Restart the HealthService
- Is agent in Pending Management?
- Restart the HealthService
- Wait 5 minutes
- Restart the HealthService
All to no avail. We found http://blogs.technet.com/operationsmgr/archive/2009/02/17/opsmgr-2007-port-requirements-for-scom-agents-in-a-dmz.aspx which suggested opening ports 88 and 389 from the agent to the RMS. This did not make sense to us since some agents were working. So we used Netmon 3.3 to trace the client while the HealthService starts. It never used any port but 5723.
We even enabled verbose diagnostic tracing (http://support.microsoft.com/kb/942864) and reviewed the logs. We saw where the 20070 event was being generated but not much interesting besides that:
5412.5956::02/19/2010-10:46:56.978 [Common] [] [Verbose] :Common::EventLogUtil::LogEvent{EventLogUtil_cpp311}Logging error event 20070 with args “<servername>”, “NULL”,”NULL”, “NULL”, “NULL”, “NULL”, “NULL”, “NULL”, “NULL”
5412.5956::02/19/2010-10:46:56.978 [Common] [] [Information] :Common::EventLogUtil::LogEvent{EventLogUtil_cpp397}Logging event 20070 from source “OpsMgr Connector” with severity Error and description “The OpsMgr Connector connected to <GatewayServer>, but the connection was closed immediately after authentication occurred. The most likely cause of this error is that the agent is not authorized to communicate with the server, or the server has not received configuration. Check the event log on the server for the presence of 20000 events, indicating that agents which are not approved are attempting to connect.”.
Solution…
We finally had to call Microsoft. After about 30 minutes of troubleshooting the engineer saw that the OpsMgrConnector.Config.xml file in the C:\Program Files\System Center Operations Manager 2007\Health Service State\Connector Configuration Cache\<MgmtGrpName> folder on the gateway server was last modified several weeks ago. He had us rename the Health Service State folder under C:\Program Files\System Center Operations Manager 2007 and restart the HealthService. After this a new Health Service State folder was created and the OpsMgrConnector.Config.xml had a much more current last modified date. We then restarted the HealthService on the agents and they reported in to the gateway server correctly.
Extend System Center Configuration Manager 2007 to Collect Fibre Channel HBA Information – Revised
January 6th, 2010Version 2…
In my original post on this subject I described how to set up ConfigMgr to grab data from the MSFC_FCAdapterHBAAttributes class under Root\wmi. Later I found out that, as least in my environment, the data in this class was unreliable. It would literally be there one day and the next it would have disappeared. So I had to change my approach. This new method of getting HBA info into ConfigMgr is not exactly elegant, but it is effective. Use it at your own risk! The process is as follows:
- Install Microsoft’s FCInfo utility on your machines with HBAs. I packaged this up and deployed to a collection populated with these machines. Note that the install will throw an error on Windows Server 2008 but the utility still works and it MUST be present for this process to work!
- Update sms_def.mof to look for HBA info in a new class called Custom_HBA_Info (later steps actually create this class). Add the below text to your sms_def.mof (always make a backup first!).
#pragma namespace ("\\\\.\\root\\CIMv2\\sms")
[SMS_Report(TRUE),
SMS_Group_Name("Custom_HBA_Info"),
SMS_Class_ID("Microsoft|Custom_Frost_HBA_Info|1.0"),
Namespace("\\\\\\\\.\\\\root\\\\CIMv2")]
class Custom_HBA_Info : SMS_Class_Template
{
[SMS_Report(TRUE), key ]
string Adapter;
[SMS_Report(TRUE)]
string NodeWWN;
[SMS_Report(TRUE)]
string PortWWN;
[SMS_Report(TRUE)]
string SerialNumber;
[SMS_Report(TRUE)]
string DriverName;
[SMS_Report(TRUE)]
string DriverVersion;
[SMS_Report(TRUE)]
string FirmwareVersion;
[SMS_Report(TRUE)]
datetime DateCollected;
};
Almost immediately after saving the sms_def.mof file ConfigMgr will compile it. You can monitor the success/failure of this in the dataldr.log file on the ConfigMgr server (<install_dir>\Logs).
3. Download my script (rename to GetHBAInfo.vbs) to a share on your network. The script creates the WMI class referenced above, runs fcinfo.exe, grabs the output, and populates it into Custom_HBA_Info.
4. Create a Software Distribution package for the script. Create a Program for the package with the following command line: %systemroot%\system32\cscript.exe GetHBAInfo.vbs
5. Schedule a recurring advertisement associated with a collection containing all your machines with HBAs. My advertisement runs once per week.
Thats it. Now, to be sure it is working:
1. Run your advertisement so that the script runs on your agents with HBAs.
2. On one of those computers use wmic to validate the presence of the namespace. Command syntax:
wmic path Custom_HBA_Info
If an Invalid Class error is displayed then either the script has not run or it has an error. The script logs it’s results under %windir%\Temp in a file that starts with GetHBAInfo_<date>_<time>.log.
3. Force a couple of agents to run their hardware inventory. Monitor the dataldr.log on the site server. You should see lots of data flying by as the agent updates it hardware info. When complete search for the string “Custom_HBA_Info”. If you find it then the data should have gotten put in your site database. Look for a table in the database called dbo.Custom_HBA_Info_Data. If it exists then you should be golden.
4. Use Resource Explorer to view one of your machines with HBAs. Under Hardware you should see Custom_HBA_Info, and your data should be there.
Special thanks to my co-worker Jason Sandys, a ConfigMgr MVP. Without his assistance I would not have gotten this to work.
Return HBA World Wide Name from WMI
December 16th, 2009I searched quite a bit looking for a way to get an FC card’s WWN from WMI. I found some information indicating it might be in the root\WMI namespace so I used Scriptomatic 2.0 to look through the classes in that namespace until I finally found it. It was in QL_FibrePortNPIVAttributes (I assume this classis added by the QLogic driver installation…yeah the QL gave it away…).
Unfortunately the numbers of the WWN are returned in decimal rather than in hex, the digit groups are returned separated by commas, and single digits are used when the number is below 10. So it looks like this:
WWPN: 80,6,11,0,0,194,154,2
Well, we can’t have that! So I wrote a script to grab the information and convert it hex, group digits with a colon and “pad” an extra zero if the decimal value was under 10. Here is the script:
On Error Resume Next
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20
arrComputers = Array(".")
For Each strComputer In arrComputers
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\WMI")
Set colItems = objWMIService.ExecQuery("SELECT * FROM QL_FibrePortNPIVAttributes", "WQL", _
wbemFlagReturnImmediately + wbemFlagForwardOnly)
For Each objItem In colItems
var1 = objItem.WWPN
For i = 0 To UBound(var1)
If len(Hex(var1(i))) < 2 then
var2 = "0" + Hex(var1(i))
Else
var2 = Hex(var1(i))
End If
tmpVar1 = tmpVar1 & ":" & var2
tmpVar2 = mid(tmpVar1, 2)
Next
wscript.echo tmpVar2
tmpVar1 = ""
tmpVar2 = ""
Next
Next
This returns a beautifully formatted (yes I am a geek) WWN…observe:
50:06:0B:00:00:C2:9A:00
Printer Migration Client Script
November 3rd, 2009If 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. NOTE: Don’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.
Server Inventory Script
August 5th, 2009As a consultant, many times I need to quickly gain information about the Windows servers in an environment. I’ve finally put something together to try and make my life easier. It is a script that opens Microsoft Excel, sets column definitions that seem useful to me, then populates the spreadsheet with a bunch of data. The data is gathered from every Windows server that is a member of the Active Directory domain you specify. Specifically, it gathers:
- Server Name
- Whether the server is online (ping’able) or not
- OS
- Service Pack
- Manufacturer
- Model
- Serial Number
- Whether it is a domain controller
- Whether it is a DNS server
- Whether it is a DHCP server
- Whether it is a WINS server (yes there are still a few of those)
- Whether it is a MSCS cluster node
- Whether it is an Exchange server
- What applications are installed (excluding Security Updates and Hotfixes)
- Whether WMI could be connected to
Dependencies
- Microsoft Excel (any later version is fine)
- Sysinternals PSTools
- Admin rights on every server in the domain (Domain Admin rights would be easiest)
I wanted to use WMI exclusively but unfortunately it is very unreliable, therefore the script heavily depends on 2 PSTools (PSInfo and PSService). You will need to download those and place them in a directory on the PC you run the script from.
Edits
The only lines you need to edit are 15 and 16, where you enter in your domain name (follow the format example!) and the path to your PSTools directory.
'your domain below strDom = "DC=ACME,DC=COM" pstoolsPath = "c:\Tools\pstools"
You can download the text of the script here or a zip file with the script here. I hope others find it helpful. As always, use this solution at your own risk. If you have problems with the script, comment on the post and I will help when I have time.
How To Use Windows 2008 to Host Storage for ESX
July 8th, 2009Disclaimer
Let me stress before I begin that the below configuration is for testing only. It involves insecure settings on the Windows 2008 server so do not use this information for a production solution.
Background
In an effort to learn about ESX 4.0, or vSphere, I set up 4 VMs within VMWare Workstation 6.5.2 using the below configuration:
- 2 VMs to run ESX 4.0. I used the instructions at xtravirt.com (get them here) to create the VMs.
- 1 VM to run vCenter server.
- 1 VM to serve as a domain controller. vCenter cannot be installed on a domain controller, hence the need for another VM. By the way, you don’t need a domain environment for vSphere, but I wanted one to test with.
All my installs went fine. However problems arose when attempting to use the vCenter server (OS: Windows 2008) as my NFS server. I had used Windows 2003 in the past with no issue for a similar set up so I was stuck for awhile. Google searches revealed others with the same issue but no solutions (that I could find). I’m happy to report I did finally get it working…steps below. The following assumptions apply:
- You have already added the File Server role with the Services for Network File System (NFS) role service and that you have an NFS share created. For a walk-thru of this go here.
- Your ESX host’s have a VMKernel port properly configured to allow access to the NFS server. Confirm the configuration is correct by vmkping’ing the NFS server from the host itself.

Steps to enable ESX hosts to access NFS share on Windows 2008.
- Edit the NTFS permissions of the shared folder to allow the ANONYMOUS LOGON group Full Control.
- On the NFS Sharing tab click the Manage NFS Sharing button. Check the Allow anonymous access box.
- Click the Permissions button. In the NFS Permissions dialog click Add. Select Host and enter the IP address of your first ESX host. Set the permissions to Read/Write and select the Allow root access box and click OK. Repeat the proceedure for your other ESX host.
- Click OK three times then Close. If a warning pops up about how insecure this configuration is, clickYes. Remember, this is for testing only.
- Open the Local Security Policyeditor (Start>Administrative tools>Local Security Policy), expand Local Policies, and click Security Options.
- Locate the Network Access: Let Everyone permissions apply to anonymous userspolicy and make sure it is set to Enabled.
- Reboot the NFS server. When it comes back up you should be able to mount the NFS share from the ESX host(s) within vCenter.
Re-Use Your (or someone else’s) VBScript Code
June 20th, 2009How 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!













