Selectively Setting Default Action Accounts in SCOM
Disclaimer: I have only tested this in SCOM 2012, but it should be possible to work in SCOM 2007 R2 with some alteration of the PowerShell syntax.
Sometimes there is a need for different agents in the same management group to have different default action accounts. This can especially be true when agents are in multiple domains. Organizations may also with to limit the use of default action accounts other than LOCAL SYSTEM to only those agents that need them.
The solution that I found was to add a property to the Health Service class. In a separate management pack I created the following class:
<ClassType ID="My.Example.MP.HealthService" Accessibility="Public" Abstract="false" Base="SC!Microsoft.SystemCenter.HealthService" Hosted="true" Singleton="false">
<Property ID="NeedsActionAccount" Type="string" Key="false" CaseSensitive="false" Length="10" MinLength="0" />
</ClassType>
Then you need to make a discovery that sets the property on your agents. I used a VBScript like this, but you could just as easily use a filtered registry discovery or a PowerShell discovery.
strSourceId = "$MPElement$"
strManagedEntityId = "$Target/Id$"
strTargetComputer = "$Target/Host/Property[Type='Windows!Microsoft.Windows.Computer']/PrincipalName$"
Set objShell = CreateObject("Wscript.Shell")
Set objSystemEnv = objShell.Environment("SYSTEM")
Const HKEY_LOCAL_MACHINE = &H80000002
Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
FoundService = False
If oReg.EnumKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Service123", arrSubKeys) = 0 Then
FoundService = True
End If
If oReg.EnumKey(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Services\ServiceXYZ\Performance", arrSubKeys) = 0 Then
FoundService = True
End If
Set oAPI = CreateObject("MOM.ScriptAPI")
Set oDiscoveryData = oAPI.CreateDiscoveryData(0, strSourceId, strManagedEntityId)
Set oInst = oDiscoveryData.CreateClassInstance("$MPElement[Name='My.Example.MP.HealthService']$")
call oInst.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", strTargetComputer)
If ( FoundService ) Then
call oInst.AddProperty("$MPElement[Name='My.Example.MP.HealthService']/NeedsActionAccount$", "Yes")
Else
call oInst.AddProperty("$MPElement[Name='My.Example.MP.HealthService']/NeedsActionAccount$", "No")
End If
Call oDiscoveryData.AddInstance(oInst)
Call oAPI.Return(oDiscoveryData)
Now the interesting part is the script that actually sets the action accounts in a self-sustaining way. I scheduled the following script to run a few times a day. As you can see above I made the decision about whether or not an action account was needed based on the presence of some installed software rather than depending on a server's hostname. Generally I avoid making any monitoring devision based on hostname whenever possible. This PowerShell script not only sets accounts where they are needed, but returns agents to LOCAL SYSTEM when action accounts are not needed.
Import-Module OperationsManager
New-SCOMManagementGroupConnection -ComputerName: "localhost"
$mg = Get-SCOMManagementGroup
$hsClass = Get-SCOMClass -name "Microsoft.SystemCenter.HealthService"
##### LOCAL SYSTEM #####
$Instances = Get-SCOMClassInstance -class $hsClass | WHERE {$_.DisplayName -like '*.lcl' `
-and $_."[Microsoft.SystemCenter.HealthService].ActionAccountIdentity".ToString() -ne 'SYSTEM' `
-and $_."[Microsoft.SystemCenter.HealthService].IsAgent".ToString() -eq 'True' `
-and $_."[My.Example.MP.HealthService].NeedsActionAccount".ToString() -eq 'No'}
If ($Instances -ne $null)
{
$newAccount = Get-SCOMRunAsAccount -Name "Local System Action Account"
Foreach ($Instance in $Instances)
{
$secureRef = $mg.GetMonitoringSecureDataHealthServiceReferenceByHealthServiceId($Instance.Id)
$currentAccount = $mg.GetMonitoringSecureData($secureRef[0].MonitoringSecureDataId)
$secureRef[0].MonitoringSecureDataId = $newAccount.Id
$secureRef[0].Update()
}
}
##### Domain asdf.lcl #####
$Instances = Get-SCOMClassInstance -class $hsClass | WHERE {$_.DisplayName -like '*.asdf.lcl' `
-and $_."[Microsoft.SystemCenter.HealthService].ActionAccountIdentity".ToString() -eq 'SYSTEM' `
-and $_."[Microsoft.SystemCenter.HealthService].IsAgent".ToString() -eq 'True' `
-and $_."[My.Example.MP.HealthService].NeedsActionAccount".ToString() -eq 'Yes'}
If ($Instances -ne $null)
{
$newAccount = Get-SCOMRunAsAccount -Name "ASDF\MyAccount"
Foreach ($Instance in $Instances)
{
$secureRef = $mg.GetMonitoringSecureDataHealthServiceReferenceByHealthServiceId($Instance.Id)
$currentAccount = $mg.GetMonitoringSecureData($secureRef[0].MonitoringSecureDataId)
$secureRef[0].MonitoringSecureDataId = $newAccount.Id
$secureRef[0].Update()
}
}
##### Domain qwerty.lcl #####
$Instances = Get-SCOMClassInstance -class $hsClass | WHERE {$_.DisplayName -like '*.qwerty.lcl' `
-and $_."[Microsoft.SystemCenter.HealthService].ActionAccountIdentity".ToString() -eq 'SYSTEM' `
-and $_."[Microsoft.SystemCenter.HealthService].IsAgent".ToString() -eq 'True' `
-and $_."[My.Example.MP.HealthService].NeedsActionAccount".ToString() -eq 'Yes'}
If ($Instances -ne $null)
{
$newAccount = Get-SCOMRunAsAccount -Name "QWERTY\MyAccount"
Foreach ($Instance in $Instances)
{
$secureRef = $mg.GetMonitoringSecureDataHealthServiceReferenceByHealthServiceId($Instance.Id)
$currentAccount = $mg.GetMonitoringSecureData($secureRef[0].MonitoringSecureDataId)
$secureRef[0].MonitoringSecureDataId = $newAccount.Id
$secureRef[0].Update()
}
}
The real trick here is the note properties. The property I added to the Health Service class shows up as a note property in powershell. Note properties aren't explicitly typed, so that's why I'm constantly calling the ToString() method.
Comments
- Anonymous
August 29, 2014
this is cool. but I still have not gotten all of it to work, but gives me good ideas. thx!