Random Post: Starting off easy...
RSS .92| RSS 2.0| ATOM 0.3
  • Home
  • About
  •  

    Issues with integrating WSUS and SCCM…

    March 17th, 2009

    While rolling out SCCM I recently ran into a little issue with WSUS integration that stumped me for a bit so I thought I’d post what I was doing incorrectly which will hopefully save someone else the troubleshooting time I spent.

    The architecture is basically a primary parent site server that maintains no clients and is used mainly for reporting and collecting the SCCM hierarchical data. Next, we have three primary child sites and each of these sites will in turn have secondary sites for the branch offices. We already had an existing WSUS implementation outside of the SCCM deployment so I planned on mimicking that layout when configuring the WSUS update synchronization hierarchy.

    In order to maintain segregated functionality of the primary parent site, I configured one of the primary child sites as the top level upstream synchronization server with Microsoft. Next, I configured the remaining two primary children as downstream servers for the WSUS updates. Here was the mistake.

    On the two primary children servers configured as downstream servers, I continuously received Sync failed: WSUS server not configured in the wsyncmgr.log. I referred back to the sync settings in both the SCCM console and the WSUS console and everything was configured correctly from what I could tell. The problem wasn’t with my settings, it was with my architecture. When WSUS is integrated into SCCM, the WSUS hierarchy is rendered invalid in favor of the SCCM replication hierarchy. Basically, the upstream/downstream relationship for WSUS is now directly related to the parent/child relationship in SCCM. In order to fix the problem, I had to move the upstream synchronization server to the primary parent server and configure all three primary children as downstream servers.

    Once, I made this change, I re-ran the WSUS configuration wizard from the WSUS console and restarted the SMSEXEC service on each primary site server and everything started synchronizing as it should.

    I also noticed that after making the change on the primary child server that was previously configured as the upstream server, I was receiving a substantial number of alerts in the smsdbmon.log with the entry: Warning: Could not write to the named pipe \\<SERVERNAME>\PIPE\SMSPROVIDER_<SITENAME>, Error=2 where <servername> is the name of the local server and <sitename> is the name of the primary site for that server. I reviewed all of the SQL configuration settings and permissions; however, the final fix was to simply re-run the WSUS configuration utility from the WSUS console again and the errors stopped.

    SCCM_WSUS_Hierarchy


    Returning HP Serial Numbers Using SNMP…

    December 30th, 2008

    I was recently asked if I knew of a automated way to get the serial numbers for our ESX hosts. Since they are Linux OS, the Win32_BIOS WMI namespace just went out the window. My next thought was to use HP SIM (Systems Insight Manager) alas, no serial numbers were stored for these hosts in the repository. It was starting to look like manually logging on to the System Management homepage for each server was going to be the way. Umm… no. I would rather use the time it would take to log on to each server (over 80 in this case) to automate the process for future use because we all know that someday someone else will ask for the same information.

    Enter SNMP. Since we are working with a Linux platform, I figured SNMP would be the best bet so I simply needed to find a way to connect. Net-SNMP to the rescue. The first order of business was to find the proper node in the SNMP tree where the serial number was stored. To do this, I used the snmpwalk.exe utility from Net-SNMP. The quickest way I found is to simply walk the entire tree; however now that I’ve done that and dumped a LOT of information, I’ll save you some time and target the proper object node with the following syntax:

    snmpwalk.exe -v 2c -c public servername enterprises > C:\snmpoutput.txt

    In this example, -v 2c tells snmpwalk to use SNMP version 2 and -c public stipulates the read community string to use when walking the OID tree. The enterprises OID is the starting point OID to begin walking all subtrees rooted underneath. Just for reference, the OID defined for enterprises by HP is .1.3.6.1.4.1; however, the MIB file has definitions that allow us to simply use the friendly name of enterprise.

    Once you have obtained the output file, search through the results for 2.2.2.5 which is the OID for the string that maintains the serial number of the server. Now that we have the entire OID for the node, we can use the snmpget.exe utility to directly target the OID tree node we want instead of enumerating everything under the enterprise node such as:

    snmpget.exe -v 2c -c public servername .1.3.6.1.4.1.232.2.2.2.5.0

    Another syntax that is a little easier on the eyes would be:

    snmpget.exe -v 2c -c public servername SNMPv2-SMI::enterprises.232.2.2.2.5.0

    which is the same as the previous command only using the defined enterprises name rather than the full OID. Now, this is all good and well for a single ESX host, but what about several? I wanted to use PowerShell to wrap up the automation functionality, but there were no .NET classes available for SNMP and using a custom assembly from CodePlex or CodeProject was a little bit of overkill just for a simple automation task so I decided to wrap it up in good old VBScript as shown below:

    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objShell = CreateObject("WScript.Shell")
    Set infile = objFSO.OpenTextFile("C:\Scripts\VBScript\ESXSerials\ESXHosts.txt")
    Set outfile = objFSO.OpenTextFile("C:\Scripts\VBScript\ESXSerials\ESXSerials.csv", 2, True)
     
    Do While Not infile.AtEndOfStream
    	str = infile.ReadLine()
    	WScript.Echo "Querying " & str
    	output = str
    	Set objExecObject = objShell.Exec("cmd /c D:\Net-SNMP\bin\snmpget.exe -v 2c -c public " & str & " SNMPv2-SMI::enterprises.232.2.2.2.5.0")
    	Do While Not objExecObject.StdOut.AtEndOfStream
    	    strText = objExecObject.StdOut.ReadLine()
    	    If Instr(strText, "232.2.2.2.5.0") > 0 Then
    	        output = output & ", " & Right(strText, 12)
    	        i=1
    	        Exit Do
    	    End If
    	Loop
    	outfile.WriteLine output
    Loop

    In the above example, I installed Net-SNMP to D:\Net-SNMP on my workstation and the tools extract into the bin folder by default. I also used a list of all the ESX hosts I was targeting in a text infile. I found the easiest way to get this info was to simply export a list of all hosts from VCenter and then mangle the CSV into a text file with only the server names. The rest should be fairly obvious in the script.

    This was one of those weird requests that makes you do a little extra research to come up with a solution. I learned a bit more about SNMP during the process and hope this helps out some others who need to pull some data from ESX.

    The MIB containing the OID information for this particular script was CPQSINFO.MIB; cpqSystemInfo is the top variable. ByteSphere has a great MIB resource area to use for research.


    VMWare Ping Test using PowerShell…

    December 23rd, 2008

    I recently had to come up with a way to verify that virtual server guests on ESX hosts were back online after restarts that were scheduled using the VMWare Infrastructure Client console. Using the VMWare Automation snap-in, I wrote a PowerShell script that will test for basic ping connectivity via WMI and then leverage VMWare cmdlets to attempt to recover any VM’s that are not responding as well as sending an email to recipients when the script fails to bring them online.

    This script was implemented as a scheduled task so I simply hardcoded the values into the script rather than using the param statement since I didn’t plan on calling it from the console; however, it should be easy enough to modify if you wanted to use it interactively. Also, since it is required to connect to the VMServer prior to running any cmdlets, I used Secure-String to generate an encrypted password with a little bit of security since it would be in a text file on the server. To generate the password file, I used the following commands directly in a PowerShell console (second line wrapped):

    $secureString = Read-Host -AsSecureString
    ConvertFrom-SecureString $secureString | out-file C:\Scripts\PowerShell\VMPingTest\acctpass.txt

    As you can see from the script below, it only takes one line of .NET to decrypt the encrypted password from the file so it’s not foolproof, but still better than plain text and NTFS only. I’ve commented the script fairly heavily, so it should be easy to customize for different environments.

    ?View Code POWERSHELL
    #Load VMWare snap in:
    Add-PSSnapin VMware.VimAutomation.Core -ErrorVariable errSnapIn -ErrorAction silentlycontinue
    if ($errSnapIn.Count -ne 0) {
    	Write-Host "VMWare snap in is required."
    	exit
    	}
    #------------------------------------------------------------------------------
    # Set up variables
    #------------------------------------------------------------------------------
    #VM Server hostname
    $VMServer = "MYSERVER.MYDOMAIN"
    #User account to connect to virtual infrastructure:
    $username = "USERNAME"
    #Directory path to secure string password:
    $pass = "C:\Path\to\securestringpass.txt"
    #Directory path to target list of VM's.
    $servers = Get-Content "C:\Path\to\serverlist.txt"
    #Directory path to list of mail recipients.
    $mailrecipients = Get-Content "C:\Path\to\recipients.txt"
    #SMTP relay server
    $smtpserver = "smtp.mydomain.com"
    #Decrypt SecureString password for account:
    $pwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((gc $pass | ConvertTo-SecureString)))
     
    #------------------------------------------------------------------------------
    # Connect to VM Server
    #------------------------------------------------------------------------------
    $server = Connect-VIServer -Server $VMServer -User $username -Password $pwd
    #Search for specific VM in the production cluster:
    $folder = Get-Folder -Name "Financial Hosts" #Target the Finanancial Hosts folder in the VM Infrastructure Client console
    $cluster = Get-Cluster -Name "Dallas HA Cluster" -Location $folder #Target the Dallas HA Cluster in the VM Infrastructure Client console
     
    #------------------------------------------------------------------------------
    # Methods
    #------------------------------------------------------------------------------
    function SendMail
    {
    	param($svr, $status)
    	foreach ($recipient in $mailrecipients)
    	{
    		$mail = new-object System.Net.Mail.MailMessage
    		$mail.Priority = 'High'
    		$mail.From = new-object System.Net.Mail.MailAddress("PINGTEST@MYDOMAIN.COM");
    		$mail.To.Add("$recipient");
     
    		#set the content
    		$mail.Subject = "$svr is not responding...";
    		$mail.Body = "$svr failed to respond to ping tests with error: $status.`n" + 
    		"This process automatically powers on virtual machines and restart virtual guests where the local network is unresponsive. `n" + 
    		"This server has not responded to automated restart tasks and needs additional attention.`n`n"
     
    		#send the message
    		$smtp = new-object System.Net.Mail.SmtpClient($smtpserver); 
    		$smtp.Send($mail);
    	}
    }
     
    #------------------------------------------------------------------------------
    # Main()
    #------------------------------------------------------------------------------
    foreach ($svr in $servers)
    {
    	$result = gwmi -class Win32_PingStatus -filter "Address='$svr'"
    	$results = $result.StatusCode
    	$vm = Get-VM -Name $svr -Location $cluster #Search for specified VM in cluster
     
    	if (($vm.PowerState -eq "PoweredOff") -and ($results -ne 0))
    	{
    		#If the VM is powered down
    		Start-VM -VM $vm.Name #Start VM
    		Start-Sleep -Seconds 60 #Allow time for VM to come online
    	}
     
    	#If the VM is powered on, but guest OS is not responding to pings
    	if (($vm.PowerState -eq "PoweredOn") -and ($results -ne 0))
    	{
    		Restart-VMGuest -VM $vm.Name #Restart VM Guest OS
    		Start-Sleep -Seconds 60 #Allow time for VM to come online
    	}
     
    	#Re-run ping test if server was originally unresponsive.
    	if ($results -ne 0)
    	{
    		$secondresult = gwmi -class Win32_PingStatus -filter "Address='$svr'"
    		$secondresults = $secondresult.StatusCode
    		switch ($secondresults)
    		{
    			0 	  { $pingresults = "Success" }
    			11001 { $pingresults = "Buffer Too Small" }
    			11002 { $pingresults = "Destination Net Unreachable" }
    			11003 { $pingresults = "Destination Host Unreachable" }
    			11004 { $pingresults = "Destination Protocol Unreachable" }
    			11005 { $pingresults = "Destination Port Unreachable" }
    			11006 { $pingresults = "No Resources" }
    			11007 { $pingresults = "Bad Option" }
    			11008 { $pingresults = "Hardware Error" }
    			11009 { $pingresults = "Packet Too Big" }
    			11010 { $pingresults = "Request Timed Out" } 
    			11011 { $pingresults = "Bad Request" }
    			11012 { $pingresults = "Bad Route" }
    			11013 { $pingresults = "TimeToLive Expired Transit" }
    			11014 { $pingresults = "TimeToLive Expired Reassembly" }
    			11015 { $pingresults = "Parameter Problem" }
    			11016 { $pingresults = "Source Quench" }
    			11017 { $pingresults = "Option Too Big" }
    			11018 { $pingresults = "Bad Destination" }
    			11032 { $pingresults = "Negotiating IPSEC" }
    			11050 { $pingresults = "General Failure" }
    		}
    	}
     
    	if ($secondresults -ne 0) 
    	{
    		SendMail $svr $pingresults
    	}
    }

    Create PowerShell Forms… Fast!

    December 19th, 2008

    The guys at Sapien have release a great utility to create PowerShell forms and since it’s free, the price is right as well. It took me 90 seconds to create the form shown below. Granted, it’s a very basic example, but creating forms in PowerShell by leveraging the System.Windows.Forms namespace takes a long time. If you need to create GUI front ends for your scripts, check out this great editor.

    ?View Code POWERSHELL
    #Generated Form Function
    function GenerateForm {
    ########################################################################
    # Code Generated By: SAPIEN Technologies PrimalForms v1.0.1.0
    # Generated On: 12/18/2008 10:04 PM
    # Generated By: Alan
    ########################################################################
     
    #region Import the Assembles
    [reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
    [reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
    #endregion
     
    #region Generated Form Objects
    $form1 = New-Object System.Windows.Forms.Form
    $textBox1 = New-Object System.Windows.Forms.TextBox
    $button1 = New-Object System.Windows.Forms.Button
    #endregion Generated Form Objects
     
    #----------------------------------------------
    #Generated Event Script Blocks
    #----------------------------------------------
    #Provide Custom Code for events specified in PrimalForms.
    $button1_OnClick= 
    {
    $wmi = gwmi -class Win32_ComputerSystem
    $textBox1.text=$wmi.Caption
     
    }
     
    #----------------------------------------------
    #region Generated Form Code
    $form1.Text = 'What''s my computer name?'
    $form1.Name = 'form1'
    $form1.DataBindings.DefaultDataSourceUpdateMode = 0
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 292
    $System_Drawing_Size.Height = 266
    $form1.ClientSize = $System_Drawing_Size
     
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 173
    $System_Drawing_Size.Height = 20
    $textBox1.Size = $System_Drawing_Size
    $textBox1.DataBindings.DefaultDataSourceUpdateMode = 0
    $textBox1.Name = 'textBox1'
    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 60
    $System_Drawing_Point.Y = 123
    $textBox1.Location = $System_Drawing_Point
    $textBox1.TabIndex = 1
     
    $form1.Controls.Add($textBox1)
     
    $button1.TabIndex = 0
    $button1.Name = 'button1'
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 111
    $System_Drawing_Size.Height = 23
    $button1.Size = $System_Drawing_Size
    $button1.UseVisualStyleBackColor = $True
     
    $button1.Text = 'Computer Name'
     
    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 93
    $System_Drawing_Point.Y = 78
    $button1.Location = $System_Drawing_Point
    $button1.DataBindings.DefaultDataSourceUpdateMode = 0
    $button1.add_Click($button1_OnClick)
     
    $form1.Controls.Add($button1)
     
    #endregion Generated Form Code
     
    #Show the Form
    $form1.ShowDialog()| Out-Null
     
    } #End Function
     
    #Call the Function
    GenerateForm