Freigeben über


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

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.
 # https://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.

Comments

  • Anonymous
    January 01, 2003
    This is a continuation of a previous post .  The only difference is this accepts a literal expression