Command Shell Examples
Useful SQL Queries
Solving the problem of automated distribution of credentials (group criteria) - Jonathan Almquist on Operations Manager - Site Home - TechNet Blogs

Solving the problem of automated distribution of credentials (group criteria)

Solving the problem of automated distribution of credentials (group criteria)

  • Comments 1
  • Likes

It’s possible to leverage a powershell script in a composite write action to distribute credentials to a health service.  The following example will distribute the specified credentials to health services instances derived from a group of windows computer objects.

Instance groups containing other types may not work as expected without modifying the code.  Also, group system name must be used in this script.  I decided to use group system name to foster accountability in the implemention, which reduces mistakes that can easily be made by entering a group display name.

There is no reverse action.  If you specify a group that contains hundreds of windows computer objects and it was a mistake, you'll have to manually delete hundreds of health service instances from your secure distribution in the Run As Account.

The script accepts the below parameters – all are required.  First set of variables (with a “$”) can be uncommented if executing without parameters, and the second set (with a “-“) is an example of parameter format you would specify if running with parameter names.

# $RootManagementServer = "srvrms01"
# $DatabaseServer = "srvsql01"
# $DatabaseName = "OperationsManager"
# $Group = "Microsoft.SystemCenter.AllComputersGroup"
# $Account = "Test Run As Account"
# $BinariesFolder = "C:\Program Files\System Center Operations Manager 2007\SDK Binaries"
# -RootManagementServer srvrms01
# -DatabaseServer srvsql01
# -DatabaseName OperationsManager
# -Group Microsoft.SystemCenter.AllComputersGroup
# -Account "Test Run As Account"
# -BinariesFolder "f:\Program Files\System Center Operations Manager 2007\SDK Binaries"
# -Debug true

param($RootManagementServer, $DatabaseServer, $DatabaseName, $Group, $Account, $BinariesFolder, $DebugFlag) #Local
 
#-------------------------------------------------------------------------------------Start Main
# This function controls program flow.
function Main
{
    $CollectionHSIDs = CreateCollectionHSIDs
    ValidateSnapIn
    $MG = ConnectToManagementGroup
    $CollectionHSOs = CreateCollectionHSOs
    $NumInstances = $CollectionHSOs.Count
    LoadHelperClass
    AddHealthServiceToAccountDistributionList
    Remove-ManagementGroupConnection $RootManagementServer
    QuitScript $QuitSuccess "Workflow completed successfully."
}#------------------------------------------------------------------------------------End Main
 
#-------------------------------------------------------------------------------------Start AddHealthServiceToAccountDistributionList
# This function adds health service instances to the Run As Account distribution list.
function AddHealthServiceToAccountDistributionList
{
    # Add health service objects to Run As Account distribution list.
    $data = $MG.ManagementGroup.GetMonitoringSecureData() | Where-Object {$_.Name -eq $Account}
    [Helper]::ApproveCredentialForDistribution($MG.ManagementGroup, $data, $CollectionHSOs)
    
    if ($DebugFlag) {DebugEvent "Sequence 6 - Completed function AddHealthServiceToAccountDistributionList"}
}#------------------------------------------------------------------------------------End AddHealthServiceToAccountDistributionList
 
#-------------------------------------------------------------------------------------Start CreateCollectionHSIDs
# This function queries the operational database for members of the specified windows 
# computer group and resolves the health service id of those instances. Then adds
# those health service id's to a collection.
# The use of system.data.sqlclient.sqlconnection requires Windows Server 2003 SP2+,
# Windows Server 2008 or Windows Server 2008 R2 (except core editions).
function CreateCollectionHSIDs
{
    # Setup the SQL connection
    $connection = new-object system.data.sqlclient.sqlconnection("Data Source=$DatabaseServer;Initial Catalog=$DatabaseName;Integrated Security=SSPI;");
    
    # Define TSQL
    [string]$query =
                "
                /*
                Get Health Service IDs
                Based on computer group (must be WC instances)
                */
                SELECT ME.Id AS 'HSID'
                from ManagedEntityGenericView AS ME INNER JOIN
                        ManagedTypeView AS MT ON ME.MonitoringClassId = MT.Id INNER JOIN
                        RelationshipGenericView AS REL ON ME.Path = REL.TargetMonitoringObjectDisplayName
                where REL.SourceMonitoringObjectFullName = '$Group' AND
                        MT.Name = 'Microsoft.SystemCenter.HealthService' AND
                        ME.IsDeleted <> 1
                "
    
    # Create the dataset
    $execute = new-object system.data.sqlclient.sqldataadapter ($query, $connection)
    $dataset = new-object system.data.dataset
    $execute.Fill($dataset) | Out-Null #speeds processing
 
    # Create an empty collection
    [System.Collections.ArrayList]$CollectionHSIDs = @()
    
    # Add each row in dataset to the collection
    foreach ($row in $dataset.Tables[0])
        {
        $CollectionHSIDs.Add($row) | Out-Null #speeds processing
        }
    
    if ($DebugFlag) {DebugEvent "Sequence 1 - Completed function CreateCollectionHSIDs"}
    
    # Pass collection to main script.
    return $CollectionHSIDs
}#------------------------------------------------------------------------------------End CreateCollectionHSIDs
 
#-------------------------------------------------------------------------------------Start CreateCollectionHSOs
# This function adds health service objects to a collection.
function CreateCollectionHSOs
{
    # Create an empty collection.
    [System.Collections.ArrayList]$CollectionHSOs = @()
    
    # Add each Health Service instance to the collection.
    foreach ($HSID in $CollectionHSIDs)
        {
        $CollectionHSOs.Add((Get-MonitoringObject -Id $HSID.HSID.ToString())) | Out-Null #speeds processing
        }
    
    if ($DebugFlag) {DebugEvent "Sequence 4 - Completed function CreateCollectionHSOs"}
    
    # Pass collection to main script.
    return $CollectionHSOs
}#------------------------------------------------------------------------------------End CreateCollectionHSOs
 
#-------------------------------------------------------------------------------------Start LoadHelperClass
# This function is used to test and load the helper class that enables distribution.
function LoadHelperClass
{
    # Create variable to test whether type is already added.
    $b=$false
    $b = Get-Type ([helper])
    trap [exception] {continue} #always throws exception if class isn't added.
    
    # Add Type Definition for helper class if not already added
    if (!$b)
        {
        Add-Type –typedefinition $src -ReferencedAssemblies ($BinariesFolder + "\Microsoft.EnterpriseManagement.OperationsManager.dll"), 'System.Xml'
        
        # Create variable to test whether type is already added.
        $b = Get-Type ([helper])
        trap [exception] {continue} #always throws exception if class isn't added.
        
        if (!$b)
            {        
            QuitScript $QuitFailure "Error in function LoadHelperClass: Unable to load helper class."
            }
        }
    
    if ($DebugFlag) {DebugEvent "Sequence 5 - Completed function LoadHelperClass"}
    
    # Recycle variable
    Remove-Variable b
}#------------------------------------------------------------------------------------End LoadHelperClass
 
#-------------------------------------------------------------------------------------Start AddAccountToProfile
# This function associates Run As Account to Run As Profile.
function AddAccountToProfile
{
    # This function not being used at this time.
}#------------------------------------------------------------------------------------End AddAccountToProfile
 
#-------------------------------------------------------------------------------------Start ValidateSnapIn
# This function is used to test and load the OM snapin.
function ValidateSnapIn
{
    # Create a variable to hold OM snapin
    $b=$false
    $b = Get-PSSnapin | Where-Object {$_.Name -like "*Microsoft.EnterpriseManagement.OperationsManager.Client*"}
    
    # Test variable.  If empty, add OM snapin.
    if (!$b)
        {
        Add-PSSnapin -Name "Microsoft.EnterpriseManagement.OperationsManager.Client" 
        
        # Refresh variable after snapin load attempt.
        $b = Get-PSSnapin | Where-Object {$_.Name -like "*Microsoft.EnterpriseManagement.OperationsManager.Client*"}
        
        # If snapin failed to load, quit script - cannot move forward.
        if (!$b)
            {
            QuitScript $QuitFailure "Error in function ValidateSnapIn: Unable to load OM snapin."
            }
        }
    
    if ($DebugFlag) {DebugEvent "Sequence 2 - Completed function ValidateSnapin"}
    
    # Recycle variable
    Remove-Variable -Name b
}#------------------------------------------------------------------------------------End ValidateSnapIn
 
#-------------------------------------------------------------------------------------Start ConnectToManagementGroup
# This function is used to connect to the root management server and set location.
function ConnectToManagementGroup
{
    # Create variable to hold connection.
    $b = (Get-ManagementGroupConnection).ManagementServerName
    
    # Test variable.  If empty, attempt to create the connection.
    if ($b -ne $RootManagementServer)
        {
        $NewConnection = New-ManagementGroupConnection -ConnectionString:$RootManagementServer
        
        # Refresh variable after connection attempt.
        $b = (Get-ManagementGroupConnection).ManagementServerName
        
        # If still unable to connect, quit script - cannot move forward.
        if ($b -ne $RootManagementServer)
            {
            QuitScript $QuitFailure "Error in function ConnectToManagementGroup: Unable to establish connection to $RootManagementServer."
            }
        # Now that we have a connection, set location to OM monitoring drive.
        Set-Location "OperationsManagerMonitoring::"
        }
    else
        {
        # Perhaps connection already existed, so we'll just move on.
        $NewConnection = Get-ManagementGroupConnection
        Set-Location "OperationsManagerMonitoring::"
        }
    
    if ($DebugFlag) {DebugEvent "Sequence 3 - Completed function ConnectToManagementGroup"}
    
    # Recycle variable
    Remove-Variable -Name b
    return $NewConnection
}#------------------------------------------------------------------------------------End ConnectToManagementGroup
 
#-------------------------------------------------------------------------------------Start QuitScript
# This function is used to set error levels and return details.
function QuitScript ($Level, $Detail)
{
    if ($Level -eq 1)
        {
        $Message = "$Detail Account distribution failed for $Account."
        Write-EventLog -LogName Application -Source OM-SecureDist -EventID 100 -EntryType Error -Message $Message
        exit
        }
    else
        {
        $Message = "$Detail Distribution occurred for $Account for $NumInstances health service instance(s)."
        Write-EventLog -LogName Application -Source OM-SecureDist -EventID 100 -EntryType Information -Message $Message
        exit
        }
}#------------------------------------------------------------------------------------End QuitScript
 
#-------------------------------------------------------------------------------------Start DebugEvent
# This function is used to write debug events.
function DebugEvent ($Detail)
{
    $Message = "$Account : $Detail"
    Write-EventLog -LogName Application -Source OM-SecureDist -EventID 101 -EntryType Information -Message $Message
}#------------------------------------------------------------------------------------End DebugEvent
 
#-------------------------------------------------------------------------------------Start Get-Type
# This function is used to test whether the helper class has been added.
# Successfully detects when type is added, but throws exception when it's not.
# http://solutionizing.net/2009/01/01/powershell-get-type-simplified-generics/
function global:Get-Type ($type = $(Throw "Please specify a type"))
{
    trap [System.Management.Automation.RuntimeException] { Throw ($_.Exception.Message) }
    
    if ($Args -and $Args.Count -gt 0)
        {
        $types = $Args[0] -as [type[]]
        if (-not $types)
            {
            $types = [type[]] $Args
            }
        if ($genericType = [type] ($type + '`' + $types.Count))
            {
            $genericType.MakeGenericType($types)
            }
        }
    else
        {
        [type] $type
        }
}#------------------------------------------------------------------------------------End Get-Type
 
#-------------------------------------------------------------------------------------Start variables
# Set variables
$QuitFailure = 1
$QuitSuccess = 0
 
# Source for helper class
$src = @"
using Microsoft.EnterpriseManagement;
using Microsoft.EnterpriseManagement.Monitoring;
using System.Collections.Generic;
public class Helper {
    public static void ApproveCredentialForDistribution(ManagementGroup mg, ISecuredData credential, MonitoringObject[] targets) {
        mg.ApproveCredentialForDistribution<MonitoringObject>(credential, new List<MonitoringObject>(targets));
    }
}
"@
 
# Set Debug Flag
if ($DebugFlag -and $DebugFlag -ne "true")
    {
    Remove-Variable DebugFlag
    }
 
#-------------------------------------------------------------------------------------End variables
 
#-------------------------------------------------------------------------------------Start EventLogSourceCheck
# Check if the OM-SecureDist source has been added to the Operations Manager log.
$b=$false
$b=[system.diagnostics.eventlog]::SourceExists("OM-SecureDist")
trap [Exception] {continue}
if (!$b)
    {
    # Create the source if it doesn't exist.
    # Requires elevated privileges.
    New-EventLog -Source OM-SecureDist -LogName Application
    }
# Recycle variable
Remove-Variable b
#-------------------------------------------------------------------------------------End EventLogSourceCheck
 
#-------------------------------------------------------------------------------------Start variables for local runtime
# $RootManagementServer = "srvrms01"
# $DatabaseServer = "srvsql01"
# $DatabaseName = "OperationsManager"
# $Group = "Microsoft.SystemCenter.AllComputersGroup"
# $Account = "Test Run As Account"
# $BinariesFolder = "C:\Program Files\System Center Operations Manager 2007\SDK Binaries"
# -RootManagementServer srvrms01
# -DatabaseServer srvsql01
# -DatabaseName OperationsManager
# -Group Microsoft.SystemCenter.AllComputersGroup
# -Account "Test Run As Account"
# -BinariesFolder "f:\Program Files\System Center Operations Manager 2007\SDK Binaries"
# -Debug true
#-------------------------------------------------------------------------------------End variables for local runtime
 
#-------------------------------------------------------------------------------------Start main program
Main

Always test scripts you find on the internet before implementing in production.

Also see the other one if you want to use literal expression.

I do not moderate this blog anymore. If you have a question regarding this post, send me a message.

Comments
  • This is a continuation of a previous post .  The only difference is this accepts a literal expression