Friday, March 11, 2016

SCSM View Manager

Hello everyone,

I was recently asked to create a tool that will allow our SCSM Administrators to more quickly and efficiently manage the Views and folders on SCSM, particularly speaking Cireson Views/Folders. The root cause was that they are on the plans of restructuring the company’s structure and the effort would take a long time with the built in methods.

My first thought was to create PowerShell scripts to automate this task, but they asked me if it was possible to create a GUI application for this instead. I was a bit hesitant at first because with GUIs you are normally bound to have to baby sit the user a lot more than with a cmdlet where the user normally has to perform their own research before being able to do much. Never the less I agreed, and here are the fruits of this work.



The application works by gathering the information from the exported Management Pack XML file, and making changes to it. It is fully developed in PowerShell working with PowerShell Studio 2015 (now 2016 edition) and I’ll get the source code into GitHub later this week.

The application has different features:

Folder Features:

  • Create a new Folder: this function is currently configured to create a Cireson folder but this can be easily modified or even a new function can be added to determine if you want to make a Cireson folder or a regular folder. (It goes without saying that you do need to have the Cireson suite installed or it won’t work) 
  • Create a new folder from template: this function will prompt you for a new name, and then will search for and clone the folder named ‘TemplateFolder’ as well as all subfolders and subitems. The function will then attempt to replace “TemplateFolder” with the new provided name on all of the created items. If a ‘TemplateFolder’ is not found then the function will fail and an error will be send out. 
    • Current Template folder example: 
  • Rename Folder: This function will prompt you for a new name and rename the name of the folder in the DisplayString XML Element 
  • Delete Folder: This function will delete the currently selected folder, and all of its subfolders and views, from the XML file. 
    • Removed Elements: 
      • <Categories> 
      • <Folder> 
      • <FolderItems> 
      • <ImageReference> 
      • <DisplayString> 
  • Clone Folder: This function will clone the folder into the same parent folder along with all of its subfolders and views.

View Features:

  • Copy View: This function will prompt you for a new name and copy the view with the new name and all of its properties to the same parent location. 
  • Delete View: This function will delete the currently selected view from the XML File. 
    • Removed Elements: 
      • <Categories> 
      • <View> 
      • <FolderItem> 
      • <ImageReference> 
      • <DisplayString> 
  • Rename View: This function will prompt you for a new name, and rename the View in the DisplayString XML Element.

Other Features:

  • The program also allows you for you to be able to drag and drop folders and views to any location. 

Requirements: 

  • This programs requires that you have Windows PowerShell 4.0 or above installed.
All of the changes that are performed are automatically saved to the XML file, so make sure you make a backup before you start making changes if you are not able to quickly export it.

I hope you’ll let me know what you think, if you have any other ideas, issues or concerns.

Download path:
https://www.dropbox.com/s/zmhvikam8snjv81/SCSM%20View%20Manager.exe?dl=0

I have made a new respository for this on GitHub that you can find here:
https://github.com/Jonatan-B/SCSMViewManager

Kind regards,
Me.

Wednesday, March 9, 2016

SCOM Deleting Inactive (Grayed) servers from CSV

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.