Modifying Explicit Group Membership in SCOM 2012 with PowerShell
This is a continuation of a Data Center Automation series of posts that I have been working on with Anders Bengtsson. Here are the first seven posts in this series:
Creating Management Packs in SCOM 2012 with PowerShell
Creating Performance Collection Rules in SCOM 2012 with PowerShell
Creating Event Based Alerting Rules in SCOM 2012 with PowerShell
Enabling or Disabling Workflows in SCOM 2012 with PowerShell
Deleting Workflows in SCOM 2012 with PowerShell
Creating Groups in SCOM 2012 with PowerShell
Adding References to MPs in SCOM 2012 with PowerShell
*Update v2.0*
Fixed…
- Group membership being modified but not updating during GroupCalc
- When all GUIDs are removed from a group it will not save properly
- When all Membership rules are removed from a group it will not save properly
Improved…
- XML handling
- Error handling
- Script output
- Reference handling
Added support for…
- Computer groups
- Multiple membership rules
- New management packs
- New groups
- Excluded instances (if they are in the UI, then they’ll get added automatically to the any membership rules created by the script)
*Update v2.1*
Improved…
- New group creation and error handling
As of this post this script is not included as an activity in the Operations Manager Admin Integration Pack but will be in the next version. This was a challenging script to write because group modification requires XML manipulation as there are no methods for managing this. I chose to only support explicit membership with this script because that is likely the most common automation need.
Each group discovery can have multiple membership rules which are really only visible by reviewing the XML of the management pack. This script modifies any of these rules, including rules added via the SCOM Console. If the appropriate membership rule does not exist at runtime, then it will be created and members added. If it does exist then it gets the current explicit members, adds any new members passed, and finally removes any members passed. It then replaces the new old list with the new list while keeping any other membership rules not created with this script intact.
Since the plan is to use this in an integration pack it requires that the instances passed to the script be in GUID format and these GUIDs are validated by the script before being added to the group. You can get the GUIDs by using a simple PowerShell command:
Get-SCOMClass –Name ‘Microsoft.Windows.Computer’ | Get-SCOMClassInstance | select name, id
If you notice after running this script that the new members show up in the console under “Explicit Members” but not when you select “View Group Members” then the UI may just not be up to date. Try the following PowerShell command:
(Get-SCOMGroup –DisplayName ‘My Test Group 1’).GetRelatedMonitoringObjects() | select name, path, id
Syntax:
.\ModifyGroupMembership.ps1 –ManagementServer ‘om01.contoso.com’ –ManagementPackID ‘custom.example.test’ –GroupID ‘custom.example.test.group.testgroup1’ –InstancesToAdd ‘8d5e843d-4d4a-3821-91d1-bc6434d6f9f1, aa390a79-b72a-bf58-412b-ebb94a139e06’ –InstancesToRemove ‘732ac45e-4e1a-9369-c9dd-e3b038537474’
Parameters:
Name | Description |
ManagementServer | Name of MS to connect to |
ManagementPackID | ID of the MP you want to create or modify |
GroupID | ID of the group you want to create or modify |
InstancesToAdd | Optional: GUIDs of the instances you want to add to the group. You can pass multiple by separating each with a comma. |
InstancesToRemove | Optional: GUIDs of the instances you want to remove from the group. You can pass multiple by separating each with a comma. |
1 Param(
2 [parameter(Mandatory=$true)]
3 $ManagementServer,
4 [parameter(Mandatory=$true)]
5 $ManagementPackID,
6 [parameter(Mandatory=$true)]
7 $GroupID,
8 $InstancesToAdd,
9 $InstancesToRemove
10 )
11
12 Write-Host "Version 2.0"
13 Write-Host "ManagementServer: "$ManagementServer
14 Write-Host "ManagementPackID: "$ManagementPackID
15 Write-Host "GroupID: "$GroupID
16 Write-Host "InstancesToAdd: "$InstancesToAdd
17 Write-Host "InstancesToRemove: "$InstancesToRemove
18
19 function GetSCOMManagementGroup
20 {
21 param($ms)
22 try
23 {
24 $mg = New-Object Microsoft.EnterpriseManagement.ManagementGroup($ms)
25 }
26 catch
27 {
28 Write-Host "Failed to Connect to SDK, Exiting:"$ms -ForegroundColor Red
29 Write-Host $_.Exception.Message -ForegroundColor Yellow
30 exit
31 }
32 return $mg
33 }
34
35 function GetManagementPackToUpdate
36 {
37 param($mg, $mpID)
38 try
39 {
40 $mp = $mg.GetManagementPacks($mpID)[0]
41 $vIncrement = $mp.Version.ToString().Split('.')
42 $vIncrement[$vIncrement.Length - 1] = ([system.int32]::Parse($vIncrement[$vIncrement.Length - 1]) + 1).ToString()
43 $mp.Version = ([string]::Join(".", $vIncrement))
44 }
45 catch
46 {
47 Write-Host "New MP:"$mpID
48 $mp = CreateManagementPack -mpID $mpID
49 $mg.ImportManagementPack($mp)
50 $mp = GetManagementPack -mg $mg -mpID $mpID
51 }
52 return $mp
53 }
54
55 function GetManagementPack
56 {
57 param ($mg, $mpID)
58 try
59 {
60 $mp = $mg.GetManagementPacks($mpID)[0]
61 }
62 catch
63 {
64 Write-Host "Management Pack Not Found, Exiting:"$mpID -ForegroundColor Red
65 Write-Host $_.Exception.Message -ForegroundColor Yellow
66 exit
67 }
68 return $mp
69 }
70
71 function CreateManagementPack
72 {
73 param($mpID)
74 $mpStore = New-Object Microsoft.EnterpriseManagement.Configuration.IO.ManagementPackFileStore
75 $mp = New-Object Microsoft.EnterpriseManagement.Configuration.ManagementPack($mpID, $mpID, (New-Object Version(1, 0, 0)), $mpStore)
76 return $mp
77 }
78
79 function GetReferenceAlias
80 {
81 param($mp, $mpID)
82 if ($mp.Name.ToUpper() -ne $mpID.ToUpper())
83 {
84 $bFound = $false
85 foreach ($ref in $mp.References)
86 {
87 $s = ($ref.Value.ToString().Split("=")[1]).Split(",")[0]
88 if ($s.ToUpper() -eq $mpID.ToUpper())
89 {
90 $bFound = $true
91 $alias = $ref.Key
92 }
93 }
94 if (!($bFound))
95 {
96 Write-Host "MP Reference Not Found, Exiting:"$mpID
97 exit
98 }
99 }
100
101 return $alias
102 }
103
104 function ValidateReference
105 {
106 param($mg, $mp, $mpID)
107 if ($mp.Name.ToUpper() -ne $mpID.ToUpper())
108 {
109 $bFound = $false
110 foreach ($ref in $mp.References)
111 {
112 $s = ($ref.Value.ToString().Split("=")[1]).Split(",")[0]
113 if ($s.ToUpper() -eq $mpID.ToUpper()) {$bFound = $true}
114 }
115 if (!($bFound))
116 {
117 Write-Host "New Reference:"$mpID
118 $mp = CreateReference -mg $mg -mp $mp -mpID $mpID
119 }
120 }
121 return $mp
122 }
123
124 function ValidateReferencesFromInstances
125 {
126 param($mg, $mp, $ht)
127
128 $htClasses = @{}
129 foreach($instance in $ht.GetEnumerator())
130 {
131 try {$htClasses.Add($instance.Value.ToString(),$instance.Value)} catch {}
132 }
133
134 foreach($instance in $htClasses.GetEnumerator())
135 {
136 $classMP = GetClassMPFromMG -mg $mg -criteria ([string]::Format("Name = '{0}'", $instance.Value))
137 $mp = ValidateReference -mg $mg -mp $mp -mpID $classMP.Name
138 }
139
140 return $mp
141 }
142
143 function CreateReference
144 {
145 param($mg, $mp, $mpID)
146 try
147 {
148 $newMP = $mg.GetManagementPacks($mpID)[0]
149 if (!($newMP.sealed))
150 {
151 Write-Host "MP to reference is not sealed, cannot add reference to"$mpID -ForegroundColor Red
152 Write-Host "Exiting" -ForegroundColor Red
153 Write-Host $_.Exception.Message -ForegroundColor Yellow
154 exit
155 }
156 }
157 catch
158 {
159 Write-Host "Referenced MP Not Found in Management Group, Exiting:"$mpID -ForegroundColor Red
160 Write-Host $_.Exception.Message -ForegroundColor Yellow
161 exit
162 }
163
164 $alias = $mpID.Replace(".","")
165 $reference = New-Object Microsoft.EnterpriseManagement.Configuration.ManagementPackReference($newMP)
166 $mp.References.Add($alias, $reference)
167 return $mp
168 }
169
170 function XMLEncode
171 {
172 param([string]$s)
173 $s = $s.Replace("&", "&")
174 $s = $s.Replace("<", "<")
175 $s = $s.Replace(">", ">")
176 $s = $s.Replace('"', """)
177 $s = $s.Replace("'", "'")
178 return $s.ToString()
179 }
180
181 function ValidateMonitoringObjects
182 {
183 param($guids, $mg)
184 [hashtable]$ht = @{}
185 $guids = $guids.Split(",")
186 foreach ($guid in $guids)
187 {
188 $guid = $guid.Trim()
189 try
190 {
191 $mo = $mg.GetMonitoringObject($guid)
192 try {$ht.Add($guid, ($mo.FullName).Split(":")[0])} catch {}
193 }
194 catch
195 {
196 try {$ht.Add($guid, 'NOTFOUND')} catch {}
197 }
198 }
199 return $ht
200 }
201
202 function GetClassMPFromMG
203 {
204 param($mg, $criteria)
205 $searchCriteria = new-object Microsoft.EnterpriseManagement.Configuration.MonitoringClassCriteria($criteria)
206 $class = ($mg.GetMonitoringClasses($searchCriteria))[0]
207 $mp = $class.GetManagementPack()
208 return $mp
209 }
210
211 function GetRelationshipMPFromMG
212 {
213 param($mg, $criteria)
214 $searchCriteria = new-object Microsoft.EnterpriseManagement.Configuration.MonitoringRelationshipClassCriteria($criteria)
215 $relationship = ($mg.GetMonitoringRelationshipClasses($searchCriteria))[0]
216 $mp = $relationship.GetManagementPack()
217 return $mp
218 }
219
220 function GetMPElementClass
221 {
222 param($mg, $mp, $class)
223
224 $criteria = ([string]::Format("Name = '{0}'", $class))
225 $refMP = GetClassMPFromMG -mg $mg -criteria $criteria
226 $alias = ""
227 if ($refMP.Name -ne $mp.Name)
228 {
229 $alias = (GetReferenceAlias -mp $mp -mpID $refMP.Name) + "!"
230 }
231 $mpElement = '$MPElement[Name="{0}{1}"]$' -f $alias, $class
232
233 return $mpElement
234 }
235
236 function GetMPElementRelationship
237 {
238 param($mg, $mp, $class, $relationship)
239
240 if (($relationship.ToString() -eq 'Microsoft.SystemCenter.ComputerGroupContainsComputer') -and ($class.ToString() -ne 'Microsoft.Windows.Computer'))
241 {
242 $mpName = 'System.Library'
243 $relationship = 'System.ConfigItemContainsConfigItem'
244 }
245 else
246 {
247 $criteria = ([string]::Format("Name = '{0}'", $relationship))
248 $mpName = (GetRelationshipMPFromMG -mg $mg -criteria $criteria).Name
249 }
250
251 $alias = ""
252 if ($mpName -ne $mp.Name)
253 {
254 $alias = (GetReferenceAlias -mp $mp -mpID $mpName) + "!"
255 }
256 $mpElement = '$MPElement[Name="{0}{1}"]$' -f $alias, $relationship
257
258 return $mpElement
259 }
260
261 function GetGroup
262 {
263 param($mg, $mp, $groupID)
264 $group = $mg.GetMonitoringClasses($groupID)[0]
265 if ($group -eq $null)
266 {
267 Write-Host "Group Not Found:"$groupID -ForegroundColor Red
268 $newGroupID = $mp.Name + ($groupID.Split("."))[-1]
269 $group = $mg.GetMonitoringClasses($groupID)[0]
270 if ($group -eq $null)
271 {
272 Write-Host "Group Not Found, Exiting:"$newGroupID -ForegroundColor Red
273 exit
274 }
275 }
276 return $group
277 }
278
279 function ValidateGroup
280 {
281 param($mg, $mp, $groupID)
282 $group = $mg.GetMonitoringClasses($groupID)[0]
283 if ($group -eq $null)
284 {
285 $groupName = ($groupID.Split("."))[-1]
286 $groupID = $groupName
287 $newGroupID = $mp.Name + "." + $groupID
288 Write-Host "New Group:"$newGroupID
289 $mp = CreateGroup -mg $mg -mp $mp -groupID $groupID -groupName $groupName
290 }
291 return $mp
292 }
293
294 function CreateGroup
295 {
296 param($mg, $mp, $groupID, $groupName)
297 $mp = ValidateReference -mg $mg -mp $mp -mpID 'Microsoft.SystemCenter.InstanceGroup.Library'
298 $mp = ValidateReference -mg $mg -mp $mp -mpID 'System.Library'
299 $alias = GetReferenceAlias -mp $mp -mpID 'Microsoft.SystemCenter.InstanceGroup.Library'
300 $systemAlias = GetReferenceAlias -mp $mp -mpID 'System.Library'
301 $formula ='<MembershipRule Comment="Empty Membership Rule">' + `
302 '<MonitoringClass>$MPElement[Name="' + $alias + `
303 '!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass>' + `
304 '<RelationshipClass>$MPElement[Name="' + $alias + `
305 '!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>' + `
306 '<Expression>' + `
307 '<SimpleExpression>' + `
308 '<ValueExpression>' + `
309 '<Property>$MPElement[Name="' + $systemAlias + `
310 '!System.Entity"]/DisplayName$' + `
311 '</Property>' + `
312 '</ValueExpression>' + `
313 '<Operator>Equal</Operator>' + `
314 '<ValueExpression>' + `
315 '<Value>False</Value>' + `
316 '</ValueExpression>' + `
317 '</SimpleExpression>' + `
318 '</Expression>' + `
319 '</MembershipRule>'
320
321 $group = New-Object Microsoft.EnterpriseManagement.Monitoring.CustomMonitoringObjectGroup($mp.Name, $groupID, (XMLEncode -s $groupName), $formula)
322 $mp.InsertCustomMonitoringObjectGroup($group)
323 return $mp
324 }
325
326 function CreateEmptyMembershipRule
327 {
328 param($mg, $mp, $relationship, $rules)
329
330 if ($relationship -eq 'Microsoft.SystemCenter.InstanceGroupContainsEntities')
331 {
332 $class = 'Microsoft.SystemCenter.InstanceGroup'
333 }
334 else
335 {
336 $class = 'Microsoft.Windows.Computer'
337 }
338
339 $propertyName = '$MPElement[Name="{0}!System.Entity"]/DisplayName$' -f (GetReferenceAlias -mp $mp -mpID 'System.Library')
340
341 $rulesNode = $rules.SelectSingleNode("/Node1/MembershipRules")
342 $rule = $rules.CreateElement("MembershipRule")
343 [void]$rule.SetAttribute("Comment", "Scripted Membership Rule")
344 [void]$rulesNode.AppendChild($rule)
345
346 $mClass = $rules.CreateElement("MonitoringClass")
347 $mClass.InnerText = (GetMPElementClass -mg $mg -mp $mp -class $class)
348 [void]$rulesNode.MembershipRule.AppendChild($mClass)
349
350 $rClass = $rules.CreateElement("RelationshipClass")
351 $rClass.InnerText = (GetMPElementRelationship -mg $mg -mp $mp -class $class -relationship $relationship)
352 [void]$rulesNode.MembershipRule.AppendChild($rClass)
353
354 $expression = $rules.CreateElement("Expression")
355 [void]$rulesNode.MembershipRule.AppendChild($expression)
356
357 $eNode = $rules.SelectSingleNode("/Node1/MembershipRules/MembershipRule/Expression")
358 $simpleExpression = $rules.CreateElement("SimpleExpression")
359 [void]$eNode.AppendChild($simpleExpression)
360
361 $sNode = $rules.SelectSingleNode("/Node1/MembershipRules/MembershipRule/Expression/SimpleExpression")
362 $valueExpression = $rules.CreateElement("ValueExpression")
363 [void]$sNode.AppendChild($valueExpression)
364
365 $vNode = $rules.SelectSingleNode("/Node1/MembershipRules/MembershipRule/Expression/SimpleExpression/ValueExpression")
366 $property = $rules.CreateElement("Property")
367 $property.InnerText = $propertyName
368 [void]$vNode.AppendChild($property)
369
370 $operator = $rules.CreateElement("Operator")
371 $operator.InnerText = "Equal"
372 [void]$sNode.AppendChild($operator)
373
374 $valueExpression = $rules.CreateElement("ValueExpression")
375 [void]$sNode.AppendChild($valueExpression)
376
377 $vNode = $sNode.ChildNodes[2]
378 $value = $rules.CreateElement("Value")
379 $value.InnerText = "False"
380 [void]$vNode.AppendChild($value)
381
382 return $rules
383 }
384
385 function GetGroupDiscovery
386 {
387 param($group)
388 $groupDiscovery = $group.GetMonitoringDiscoveries()[0]
389 if ($groupDiscovery -eq $null)
390 {
391 Write-Host "Group Discovery Not Found, Exiting" -ForegroundColor Red
392 exit
393 }
394 return $groupDiscovery
395 }
396
397 function GetGroupMembershipRules
398 {
399 param($config)
400 $rulesStart = $config.IndexOf("<MembershipRules>")
401 $rulesEnd = ($config.IndexOf("</MembershipRules>") + 18) - $rulesStart
402 $rules = $config.Substring($rulesStart, $rulesEnd)
403 $rules = '<Node1>' + $rules + '</Node1>'
404 return $rules
405 }
406
407 function GetGroupInstances
408 {
409 param($mg, $mp, $groupID)
410
411 $group = GetGroup -mg $mg -mp $mp -groupID $groupID
412 $discovery = GetGroupDiscovery -group $group
413 $configuration = $discovery.DataSource.Configuration
414 $rules = GetGroupMembershipRules -config $configuration
415 $xPath = "/Node1/MembershipRules/MembershipRule/IncludeList/MonitoringObjectId"
416 $guids = Select-Xml -Content $rules -XPath $xPath
417 $existingInstances = @{}
418
419 foreach($instance in $guids)
420 {
421 try {$existingInstances.Add($instance.ToString(),$instance)} catch {}
422 }
423
424 return $existingInstances
425 }
426
427 function UpdateGroup
428 {
429 param($mg, $mp, $groupID, $instancesToAdd, $instancesToRemove)
430
431 $existingInstances = GetGroupInstances -mg $mg -mp $mp -groupID $groupID
432
433 if ($instancesToAdd -ne $null){$instancesToAdd = ValidateMonitoringObjects -guids $instancesToAdd -mg $mg}
434 else {$instancesToAdd = @{}}
435
436 $instancesToAdd = RemoveEntriesFromHashTable -guids $instancesToRemove -existingInstances $existingInstances -ht $instancesToAdd
437 $mp = ValidateReferencesFromInstances -mg $mg -mp $mp -ht $instancesToAdd
438
439 Write-Host "Update MP:"$mp.Name
440 $mp.AcceptChanges()
441 $mp = GetUpdatedGroupDiscovery -mg $mg -mp $mp -groupID $groupID -instancesToAdd $instancesToAdd -instancesToRemove $instancesToRemove
442
443 return $mp
444 }
445
446 function GetUpdatedGroupDiscovery
447 {
448 param($mg, $mp, $groupID, $instancesToAdd, $instancesToRemove)
449 $group = GetGroup -mg $mg -mp $mp -groupID $groupID
450 $discovery = GetGroupDiscovery -group $group
451 $configuration = $discovery.DataSource.Configuration
452 $relationship = GetRelationshipType -mg $mg -discovery $discovery
453 [xml]$rules = GetGroupMembershipRules -config $configuration
454 $exclusions = GetExclusions -rules $rules
455
456 foreach($instance in $instancesToAdd.GetEnumerator())
457 {
458 $rules = AddGUIDToDiscovery -guid $instance.Key.ToString() -mg $mg -mp $mp -class $instance.Value.ToString() -rules $rules -exclusions $exclusions -relationship $relationship
459 }
460
461 if ($instancesToRemove -ne $null)
462 {
463 foreach($instance in $instancesToRemove.Split(","))
464 {
465 Write-Host "Delete GUID:"$instance
466 $rules = RemoveGUIDFromDiscovery -guid $instance.Trim() -rules $rules
467 }
468
469 $rules = DeleteEmptyIncludeNodes -mg $mg -mp $mp -rules $rules -relationship $relationship
470 }
471
472 $configuration = CleanUpDiscoveryConfiguration -configuration $configuration -rules $rules
473
474 $discovery.Status = [Microsoft.EnterpriseManagement.Configuration.ManagementPackElementStatus]::PendingUpdate
475 $discovery.DataSource.Configuration = $configuration.ToString().Trim()
476 $mp = $discovery.GetManagementPack()
477 Write-Host "Update MP:"$mp.Name
478 [void]$mp.AcceptChanges()
479 [void]$mg.RefreshMonitoringGroupMembers($mp)
480
481 return $mp
482 }
483
484 function DeleteEmptyIncludeNodes
485 {
486 param($mg, $mp, $relationship, $rules)
487 $xPath = "/Node1/MembershipRules/MembershipRule"
488 $nodes = $rules.SelectNodes($xPath)
489
490 foreach ($node in $nodes)
491 {
492 if (($node.IncludeList.ChildNodes.Count -eq 0) -and ($node.IncludeList.MonitoringObjectId.Count -eq 0))
493 {
494 if ((($node.SelectSingleNode("Expression")).Name -eq 'Expression') -and (($node.SelectSingleNode("IncludeList")).Name -eq 'IncludeList'))
495 {
496 [void]$node.RemoveChild($node.SelectSingleNode("IncludeList"))
497 }
498 elseif (($node.SelectSingleNode("IncludeList")).Name -eq 'IncludeList')
499 {
500 [void]$node.ParentNode.RemoveChild($node)
501 }
502 }
503 }
504
505 if ($rules.SelectNodes($xPath).Count -eq 0)
506 {
507 $rules = CreateEmptyMembershipRule -mg $mg -mp $mp -relationship $relationship -rules $rules
508 }
509
510 return $rules
511 }
512
513 function CleanUpDiscoveryConfiguration
514 {
515 param($configuration, $rules)
516
517 $newRules = ($rules.OuterXml).Replace("<Node1>", "").Replace("</Node1>", "")
518 $i = $configuration.IndexOf("<MembershipRules>")
519 [string]$configuration = $configuration.SubString(0, $i) + $newRules
520
521 return $configuration
522 }
523
524 function GetRelationshipType
525 {
526 param($mg, $discovery)
527 $id = ($discovery.DiscoveryRelationshipCollection[0].TypeID).ToString().Split("=")[1]
528 $criteria = ([string]::Format("Id = '{0}'", $id))
529 $searchCriteria = new-object Microsoft.EnterpriseManagement.Configuration.MonitoringRelationshipClassCriteria($criteria)
530 $relationship = ($mg.GetMonitoringRelationshipClasses($searchCriteria))[0]
531 return $relationship
532 }
533
534 function GetExclusions
535 {
536 param($rules)
537 $xPath = "/Node1/MembershipRules/MembershipRule/ExcludeList/MonitoringObjectId"
538 $guids = $rules.SelectNodes($xPath)
539 $exclusions = @{}
540
541 foreach($instance in $guids)
542 {
543 try {$exclusions.Add($instance.InnerText.ToString(),$instance.InnerText.ToString())} catch {}
544 }
545
546 return $exclusions
547 }
548
549 function AddGUIDToDiscovery
550 {
551 param($mg, $mp, $guid, $class, $rules, $exclusions, $relationship)
552
553 $guids = GetIncludedGUIDS -rules $rules
554
555 if (!($guids.Contains($guid)))
556 {
557 $classes = GetMembershipRuleMonitoringClasses -rules $rules
558 Write-Host "New GUID:"$guid":"$class
559 if ($classes.ContainsKey($class.ToString()))
560 {
561 $rules = AddIncludeGUID -rules $rules -guid $guid -class $classes.Get_Item($class.ToString())
562 }
563 else
564 {
565 Write-Host "New Membership Rule:"$class
566 $rules = CreateNewMembershipRule -mg $mg -mp $mp -rules $rules -guid $guid -class $class -exclusions $exclusions -relationship $relationship
567 }
568 }
569
570 return $rules
571 }
572
573 function CreateNewMembershipRule
574 {
575 param($mg, $mp, $guid, $class, $rules, $exclusions, $relationship)
576
577 [xml]$xml = $rules
578
579 $rulesNode = $xml.SelectSingleNode("/Node1/MembershipRules")
580 $rule = $xml.CreateElement("MembershipRule")
581 [void]$rule.SetAttribute("Comment", "Scripted Membership Rule")
582 [void]$rulesNode.AppendChild($rule)
583
584 $mClass = $xml.CreateElement("MonitoringClass")
585 $mClass.InnerText = (GetMPElementClass -mg $mg -mp $mp -class $class)
586 [void]$rulesNode.MembershipRule.AppendChild($mClass)
587
588 $rClass = $xml.CreateElement("RelationshipClass")
589 $rClass.InnerText = (GetMPElementRelationship -mg $mg -mp $mp -class $class -relationship $relationship)
590 [void]$rulesNode.MembershipRule.AppendChild($rClass)
591
592 $iList = $xml.CreateElement("IncludeList")
593 [void]$rulesNode.MembershipRule.AppendChild($iList)
594
595 $count = ($rulesNode.ChildNodes.Count) - 1
596 $includeNode = $rulesNode.ChildNodes[$count].ChildNodes[2]
597 $mObjectId = $xml.CreateElement("MonitoringObjectId")
598 $mObjectId.InnerText = $guid.Trim()
599 [void]$includeNode.AppendChild($mObjectId)
600
601 if ($exclusions.Count -gt 0)
602 {
603 $eList = $xml.CreateElement("ExcludeList")
604 [void]$rulesNode.MembershipRule.AppendChild($eList)
605
606 foreach ($guid in $exclusions.GetEnumerator())
607 {
608 $excludeNode = $rulesNode.ChildNodes[$count].ChildNodes[3]
609 $mObjectId = $xml.CreateElement("MonitoringObjectId")
610 $mObjectId.InnerText = $guid.Value.ToString().Trim()
611 [void]$excludeNode.AppendChild($mObjectId)
612 }
613 }
614
615 return $xml
616 }
617
618 function AddIncludeGUID
619 {
620 param($guid, $class, $rules)
621
622 [xml]$xml = $rules
623 $xPath = "/Node1/MembershipRules/MembershipRule"
624 $ruleNodes = $xml.SelectNodes($xPath)
625
626 foreach ($rule in $ruleNodes)
627 {
628 $className = ($rule.Get_InnerXML()).Split('"')[1]
629 if ($className -eq $class)
630 {
631 $includeNode = $rule.SelectSingleNode("IncludeList")
632 if ($includeNode -ne $null)
633 {
634 $child = $xml.CreateElement("MonitoringObjectId")
635 $child.InnerText = $guid
636 [void]$includeNode.AppendChild($child)
637 break
638 }
639 }
640 }
641
642 return $xml
643 }
644
645 function GetMembershipRuleMonitoringClasses
646 {
647 param($rules)
648
649 [xml]$xml = $rules
650 $xPath = "/Node1/MembershipRules/MembershipRule"
651 $ruleNodes = $xml.SelectNodes($xPath)
652 $ht = @{}
653
654 foreach ($rule in $ruleNodes)
655 {
656 $includeNode = $rule.SelectSingleNode("IncludeList")
657 if ($includeNode -ne $null)
658 {
659 $fullPath = ($rule.Get_InnerXML()).Split('"')[1]
660 $class = $fullPath.Split("!")[1]
661 try { $ht.Add($class.ToString(), $fullPath) } catch {}
662 }
663 }
664
665 return $ht
666 }
667
668 function GetIncludedGUIDs
669 {
670 param($rules)
671 $xPath = "/Node1/MembershipRules/MembershipRule/IncludeList/MonitoringObjectId"
672 $guids = $guids = $rules.SelectNodes($xPath)
673 $ht = @{}
674 foreach ($g in $guids) { try { $ht.Add($g.InnerText.ToString(), $g.InnerText.ToString()) } catch {} }
675 return $ht
676 }
677
678 function RemoveGUIDFromDiscovery
679 {
680 param($guid, $rules)
681 $xPath = "/Node1/MembershipRules/MembershipRule/IncludeList[MonitoringObjectId='{0}']/MonitoringObjectId" -f $guid.ToString()
682 $guids = $rules.SelectNodes($xPath)
683
684 foreach ($g in $guids)
685 {
686 if ($g.InnerText -eq $guid.ToString())
687 {
688 [void]$g.ParentNode.RemoveChild($g)
689 }
690 }
691
692 return $rules
693 }
694
695 function RemoveEntriesFromHashTable
696 {
697 param($guids, $existingInstances, $ht)
698 if ($guids -ne $null)
699 {
700 $guids = $guids.Split(",")
701 foreach ($guid in $guids)
702 {
703 $guid = $guid.Trim()
704 try
705 {
706 $ht.Remove($guid)
707 }
708 catch {}
709 }
710 }
711
712 foreach ($guid in $existingInstances.GetEnumerator())
713 {
714 if ($ht.ContainsKey($guid.Key.ToString()))
715 {
716 $ht.Remove($guid.Key.ToString())
717 }
718 }
719 return $ht
720 }
721
722 try { Import-Module OperationsManager } catch
723 {
724 Write-Host "SCOM Module Not Found, Exiting" -ForegroundColor Red
725 Write-Host $_.Exception.Message -ForegroundColor Yellow
726 exit
727 }
728
729 $MG = GetSCOMManagementGroup -ms $ManagementServer
730 $MP = GetManagementPackToUpdate -mg $MG -mpID $ManagementPackID
731 $MP = ValidateGroup -mg $MG -mp $MP -groupID $GroupID
732 $MP = UpdateGroup -mg $MG -mp $MP -groupID $GroupID -instancesToAdd $InstancesToAdd -instancesToRemove $InstancesToRemove
733
734 Write-Host "Script Complete"
ModifyGroupMembership.renametops1
Comments
- Anonymous
October 02, 2013
Any idea why this error is happening whenever I run this script. It happens both just creating the groups and when using the -InstancesToAdd argument. New Reference: Microsoft.SystemCenter.InstanceGroup.Library You cannot call a method on a null-valued expression. At S:ModifyGroupMembership.ps1:112 char:32
- $s = ($ref.Value.ToString <<<< ().Split("=")[1]).Split(",")[0] + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At S:ModifyGroupMembership.ps1:113 char:21
- if ($s.ToUpper <<<< () -eq $mpID.ToUpper()) {$bFound = $true} + CategoryInfo : InvalidOperation: (ToUpper:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull New Reference: System.Library You cannot call a method on a null-valued expression. At S:ModifyGroupMembership.ps1:87 char:32
- $s = ($ref.Value.ToString <<<< ().Split("=")[1]).Split(",")[0] + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At S:ModifyGroupMembership.ps1:88 char:21
- if ($s.ToUpper <<<< () -eq $mpID.ToUpper()) + CategoryInfo : InvalidOperation: (ToUpper:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At S:ModifyGroupMembership.ps1:87 char:32
- $s = ($ref.Value.ToString <<<< ().Split("=")[1]).Split(",")[0] + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At S:ModifyGroupMembership.ps1:88 char:21
- if ($s.ToUpper <<<< () -eq $mpID.ToUpper()) + CategoryInfo : InvalidOperation: (ToUpper:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull MP Reference Not Found, Exiting: Microsoft.SystemCenter.InstanceGroup.Library
Anonymous
October 09, 2013
Hi, it's hard to tell where the issue is just by the exception. Can you send me more details, such as the existing management pack you're trying to modify? I assume it doesn't happen when you pass in a management pack that doesn't exist? Thanks.Anonymous
December 04, 2013
Thanks, this is a great script! I just had to search for the correct input for the parameters ManagementPackID & GroupID. Because the it is slightly different than the Ids i get from the get-scomgroup and get-scommanagementpack .id value.Anonymous
February 23, 2014
Some additional clarification on the inputs for "ManagementPackID" and "GroupID" would be appreciated, as the default "ID" properties of the returned objects from "Get-SCOMGroup" and "Get-SCOMManagementPack" don't appear to work.Anonymous
March 09, 2014
Hey Greg. In the XML the management pack ID is the name of the MP file itself (minus the file extension). The group ID is under <TypeDefinitions><EntityTypes><ClassType ID="<thegroupid">.Anonymous
June 13, 2014
Hi Russ, thank you for the script. Got a few messages, but it did the job. I wonder if the format I'm passing to the script has some casting issue: Method invocation failed because [System.Guid] does not contain a method named 'Split'. At C:UsersadministratorModifyGroupMembership.PS1:185 char:3
- $guids = $guids.Split(",")
- ~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound Method invocation failed because [System.Guid] does not contain a method named 'Trim'. At C:UsersadministratorModifyGroupMembership.PS1:188 char:5
- $guid = $guid.Trim()
- ~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound Method invocation failed because [System.Guid] does not contain a method named 'Trim'. At C:UsersadministratorModifyGroupMembership.PS1:188 char:5
- $guid = $guid.Trim()
- ~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound My Id collection is an array, which seems to work fine, but causes the errors above. Guid ---- f25d1a58-53b5-0936-780b-8326e37bcef4 00accda3-17b4-f6b7-e956-adaa17fdc051 That might be something the script could check/handle. Just a suggestion. Thanks again! Jose Fehse
Anonymous
June 16, 2014
Jose, it looks like the script expects a string, not a GUID. Try putting quotes around the GUIDs when you pass them in (or cast them to a string).Of course you could modify the script to remove that logic as well since you're definitely passing in good data.Anonymous
October 01, 2014
Hi Russ, Thank you for the amazing Script. It's been super useful. I just wanted to bring this to your attention, as I'm not sure if it's a bug or some issue with the way the group is setup. Do you have any idea what the cause might be? Exception calling "AcceptChanges" with "0" argument(s): "Verification failed with 1 errors:
Error 1: Found error in 1|QUT.IT.Network.Servers|1.0.0.0|UINameSpace889d6dc7465f4dd691924fc7b9819c6b.Group.DiscoveryRule/GroupPopulationDataSource|| with message: The configuration specified for Module GroupPopulationDataSource is not valid. : Cannot find specified MPElement MicrosoftWindowsLibrary!Microsoft.Windows.Computer in expression: $MPElement[Name="MicrosoftWindowsLibrary!Microsoft.Windows.Computer"]$ Cannot resolve identifier MicrosoftWindowsLibrary!Microsoft.Windows.Computer in the context of management pack QUT.IT.Network.Servers. Unknown alias: MicrosoftWindowsLibrary
" At C:essscriptsSCOMAutomationAutoupdate_NOC_GroupsModifyGroupMembership.ps1:467 char:3
- $mp.AcceptChanges()
- ~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : ManagementPackException
Anonymous
October 13, 2014
Hey Greg, could be an issue but I'm not exactly sure what it is based on the output. It is has to do with the alias in the manifest of the MP though.Anonymous
January 22, 2015
The comment has been removedAnonymous
January 23, 2015
I unfortunately did not, but this script could easily be launched from Orchestrator, SMA, etc...