Tag Archives: MimbolovePowershell

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):

image

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:

SNAGHTML52cdecf

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:

Param($globalSelectedItems)

$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["Id"]=$EventID
 $dataObject["No"]=$i
 $dataObject["LocalTime"]=$LocalTime
 $dataObject["Latitude"]=$Latitude
 $dataObject["Longitude"]=$Longitude
 $dataObject["Altitude"]=$Altitude
 $dataObject["ErrorRadius (Metres)"]=$ErrorRadius
 $ScriptContext.ReturnCollection.Add($dataObject)
 $i++
 } 
}

 

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:

image

From Section 3:

image

Conclusion

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

Background

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

Instruction:

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

SNAGHTML436c9dc6

Examples:

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

image

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

image

Download

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.

Credit

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

Background

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):

image

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.

SNAGHTML11522d3b

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

image

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

Solution

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:

image

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:

SNAGHTML16816481

Search in XML:

SNAGHTML16828dce

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”:

image

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:

image

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

SNAGHTML1699fb1e

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 6.0.0.5 in MP Viewer, there are many server components have been defined, such Network component:

image

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:

image

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:

image

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:

image

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:

image

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.

Get-AlertRules:

Function Get-AlertRules
{
PARAM (
[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
[void]$arrAlertRules.Add($rule)
}
}
}
}
,$arrAlertRules
}

Get-AlertMonitors:

Function Get-AlertMonitors
{
PARAM (
[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
[void]$arrAlertMonitors.Add($Monitor)
}
}
,$arrAlertMonitors
}

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:

Load-SDK:

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

Get-MonitoringClass:

Function Get-MonitoringClass
{
PARAM (
[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)
$MonitoringClasses
}

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:

SNAGHTML1f11d89

Here’s an example of how to these functions:

#Load SDK DLL's
Load-SDK

#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
$MonitoringClassName

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

#Rule Count
$alertRules.count

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

#Monitor Count
$AlertMonitors.Count

SNAGHTML1f339a2

Logics in these 2 functions:

Get-AlertRules:

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.

Get-AlertMonitors:

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.

image

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.

SNAGHTML6bd3b2

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:

Start-Lab.ps1:

  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).

Stop-Lab.ps1:

  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:

image

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):

image

Here are the live demos for both scripts:

Start-Lab:

Stop-Lab:

For your reference, the scripts can be downloaded HERE.

Conclusion

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. :)

PowerShell Module to resize console – Updated

Written by Tao Yang

PSWAIcon Few days ago I wrote a PowerShell module that contains 1 cmdlet / function to resize the PowerShell console windows.

It was a quick job that I did less than half an hour. I wrote it primarily to make PowerShell Web Access (PSWA) more user friendly.

Over the last couple of days, I spent a bit more time on this module, and add a lot more functionality to it. The original module had 107 lines of code, and the updated one has 591 lines.

Here’s a list of new features:

Additional cmdlets

This module now contains the following cmdlets:

  • Resize-Console
  • Get-CurrentConsoleSize
  • Save-ConsoleProfile
  • Remove-ConsoleProfile
  • Get-ConsoleProfile
  • Update-ConsoleProfile

I’ll go through each cmdlets later

Aliases for all cmdlets and parameters

Since I wrote this module primarily for PSWA, and I intend to access PSWA primarily from mobile devices such as phones and tablets, I need to make the module easier to use. I am sure I’m not the only person who’s suffering from fat finger syndrome. I HATE typing on tablets and phones. Even with the Type Cover 2 for my Surface Pro 2, I found the keys are too small. So less is more, aliases really help when I use mobile devices because I don’t have to type as much.

Buffer width is always same as window width when re-sizing

When working on any kind of command prompts (cmd or PowerShell), I really don’t like the horizontal scroll bar. by making the buffer width always the same as window width, I don’t have to see the horizontal bars anymore.

Resize-Console –max switch

Or “rsc –m” if use aliases. The –max switch will move the window to the top left corner of the PRIMARY monitor and maximize the window size. It also set the buffer height to the maximum value of 9999. – This is equivalent to maximizing a normal window.

Every time I get on to a box (most likely via RDP) for the first time, I always had to manually set the PowerShell console size to suit my needs. then next time I RDP in from another computer with different display resolutions, I often had to set it again.

With resize-console –max, it will always set the console to the maximum size and occupies the entire screen. It will make my life so much easier. Not that I have OCD(Obsessive Compulsive Disorder), but for those ones who do, this function would make you much happier I suppose :). I’ll demonstrate this in the Youtube video at the end of this article.

Note: for this functionality, I used some of the code from Richard Siddaways’s blog. So thanks to Richard.

Resize-Console –Profile

I have included a XML file (profiles.xml) in this module. We can save pre-defined console dimension (Window width and height) to this XML so we can use them later.

i.e.:

SNAGHTML1aca2b96

The screen size and resolution are different among different computers and mobile devices. I have created different profiles for each type of devices that I own, so when I use a particular device to access PSWA, I can simply apply the appropriate screen size to suit that device.

i.e. the screenshot below is taken from my mobile phone (Samsung Note3):

image

when I applied the profile “Note3”, the PSWA console fits perfectly on the screen.

Or, on my 10.1 inch tablet Samsung Galaxy Tab2:

image

I used aliases, applied the “tab2” profile to fit the screen.

With the introduction to the –profile functions, this module now includes these functions for CRUD operations:

  • Save-ConsoleProfile
  • Remove-ConsoleProfile
  • Get-ConsoleProfile
  • Update-ConsoleProfile

For details of each functions, You can refer to the help information (get-help)

Note: only administrators will be able to modify the profiles.xml because it’s located in a system folder. so if UAC is enabled, admins will need launch powershell console as Administrator in order to use the Remove-ConsoleProfile and Update-ConsoleProfile cmdlet.

Get-CurrentConsoleSize

This one simply display the current window size and buffer size on screen. it’s reading the properties of “$host.ui.rawui”

SNAGHTML1ad77daf[4]

For your reference, here’s a recorded demo that I have updated to Youtube:

Please watch this video in full screen and 720P/1080P or you may not be able to see what’s happening on the powershell console.

You can download the module HERE. simply unzip and copy the whole folder to C:\Windows\System32\WindowsPowerShell\v1.0\Modules

So why am I spending time on this PowerShell project rather than System Center, which is my bread and butter? That would be the topic for my next blog article. :)

Please feel free to contact me for any issues or suggestions.

Until next time, happy PowerShelling :)

PowerShell Module: Resize-Console

Written by Tao Yang

I’m currently working on a solution that relies on PowerShell Web Access (Hopefully I can finish tonight and blog it in next couple of days).

I have been a bit hesitate to use use PWSA every since I firstly tried it out briefly back in 2012 (and blogged my experience here).

Why am I hesitated? this is why:

image

The interface is just not that user-friendly with such a small window and that much useless space. There is no way to easily resize the window.

In my original blog post, I posted a simple script to increase the size. Today, I spent a little bit more time, wrote a module based on the original code, and of course made it more flexible.

This module has only one function called Resize-Console:


Function Resize-Console
{
<#  .Synopsis Resize PowerShell console window .Description Resize PowerShell console window. Make it bigger, smaller or increase / reduce the width and height by a specified number .Parameter -Bigger Increase the window's both width and height by 10. .Parameter -Smaller Reduce the window's both width and height by 10. .Parameter Width Resize the window's width by passing in an integer. .Parameter Height Resize the window's height by passing in an integer. .Example # Make the window bigger. Resize-Console -bigger  .Example # Make the window smaller. Resize-Console -smaller  .Example # Increase the width by 15. Resize-Console -Width 15  .Example # Reduce the Height by 10. Resize-Console -Height -10  .Example # Reduce the Width by 5 and Increase Height by 10. Resize-Console -Width -5 -Height 10 #>

[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$false,HelpMessage="Increase Width and Height by 10")][Switch] $Bigger,
[Parameter(Mandatory=$false,HelpMessage="Reduce Width and Height by 10")][Switch] $Smaller,
[Parameter(Mandatory=$false,HelpMessage="Increase / Reduce Width" )][Int32] $Width,
[Parameter(Mandatory=$false,HelpMessage="Increase / Reduce Height" )][Int32] $Height
)

#Get Current Buffer Size and Window Size
$bufferSize = $Host.UI.RawUI.BufferSize
$WindowSize = $host.UI.RawUI.WindowSize
If ($Bigger -and $Smaller)
{
Write-Error "Please make up your mind, you can't go bigger and smaller at the same time!"
} else {
if ($Bigger)
{
$NewWindowWidth = $WindowSize.Width + 10
$NewWindowHeight = $WindowSize.Height + 10

#Buffer size cannot be smaller than Window size
If ($bufferSize.Width -lt $NewWindowWidth)
{
$bufferSize.Width = $NewWindowWidth
}
if ($bufferSize.Height -lt $NewWindowHeight)
{
$bufferSize.Height = $NewWindowHeight
}
$WindowSize.Width = $NewWindowWidth
$WindowSize.Height = $NewWindowHeight

} elseif ($Smaller)
{
$NewWindowWidth = $WindowSize.Width - 10
$NewWindowHeight = $WindowSize.Height - 10
$WindowSize.Width = $NewWindowWidth
$WindowSize.Height = $NewWindowHeight
}

if ($Width)
{
#Resize Width
$NewWindowWidth = $WindowSize.Width + $Width
If ($bufferSize.Width -lt $NewWindowWidth)
{
$bufferSize.Width = $NewWindowWidth
}
$WindowSize.Width = $NewWindowWidth
}
if ($Height)
{
#Resize Height
$NewWindowHeight = $WindowSize.Height + $Height
If ($bufferSize.Height -lt $NewWindowHeight)
{
$bufferSize.Height = $NewWindowHeight
}
$WindowSize.Height = $NewWindowHeight

}
#commit resize
$host.UI.RawUI.BufferSize = $buffersize
$host.UI.RawUI.WindowSize = $WindowSize
}

}

image

I have copied the folder containing this module to C:\Windows\System32\PowerShell\v1.0\Modules folder on the server hosting PSWA site so it is available for everyone.

Here’s a screen video capture if you want to see it in action:

Note: Please watch this video in full screen (by double-clicking the video) and choose 720P if you can. Otherwise you might not see much in such a small window.

This module also works on normal PowerShell prompt windows. You can download this PSConsole module HERE. to set it up, simply copy to the folder I mentioned above.

Few PowerShell One-Liners To Check WinRM Settings on Remote Machines

Written by Tao Yang

To Check if WinRM has been enabled on a Remote machine:

$RemoteMachine = “Remote Machine Name”

[system.convert]::ToBoolean(((winrm get winrm/config/winrs -r:$remotemachine | ?{$_ -imatch "AllowRemoteShellAccess"}).split("="))[1].trim())

To Check the Default HTTP listener port on a remote machine:

$RemoteMachine = “Remote Machine Name”

[System.Convert]:: ToInt32(((winrm get winrm/config/Service/DefaultPorts -r:$RemoteMachine | ?{$_ -imatch "HTTP = " }).split("="))[1].trim())

To Check the Default HTTPS listener port on a remote machine:

$RemoteMachine = “Remote Machine Name”

[System.Convert]:: ToInt32(((winrm get winrm/config/Service/DefaultPorts -r:$RemoteMachine | ?{$_ -imatch "HTTPS = " }).split("="))[1].trim())

image

Finding OpsMgr Management Group Installation Date Using PowerShell

Written by Tao Yang

As part of what I’m working on at the moment, I need to find out when the OpsMgr 2012 management group was initially installed using PowerShell (the installation time of the first management server).

To do so, I can either use the OpsMgr SDK or the OperationsManager PowerShell module. I’ve developed below scripts to run locally on a management server:

Using SDK:

$MgmtServer = $Env:COMPUTERNAME
#Connect to SCOM management group
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager.Common") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager") | Out-Null
$MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings($MgmtServer)
$MG = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)
$MG.GetManagementGroupMonitoringObject()

TimeAdded property indicates the MG initial installation date.

image

Using OperationsManager PowerShell module:

import-module OperationsManager
$mg = Get-SCOMManagementGroup
$mg.GetManagementGroupMonitoringObject() | format-list

image

Using SCOM 2012 SDK to Retrieve Resource Pools Information

Written by Tao Yang

Today I needed to retrieve information about SCOM 2012 resource pools in a PowerShell script, I needed to do this directly via SDK, rather than using the OperationsManager PowerShell module. I couldn’t find any existing scripts via Google, so I spent some time playing with SDK and finally found it. Since it seems no one has mentioned it on the web, here’s how I did it:

Firstly, load the SDK DLL’s. I always use below function:

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

Load-SDK

Secondly, connect to the management group. Since I’ll be running this script on a management server, I’m connecting to the management group via the SDK service on the local machine:

$MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings($env:computername)
$MG = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)

Then, get the management group administration

$Admin = $MG.Administration

Finally, get all resource pools

$ResourcePools = $admin.GetManagementServicePools()

image

In the past, I’ve been using the GetAdministration() method from the management group object to retrieve MG administration object. This time, When I did it, the MG administration object returned from the method does not contain a method for GetManagementServicePools. I then realised the management group contains a property called Administration. the object type is the same as what’s returned from GetAdministration() method.

image

But it looks like the object returned from the “Administration” property contains more members:

image

This is just a quick observation. In the future, I’ll remember to check both places.