Microsoft Windows Server 2016 and above BPA Monitoring Write Action Module Type

Microsoft.Windows.Server.10.0.Monitoring.BPA.Task.WA.ModuleType (WriteActionModuleType)

This module type is used to show Best Practices Analyzer compliance results.

Element properties:

TypeWriteActionModuleType
IsolationAny
AccessibilityInternal
RunAsDefault
InputTypeSystem.BaseData
OutputTypeSystem.CommandOutput

Member Modules:

ID Module Type TypeId RunAs 
WA WriteAction Microsoft.Windows.PSScriptWriteAction Default

Overrideable Parameters:

IDParameterTypeSelectorDisplay NameDescription
IncludeCompliantbool$Config/IncludeCompliant$Include Compliant rules.This parameter defines whether to show compliant BPA rules or not.

Source Code:

<WriteActionModuleType ID="Microsoft.Windows.Server.10.0.Monitoring.BPA.Task.WA.ModuleType" Accessibility="Internal">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="IncludeCompliant" type="xsd:boolean"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IncludeCompliant" Selector="$Config/IncludeCompliant$" ParameterType="bool"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<WriteAction ID="WA" TypeID="Microsoft.Windows.PSScriptWriteAction">
<ScriptName>Microsoft.Windows.Server.10.0.Monitoring.BPA.ShowBPAresults.Script.ps1</ScriptName>
<Arguments>"-IncludeCompliant '$Config/IncludeCompliant$'"</Arguments>
<ScriptBody><Script>
Param($IncludeCompliant)
# State type constants
$STATE_SUCCESS = "Success"
$STATE_WARNING = "Warning"
$STATE_ERROR = "Analysis"

$WarningRulesCount = 0
$ErrorRulesCount = 0

$BpaNotSupportedStatus = 1
$BpaNotLoadedStatus = 2
$BpaModelsNotLoaded = 3
$BpaSuccess = 0
$SrvModule = "ServerManager"
$BpaModule = "BestPractices"
$BpaInitFailed = "BPA Task Initialization failed."
$BpaNotSupported = "There are no Best Practices Analyzer modules found. Scan result is not supported on this Operating System."
$BpaLoadProblem = "Can not load BPA Scan Module"
$BpaModelLoadProblem = "Can not get BPA Models"
$Description = "There are no Best Practices Analyzer modules found."
$DESCRIPTION_NOMODULES = "There are no Best Practices Analyzer modules found."
$DESCRIPTION_WARNING = "Best Practices Analyzer compliance verification was completed successfully with {0} warnings and {1} errors."
$DESCRIPTION_ERROR = "Best Practices Analyzer can not complete compliance verification."
$BpaResultLoadFailed = "Best Practices Analyzer can not get result"
$BpaCategoryError = "ResourceUnavailable"
$BpaScanNotRun = "BPA scan was not run or was not completed successfuly."
$BpaScanMonitor = "Enable Windows Server 2016 Operating System BPA Monitor"
$BpaEmptyResults = "BPA scan does not return any result."
$BpaErrorResults = "Can not get Best Practices Analyzer compliance result. Command was completed with errors. Error: {0} ."
$BpaScanMode = "Analysis"

Function Get-BpaWarningsAndErrorCounts($Detail,[ref]$WarningRulesCount,[ref]$ErrorRulesCount)
{
if([string]::IsNullOrEmpty($Detail))
{
return
}

$WarningCount = 0
$ErrorCount = 0



foreach($scanDetail in $Detail)
{
if ($BpaScanMode -eq $scanDetail.ScanMode)
{
$tWarninCount = 0
$tErrorCount = 0

Get-BpaCountFromDetail -Detail $scanDetail -WarningMessages ([ref] $tWarninCount) -ErrorMessages ([ref] $tErrorCount)
$WarningCount += $tWarninCount
$ErrorCount += $tErrorCount
}
else
{
continue
}
}

$WarningRulesCount.Value = $WarningCount
$ErrorRulesCount.Value = $ErrorCount
}


Function Get-BpaCountFromDetail($Detail,[ref]$WarningMessages,[ref]$ErrorMessages)
{
if([string]::IsNullOrEmpty($Detail))
{
return $null
}

if([Microsoft.BestPractices.CoreInterface.InvokeBpaModelOutputDetail] -ne $Detail.GetType())
{
retrun $null
}

$WarnCount = 0
$ErrorCount = 0
[int]::TryParse($Detail.WarningMessages,[ref]$WarnCount)
[int]::TryParse($Detail.ErrorMessages,[ref]$WarnCount)

$WarningMessages.Value = $WarnCount
$ErrorMessages.Value = $ErrorCount

return 0
}

Function Convert-ToBoolean([string] $sBool)
{
[bool] $result = $false
[bool] $iresult = $false

if ($false -eq [string]::IsNullOrEmpty($sBool) )
{
$result = $sBool.Equals("true",[System.StringComparison]::InvariantCultureIgnoreCase)
$iresult = $sBool.Equals("1",[System.StringComparison]::InvariantCultureIgnoreCase)
$result = $result -or $iresult
}

return $result
}

Function Load-Module ([string] $ModuleName)
{
if ([string]::IsNullOrEmpty($ModuleName) )
{
return $false
}

$ErrorActionPreference="SilentlyContinue"
$error.Clear()

$retval = $false
$cim = Get-Module -Name $ModuleName

########Check for powershell 1.0
if ($error.Count -ne 0)
{
$type = $error[0].Exception.GetType()
if ([System.Management.Automation.CommandNotFoundException] -eq $type)
{
$error.Clear()
return $retval
}

$error.Clear()
}

if ($null -eq $cim)
{
Import-Module $ModuleName
if ($error.Count -eq 0)
{
$retval = $true
}

$error.Clear()
}
else
{
$retval = $true
}

return $retval


}

Function Check-Module([string] $ModuleName)
{

$IsExist = $false
if ([string]::IsNullOrEmpty($ModuleName) )
{
return $IsExist
}

$ErrorActionPreference="SilentlyContinue"
$error.Clear()

$module = Get-Module -ListAvailable -Name $ModuleName
if ($error.Count -ne 0)
{
$type = $error[0].Exception.GetType()
if ([System.Management.Automation.CommandNotFoundException] -eq $type)
{
$error.Clear()
return $IsExist
}

$error.Clear()
}

$IsExist = $module -ne $null

return $IsExist
}

Function Check-BpaRequirement($ModelsToRun,$ImportedAssemblies)
{
$IsSrvManager = Check-Module -ModuleName $SrvModule
$IsBpaModule = Check-Module -ModuleName $BpaModule
$IsSrvManagerLoaded = $false
$IsBpaModuleLoaded = $false

if ($false -eq $IsBpaModule)
{
Write-Host $BpaNotSupported
return $BpaNotSupportedStatus
}

SafeImportModule -ModuleName $BpaModule -LoadedAssemblies $ImportedAssemblies
if ( 0 -eq $ImportedAssemblies.Count)
{
Write-Host $BpaLoadProblem
return $BpaNotLoadedStatus
}

if ($true -eq $IsSrvManager)
{
$IsSrvManagerLoaded = Load-Module -ModuleName $SrvModule
if($true -eq $IsSrvManagerLoaded)
{
Get-InstalledFeatures -ModelsToRun $ModelsToRun

}
else
{
$IsModelsLoaded = Get-BpaModels -ModelsToRun $ModelsToRun
if($false -eq $IsModelsLoaded)
{
return $BpaModelsNotLoaded
}
}
}
else
{
$IsModelsLoaded = Get-BpaModels -ModelsToRun $ModelsToRun
if($false -eq $IsModelsLoaded)
{
return $BpaModelsNotLoaded
}
}

return $BpaSuccess
}

Function Get-BpaModels($ModelsToRun)
{
$ErrorActionPreference="SilentlyContinue"
$error.Clear()

if($null -eq $ModelsToRun)
{
return
}

$BpaModels = Get-BpaModel
if (0 -ne $error.Count)
{
Write-Host $BpaModelLoadProblem
return $false
}

foreach($BpaModel in $BpaModels)
{
if ([string]::IsNullOrEmpty($BpaModel.Id) )
{
continue
}

[void]$ModelsToRun.Add($BpaModel.Id)
}

return $true
}



Function Get-InstalledFeatures($ModelsToRun)
{
$ErrorActionPreference="SilentlyContinue"
$error.Clear()

if($null -eq $ModelsToRun)
{
return
}

$WinFeatureToBPAmodel = @{
"ADRMS" = "Microsoft/Windows/ADRMS";
"Application-Server" = "Microsoft/Windows/ApplicationServer";
"AD-Certificate" = "Microsoft/Windows/CertificateServices";
"DHCP" = "Microsoft/Windows/DHCPServer";
"AD-Domain-Services" = "Microsoft/Windows/ADRMS";
"DNS" = "Microsoft/Windows/DNSServer";
"File-Services" = "Microsoft/Windows/FileServices";
"Hyper-V" = "Microsoft/Windows/Hyper-V";
"ADLDS" = "Microsoft/Windows/LightweightDirectoryServices" ;
"NPAS" = "Microsoft/Windows/NPAS";
"Remote-Desktop-Services" = "Microsoft/Windows/TerminalServices";
"Web-Server" = "Microsoft/Windows/WebServer";
"UpdateServices" = "Microsoft/Windows/UpdateServices";
"VolumeActivation" = "Microsoft/Windows/VolumeActivation";
"Failover-Clustering" = "Microsoft/Windows/ClusterAwareUpdating";
"RemoteAccess" = "Microsoft/Windows/RemoteAccessServer";
"MSMQ" = "Microsoft/Windows/MSMQ";
"OOB-WSUS" = "Microsoft/Windows/WSUS";

}


foreach ($feature in $WinFeatureToBPAmodel.Keys)
{
if ($true -eq (Get-WindowsFeature $feature).Installed)
{
$BpaModel = $WinFeatureToBPAmodel[$feature]
if($false -eq [string]::IsNullOrEmpty($BpaModel) )
{
if ($feature -ieq "Failover-Clustering")
{
$ClusterName = (Get-ItemProperty -Path "HKLM:\Cluster" -Name "ClusterName").ClusterName
if ([string]::IsNullOrEmpty($ClusterName))
{
continue
}
}

[void]$ModelsToRun.Add($BpaModel)
}
}
}

$error.Clear()

}

function CreateStatePropertyBag
{
param ($StateProperty = $StateProperty, $Message = $Message,$momAPI)

if($null -eq $momAPI)
{
return
}

$SCRIPT_NAME = $MyInvocation.ScriptName
if ($StateProperty -ne $STATE_SUCCESS -and $StateProperty -ne $STATE_WARNING -and $StateProperty -ne $STATE_ERROR)
{
Write-Warning "$SCRIPT_NAME - State property is not properly defined: $StateProperty"
return
}

$ErrorActionPreference="SilentlyContinue"
$error.Clear()
$StatePropertyBag = $momAPI.CreateTypedPropertyBag($PROPERTY_TYPE_STATE)

if ($StatePropertyBag -eq $null)
{
Write-Warning "$SCRIPT_NAME - Unable to create state property bag"
return
}

$StatePropertyBag.AddValue('State', $StateProperty)
Write-Verbose "$SCRIPT_NAME - added state property bag value for state $StateProperty"

if ($Message -ne $null)
{
$StatePropertyBag.AddValue('Message', $Message)
Write-Verbose "$SCRIPT_NAME - added state property bag value for message $Message"
}

# Submitting state property bag
$StatePropertyBag

#Write-Host "$SCRIPT_NAME - state property bag returned"
}

Function Get-BpaResults
{
param ([string]$BPAModel,[ref]$WarnCount,[ref]$ErrorCount)
$ErrorActionPreference = "SilentlyContinue"
$error.Clear()

$BPAResults = (Get-BPAResult -ModelId $BPAModel ) | Where-Object {$_.Severity -eq "Warning" -or $_.Severity -eq "Error"}

$WarnCount.Value = 0
$ErrorCount.Value = 0
$wCount = 0
$eCount = 0

if (0 -ne $error.Count -and $null -eq $BPAResults)
{

return
}

$Warnings = $BPAResults | Where-Object {$_.Severity -eq "Warning"}
$Errors = $BPAResults | Where-Object {$_.Severity -eq "Errors"}

if ($null -ne $Warnings )
{
$wCount = $Warnings.Count
$WarnCount.Value = $wCount
}

if ($null -ne $Errors )
{
$eCount = $Errors.Count
$ErrorCount.Value = $eCount
}

}

Function Get-AllBpaResults
{
param ($BPAModels,[ref]$WarnCount,[ref]$ErrorCount)

$ErrorActionPreference = "SilentlyContinue"
$error.Clear()

$Warn = 0
$Err = 0
$Warns = 0
$Errors = 0
foreach($BPAModel in $BPAModels)
{
Get-BpaResults -BPAModel $BPAModel -WarnCount ([ref]$Warn) -ErrorCount ([ref]$Err)
$Warns = $Warns + $Warn
$Errors = $Errrors + $Err
}

$WarnCount.Value = $Warns
$ErrorCount.Value = $Errors
}
function SafeImportModule()
{
param ($ModuleName = $ModuleName,$LoadedAssemblies)

$LOCAL_SYSTEM_SRC = "SafetyImportModule"
$LOCAL_SYSTEM_ID = 104
$NestedModulesMarker = "NestedModules*"
$AssemblyMarker = "dll"
$ErrorActionPreference = "SilentlyContinue"
$error.Clear()

$SCRIPT_NAME = $MyInvocation.ScriptName


if ($null -eq $LoadedAssemblies -or [string]::IsNullOrEmpty($ModuleName) )
{
return
}

$Module = Get-Module -ListAvailable -Name $ModuleName
if ($null -eq $Module )
{
return
}

$NestedModulesString = cat $Module.Path | Where-object {$_ -like $NestedModulesMarker}

if ([string]::IsNullOrEmpty($NestedModulesString))
{
return
}

$AssemblyCollection = $NestedModulesString.Split((",","'"),[System.StringSplitOptions]::RemoveEmptyEntries) | Where-Object {$_.Contains($AssemblyMarker)}

foreach ($AssemblyName in $AssemblyCollection)
{
$AssemblyFullPath = Join-Path $Module.ModuleBase $AssemblyName
try
{
$Assembly = [System.Reflection.Assembly]::LoadFile($AssemblyFullPath)
Import-Module -Assembly $Assembly
[void]$LoadedAssemblies.Add($Assembly.Location)
}
catch [System.IO.FileLoadException]
{
$momAPI.LogScriptEvent($LOCAL_SYSTEM_SRC, $LOCAL_SYSTEM_ID, $EVENT_TYPE_ERROR, $SCRIPT_NAME + "Could not load {0}" -f $AssemblyFullPath)
$LoadedAssemblies.Clear()
break
}
catch [System.IO.FileNotFoundException]
{
$momAPI.LogScriptEvent($LOCAL_SYSTEM_SRC, $LOCAL_SYSTEM_ID, $EVENT_TYPE_ERROR, $SCRIPT_NAME + "File {0} Not Found" -f $AssemblyFullPath)
$LoadedAssemblies.Clear()
break
}

}

}

function RemoveImportedAssembly
{
param ($AssemblyName = $AssemblyName)
$Modules = Get-Module
foreach($Module in $Modules)
{
if ($Module.Path -ieq $AssemblyName)
{
Remove-Module $Module
}
}
}

Function Unload-Modules($ImportedAssemblies)
{
foreach ($ImportedAssembly in $ImportedAssemblies)
{
RemoveImportedAssembly($ImportedAssembly)
}
}
function WriteBPAResult
{
param ([Microsoft.BestPractices.CoreInterface.Result]$BPAResult, [boolean]$IncludeAll)

$Delimiter = New-Object String("-",12)
if ($BPAResult.Problem -eq $null)
{
if ($IncludeAll)
{
Write-Host $(-Join("Title : ", $BPAResult.Title))
Write-Host $(-Join("Compliance : ", $BPAResult.Compliance))
Write-Host $Delimiter
}
}
else
{
Write-Host $(-Join("Title : ", $BPAResult.Title))
Write-Host $(-Join("Problem : ", $BPAResult.Problem))
Write-Host $(-Join("Impact : ", $BPAResult.Impact))
Write-Host $(-Join("Resolution : ", $BPAResult.Resolution))
Write-Host $Delimiter
}
}

function WriteModelResults
{
param ([string]$BPAModel, [boolean]$IncludeAll)
$ErrorActionPreference = "SilentlyContinue"
$error.Clear()
$BPAResults = Get-BPAResult -ModelId $BPAModel -ErrorAction SilentlyContinue

if (0 -ne $error.Count -and $null -eq $BPAResults)
{

if ($BpaCategoryError -ieq $error[0].CategoryInfo.Category)
{
Write-Host $BpaScanNotRun
Write-Host $BpaScanMonitor
}
else
{
$msg = $error[0].Exception.Message
$msg = $BpaErrorResults -f $msg
Write-Host $msg
}

return
}


if ($null -eq $BPAResults )
{
Write-Host $BpaEmptyResults
return

}

$NotCompliantResults = $BPAResults | Where-Object {$_.Problem -ne $null}

[boolean] $IsModelCompliant = [boolean]($NotCompliantResults -eq $null)

if ($false -eq $IsModelCompliant -or $true -eq $IncludeAll)
{
Write-Host "Server Role: " $BPAModel
$RoleSeparator = New-Object String("=", [int] (14 + $BPAModel.Id.Length))
Write-Host $RoleSeparator
}

foreach ($BPAResult in $BPAResults)
{
WriteBPAResult -BPAResult $BPAResult -IncludeAll $IncludeAll
}

}



Function Run-BPAResult()
{
$ErrorActionPreference = "SilentlyContinue"
$error.Clear()

$BpaModels = New-Object System.Collections.ArrayList($null)
$ImportedAssemblies = New-Object System.Collections.ArrayList($null)

if (0 -ne $error.Count)
{
Write-Host $BpaInitFailed
return
}

$iResult = Check-BpaRequirement -ModelsToRun $BpaModels -ImportedAssemblies $ImportedAssemblies

if ($BpaSuccess -ne $iResult)
{
if ($BpaModelLoadProblem -eq $iResult)
{
Unload-Modules -ImportedAssemblies $ImportedAssemblies
}

return
}

$IncludeAll = Convert-ToBoolean -sBool $IncludeCompliant

foreach ($BPAModel in $BPAModels)
{
WriteModelResults -BPAModel $BPAModel -IncludeAll $IncludeAll
}

Unload-Modules -ImportedAssemblies $ImportedAssemblies
}

Run-BPAResult</Script></ScriptBody>
<TimeoutSeconds>300</TimeoutSeconds>
</WriteAction>
</MemberModules>
<Composition>
<Node ID="WA"/>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.CommandOutput</OutputType>
<InputType>System!System.BaseData</InputType>
</WriteActionModuleType>