Hello everyone,
We have been cleaning out our SCOM environment from servers that have been decommissioned from the environment but have not yet been deleted. This turned out to be a quite a bother because you have to one by one find the inactive (grayed) server in the Managed Servers list, right click and delete.
Of course this called for some PowerShell to get the job done faster, but I couldn't find any cmdlets that would help me get this done. My google-fu took me to this
article where I was able to gather the servers that were inactive.
Acquired source code:
#Import the
OperationsManager module
Import-Module
OperationsManager
#Define the output file
$file=”C:\Temp\GreyAgents.txt”
#Get the agent class and
the each object which is in a grey state
$agent
= Get-SCClass
-name “Microsoft.SystemCenter.Agent"
$objects
= Get-SCOMMonitoringObject
-class:$agent
| where {$_.IsAvailable
–eq $false}
#Export each objet into
a file
foreach($object in $objects){
$object.DisplayName+ “,”+ $object.HealthState |
Out-File $file
-Append
}
* I had to fix a typo in the original code
Now that I had the objects I had to find out how to remove them, and I found my solution in this msdn
article
Acquired source code:
# Summary
# Delete an agent hosted by a non-existent
computer.
# Params
# AgentNames - An array of strings that
contain the FQDN of agents to delete.
# Returns
# None
function
global:Delete-Agent([System.String[]] $agentNames)
{
$NoAgentsErrorMsg = "`nNo agent
names specified. Please specify the FQDN for each agent you want to
delete.`n";
if ($agentNames
-eq $null)
{
Write-Host $NoAgentsErrorMsg;
return;
}
$administration = (get-item .).ManagementGroup.GetAdministration();
$agentManagedComputerType = [Microsoft.EnterpriseManagement.Administration.AgentManagedComputer];
$genericListType = [System.Collections.Generic.List``1]
$genericList = $genericListType.MakeGenericType($agentManagedComputerType)
$agentList =
new-object $genericList.FullName
foreach ($agentName
in $agentNames)
{
$agent =
Get-Agent |
where {$_.PrincipalName -eq
$agentName}
if ($agent
-eq $null)
{
$msg
= "Agent '{0}' not found." -f $agentName;
Write-Host
$msg;
}
else
{
$agentList.Add($agent);
}
}
if ($agentList.Count -eq 0)
{
Write-Host $NoAgentsErrorMsg;
return;
}
$genericReadOnlyCollectionType = [System.Collections.ObjectModel.ReadOnlyCollection``1]
$genericReadOnlyCollection = $genericReadOnlyCollectionType.MakeGenericType($agentManagedComputerType)
$agentReadOnlyCollection = new-object $genericReadOnlyCollection.FullName @(,$agentList);
$msg =
"`nDeleting {0} agents:`n" -f $agentReadOnlyCollection.Count;
Write-Host $msg;
foreach ($agent
in $agentReadOnlyCollection)
{
Write-Host $agent.PrincipalName;
}
$administration.DeleteAgentManagedComputers($agentReadOnlyCollection);
}
However, this isn't quite what I was looking for as I wanted something to remove the servers from the CSV file that I have, and so I had to modify the script and this is the end result:
# Get the Class Object
$agentClass
= Get-SCClass
-Name "Microsoft.SystemCenter.Agent"
-ComputerName SCOMSERVERS[blankifyourrunfromManagementServer]
# Get the
MonitoringObject based on the class, and filter out only the available ones and
the ones that have the HealthState not as Uninitialzied
# This will prevent that
new servers will be removed.
$inactiveObjects
= Get-SCOMMonitoringObject
-Class $agentClass
-ComputerName SCOMSERVERS[blankifyourrunfromManagementServer]
| ? IsAvailable -eq
$false |
? HealthState
-ne "Uninitialized"
# Import your CSV File,
my column name is "InactiveServers" and I added the Unique key to
ensure no repeats are added which would cause an issue later.
$csv
= (Import-Csv
C:\temp\SCOMInactiveServers.csv | select * -Unique)
# Createa a new
GenericList
$genericListType
= [System.Collections.Generic.List``1]
$genericList
= $genericListType.MakeGenericType([Microsoft.EnterpriseManagement.Administration.AgentManagedComputer])
$agentList
= New-Object
$genericList.FullName
# Get the
ManagementGroup Object
$managementGroup
= Get-SCOMManagementGroup
-ComputerName SCOMSERVERS[blankifyourrunfromManagementServer]
# Get the Administration
Property from the ManagementGroup Object
$Administration
= $managementGroup.Administration
foreach($server in $csv.InactiveServers)
# Loop through the servers in your CSV file
{
# Create a new report object
$reportObject = New-Object psobject
# Add a member property to the object that
will represent the name
$reportObject | Add-Member -MemberType NoteProperty
-Name "InactiveServers"
-Value $server
if($inactiveObjects.DisplayName -contains
$server) #
Check if the server is found in the inactive objects
{
$reportObject | Add-Member -MemberType NoteProperty
-Name "Status"
-Value "Inactive"
# If the server is found in the inactive
list then we do want to remove it
# Get the SCOMAgent object and add it to
the Generic List created above
$agent =
Get-SCOMAgent -DNSHostName
$server -ComputerName
SCOMSERVERS[blankifyourrunfromManagementServer]
$agentList.Add($agent)
} # endif
else #
If it is not found that means that the server is now back to being active and
we don't want to remove it anymore
{
# Edit #1
$agent
= Get-SCOMAgent
-DNSHostName $server
-ComputerName wp-scom-ms-01
if($agent
-eq $null)
{
$reportObject
| Add-Member
-MemberType NoteProperty
-Name "Status"
-Value "Offline"
}
else
{
$reportObject
| Add-Member
-MemberType NoteProperty
-Name "Status"
-Value "Active"
}
} # endelse
# Export the results of the query to a CSV
file.
$reportObject | Export-Csv C:\temp\SCOMInactiveServers.csv -Append
} #
endloop
# Create a ReadOnly
Collection
<#
Truth be told I'm not 100% sure why this is
needed, and I did tried it without it and got some errors,
but then found out it was because of the
"Unique" value on line 8 and wasn't able to test again
#>
$genericReadOnlyCollectionType
= [System.Collections.ObjectModel.ReadOnlyCollection``1]
$genericReadOnlyCollection
= $genericReadOnlyCollectionType.MakeGenericType([Microsoft.EnterpriseManagement.Administration.AgentManagedComputer])
$agentReadOnlyCollection
= New-Object
$genericReadOnlyCollection.FullName @(,$agentList)
# Print out a message
with the number of computers that will be removed
$msg
= "`nDeleting
{0} agents:`n" -f $agentReadOnlyCollection.Count;
Write-Host
$msg;
# Removes the computers
from SCOM.
$Administration.DeleteAgentManagedComputers($agentReadOnlyCollection)
The comments pretty much explain the logic that I followed but in a TL;DR version: The script will gather all of the currently inactive servers and then check that the servers in your list are indeed inactive, and export a report of it, before sending them to be removed.
Do let me know if you find this helpful or if you have any feedback, questions or concerns.
Kind regards,
Me.
Edit:
I made a change for reporting purposes on the Else on line 35. This was mainly because it was showing "active" for servers that were active or that no longer exist on the SCOM Environment.