Introduction
This is the 5th instalment of the Automating OpsMgr series. Previously on this series:
- Automating OpsMgr Part 1: Introducing OpsMgrExtended PowerShell / SMA Module
- Automating OpsMgr Part 2: SMA Runbook for Creating ConfigMgr Log Collection Rules
- Automating OpsMgr Part 3: New Management Pack Runbook via SMA and Azure Automation
- Automating OpsMgr Part 4:Creating New Empty Groups
In the previous post (part 4), I have demonstrated a runbook creating new empty instance groups and computer groups using the OpsMgrExtended module. As I also mentioned in Part 4, I will dedicate few posts on creating and managing OpsMgr groups. So, this post is the 2nd one on this topic.
In OpsMgr, groups can be populated via Explicit memberships (static) or Dynamic Memberships (query based), or combination of both:
In this post, I will demonstrate how to use a runbook to add a Windows computer object to a computer group via Explicit membership.
Runbook Add-ComputerToComputerGroup
Workflow Add-ComputerToComputerGroup
{
Param(
[Parameter(Mandatory=$true)][String]$GroupName,
[Parameter(Mandatory=$true)][String]$ComputerPrincipalName
)
#Get OpsMgrSDK connection object
$OpsMgrSDKConn = Get-AutomationConnection -Name "OpsMgrSDK_TYANG"
$bComputerAdded = Inlinescript {
#Connecting to the management group
$MG = Connect-OMManagementGroup -SDKConnection $USING:OpsMgrSDKConn
#Get the windows computer object
Write-Verbose "Getting the Windows computer monitoring object for '$USING:ComputerPrincipalName'"
$WinComputerObjectCriteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectGenericCriteria("FullName = 'Microsoft.Windows.Computer:$USING:ComputerPrincipalName'")
$WinComputer = $MG.GetMonitoringObjects($WinComputerObjectCriteria)[0]
If ($WinComputer -eq $null)
{
Write-Error "Unable to find the Microsoft.Windows.Computer object for '$USING:ComputerPrincipalName'."
Return $false
}
$WinComputerID = $WinComputer.Id.ToString()
Write-Verbose "Monitoring Object ID for '$USING:ComputerPrincipalName': '$WinComputerID'"
#Get the group
Write-Verbose "Getting the computer group '$USING:GroupName'."
$ComputerGroupClassCriteria = New-Object Microsoft.EnterpriseManagement.Configuration.MonitoringClassCriteria("Name='$USING:GroupName'")
$ComputerGroupClass = $MG.GetMonitoringClasses($ComputerGroupClassCriteria)[0]
If ($ComputerGroupClass -eq $null)
{
Write-Error "$Using:GroupName is not found."
Return $false
}
#Check if this monitoring class is actually a computer group
Write-Verbose "Check if the group '$USING:GroupName' is a computer group"
$ComputerGroupBaseTypes = $ComputerGroupClass.GetBaseTypes()
$bIsComputerGroup = $false
Foreach ($item in $ComputerGroupBaseTypes)
{
If ($item.Id.Tostring() -eq '0c363342-717b-5471-3aa5-9de3df073f2a')
{
$bIsComputerGroup = $true
}
}
If ($bIsComputerGroup -eq $false)
{
Write-Error "$Using:GroupName is not a computer group"
Return $false
}
#Get Group object
$ComputerGroupObject = $MG.GetMonitoringObjects($ComputerGroupClass)[0]
#Get Group population discovrey
Write-Verbose "Getting the group discovery rule"
$ComputerGroupDiscoveries = $ComputerGroupObject.GetMonitoringDiscoveries()
$iGroupPopDiscoveryCount = 0
$GroupPopDiscovery = $null
Foreach ($Discovery in $ComputerGroupDiscoveries)
{
$DiscoveryDS = $Discovery.DataSource
#Microsft.SystemCenter.GroupPopulator ID is 488000ef-e20b-1ac4-d3b1-9d679435e1d7
If ($DiscoveryDS.TypeID.Id.ToString() -eq '488000ef-e20b-1ac4-d3b1-9d679435e1d7')
{
#This data source module is using Microsft.SystemCenter.GroupPopulator
$iGroupPopDiscoveryCount = $iGroupPopDiscoveryCount + 1
$GroupPopDiscovery = $Discovery
Write-Verbose "Group Populator discovery found: '$($GroupPopDiscovery.Name)'"
}
}
If ($iGroupPopDiscoveryCount.count -eq 0)
{
Write-Error "No group populator discovery found for $Group."
Return $false
}
If ($iGroupPopDiscoveryCount.count -gt 1)
{
Write-Error "$Group has multiple discoveries using Microsft.SystemCenter.GroupPopulator Module type."
Return $false
}
#Get the MP of where the group populator discovery is defined
$GroupPopDiscoveryMP = $GroupPopDiscovery.GetManagementPack()
Write-Verbose "The group populator discovery '$($GroupPopDiscovery.Name)' is defined in management pack '$($GroupPopDiscoveryMP.Name)'."
#Write Error and exit if the MP is sealed
If ($GroupPopDiscoveryMP.sealed -eq $true)
{
Write-Error "Unable to update the group discovery because it is defined in a sealed MP: '$($GroupPopDiscoveryMP.DisplayName)'."
Return $false
}
Write-Verbose "Updating the discovery data source configuration"
$GroupDSConfig = $GroupPopDiscovery.Datasource.Configuration
$GroupDSConfigXML = [XML]"<Configuration>$GroupDSConfig</Configuration>"
#Detect if any MembershipRule segment contains existing static members
$bComputerAdded = $false
Foreach ($MembershipRule in $GroupDSConfigXML.Configuration.MembershipRules.MembershipRule)
{
If ($MembershipRule.IncludeList -ne $Null -and $bComputerAdded -eq $false)
{
#Add the monitoroing object ID of the Windows computer to the <IncludeList> node
Write-Verbose "Adding '$USING:ComputerPrincipalName' monitoring Object ID '$WinComputerID' to the <IncludeList> node in the group populator configuration"
$NewMOId = $MembershipRule.IncludeList.AppendChild($GroupDSConfigXML.CreateElement("MonitoringObjectId"))
$NewMOId.InnerText = $WinComputerID
$bComputerAdded = $true
}
}
#If none of the MembershipRule has <IncludeList segment>, create it in the first MembershipRule
If ($bComputerAdded -eq $false)
{
If ($GroupDSConfigXML.Configuration.MembershipRules.MembershipRule -Is [System.Array])
{
Write-Verbose "Multiple Membership rules. creating <IncludeList> within the first <MembershipRule>"
$IncludeListNode = $GroupDSConfigXML.Configuration.MembershipRules.MembershipRule[0].AppendChild($GroupDSConfigXML.CreateElement("IncludeList"))
} else {
Write-Verbose "There is only one Membership rule. creating <IncludeList> in it."
$IncludeListNode = $GroupDSConfigXML.Configuration.MembershipRules.MembershipRule.AppendChild($GroupDSConfigXML.CreateElement("IncludeList"))
}
$NewMOId = $IncludeListNode.AppendChild($GroupDSConfigXML.CreateElement("MonitoringObjectId"))
$NewMOId.InnerText = $WinComputerID
}
$UpdatedGroupPopConfig = $GroupDSConfigXML.Configuration.InnerXML
#Updating the discovery
Write-Verbose "Updating the group discovery"
Try {
$GroupPopDiscovery.Datasource.Configuration = $UpdatedGroupPopConfig
$GroupPopDiscovery.Status = [Microsoft.EnterpriseManagement.Configuration.ManagementPackElementStatus]::PendingUpdate
$GroupPopDiscoveryMP.AcceptChanges()
$bComputerAdded = $true
} Catch {
$bComputerAdded = $false
}
$bComputerAdded
}
If ($bComputerAdded -eq $true)
{
Write-Output "Done."
} else {
throw "Unable to add '$ComputerPrincipalName' to group '$GroupName'."
exit
}
}
When using this runbook, you will need to update line 9 of the runbook, and replace the SMA connection name with the one you’ve used in your SMA environment:
This runbook requires 2 mandatory parameters:
- Windows computer principal name (FQDN) - which is the key property of the Windows Computer object
- The group name - it’s the internal name, not the display name. I did not use the display name because it is not unique. i.e.
Note:
If the group was created using the New-OMComputerGroup and New-OMInstanceGroup functions from the OpsMgrExtended module, these 2 functions would automatically prepend the management pack name in front of the group name specified by the user (if the management pack name is not already the prefix of the specified names). I forgot to mention this behaviour in my previous post (Part 4).
Since the OpsMgrExtended module does not (yet) have a function to add a computer to a computer group, I wrote this runbook to perform this task directly via OpsMgr SDK (therefore all within the inlinescript). The high level steps for this runbook is listed below:
- Establish OpsMgr management group connection (and the SDK assemblies will be loaded automatically).
- Get the Windows computer monitoring object
- Get the computer group monitoring class (singleton class)
- Check if the group object specified is indeed a computer group
- Get the computer group instance
- Get the computer group discovery
- Make sure the discovery is defined in an unsealed management pack
- Detect if any “MembershipRule” segments in discovery data source module contains existing static members
- If there are existing static members in one of the membership rule, add the Windows computer as a static member in the membership rule.
- If none of the membership rules contain static members, define a static member section (“
") in the first Membership Rule. - Update the unsealed management pack where the discovery is defined.
Executing Runbook
Group membership before execution:
Executing runbook:
Group membership after execution:
Conclusion
In this post, I have demonstrated how to use a runbook and OpsMgrExtended module to add a Windows computer object as a static member of a computer group.
I’ve also demonstrated even when an activity is not pre-defined in the OpsMgrExnteded module, we can still leverage OpsMgrExnteded module to perform the task because we can directly interact with OpsMgr management groups and SDKs via this module - by using the Connect-OMManagementGroup function, the SDK will be loaded automatically.
When I was writing this runbook few days ago, I have realised this is something I should have included in the OpsMgrExtended module because it could be very common and useful. Although I’ve published this as a rather complex runbook at this stage, I will probably included this as an additional function in the future release of OpsMgrExtended module.
I will demonstrate how to add a explicit member to an instance group in the Part 6 of this series.
Leave a comment