Tag Archives: MimbolovePowershell

A Simplified Way to Send Emails and Mobile Push Notifications in SMA

Written by Tao Yang


For those who knows me, I’m an OpsMgr guy. I spend a lot of time in OpsMgr and I am very used to the way OpsMgr sends notifications (using notification channels and subscribers).

In OpsMgr, I like the idea of saving the SMTP configuration and notification recipients’ contact details into the system so everyone who has got enough privilege can use these configurations (when configuring alert subscriptions).

Over the last few months, I have spent a lot of time on SMA (Service Management Automation). As I started building more and more runbooks and integration modules, I really miss the simple way of sending notifications in OpsMgr. Although there is a built-in PowerShell cmdlet for sending emails (Send-MailMessage), it requires a lot of input parameters, and the runbook author needs to have all the SMTP information available. I thought it would be nice if I could save SMTP settings as connection objects (similar to notification channels in OpsMgr), and recipients’ contact details (email and mobile device push notification services’ api keys) also as connection objects (similar to subscribers in OpsMgr).

To achieve my goals, I have created 2 SMA Integration modules:

Module Name Connection Type Name PowerShell Functions
SendEmail SMTPServerConnection Send-Email
SendPushNotification SMAAddressBook Send-MobilePushNotification

SendEmail Module

This module defines a connection type where can be used to save all SMTP related information:

  • SMTP Server address
  • Port
  • Authentication Method (Anonymous, Integrate or Credential)
  • User name
  • Password
  • Sender Name
  • Sender Address
  • UseSSL (Boolean)




This module also provides a PowerShell function called “Send-Email”. Since when retrieving an automation connection in SMA, a hash table is returned, Not only you can pass individual SMTP parameters into the Send-Email function, you can also simply pass the SMA connection object that you have retrieved using “Get-AutomationConnection” cmdlet. for more information, please refer to the help topic of this function, and the sample runbook below.

SendPushNotification Module

This module provides a connection type called SMAAddressBook. It can be used like an address book to store recipient’s contact details:

  • Display Name
  • Email Address (optional)
  • NotifiyMyAndroid API Key (optional, encrypted)
  • Prawl (iOS push notification) API Key (optional, encrypted)
  • NotifyMyWndowsPhone API Key (optional, encrypted)



This module also provides a PowerShell function called Send-MobilePushNotification. It can be used to send push notification to either Prawl, NotifyMyAndroid or NotifyMyWindowsPhone.

Sample Runbook

As you can see from this sample, the runbook author does not need to know the SMTP server information (including login credentials), nor the contact details of the recipient. The runbook can simply pass the SMTP connection object (PowerShell Hash Table) into the Send-Email function.

After I executed this runbook, I received the notification via both Email and Android push notification:




Please download from the download link below. Once downloaded, please import the zip files below into SMA:


Download Link

Related Posts

OpsMgr Alerts Push Notification to iOS (And Android, And Windows Phone) Devices

Authoring Integration Modules for SMA


As shown in the sample above, once the SMTP details are saved in SMTP connection objects, and recipients’ contact details are saved as SMAAddressBook connections, it is really simple to utilise the functions provided by these 2 modules to send notifications.

Also, I’d like to point out I had to create 2 integration modules instead of 1 because I need to create 2 kinds of connections. Having said that, these 2 modules do not depend on each other and can be used separately too.

As many people referring to SMA modules and runbooks as Lego pieces, I will definitely to share more and more my Lego pieces as they’ve been developed. In the meantime, please feel free to contact me if you have questions or suggestions.

Using PowerShell and OpsMgr SDK to Get and Set Management Group Default settings

Written by Tao Yang

Over the last couple of days, I have written few additional functions in the OpsMgrSDK PowerShell / SMA module that I’ve been working on over the last few months. Two of these functions are:

  • Get-MGDefaultSettings – Get ALL default settings of an OpsMgr 2012 (R2) management group
  • Set-MGDefaultSetting – Set any particular MG default setting

Since I haven’t seen anything similar to these on the net before, although they will be part of the module when I release it to the public later, I thought they are pretty cool and I’ll publish the code here now.


This function returns an arraylist which contains ALL the default settings of the management group.


$DefaultSettings = Get-MGDefaultSettings -SDK “OpsMgrMS01″ –verbose


As you can see, this function retrieves ALL default settings of a management group. It returns the following properties:

  • SettingFullName: The full name of the assembly type of the setting. This is required when using the Set-MGDefaultSetting function to set the value.
  • SettingName: The name of the assembly type of the setting. consider it as the setting category
  • FieldName: The actual name of the setting. It is required when using the Set-MGDefaultSetting function.
  • Value: The current default value of the setting.
  • AllowOverride: When it’s true, this value can be overridden to a particular instance (differ from the default value).

If you want to retrieve a particular setting, you can always use pipe (“|”) and where-object to filter to the particular setting:





Set-MGDefaultSetting -SDK “OpsMgrMS01″ -SettingType Microsoft.EnterpriseManagement.Administration.Settings+ManagementGroup+AlertResolution -FieldName AlertAutoResolveDays -Value 3 –verbose


I think these two functions are particularly useful when managing multiple management groups. they can be used in automation products such as SC Orchestrator and SMA, to synchronise settings among multiple management groups (i.e. Test vs Dev vs Prod).

PowerShell Script to Add MP References to Unsealed Management Packs

Written by Tao Yang


Few months ago, I have written a script to remove obsolete MP references from unsealed management packs and have also built this into the OpsMgr Self Maintenance MP. Last week, I needed to write a script to do the opposite: creating obsolete MP references in unsealed MPs.

In the past, some of the MPs I have released had issues with creating overrides in the OpsMgr operational console. i.e. the OpsMgr 2012 Self Maintenance MP and the ConfigMgr 2012 Client MP. Both of them have one thing in common: the phrase “2012” is a part of the MP namespace, and if someone tries to create an override for these MPs in the operational console, he / she will get an “Alias atribute is invalid” error:


When I was testing the latest release ConfigMgr 2012 Client MP (version last week, I also got this error when assigning a RunAs account to the RunAs profile defined in the MP – because the assignment is basically a Secure Reference Override, and a MP reference to the ConfigMgr 2012 Client Library MP needs to be created in the Microsoft.SystemCenter.SecureReferenceOverride MP.

Although we can easily workaround this issue by exporting the unsealed MP, add the MP reference in by manually editing the XML, I thought I’ll write a PowerShell script to do this to make everyone’s life easier.


To make it a bit easier for the users, this PowerShell function CAN ONLY be used on a OpsMgr management server.

Usage Example:

Add-MPRef -ReferenceMPName “ConfigMgr.2012.Client.Library” -Alias “C2CL” -UnsealedMPName “Microsoft.SystemCenter.SecureReferenceOverride” –Verbose


By using this script, we can pick the alias name that we prefer. Although this script is already included in the ConfigMgr 2012 Client MP package, I’d also like to share this script on this blog. For me, it’s a rare scenario that I had to do this, but I hope this can also help someone out there.

SMA Runbook: Update A SharePoint 2013 List Item

Written by Tao Yang


This blog hasn’t been too active lately. I’ve been spending a lot of time learning the new member in System Center family: Service Management Automation.

Yesterday, I needed a SMA runbook to update SharePoint 2013 list items, I found a sample from a blog post by Christian Booth, which contains a SMA runbook written by Ryan Andorfer, a System Center Cloud and Datacenter MVP.  Looks Ryan’s code was written for SharePoint 2010, which does not work for SharePoint 2013 because the SharePoint REST API has been updated. So I have spent some time, learned a bit more about SharePoint 2013’s REST API, and developed a new runbook for SharePoint 2013 based on Ryan’s code.

PowerShell Code

Here’s the finished work:

Unlike Ryan’s code, which also monitors the SP list, my runbook ONLY updates a specific list item.

Pre-Requisite and Parameters

Prior to using this runbook, you will need to save a credential in SMA which has  access to the SharePoint site


The runbook is expecting the following parameters:

SharePointSiteURL: The URL to the sharepoint site. i.e. http://SharepointServer/Sites/DemoSite

SavedCredentialName: name of the saved credential to connect to SharePoint site

ListName: Name of the list. i.e. “Test List”

ListItemID: the ID for the list item that the runbook is going to update

PropertyName: the field / property of the item that is going to be updated.

PropertyValue: the new value that is going to be set to the list item property.

Note: The list Item ID is the reference number for the item within the list. If you point the mouse cursor to the item, you will find the list item ID in the URL.


Putting it into Test:

To test, I’ve created a new list as shown in the above screenshot, I have kicked off the runbook with the the following parameters:



Here’s the result:



Using It Together With Orchestrator SharePoint IP

Since this SMA runbook requires the List Item ID to locate the specific list item, when you design your solution, you will need to find a way to retrieve this parameter prior to calling this runbook.

If you are also using SC Orchestrator and have deployed the SharePoint IP, you can use the “Monitor List Items” activity, and the List Item ID is published by this activity:



Although I’m still a newbie when comes to SMA, it got me really excited. Before its time, when I design Orchestrator runbooks, I often ended up just write the entire solution in PowerShell and then chopped up my PowerShell scripts into many “Run .Net Script” activities. I thought, wouldn’t it be nice if there is an automation engine that only uses PowerShell? Well, looks like SMA is the solution. I wish I have started using it sooner.

If you are like me and want to learn more about this product, i highly recommend you to read the Service Management Automation Whitepaper (currently version 1.0.4) from my fellow SCCDM MVP Michael Rueefli. I have read it page by page like a bible!

How to Create a PowerShell Console Profile Baseline for the Entire Environment

Written by Tao Yang


Often when I’m working in my lab, I get frustrated because the code in PowerShell profiles varies between different computers and user accounts. And your user profile is also different between the normal PowerShell command console and PowerShell ISE. I wanted to be able to create a baseline for the PowerShell profiles across all computers and all users, no matter which PowerShell console is being used (normal command console vs PowerShell ISE).

For example, I would like to achieve the following when I start any 64 bit PowerShell consoles on any computers in my lab under any user accounts:

This is what I want the consoles to look like:



Although I can manually copy the code into the profiles for each of my user accounts and enable roaming profile for  these users, I don’t want to take this approach because it’s too manual and I am not a big fan of roaming profiles.


My approach is incredibly simple, all I had to do is to create a simple script and deployed it as a normal software package  using ConfigMgr. I’ll now go through the steps.

All Users All Hosts Profile

Firstly, there are actually not one (1), but six (6) different PowerShell profiles (I have to admit, I didn’t know this until now Smile with tongue out). This article from the Scripting Guy explained it very well. Based on this article, I have identified that I need to work on the All Users All Hosts profile. Because I want the code to run regardless which user account am I using, and no matter whether I’m using the normal command console or PowerShell ISE.


As I mentioned previously, because I want to use the PSConsole module I have developed earlier, I need to make sure this module is deployed to all computers in my lab. To do so, I have created a simple msi to copy the module to the PowerShell Module’s folder and deployed it to all the computers using ConfigMgr. I won’t go through how I created the msi here.

Code Inside the All Users All Hosts profile

The All Users All Hosts profile is located at $PsHome\profile.ps1


Here’s the code I’ve added to this profile:

if (Get-module -name PSConsole -List)
Import-Module PSConsole

$host.UI.RawUI.BackgroundColor = "Black"
$host.UI.RawUI.ForegroundColor = "Green"
$host.UI.RawUI.WindowTitle = $host.UI.RawUI.WindowTitle + "  - Tao Yang Test Lab"
If ($psISE)
$psISE.Options.ConsolePaneBackgroundColor = "Black"
} else {
Resize-Console -max -ErrorAction SilentlyContinue
set-location C:\

Note: The $psISE variable only exists in the PowerShell ISE environment, therefore I’m using it to identify which console am I currently in and used an IF… Else… statement to control what’s getting executed within PowerShell ISE and normal PowerShell console.

Script To create All Users All Hosts Profile

Next, I have created a PowerShell script to create the All Users All Hosts profile:

# Script Name:        CreateAllUsersAllHostsProfile.ps1
# DATE:               03/08/2014
# Version:            1.0
# COMMENT:            - Script to create All users All hosts PS profile

$ProfilePath = $profile.AllUsersAllHosts

#Create the profile if doesn't exist
If (!(test-path $ProfilePath))
New-Item -Path $ProfilePath -ItemType file -Force

#content of the profile script
$ProfileContent = @"
if (Get-module -name PSConsole -List)
Import-Module PSConsole

<code>$host.UI.RawUI.BackgroundColor = &quot;Black&quot;
</code>$host.UI.RawUI.ForegroundColor = &quot;Green&quot;
<code>$host.UI.RawUI.WindowTitle = </code>$host.UI.RawUI.WindowTitle + &quot;  - Tao Yang Test Lab&quot;
If (<code>$psISE)
</code>$psISE.Options.ConsolePaneBackgroundColor = &quot;Black&quot;
} else {
Resize-Console -max -ErrorAction SilentlyContinue
set-location C:\
#write contents to the profile
if (test-path $ProfilePath)
Set-Content -Path $ProfilePath -Value $ProfileContent -Force
} else {
Write-Error &quot;All Users All Hosts PS Profile does not exist and this script failed to create it.&quot;

As you can see, I have stored the content in a multi-line string variable. The only thing to pay attention to is that I have to add the PowerShell escape character backtick (`)  in front of each variable (dollar sign $).

This script will overwrite the profile if already exists, so it will make sure the profile is consistent across all computers.

Deploy the Profile Creation Script Using ConfigMgr

In SCCM, I have created a Package with one program for this script:


Command Line: %windir%\Sysnative\WindowsPowerShell\v1.0\Powershell.exe .\CreateAllUsersAllHostsProfile.ps1

Note: I’m using ConfigMgr 2012 R2 in my lab, although the ConfigMgr client seems to be 64-bit, this command will still be executed under 32-bit environment. Therefore I have to use “Sysnative” instead of “System32” to overcome 32-bit redirection in 64-bit OS.

I created a re-occurring deployment for this program:


I’ve set it to run it once a day at 8:00am and always rerun.


This is an example on how we can standardise the baseline of PowerShell consoles within the environment. Individual users will still be able to add the users specific stuff in different profiles.

For example, on one of my computers, I have added one line to the default Current User Current Host profile:


In the All Users All Hosts profile, I have set the location to C:\, but in the Current User Current Host profile, I’ve set the location to “C:\Scripts\Backup Script”. The result is, when I started the console, the location is set to “C:\Scripts\Backup Script”. Obviously the Current User Current Host profile was executed after the All Users All Hosts profile. Therefore we can use the All Users All Hosts profile as a baseline and using Current User Current Host profile as a delta Smile.

OpsMgr 2012: A Trick to Drive Another Contextual Widget From PowerShell Grid Widget

Written by Tao Yang

PowerShell Grid widget and PowerShell Web Browser Widget were released as part of OpsMgr 2012 SP1 UR6 and R2 UR2. To me, these two widgets have opened a window of opportunities, because by using PowerShell, it allows OpsMgr 2012 users to customise and present the data exactly the way they wanted on dashboards.

Since it has been released, many people have share their work. Recently, Microsoft has started a new repository for the PowerShell widgets in TechNet Gallery.

The best article for the PowerShell Grid Widget that I have seen so far is from Oleg Kapustin’s blog: SCOM Powershell Grid Widget for Mere Mortals. In Oleg’s article (and seems to be a common practice), for each item that to be listed by the PowerShell Grid Widget, a unique Id is assigned to it (an auto incremented number):


Today, I want to share a small trick with you, something I’ve only picked up couple of days ago when I was writing the Location History dashboard for the 3rd part of my Location, Location, Location series. This is what the dashboard looks like:


On this dashboard, users suppose to make their way from section 1 (state widget) to section 2 (PowerShell Grid Widget) and finally to section 3 (PowerShell Web Browser Widget). The PowerShell script in section 2 retrieves particular events generated by the object from section 1 using OpsMgr cmdlets, then display the data on this customised list. This script is listed below:


$i = 1
foreach ($globalSelectedItem in $globalSelectedItems)
 $MonitoringObjectID = $globalSelectedItem["Id"]
 $MG = Get-SCOMManagementGroup
 $globalSelectedItemInstance = Get-SCOMClassInstance -Id $MonitoringObjectID
 $Computername = $globalSelectedItemInstance.DisplayName
 $strInstnaceCriteria = "FullName='Microsoft.Windows.Computer:$Computername'"
 $InstanceCriteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectGenericCriteria($strInstnaceCriteria)
 $Instance = $MG.GetMonitoringObjects($InstanceCriteria)[0]
 $Events = Get-SCOMEvent -instance $Instance -EventId 10001 -EventSource "LocationMonitoring" | Where-Object {$_.Parameters[1] -eq 4} |Sort-Object TimeAdded -Descending | Select -First 50
 foreach ($Event in $Events)
 $EventID = $Event.Id.Tostring()
 $LocalTime = $Event.Parameters[0]
 $LocationStatus = $Event.Parameters[1]
 $Latitude = $Event.Parameters[2]
 $Longitude = $Event.Parameters[3]
 $Altitude = $Event.Parameters[4]
 $ErrorRadius = $Event.Parameters[5].trimend(".")
 $dataObject = $ScriptContext.CreateInstance("xsd://foo!bar/baz")
 $dataObject["ErrorRadius (Metres)"]=$ErrorRadius


Because I need to drive the contextual PowerShell Web Browser widget (section 3) from the PowerShell Grid Widget (section 2), the script used in section 3 needs to locate the exact event selected in section 2. As per Oleg’s article, based on his experiment, the only property passed between widgets is the “Id” property (of the data object). therefore, instead of using an auto increment number as the value for “Id” property as demonstrated in the previous screenshot from Oleg’s blog, I assigned the actual event Id as the data object Id so script in section 3 can use the event ID to retrieve data from the particular event.

From Section 2:


From Section 3:



Please keep in mind, the only property (and its value) for $globalselectedItems that travels between contextual widgets is “Id” property. if you want to drive another contextual widget based on the data passed from a PowerShell Grid Widget, please make sure you use the actual Id of the OpsMgr object (monitoring object, class, event, alert, etc…) so the next widget can use this Id to retrieve the object from OpsMgr.

PowerShell Script: Remove Obsolete References from Unsealed OpsMgr Management Packs

Written by Tao Yang


Last month, in TechEd North America, Cameron Fuller demonstrated a PowerShell script to search and remove obsolete MP references from an unsealed management pack. The script was written by Cameron’s colleague Matthew Dowst. You can watch Cameron’s presentation here and get the script here.

After TechEd, Cameron emailed me and suggest me to add this script into my OpsMgr Self Maintenance management pack. So before I built this functionality into the Self Maintenance MP, I have written a similar stand-alone script as a proof-of-concept.

Script Highlights:

The differences between my version and Matthew Dowst version are:

  • No need to export and re-import unsealed management packs: My script directly reads and updates MP contents using SCOM SDK. therefore unsealed MPs don’t need to be exported and re-imported.
  • Scan through all unsealed MPs: My script go through all unsealed MPs rather than individual XML files.
  • Option to backup MPs before changes are made: the script accept parameters to backup original unsealed MPs before any changes are made.
  • Option to increase MP version or keep version the same: Users can choose whether the MP version should be increased.
  • Allow test run (-WhatIf): Users can use –WhatIf switch to test run the script before changes are made.
  • MP Verify: the script verifies the MP before and after changes. if MP verify fails (including pre-existing errors), no changes will be made to the particular MP.
  • Allow Users to customize a “white list” for common MPs: When obsolete references are detected for the “common management packs” defined in the CommonMPs.XML (placed in the same folder as the script), these references will be ignored. This is because these common management packs are referenced in many out-of-box unsealed management packs by default. Additionally, since it is very unlikely these management packs will ever be deleted from the management group, therefore it should not be an issue when they are referenced in other management packs. Users can manually add / remove MPs from the list by editing the CommonMPs.XML. I have pre-populated the white list and included the following MPs:
    • Microsoft.SystemCenter.Library
    • Microsoft.Windows.Library
    • System.Health.Library
    • System.Library
    • Microsoft.SystemCenter.DataWarehouse.Internal
    • Microsoft.SystemCenter.Notifications.Library
    • Microsoft.SystemCenter.DataWarehouse.Library
    • Microsoft.SystemCenter.OperationsManager.Library
    • System.ApplicationLog.Library
    • Microsoft.SystemCenter.Advisor.Internal
    • Microsoft.IntelligencePacks.Types
    • Microsoft.SystemCenter.Visualization.Configuration.Library
    • Microsoft.SystemCenter.Image.Library
    • Microsoft.SystemCenter.Visualization.ServiceLevelComponents
    • Microsoft.SystemCenter.NetworkDevice.Library
    • Microsoft.SystemCenter.InstanceGroup.Library
    • Microsoft.Windows.Client.Library


You can run this script on any computers have OpsMgr 2012 console /agent / management server installed. The script includes a help documentation. you can access it via:

get-help .\MPReferencesCleanUp.ps1 –full



#1. Test run using -WhatIf: .\MPReferencesCleanUp.ps1 -ManagementServer “OPSMGRMS01″ –BackupBeforeModify –BackupLocation “C:\Temp” -IncrementVersion –WhatIf


#2. Real run without –WhatIf: .\MPReferencesCleanUp.ps1 -ManagementServer “OPSMGRMS01″ –BackupBeforeModify –BackupLocation “C:\Temp” –IncrementVersion



The script can be downloaded HERE.

What’s next?

As I mentioned in the beginning, the next version of the OpsMgr 2012 Self Maintenance MP will have the ability to detect and remove these obsolete references. The MP is pretty much done. I’ve sent it to few people to test. I should be able to publish it in few days. Despite the new functionalities of the self maintenance MP, this script will still be a good standalone tool to run ad-hoc when needed.


I’d like to thank the following people for testing and advices provided to this script (in random order):

  • Cameron Fuller
  • Raphael Burri
  • Marnix Wolf
  • Bob Cornelissen
  • Dan Kregor

I also want to thank Matthew Dowst for the original script and Matthew Long for his blog post where I got the ideas from.

Lastly, as always, please feel free to contact me if you have questions / issues.

Programmatically Generating the OpsMgr 2012 Alert Update Connector Configuration XML

Written by Tao Yang


After been working on a project for over a year, I’ve start to see some light at the end of the tunnel. The last task I have in order to production-transition the 4 OpsMgr 2012 R2 management groups that I have designed and built is to configure integration between our ticket logging tool and OpsMgr to allow alerts to be automatically logged as IM’s.

Back in the OpsMgr 2007 days, before I started with the organisation, one of my colleagues have designed a set of very comprehensive Opalis policies (yes, they were called policies instead of runbooks back then) to populate various information for alerts such as product types, problem types, endpoints NetBIOS names, etc. then forward alerts to the ticketing system. In my opinion, my colleague did a very good job designing those Opalis policies and it was bullet-proof back then. But as the time goes by, we have been writing / introducing new management packs to monitor additional applications. This set of Opalis policies have become a pain in the butt to update to keep up with the changes because the logics have become really complicated.

Now, it’s up to me to migrate this set of Opalis policies to Orchestrator 2012 R2 and modify them to work with each OpsMgr 2012 R2 management groups. Because we did not just build a 2012 management group for each 2007 management group and we will have different agents / management packs in each 2012 MG, also in the new environment, we are going to have different support groups managing same applications (but different groups of agents), so we are moving to a multi-tenant setup if you like. So when I opened up the old Opalis policies and had a look, I’ve decided to give the 2012 Alert Update Connector a try and see if it will help me simplifies the Orchestrator runbooks.

My Initial Experience with Alert Update Connector

Because of these two excellent blog posts, It was very easy for me to setup the connector to test:

OpsMgr: Public release of the Alert Update Connector

SCOM Alert Updater Service – connector example updating SCOM alerts

In my opinion, based on my requirements, the only place that I think that needs improvement is how the configuration XML file is populated using the GUI tool(ConnectorConfiguration.exe):


Improvements required in my opinion:

01. Perf and event collection rules are available for selection (i.e. all the highlighted ones above). These rules will never generate alert, There is no need to add them to the configuration XML.

02. Although I can select multiple workflows at once, and specify the fields to update, it’s still a manual process and very time consuming if I want to configure all alerts in a management group. Also, being manual means the process is prone to human error. I would love to be able to configure all alerts at once, in bulk. it’s like cherry picking using hands VS. harvesting the entire field using a harvester.


03. It’s hard to find workflows in the configuration xml which this GUI tool populated:


I’ve quickly generated a XML using ConnectorConfiguration.exe as shown above. I can’t really identify the workflow by just reading it.


In order to overcome these issues, and establish a process to maintain and update the configuration XML in the years to come, I have written a PowerShell script to generate the Alert Update Connector configuration XML based on a set of policies I have defined.

This script (called “ConfigAlertUpdateConnector.ps1”) is expecting an input XML file called “ConfigAlertUpdateConnector.xml” from the same directory. The “ConfigAlertUpdateConnector.xml” stores all the policies that I have defined.

Let’s look at the finishing piece first. The output of this script looks like this:


As you can see, not only every eligible rule / monitor has been populated according to the policies I defined, I added a comment line (highlighted) that contains the following information:

  • Workflow type (rule or monitor)
  • Rule / Monitor Name
  • Rule / Monitor Display Name
  • Target Class Name
  • Target Class DisplayName

it would be so much easier to search for a particular alert in this XML than the one generated by the GUI interface. We can simply copy the monitor / rule display name from the Operations Console and search in XML:

Display name from the Operations console:


Search in XML:


In the script, I have also filtered out all rules and monitors that do not generate alerts so they won’t appear in the output XML.

Now let’s take a look at the “ConfigAlertUpdateConnector.xml”:


Each Policy is defined within a <AlertUpdateRule> tag. Under <AlertUpdateRule>, There is a <ClassSearchPhrase> tag. you can specify the search phrase for either target class name or display name, or both. When both name and display name are specified, both criteria must be true during search. For any classes that have returned from the search result, the alerts generated by any workflows targeting these classes will have the properties updated as what’s defined in the <PropertiesToModify> tag. Note, the schema within <PropertiesToModify> is same as the Alert Update Connector configuration file (the output).

Hint: Using Name VS Display Name in <ClassSearchPhrase>

The Name refers to the actual class ID from the management pack where the target class is defined. i.e. Some classes defined in VMM 2012 MP:


The Display Name is what you see in the Operations Console.


As you can see, normally, all classes defined in a particular MP will have the same prefix. in this case, with SCVMM 2012, the prefix is “Microsoft.SystemCenter.VirtualMachineMananager.2012”.

all VMM 2008 classes will have “Microsoft.SystemCenter.VirtualMachineMananager.2008”. So if I want to update all the alerts generated from Microsoft’s VMM MPs regardless the version, I’d use “Microsoft.SystemCenter.VirtualMachineMananager” as the NAME search phrase.

Another example, if I want to update any alerts generated for any “disks” related classes defined in Windows ServerOS MPs, I’d use both Name and Display search phrase:

  • DisplayName=”disk”
  • Name=”Microsoft.Windows.Server”

This leads to another issue. I thought I had everything covered, until I configured Fujitsu PRIMERGY server MP (sorry to use Fujitsu as an example in my blog again :P).

Use of Exceptions

If I open up Fujitsu MP version in MP Viewer, there are many server components have been defined, such Network component:


But when I looked at the rules in this MP, all the ones related to the network component are targeting the top level Fujitsu PRIMERGY Server class rather than the Network component:


In fact, all the rules in this MP are targeting the Server class. I’m not sure how many MPs out there are targeting workflows in “less-appropriate” classes, so in order to work around this issue, I have coded my script to also process exceptions. This is why in my screenshot for “ConfigAlertConnector.xml” above, in the Fujitsu section, I have a lot of exceptions defined:


As shown above, by default, any Fujitsu alerts will have CustomField1 updated to “FUJ_MISC”, which is the default value. However, if the workflow’s (rule / monitor) Display Name contains the phrase “network”, the value for CustomField1 will be set to “FUJ_NIC”. The other 2 default properties defined (ResolutionState and Owner”) will remain the same. In the output xml file, it looks like this:


The first one have the “network” exception applied so the value has set to “FUJ_NIC”. the second one does not have any exceptions applied so it has the default value of “FUJ_MISC”.

Note: When exceptions are specified in the policies, the script will only apply an exception if both property name and GroupIdFilter match the default value:


Executing “ConfigAlertUpdateConnector.ps1”

Once all the policies have been configured in “ConfigAlertUpdateConnector.xml”, and this XML file is placed at the same folder as the ps1 script, we can simply run the script without any parameters. If the output file already exists in the script directory, the script will append the output file name with the current time stamp and move it to a sub folder called “Archive”. In my work’s fully tuned test management group, this script took less than 2 minutes to run, and generated the config file containing just less than 3000 workflows for Alert Update Connector.

In future, when we add new management packs or update / delete existing management packs, we can simply make minor modifications to the existing policies in “ConfigAlertUpdateConnector.xml” and re-run this script to generate the config file for Alert Update Connector.

You can download the script, the sample “ConfigAlertUpdateConnector.xml” and the sample output file HERE.

Lastly, I encourage anyone using the OpsMgr 2012 Alert Update Connector to try this script and any feedbacks are welcome. I believe I have covered everything in terms of how to configure the input xml (“ConfigAlertUpdateConnector.xml”). If I have missed anything, please feel free to drop me an email.

PowerShell Functions: Get OpsMgr Alert Generating Rules and Monitors

Written by Tao Yang

This is my second post today. Bad weather, both wife and daughter have got flu. So I’m sitting home catching up with blogs…

I wrote 2 functions as part of a PowerShell script I’ve been working on: Get-AlertRules and Get-AlertMonitors.

As the names suggest, these two functions get all Rules / Monitors of a particular monitoring class that generate alerts.

I didn’t end up using these 2 functions in my script, but I thought they are too good to be trashed. so I thought I’ll put them here for future reference.


Function Get-AlertRules
[Parameter(Mandatory=$true,HelpMessage="OpsMgr Management Group Connection" )][Microsoft.EnterpriseManagement.ManagementGroup] $ManagementGroup,
[Parameter(Mandatory=$false,HelpMessage="Monitoring Class Name" )][string] $MonitoringClassName = $null
$arrAlertRules = New-object System.Collections.ArrayList
#Get GenerateAlert WriteAction module
$HealthMPId = [guid]"0abff86f-a35e-b08f-da0e-ff051ab2840c" #this is unique
$HealthMP = $MG.GetManagementPack($HealthMPId)
$AlertWA = $HealthMP.GetModuleType("System.Health.GenerateAlert")
$AlertWAId = $AlertWA.Id
#firstly get all monitoring classes
#Populate Search criteria
If ($MonitoringClassName)
$strClassCriteria = "Name = '$MonitoringClassName'"
} else {
$strClassCriteria = "Name LIKE '%'"
$ClassCriteria = New-Object Microsoft.EnterpriseManagement.Configuration.MonitoringClassCriteria($strClassCriteria)
$MonitoringClasses = $MG.GetMonitoringClasses($ClassCriteria)
Foreach ($MC in $MonitoringClasses)
$MCId = $MC.Id
$strRuleCriteria = "TargetMonitoringClassId = '$MCId'"
$RuleCriteria = New-Object Microsoft.EnterpriseManagement.Configuration.MonitoringRuleCriteria($strRuleCriteria)
$Rules = $MG.GetMonitoringRules($RuleCriteria)
Foreach ($rule in $Rules)
#Unfortunately, we cannot use a member module name/id in MonitoringRUleCriteria.
#So we have to manually filter out the rules with GenerateAlert Write Action Module
#Check if it has a GenerateAlert WriteAction module
$bAlertRule = $false
Foreach ($WAModule in $Rule.WriteActionCollection)
if ($WAModule.TypeId.Id -eq $AlertWAId)
#this rule generates alert
$bAlertRule = $true
} else {
#need to detect if it's using a customized WA which the GenerateAlert WA is a member of
$WAId = $WAModule.TypeId.Id
$WASource = $MG.GetMonitoringModuleType($WAId)
#Check each write action member modules in the customized write action module...
Foreach ($item in $WASource.WriteActionCollection)
$itemId = $item.TypeId.Id
If ($ItemId -eq $AlertWAId)
$bAlertRule = $true

if ($bAlertRule)
#Add to arraylist


Function Get-AlertMonitors
[Parameter(Mandatory=$true,HelpMessage="OpsMgr Management Group Connection" )][Microsoft.EnterpriseManagement.ManagementGroup] $ManagementGroup,
[Parameter(Mandatory=$false,HelpMessage="Monitoring Class Name" )][string] $MonitoringClassName = $null
$arrAlertMonitors = New-object System.Collections.ArrayList
#firstly get all monitoring classes
#Populate Search criteria
If ($MonitoringClassName)
$strClassCriteria = "Name = '$MonitoringClassName'"
} else {
$strClassCriteria = "Name LIKE '%'"
$ClassCriteria = New-Object Microsoft.EnterpriseManagement.Configuration.MonitoringClassCriteria($strClassCriteria)
$MonitoringClasses = $MG.GetMonitoringClasses($ClassCriteria)
Foreach ($MC in $MonitoringClasses)
$MCId = $MC.Id
$strMonitorCriteria = "TargetMonitoringClassId = '$MCId' AND AlertOnState IS NOT NULL"
$MonitorCriteria = New-Object Microsoft.EnterpriseManagement.Configuration.MonitorCriteria($strMonitorCriteria)
$Monitors = $MG.getmonitors($MonitorCriteria)
Foreach ($Monitor in $Monitors)
#Add to arraylist

Both functions are expecting an OpsMgr management group connection and the name (not the display name, but the Class ID from the management pack where the class is defined). so in order to use these 2 functions, I’ll need 2 other functions:


function Load-SDK()
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager.Common") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager") | Out-Null


Function Get-MonitoringClass
[Parameter(Mandatory=$true,HelpMessage="OpsMgr Management Group Connection" )][Microsoft.EnterpriseManagement.ManagementGroup] $ManagementGroup,
[Parameter(Mandatory=$true,HelpMessage="Monitoring Class Display Name" )][string] $MonitoringClassDisplayName
#Populate Search criteria
$strClassCriteria = "DisplayName = '$MonitoringClassDisplayName'"
$ClassCriteria = New-Object Microsoft.EnterpriseManagement.Configuration.MonitoringClassCriteria($strClassCriteria)
#Search monitoring class
$MonitoringClasses = $MG.GetMonitoringClasses($ClassCriteria)

as the name suggests, Load-SDK function loads OpsMgr SDK, when can then create the connection to the management group. Get-MonitoringClass function gets the Monitoring Class object based on it’s display name (the name you see in the Operations Console), such as this one:


Here’s an example of how to these functions:

#Load SDK DLL's

#Connect to the management group via management server "OpsMgrMS01":
$MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings("OPSMGRMS01")
$MG = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)

#Get the monitoring class
$MonitoringClassDisplayName = "Data Access Service"
$MonitoringClass = Get-MonitoringClass $MG $MonitoringClassDisplayName
$MonitoringClassName = $MonitoringClass.Name

#Display Monitoring Class Name

#Get Alert rules
$AlertRules = Get-AlertRules $MG $MonitoringClassName

#Rule Count

#Alert Monitors
$AlertMonitors = Get-AlertMonitors $MG $MonitoringClassName

#Monitor Count


Logics in these 2 functions:


Searching for rules either have the “System.Health.GenerateAlert” module from System.Health.Library MP as a Write Action member module, or one of the rule’s Write Action member modules has “System.Health.GenerateAlert” as its member.


This function is much easier to write than Get-AlertRules. I’ simply search for monitors that “AlertOnState” property is not NULL. Please keep in mind this function does not only return unit monitors, but also aggregate and dependency monitors.

Both functions return a “System.Collections.ArrayList” containing the rules / monitors. Since I used the OpsMgr SDK directly, instead of it’s PowerShell snapin or module. these functions should work in both 2007 and 2012. – And this is one of the reasons why I always just use SDK, hardly use the snapin or the module :)

I’ve also zipped up all the code used in this article. You can download them HERE. I know it’s a bit hard to read the code in WordPress :)

Remotely Powering On and Off My Entire Home Lab

Written by Tao Yang

RemoteMy home lab consists of 3 PCs running Hyper-V and a HP Proliant Microserver N54L running SCVMM. I have previously blogged the lab setup in a 2-part blog posts (Part 1, Part 2). These 2 blog articles was written back in October 2012, although there are few changes in the current setup (new hardware, etc), but the overall setup is pretty much the same.

All 4 machines in my lab have been constantly running 24×7, except when we go on holidays or there’s a power outage (which doesn’t happen very often). This is largely because I just can’t be bothered to spend time start and shutdown all the physicals and virtuals every time I use the lab, not to mention I often access my lab when I’m in the office via RDP using my Surface Pro 2 with an external monitor. Because all of the computers are PC grade hardware, there are no out-of-band management cards (i.e. iLo, DRAC, etc.) on these boxes, I had no way to remotely start them when I was in the office.

In order to reduce the “carbon footprint”, and more importantly, my electricity bill, I have been wanting to automate the the start and shutdown process of the entire lab for a while. Last weekend, I finally got around to it, and accomplished it by using only Wake On LAN (WOL) and PowerShell (with PowerShell Web Access, WinRM and CredSSP).

Because one of the PCs in my lab is my main desktop (running Windows 8.1 with Hyper-V role enabled), this PC is always running. my solution is to use this desktop (called “Study”) to interact with other physical computers in the lab. I’ll now go through the steps I took to archive this goal:

1. confirm / configure Wake-On-LAN on all physical computers in the lab.

I installed a freeware called AquilaWOL on my “Study” PC, made sure I can WOL all other computers.


During my testing, the HP Microserver and one of the Hyper-V box (HyperV01, the one with the Intel motherboard) had no problem at all. However, the other HyperV box HyperV02, would not WOL. after some research, it seemed like a known issue with the motherboard that only be able to WOL when the computer is at sleep, not when it’s powered off. Luckily other than the on-board Marvell NIC, I also have a dual port Intel GB NIC and a single port Intel desktop GB NIC on this computer. the dual port NIC also wouldn’t work. but the desktop NIC worked :)

2. Installed a Windows Server 2012 R2 virtual machine on my “Study” PC.

I named this VM “JUMP01” because I intend to use this as a jump box. I connected this VM to the virtual switch  which is on the same subnet as all physical computers – so I don’t rely on switch/routers to relay WOL packets. I also need my AD environment to be available when running the script, but because I have a domain controller already running as a VM on my Study PC, no additional VM’s are required.

3. Installed and configured PowerShell Web Access on the Jump server.

So I don’t have to RDP to the jump server to run the scripts. This also enables me to power on / off the lab from any mobile devices with a browser. I have followed my previous blog post to install and configure PSWA. I also wrote a PowerShell module to resize PowerShell console size to make PSWA more user-friendly for mobile devices.

4. Developed PowerShell scripts to power on and power off the physical and virtuals.

I wrote 2 scripts: start-lab.ps1 and stop-lab.ps1. both scripts read required information from a XML file (labconfig.xml). this XML file contains all required information for my lab environment.


The PowerShell scripts utilise WinRM and CredSSP to interact with each physical computers (using PSSessions and Invoke-Command).

Below is a list of steps each script performs:


  1. Read XML, get information for all computers that are a member of the lab
  2. ping each lab member, send WOL magic packet if ping failed
  3. wait for 90 seconds (configurable via XML)
  4. Check OS readiness (configurable via XML)
    • Minimum up time
    • all required services are running
  5. Once the OS is ready on the Hyper-V hosts, start VMs in groups hardcoded in the script (based on my naming standard in the lab).
    • firstly start the CentOS VM configured as routers (they are configured to auto start with the host, but just in case they did not start).
    • Then start the domain controller
    • Then start all VMs hosting SQL databases (OpsMgr DB, ConfigMgr site servers, Service Manager DB, etc).
    • Then start all other VMs except OpsMgr management servers
    • Lastly, start all OpsMgr management servers (they must be started at the end so I don’t get any alerts).


  1. Read XML, get information for all computers that are a member of the lab
  2. ping each lab member, ignore the lab member if it does not respond to ping
  3. shutdown VM’s in order (which is the reverse order as the start-lab.ps1)
  4. double check if all VM’s are completely shutdown, if not, forcibly turn them off
  5. shutdown Hyper-V servers (and non-hyper-v physicals).

Note: the WOL function in my script is taken from here: http://gallery.technet.microsoft.com/scriptcenter/Send-WOL-packet-using-0638be7b

5. Created a simple PowerShell module to execute the 2 steps I wrote.

On the jump server, I created a powershell module called “LabAdmin”, which contains 2 functions that simply execute the powershell script:


6. Configured port forwarding on my ADSL router to allow me to access the PSWA site from the Internet

This allows me to manage my lab even when I’m not home.

i.e. starting the lab via my mobile phone (over 4G):


Here are the live demos for both scripts:



For your reference, the scripts can be downloaded HERE.


I live in Australia, one of the countries with the highest electricity prices. It is time for me to do something to cut down the running cost of my home lab – especially when my colleagues told me their average electricity bills are only half of mine.

Now, I can remotely start my entire lab anywhere via my mobile phone, and it only takes me a single command to shut down the lab, I won’t need to have them running 24×7. So I’m hoping my implemented this new feature in my lab, I should be able to see some noticeable reductions in my next power bill. :)