locked
Powershell command to UPDATE existing group with subgroups RRS feed

  • Question

  • I have a powershell script that will create groups in SCOM, it will also create groups with subgroups.

    the problem is that the subgroups will be dynamic - not that much but over time - weeks/months a new subgroup might appear and would have to be added as a 2nd or 3rd subgroup.

    my question is, how would I be able to update the existing group ??

    right now I am just using a "stupid" approach where I just delete the group and then create it again with the right subgroups.
    the delete works fine, if it has subgroups, those are not affected, they remain, but are no longer a subgroup of anything, which is good, so I can add them again right after the delete...
    I am just wondering if there is an update option rather than delete and create again.

    background: we have an external CMDB in which we have info about the location, city, country, continent. we discover those properties in a custom sealed MP. and the locationgroups have dynamic membership where computers will become a member based on their discovered location key

    so we create a group for each location that has 1 or more servers.
    then I create the city group, and make all the locations in that city a subgroup of the city group.
    Create country groups, and make all cities in that country member of that group.
    create continents and make all countries for that continent member of that group.
    since we have more than 700 locations globally, I don't want to create them manually, and it is fairly dynamic, locations are opened and closed, or they move to another location - so I want to update it every week or so. if lucky, nothing has changed and the script is pretty quick....

    so.. I know how to create the groups, I know how to create groups with subgroups, I know how to compare existing groups and determine if it has all the subgroups that it should have, but if I determine a city that is "missing" a location for example, then how to update it ?? I now delete it and create it again, but it feels not right to do it that way....

    just for completeness, here is the entire script that does all of the above... except the update...

    $ManagementServer="SCOMMS01"
    $ManagementPackID="TestGroups"
    $SqlConnection = New-Object System.Data.SqlClient.SqlConnection
    $SqlCmd = New-Object System.Data.SqlClient.SqlCommand
    $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
    $SqlServer = "SQLDW02"
    $db = "CMDB"
    $global:ret1=0
    
    Function Main
    {
    	initializeSQL
    	#Lets start with creating the groups with dynamic population
    	$qry = "select DISTINCT bb.Location from ServerInfo aa 
    			inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress"
    	$DataSet = New-Object System.Data.DataSet
    	$dataset = querySQL $qry
    	if ($global:ret1 -le 0) { exit }
    	foreach ($a in $DataSet.Tables[0])
    	{
    		$Groupname = (XMLEncode $a.Location)
    		if (!(Get-SCOMGroup $Groupname))
    		{
    			#Group does not exist yet, so let's create it
    			Creategroup $ManagementPackID $Groupname
    		}
            Else
            {write-host "$Groupname already exist"}
    	}
    	#all locations have been created, so now we start building the level above it (city)
    	$qry = "select DISTINCT cc.City from ServerInfo aa 
    			inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress 
    			left join dbo.Locations cc on cc.LocationID=bb.LocationID"
    	$DataSet1 = New-Object System.Data.DataSet
    	$dataset1 = querySQL $qry
    	if ($global:ret1 -le 0) { exit }
    	foreach ($a in $DataSet1.Tables[0])
    	{
    		$city = (XMLEncode $a.city)
    		$citygrp = Get-SCOMGroup $city
    		#collect all locations that are in that city
    		$qry = "select DISTINCT bb.Location
    				from ServerInfo aa
    				inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress
    				left join dbo.Locations cc on cc.LocationID=bb.LocationID
    				where cc.City = '$city'"
    		$DataSet2 = New-Object System.Data.DataSet
    		$dataset2 = querySQL $qry
    		if ($global:ret1 -le 0) { continue }
    		#collect all locations in the city and store the id in $subgrps
    		$subgrps = @()
    		foreach ($loc in $DataSet2.Tables[0])
    		{
    			$subgrps += (Get-SCOMGroup $loc.Location).id
    		}
    		if (!($citygrp))
    		{
    			#city does not exist yet
    			CreateGroupWithSubgroup $ManagementPackID $city $subgrps
    		}
    		Else
    		{
    			#city exists so let's check if it contains all subgroups already, if so then there is nothing to do...
    			$citysubs=@()
    			foreach($ee in ($citygrp.GetChildMonitoringObjectGroups())){$citysubs += $ee.id}
    			if (!(CompareGroupGUIDs $citysubs $subgrps))
    			{
    				#not equal so let's Delete the group and create a new one
    				$mp.DeleteMonitoringObjectGroup($citygrp)
    				start-sleep 30
    				CreateGroupWithSubgroup $ManagementPackID $city $subgrps
    			}
                Else
                {
                    Write-Host "$city already exist, with the right subgroups"
                }
    			#if they are equal, do nothing, proceed to next city
    		}
    	}
    	#all Cities have been created, so now we start building the level above it (country)
    	$qry = "select DISTINCT cc.Country from ServerInfo aa 
    			inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress 
    			left join dbo.Locations cc on cc.LocationID=bb.LocationID"
    	$DataSet2 = New-Object System.Data.DataSet
    	$DataSet2 = querySQL $qry
    	if ($global:ret1 -le 0) { exit }
    	foreach ($a in $DataSet2.Tables[0])
    	{
    		$country = (XMLEncode $a.Country)
    		$Countrygrp = Get-SCOMGroup $Country
    		#locate all cities that are in this country
    		$qry = "select DISTINCT cc.City
    				from ServerInfo aa
    				inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress
    				left join dbo.Locations cc on cc.LocationID=bb.LocationID
    				where cc.Country = '$Country'"
    		$DataSet3 = New-Object System.Data.DataSet
    		$dataset3 = querySQL $qry
    		if ($global:ret1 -le 0) { continue }
    		#collect all locations in the Country and store the id in $subgrps
    		$subgrps = @()
    		foreach ($loc in $DataSet3.Tables[0])
    		{
    			$subgrps += (Get-SCOMGroup $loc.City).id
    		}
    		if (!($Countrygrp))
    		{
    			#Country does not exist yet
    			CreateGroupWithSubgroup $ManagementPackID $Country $subgrps
    		}
    		Else
    		{
    			#Country exists so let's check if it contains all subgroups already, if so then there is nothing to do...
    			$Countrysubs=@()
    			foreach($ee in ($Countrygrp.GetChildMonitoringObjectGroups())){$Countrysubs += $ee.id}
    			if (!(CompareGroupGUIDs $Countrysubs $subgrps))
    			{
    				#not equal so let's Delete the group and create a new one
    				$mp.DeleteMonitoringObjectGroup($Countrygrp)
    				start-sleep 30
    				CreateGroupWithSubgroup $ManagementPackID $Country $subgrps
    			}
                Else
                {
                    Write-Host "$Country already exist, with the right subgroups"
                }
    			#if they are equal, do nothing, proceed to next Country
    		}
    	}
    	#all Countries have been created, so now we start building the level above it (Continent)
    	$qry = "select DISTINCT cc.Continent from ServerInfo aa 
    			inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress 
    			left join dbo.Locations cc on cc.LocationID=bb.LocationID"
    	$DataSet4 = New-Object System.Data.DataSet
    	$DataSet4 = querySQL $qry
    	if ($global:ret1 -le 0) { exit }
    	foreach ($a in $DataSet4.Tables[0])
    	{
    		$Continent = (XMLEncode $a.Continent)
    		$Continentgrp = Get-SCOMGroup $Continent
    		#collect all countries that are in this continent
    		$qry = "select DISTINCT cc.country
    				from ServerInfo aa
    				inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress
    				left join dbo.Locations cc on cc.LocationID=bb.LocationID
    				where bb.Location like 'Netherlands%'"
    		$DataSet5 = New-Object System.Data.DataSet
    		$dataset5 = querySQL $qry
    		if ($global:ret1 -le 0) { continue }
    		#collect all locations in the Continent and store the id in $subgrps
    		$subgrps = @()
    		foreach ($loc in $DataSet5.Tables[0])
    		{
    			$subgrps += (Get-SCOMGroup $loc.country).id
    		}
    		if (!($Continentgrp))
    		{
    			#Continent does not exist yet
    			CreateGroupWithSubgroup $ManagementPackID $Continent $subgrps
    		}
    		Else
    		{
    			#Continent exists so let's check if it contains all subgroups already, if so then there is nothing to do...
    			$Continentsubs=@()
    			foreach($ee in ($Continentgrp.GetChildMonitoringObjectGroups())){$Continentsubs += $ee.id}
    			if (!(CompareGroupGUIDs $Continentsubs $subgrps))
    			{
    				#not equal so let's delete it and create the proper one.
    				$mp.DeleteMonitoringObjectGroup($Continentgrp)
    				start-sleep 30
    				CreateGroupWithSubgroup $ManagementPackID $Continent $subgrps
    			}
                Else
                {
                    Write-Host "$Continent already exist, with the right subgroups"
                }
    			#if they are equal, do nothing, proceed to next Continent
    		}
    	}
    	#all continents have been created so let's create the last level - Hemisphere
    	$dataset6="Eastern Hemisphere","Western Hemisphere"
    	foreach ($a in $DataSet4.Tables[0])
    	{
    		$hemisphere = (XMLEncode $a)
    		$hemispheregrp = Get-SCOMGroup $Hemisphere
    		if ($hemisphere -match "Western")
    		{
    			$qry = "select DISTINCT cc.Continent
    					from ServerInfo aa
    					inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress
    					left join dbo.Locations cc on cc.LocationID=bb.LocationID
    					where cc.continent LIKE '%America%'"
    		}
    		Else
    		{
    			$qry = "select DISTINCT cc.Continent
    					from ServerInfo aa
    					inner join dbo.SubNetToLoc bb on aa.SubNetAddress = bb.SubNetAddress
    					left join dbo.Locations cc on cc.LocationID=bb.LocationID
    					where cc.continent NOT LIKE '%America%' and bb.Location like 'Netherlands%'"
    		}
    		$DataSet7 = New-Object System.Data.DataSet
    		$dataset7 = querySQL $qry
    		if ($global:ret1 -le 0) { continue }
    		#collect all locations in the city and store the id in $subgrps
    		$subgrps = @()
    		foreach ($loc in $DataSet7.Tables[0])
    		{
    			$subgrps += (Get-SCOMGroup $loc.Continent).id
    		}
    		if (!($hemispheregrp))
    		{
    			#city does not exist yet
    			CreateGroupWithSubgroup $ManagementPackID $hemisphere $subgrps
    		}
    		Else
    		{
    			#city exists so let's check if it contains all subgroups already, if so then there is nothing to do...
    			$Continentsubs=@()
    			foreach($ee in ($citygrp.GetChildMonitoringObjectGroups())){$Continentsubs += $ee.id}
    			if (!(CompareGroupGUIDs $Continentsubs $subgrps))
    			{
    				#not equal so let's delete and create properly again
    				$mp.DeleteMonitoringObjectGroup($hemispheregrp)
    				start-sleep 30
    				CreateGroupWithSubgroup $ManagementPackID $hemisphere $subgrps
    			}
                Else
                {
                    Write-Host "$city already exist, with the right subgroups"
                }
    			#if they are equal, do nothing, proceed to next city
    		}
    	}
    }
    
    Function initializeSQL
    {
    	$SqlConnection.ConnectionString = "Data Source=$Sqlserver;Initial Catalog=$db;User Id=test;Password=password;"
    	$SqlCmd.CommandTimeout = 0
    	$SqlCmd.Connection = $SqlConnection
    }
    
    Function querySQL ($qry)
    {
    	$DataSet = New-Object System.Data.DataSet
    	$SqlCmd.CommandText = $qry
    	$SqlAdapter.SelectCommand = $SqlCmd
    	$global:ret1 = $SqlAdapter.Fill($DataSet)
    	return $DataSet
    }
    
    function CompareGroupGUIDs($group1,$group2)
    {
    	[string]$grpstr1=""
    	foreach($ff in $group1){$grpstr1+=$ff.guid}
    	[string]$grpstr2=""
    	foreach($ff in $group2){$grpstr2+=$ff.guid}
    	if ($grpstr1 -eq $grpstr2)
    	{ return $True }
    	Else
    	{ Return $False }
    }
    
    function CreateManagementPack
    {
      param([object]$MG, [string]$ManagementPackID)
      $MPStore = New-Object Microsoft.EnterpriseManagement.Configuration.IO.ManagementPackFileStore
      $MP = New-Object Microsoft.EnterpriseManagement.Configuration.ManagementPack($ManagementPackID, $ManagementPackID, (New-Object Version(1, 0, 0)), $MPStore)
      $MG.ImportManagementPack($MP)
    }
    
    function XMLEncode
    {
      param([string]$s)
      $s = $s.Replace("&", "&")
      $s = $s.Replace("<", "&lt;")
      $s = $s.Replace(">", "&gt;")
      $s = $s.Replace('"', "&quot;")
      $s = $s.Replace("'", "&apos;")
      return $s.ToString()
    }
    
    Function CreateGroupId
    {
      param([string]$s)
      $s = $s.Replace("&", "")
      $s = $s.Replace("<", "")
      $s = $s.Replace(">", "")
      $s = $s.Replace('"', "")
      $s = $s.Replace("'", "")
      $s = $s.Replace(" ", "")
      $s = $s.Replace(",", "")
      $s = $s.Replace("-", "")
      $s = $s.Replace("_", "")
      return $s.ToString()    
    }
    
    Function CreateGroupWithSubgroup($ManagementPackID,$GroupName,$groupGUID)
    {
    	#build formula1 with dynamic amount of subgroups - this same routine is also called if the subgroups change
        $GroupId = CreateGroupId $groupname
    	$formula1 = '<MembershipRule>' +`
    				'<MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass>' +`
    				'<RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>' +`
    				'<IncludeList>'
        foreach ($id in $groupGUID)
        {
            $formula1 += '<MonitoringObjectId>' +`
                         $id.guid +`
                         '</MonitoringObjectId>'
        }
    	$formula1 += '</IncludeList>' +`
    				 '</MembershipRule>'
                     
    	$Formula1 = $Formula1.Replace("SCIG", $global:Alias)
    	#Write-Host "Creating Group with subgroup"
    	$Group = New-Object Microsoft.EnterpriseManagement.Monitoring.CustomMonitoringObjectGroup($ManagementPackID, $GroupID, (XMLEncode -s $GroupName), $Formula1)
    	$Group.Description = (XMLEncode -s "Group with subgroup")
    
    	Write-Host "Creating $Groupname with subgroup"
    	try
    	{
    	   $MP.InsertCustomMonitoringObjectGroup($Group)
           $MP.AcceptChanges()
    	}
    	catch [System.Exception]
    	{
    	Write-Host $_.Exception
    	}
    }
    
    Function UpdateGroupWithSubgroup($ManagementPackID,$GroupName,$groupGUID)
    {
    	#build formula1 with dynamic amount of subgroups - this same routine is also called if the subgroups change
        $GroupId = CreateGroupId $groupname
    	$grp = Get-SCOMGroup $groupname
    	$formula1 = '<MembershipRule>' +`
    				'<MonitoringClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroup"]$</MonitoringClass>' +`
    				'<RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>' +`
    				'<IncludeList>'
        foreach ($id in $groupGUID)
        {
            $formula1 += '<MonitoringObjectId>' +`
                         $id.guid +`
                         '</MonitoringObjectId>'
        }
    	$formula1 += '</IncludeList>' +`
    				 '</MembershipRule>'
                     
    	$Formula1 = $Formula1.Replace("SCIG", $global:Alias)
    	Write-Host "Updating $Groupname with subgroup"
    	try
    	{
    		$grp.ApplyTemplate ($formula1)
    		$MP.AcceptChanges()
    	}
    	catch [System.Exception]
    	{
    	Write-Host $_.Exception
    	}
    }
    
    Function Creategroup($ManagementPackID,$GroupName)
    {
    	$Formula =  '<MembershipRule Comment="CCC">' +`
    				'<MonitoringClass>$MPElement[Name="ZZZ!BHI.SCOM.Custom.Discoveries.BHILocationData"]$</MonitoringClass>' +`
    				'<RelationshipClass>$MPElement[Name="SCIG!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>' +`
                    '<Expression>' +`
                    '<SimpleExpression>' +`
                    '<ValueExpression>' +`
                    '<Property>$MPElement[Name="ZZZ!SCOM.Custom.Discoveries.LocationData"]/Location$</Property>' +`
                    ' </ValueExpression>' +`
                    '<Operator>Equal</Operator>' +`
    			    '<ValueExpression>' +`
    				'<Value>CCC</Value>' +`
    			    '</ValueExpression>' +`
                    '</SimpleExpression>' +`
                    '</Expression>' +`
    				'</MembershipRule>'
    				
    	$Formula = $Formula.Replace("SCIG", $global:Alias)
        $Formula = $Formula.Replace("ZZZ", $global:AliasLoc)
    	$Formula = $Formula.Replace("CCC", $GroupName)
        $GroupId = CreateGroupId $groupname
    	#Write-Host "Creating Group"
    	$Group = New-Object Microsoft.EnterpriseManagement.Monitoring.CustomMonitoringObjectGroup($ManagementPackID, $GroupID, (XMLEncode -s $GroupName), $Formula)
    	$Group.Description = (XMLEncode -s "$GroupName Group")
    
    	Write-Host "Creating $Groupname"
    	try
    	{
    	   $MP.InsertCustomMonitoringObjectGroup($Group)
           $MP.AcceptChanges()
    	}
    	catch [System.Exception]
    	{
    	Write-Host $_.Exception
    	}
    }
    
    ####################################### Start ##############################################
    #import-module if needed
    $Module = get-module|where {$_.Name -match "OperationsManager"}
    if (!($Module)){
        Write-Host "Import OperationsManager Module"
        import-module OperationsManager
    }
    Write-Host "Connecting to SCOM Management Group"
    $ManagementServer = New-Object Microsoft.EnterpriseManagement.ManagementGroup($ManagementServer)
    
    Write-Host "Getting MP Information and Incrementing Version"
    try
    {
      $mp = $ManagementServer.GetManagementPacks($ManagementPackID)[0]
      $VIncrement = $mp.Version.ToString().Split('.')
      $VIncrement[$VIncrement.Length - 1] = ([system.int32]::Parse($VIncrement[$VIncrement.Length - 1]) + 1).ToString()
      $MP.Version = ([string]::Join(".", $VIncrement))
    }
    catch
    {
      Write-Host "MP Not Found, Creating New MP"
      CreateManagementPack $ManagementServer $ManagementPackID
      start-sleep -Seconds 30
      $mp = $ManagementServer.GetManagementPacks($ManagementPackID)[0]
    }
    
    ##################################################
    Write-Host "Getting Alias for the Microsoft.SystemCenter.InstanceGroup.Library Management Pack Reference"
    try
    {
    $global:Alias = $mp.References.GetAlias(($MP.References | where {$_.Name -match 'Microsoft.SystemCenter.InstanceGroup.Library'}))
    }
    Catch
    {}
    if(!($global:Alias))
    {
      Write-Host "Management Pack Reference Not Found, Creating one"
      $mptoadd = $ManagementServer.GetManagementPacks("Microsoft.SystemCenter.InstanceGroup.Library")[0]
      $MPReference = New-Object Microsoft.EnterpriseManagement.Configuration.ManagementPackReference($MPToAdd)
      $mp.References.Add("GroupRef",$MPReference)
      $MP.AcceptChanges()
      start-sleep -Seconds 30
      $MP = $ManagementServer.GetManagementPacks($ManagementPackID)[0]
      $global:Alias = $mp.References.GetAlias(($MP.References | where {$_.Name -match 'Microsoft.SystemCenter.InstanceGroup.Library'}))
      If (!($global:Alias)){exit}
    }
    ##############################
    Write-Host "Getting Alias for the BHI.SCOM.Custom.Discoveries Management Pack Reference"
    try
    {
    $global:AliasLoc = $mp.References.GetAlias(($MP.References | where {$_.Name -match 'BHI.SCOM.Custom.Discoveries'}))
    }
    catch
    {}
    if(!($global:Aliasloc))
    {
      Write-Host "Management Pack Reference Not Found, Creating one"
      $mptoadd = $ManagementServer.GetManagementPacks("BHI.SCOM.Custom.Discoveries")[0]
      $MPReference = New-Object Microsoft.EnterpriseManagement.Configuration.ManagementPackReference($MPToAdd)
      $mp.References.Add("BHISCOMCustomDiscoveries",$MPReference)
      $MP.AcceptChanges()
      start-sleep -Seconds 30
      $MP = $ManagementServer.GetManagementPacks($ManagementPackID)[0]
      $global:AliasLoc = $mp.References.GetAlias(($MP.References | where {$_.Name -match 'BHI.SCOM.Custom.Discoveries'}))
      If (!($global:AliasLoc)){exit}
    }
    
    
    main
    
    Write-Host "Script Completed"


    • Edited by A. Prins Monday, June 1, 2015 4:39 PM
    Monday, June 1, 2015 3:33 PM

All replies

  • small update for one function, the original would say it does not match if the order of subgroups is different...

    function CompareGroupGUIDs($group1,$group2)
    {
    	$Result = $True
    	foreach($ff in $group1)
    	{
    		if ($group2 -notcontains $ff.guid){$result = $false}
    	}
    	foreach($gg in $group2)
    	{
    		if ($group1 -notcontains $gg.guid){$result = $false}
    	}
    	return $result
    }


    Monday, June 1, 2015 4:29 PM