Skip to content


Verifying a management pack using the SCOM SDK

So I came across quite a problem when I lost one of the MP files for one of my custom management packs.  I had a new, updated version, but how do I verify upgrade compatibility without the file?

In a normal situation when you have both of the files, you can use the mpverify.exe utility to do so.  That would look something like this:

mpverify managementpack.xml /I C:\MyManagementPackDir /CheckUpgrade C:\OldMPs\OlderVersionOfMP.xml

I was in quite a quandry because I lost the older version. However, there is an option!

Using the Microsoft.EnterpriseManagement.Configuration namespace, there is a handy class called ManagementPack, which in turn has a member called CheckVersionCompatibility()

At first, I was a little puzzled, as this method can only check the instance of ManagementPack against an OLDER version of itself.

Soon after I realized the constructor for ManagementPack can take a file path. Looks something like this:

$newmp = [Microsoft.EnterpriseManagement.Configuration.ManagementPack](“C:\InsertMPNameHere.xml”)

So now I have my updated version in an Object, from which I can call the CheckVersionCompatibility() method, which will verify the managementpack for me, which looks like:

$currentmp = get-managementpack –name “InsertMPNameHere”
$newmp.CheckVersionCompatibility($currentmp)

Sweet! It works! But wait. It tell me it worked, how can I be sure? It returns System.Void?

After consulting with the infamous Jakub@Work, he ensured me that the only time it fails is when it throws a ManagementPackException.

They could have at least told it to return 0 if it worked correctly. Instead the only option is to hope it throws an exception to tell you something is wrong.

So, if you’re going to use ManagementPack.CheckVersionCompatibility(), put it in a trusty try-catch statement so you can handle the exceptions in the way of your choice.

Posted in Powershell, RC1, System Center Operations Manager, V1.


SCOM Event ID 10801 and 33333

Do you have systems logging the following events in your event log on the RMS or another management server?

 

Event Type:      Warning

Event Source:   DataAccessLayer

Event Category: None

Event ID:          33333

Date:                2/25/2009

Time:                12:36:50 AM

User:                N/A

Computer:         MANAGEMENTSERVER

Description:

Data Access Layer rejected retry on SqlError:

 Request: p_DiscoverySourceUpsert — (DiscoverySourceId=8bd389ab-7e6b-fa90-e8fb-a0849678f0a4), (DiscoverySourceType=0), (DiscoverySourceObjectId=7727e469-3a35-fddf-f255-f9404b3383df), (IsSnapshot=True), (TimeGenerated=2/25/2009 8:36:15 AM), (BoundManagedEntityId=3ca98a65-c8be-3f04-6394-ec5ecc85107d), (IsDiscoveryPackageStale=), (RETURN_VALUE=1)

 Class: 16

 Number: 777980004

 Message: Discovery data has been received from a rule targeted to a non-existent entity. The discovery data will be dropped.

 

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

 

Event Type:      Error

Event Source:   Health Service Modules

Event Category: None

Event ID:          10801

Date:                2/25/2009

Time:                12:36:50 AM

User:                N/A

Computer:         MANAGEMENTSERVER

Description:

Discovery data couldn’t be inserted to the database. This could have happened because  of one of the following reasons:

 

            - Discovery data is stale. The discovery data is generated by an MP recently deleted.

            - Database connectivity problems or database running out of space.

            - Discovery data received is not valid.

 

 The following details should help to further diagnose:

 

 DiscoveryId: 7727e469-3a35-fddf-f255-f9404b3383df

 HealthServiceId: 778a7339-3866-9639-eb1d-60e29a16962d

 Discovery data has been received from a rule targeted at a non-existent monitoring object id.

MonitoringObjectId: 3ca98a65-c8be-3f04-6394-ec5ecc85107d

RuleId: 7727e469-3a35-fddf-f255-f9404b3383df.

 

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

 

If so, run the following query on your Operations Database for find what machines are causing events 10801 and 33333:

 

Select * from dbo.BaseManagedEntity where BaseManagedEntityID = ‘<GUID>’

 

So if your event 10801 contained the following:

 

DiscoveryId: 7727e469-3a35-fddf-f255-f9404b3383df

 HealthServiceId: 778a7339-3866-9639-eb1d-60e29a16962d

 Discovery data has been received from a rule targeted at a non-existent monitoring object id.

MonitoringObjectId: 3ca98a65-c8be-3f04-6394-ec5ecc85107d

RuleId: 7727e469-3a35-fddf-f255-f9404b3383df.

 

You would take the HealthServiceID and plug it into the query.

 

select * from dbo.BaseManagedEntity where BaseManagedEntityID = ‘778a7339-3866-9639-eb1d-60e29a16962d’

 

This would give you the following result:

 

778A7339-3866-9639-EB1D-60E29A16962D        58295   AB4C891F-3359-3FB6-0704-075FBFE36710            Microsoft.SystemCenter.HealthService:AGENTSYSTEM.DOMAIN.AD      AGENTSYSTEM.DOMAIN.AD    NULL            AGENTSYSTEM.DOMAIN.AD    4594FC6A-5F20-6BC3-764C-EF874846EB4F       1          0          2008-11-04 04:44:58.197     2008-11-04 04:34:02.147

 

 

(I am still researching how to resolve this and will update when done. Also, this information was kindly passed onto me by Frank at MS.)

Posted in RC1, System Center Operations Manager.


Powershell, Log Files, and log4net! Oh my!

I decided that the logging I was doing in my scripts was not good enough, so I started to learn log4net. Now that posed a problem.

 

Log4Net is not exactly built for powershell, but it is .Net capable.

The following is my test-log script which requires the .Net 2.0 log4net.dll and log4net.config (XML File) to be in the run directory:
# ==============================================================================================
#
# Microsoft PowerShell Source File -- Created with SAPIEN Technologies PrimalScript 2007
#
# NAME:
#
# AUTHOR: Jason Rydstrand
# DATE  : 2/14/2009
#
# COMMENT: This is a work in progress to show off the ability of log4net in powershell.
#   Currently this requires the log4net.dll and the log4net XML config file to
#   be in the same directory as the script.
#
# ==============================================================================================

# Clearn $error so that previous errors are not logged.
$error.Clear()

# Get the currentPath by getting full path and spliting the \> off the end.
$currentPath = Split-Path (Get-ChildItem (Get-Variable MyInvocation).Value.MyCommand.Path).FullName

# Create full path to the log4netDll
$log4netDll = Join-Path $currentPath "log4net.dll"

# Load the log4netDll Reference
[Void][System.Reflection.Assembly]::LoadFile($log4netDll)

# Reset any log4net configuration to defaults.
[log4net.LogManager]::ResetConfiguration()

# Set the config file name
$log4netConfigName = "log4net.config"

# Create full path to the log4net config file
$configFile = Get-ChildItem ( Join-Path $currentPath "log4net.config")

# Load the log4net xml config file
[log4net.Config.XmlConfigurator]::ConfigureAndWatch($configFile)

# Initialize the logg manager for this host.
$log = [log4net.LogManager]::GetLogger($Host.GetType())

# Check if Debug is Enabled in the config file. If Debug is Enabled, create a debug message.
if($log.IsDebugEnabled)
{
$log.debug("Debug Enabled!")
}

# Setup a trap for all exceptions.

trap
{

    $Log.Error("Failed with the following exception:  " + $error)
    # Clear the error variable so next trap won't repeat the exception.
    $error.Clear()
   #return/continue/break
   continue
}

# Create differet message levels for Info and Debug
$log.Info("Start of code.")
$log.Debug("We are debugging!")

# Run a invalid command to test trap to log a Error
foo-bar

# Log a warning. "Still need to learn how to do different trap levels."
$log.Warn("We are warning!")
$log.Info("End of code.")

# Shutdown log4net so log file can be manipulated.

[log4net.LogManager]::Shutdown()

 
The following is the contents of my log4net.config file:
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<!-- FileAppender Values -->
<file value="C:\Powershell\testing\" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<staticLogFileName value="false" />
<datePattern value="yyyy\-MM\-dd\-\l\o\g\t\e\s\t\.\l\o\g" />

<!-- Console uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<!-- Print the date in ISO 8601 format -->
<conversionPattern value="%newline%date [%thread] %-5level [%x] - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>

What this does is setup a RollingLogFileAppender that will roll the log file once a day. The log file will be named year-month-day-logtest.log. As you can see in my scripts, I currently use c:\powershell\testing as my working directory, so you will need to change this to what you use.

My goal at this point is to figure out how to create a date/time log file name that will create a new file on each run with out rolling it on the date. There are many other examples on how to do a Console Log, and I have used them, but was unable to figure out how to convert them into a true log file output.

Posted in Powershell, V1, V2.


SCOM Agent Health – Script re-created to not require Excel

Well, I found this great script to create a spread sheet of all the agents and there health state from SCOM.

Problem was it required Excel to be on the machine that is running it, and you couldn’t define what Management Servers to pull for.

I plan on re-writing this in the next day or so to allow for command line arguements.

So here is my rendition of it:  (EDIT: I completely re-did this to add a couple of things.)

  1. Functions are now broken out for PingServer and CheckHealthService.
  2. Using stringbuilder instead of $output = $output + something (cleaner code)
  3. Added a if statement that wouldn’t run the CheckHealthService if PingServer failed.

 
# ==============================================================================================
#
# Microsoft PowerShell Source File — Created with SAPIEN Technologies PrimalScript 2007
#
# NAME: Check-AgentHealth
#
# AUTHOR: Jason Rydstrand
# DATE  : 2/9/2009
#
# COMMENT: This is used to check the health of Scom Agents
#
# CREDITS: Original Script can be found at:
# http://myitforum.com/cs2/blogs/yli628/archive/2008/07/10/powershell-script-to-check-opsmgr-agents-service-and-healthstate.aspx
#
# ==============================================================================================
# Load Assembly

[System.Reflection.Assembly]::LoadWithPartialName(’system.serviceprocess’)

# Set reaction to errors

$erroractionpreference = “SilentlyContinue”

# Declare Functions

Function PingServer
{
$ping = new-object System.Net.NetworkInformation.Ping
$Reply = $ping.send($strServer.Computername)
if ($Reply.status –eq “Success”)
{
[void]$strBld.Append(“,Online”)
}
else
{
[void]$strBld.Append(”,Offline”)
}
}

Function CheckHealthService
{
$HealthService = [System.ServiceProcess.ServiceController]::GetServices($strServer.Computername) | where{$_.name -eq ‘HealthService’}
If ($HealthService.status -eq “Running”)
{
[void]$strBld.Append(”,Running”)
}
ElseIf($HealthService.Status -eq “Stopped”)
{
[void]$strBld.Append(”,Stopped”)
}
Else
{
[void]$strBld.Append(”,Not Sure”)
}
}
# Initialize the String Builder

$strBld = New-Object System.Text.StringBuilder

# Set Header Row

[void]$strBld.Append(”Machine Name,Ping Status,Health Service Status,Health State,Primary Management Server`r`n”)

#Start

#get all the agent managed servers from OpsMgr

$ColServers = get-agent | where {($_.ComputerName -like “SERVER1*”) -or ($_.PrimaryManagementServerName -like “MANAGEMENTNODE2*”) -or ($_.PrimaryManagementServerName -like “MANAGEMENTNODE3*”)}

foreach ($strServer in $colServers)
{

[void]$strBld.Append($strServer.ComputerName.ToUpper())

# Ping each server

PingServer

#Check if the HealthService is running if ping worked

if ($strBld.ToString() -match “,Online$”)
{
CheckHealthService
}

else
{
[void]$strBld.Append(”,Failed Ping”)
}

# get the HealthState of the agents

[void]$strBld.Append(”,” + $strServer.HealthState)

# get the Primary Management Server
# Edit the Second Line to remove the last part of the FQN if you wish, otherwise comment it out.

$primaryManagementServer = $strServer.PrimaryManagementServerName.ToUpper()
$primaryManagementServer = $primaryManagementServer -replace(”.FQN”,”")

[void]$strBld.Append(”,” + $primaryManagementServer + “`r`n”)

}

$strBld.ToString() | Out-File -encoding UTF8 -FilePath C:\test.csv

#End
 

 

Posted in Powershell, RC1, System Center Operations Manager, V1.


Search Active Directory and Return All Users with Information

The following script is fairly well commented. I thank the userserv of #Powershell on Freenode for helping me troubleshoot this, and one user (twometertwo) who wrote the baises of code for this.
# ==============================================================================================
#
# Microsoft PowerShell Source File -- Created with SAPIEN Technologies PrimalScript 2007
#
# NAME: Get-ADUsers
#
# AUTHOR: Jason Rydstrand
# DATE  : 2/6/2009
#
# COMMENT: This script is to Poll AD for a list of all users and their contact information.
#     For more information on Searching AD in Powershell:
#     http://www.microsoft.com/technet/scriptcenter/topics/winpsh/searchad.mspx
#
# ==============================================================================================

# First we setup the filter we are going to pass to AD ( Options: objectCategory, Department )
# This is also where you can narrow your filter down to specific users or groups.

$strFilter = "(&(objectCategory=User))"

# Initialize a AD Object to be worked with.
 
$objDomain = New-Object System.DirectoryServices.DirectoryEntry

# Initialize a Object to Search AD
 
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher

# Set the Properties of the Searcher Object
# SearchRoot: Sets the Searcher Root to the AD Object

$objSearcher.SearchRoot = $objDomain

# PageSize: This sets how many results to pull before starting display.

$objSearcher.PageSize = 1000

# Apply the Filter as a property to the Searcher Object

$objSearcher.Filter = $strFilter

# Sets the SearchScope to parse through the tree.

$objSearcher.SearchScope = "Subtree"
 
# Run the Searcher Object for all results and assign it to the variable of $a

$a = $objSearcher.findall()

# Select what to display from the result set stored in the $a variable, then export it to CSV with no Object Header Type.
# In this section you can add more fields by duplicating one of the @{Name lines and changing the properties.
# If you want to display the results to screen, remove "| Export-Csv c:\testoutpute.csv -notype"

$a | select `
@{Name = "User Name"; Expression = {$_.properties.name}},`
@{Name = "Title"; Expression = {$_.properties.item("Title")}}, `
@{Name = "Email"; Expression = {$_.properties.item("mail")}}, `
@{Name = "Office Number"; Expression = {$_.properties.item("telephonenumber")}}, `
@{Name = "Mobile Number"; Expression = {$_.properties.item("Mobile")}}, `
@{Name = "Home Number"; Expression = {$_.properties.item("homephone:")}}, `
@{Name = "Pager"; Expression = {$_.properties.item("Pager")}} | Export-Csv c:\testoutput.csv -notype

 

Posted in Powershell, V1.


Create Event with Custom Information

This is primarly for Powershell V1 as V2 has a Write-Event or something like that.

I will come back and clean this up at some point along with the other post.
# Variables

$EventTarget = "."
$EventLogTarget = "System"
$EventSource = "Service Control Manager"
$EventText = "This is a test event"
$ErrorLevel = "Error"
$EventId = 4506

# Code to create event from variables

$EventLog = New-Object System.Diagnostics.EventLog($EventLogTarget)
$EventLog.MachineName = $EventTarget
$EventLog.Source = $EventSource
$EventLog.WriteEntry($EventText,$ErrorLevel,$EventID)

Posted in Powershell, V1.


Rename Multiple Files

I will come back and explain this tonight.

For now here is the code block.
foreach ($file in Get-ChildItem -Recurse) { if ($file.Name -match "\[test\]") { Move-Item -literal $file.Name $($file.Name -replace "\[test\]", "") }}

 

Posted in Powershell, V1, V2.


Powershell script to check drive space

I found this on the Black Mountain blog by sepeck and felt I needed to log it on my own site as a reminder to myself.
# Function to get and calculate drives and free space
# Defaults to localhost if no following name is entered
function Get-DriveSpace([string]$SystemName = "localhost") {
$driveinfo = get-wmiobject win32_logicaldisk -filter "drivetype=3" -computer $SystemName
$driveinfo | select deviceid, `
@{Name="FreeSpace";Expression={($_.freespace/1GB).tostring("0.00")}}, `
@{Name="DriveSize";Expression={($_.size/1GB).tostring("0.00")}}, `
@{Name="Percentfree";Expression={((($_.freespace/1GB)/($_.size/1GB))*100).tostring("0.00")}}
}

Posted in Powershell, V1, V2.


Powershell Exit Codes

An interesting question was posed today on the #powershell channel on Freenode of how to pass exit codes from powershell.

The user was trying to pass them to a command shell, and the %errorlevel% was not being set.

The following is a example of how to do this (saved in file C:\testing.ps1):
$condition = 1

if ($condition -eq 1)
{
  exit 33
 }

Write-Host "Did not set"
To run the script and have the errorlevel passed to the CMD Console:
C:\>powershell -Command "& { c:\testing.ps1; exit $LASTEXITCODE }"
To check that it set:
C:\>echo %errorlevel%
This one will also work in powershell by just running the script with:
PS C:\>.\testing.ps1
To check that it set:
PS C:\>$LASTEXITCODE
There are other methods of doing this, but those methods will either run the Write-Host every time, or will close the powershell console when ran inside powershell itself.

One thing too add. The code at the top technically is bad form. The proper way to write it would be as follows:
function DoSomething
  {

      $condition = 1

      if ($condition -eq 1)
          {
              exit 33
          }

      else
          {
               Write-Host "Did not set"
          }

  }

DoSomething
By writeing it this way, you can call your function that has error code capability and see that it failed on that function, and it will exit before running the next function.

Example:
function DoSomething
  {

      $condition1 = 1

      if ($condition1 -eq 1)
          {
              exit 33
          }

      else
          {
               Write-Host "`$condition1 did not meet."
          }

  }

function DoSomethingElse
  {

      $condition2 = 1

      if ($condition2 -eq 1)
          {
              exit 34
          }

      else
          {
               Write-Host "`$condition2 did not meet."
          }

  }

DoSomething

DoSomethingElse

Write-Host "`$condition1 and `$condition2 did not meet."
If you change $condition1 to something other then 1, the next function will run and exit out. If you also change $condition2 to something other then 1, both the else statements will run, and the last Write-Host will run.

The Exit code if it runs all the way through will be set to 0. Meaning a good run.

Posted in Powershell, V1, V2.


Yet another Powershell and SCOM Blog

Hello everyone.

I know there are tons of Powershell blogs out there, and there are even tons of SCOM blogs out there.

So why am I creating one of my own?

Two reasons actually.

1. To have a centeral place to remind myself what I have forgotten over time.
2. To share what I have learned with others who could need it.

As this site is still being setup, I will leave this post here.

Posted in General.