Private/Get-LocalDetectionMethods.ps1

<#
.Synopsis
Created on: 17/02/2024
Update on: 16/03/2024
Created by: Ben Whitmore
Filename: Get-LocalDetectionMethods.ps1
 
.Description
Function to get the local detection methods from the detection methods xml object
 
.PARAMETER LogId
The component (script name) passed as LogID to the 'Write-Log' function.
This parameter is built from the line number of the call from the function up the
 
.PARAMETER XMLObject
The XML content object to extract the detection methods from
#>


function Get-DetectionMethod {
    param (
        [Parameter(Mandatory = $false, ValuefromPipeline = $false, HelpMessage = "The component (script name) passed as LogID to the 'Write-Log' function")]
        [string]$LogId = $($MyInvocation.MyCommand).Name,
        [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 0, HelpMessage = 'The local detection methods XML content')]
        [object]$XMLObject
    )
    begin {
        # Convert the XMLObject to an XML document
        [xml]$xmlDocument = $XMLObject

        # Create a namespace manager for the XML document
        $namespaceManager = New-Object System.Xml.XmlNamespaceManager($xmlDocument.NameTable)
        $namespaceManager.AddNamespace("def", "http://schemas.microsoft.com/SystemCenterConfigurationManager/2009/AppMgmtDigest")
    }
    process {
        # Create an empty array to store the settings
        $settings = @()

        # Create a helper function to iterate through the XML nodes and find SettingReferences
        function Find-SettingReferences {
            param (
                [System.Xml.XmlNode]$node,
                [ref]$rules
            )

            # If the node is a SettingReference, add it to the rules
            if ($node.Name -eq 'SettingReference') {
                $logicalName = $node.SettingLogicalName
                if ($logicalName) {
                    if (-not $rules.Value.ContainsKey($logicalName)) {
                        $rules.Value[$logicalName] = @()
                    }
                    $parentExpression = $node.ParentNode.ParentNode
                    $ruleDetail = @{
                        Operator         = $parentExpression.Operator
                        ConstantValue    = $parentExpression.Operands.ConstantValue.Value
                        ConstantDataType = $parentExpression.Operands.ConstantValue.DataType
                    }
                    $rules.Value[$logicalName] += $ruleDetail
                }
            }
            # If the node is an Operands or Expression node, iterate through its child nodes to get SettingReferences
            elseif ($node.Name -eq 'Operands' -or $node.Name -eq 'Expression') {
                foreach ($childNode in $node.ChildNodes) {
                    Find-SettingReferences -Node $childNode -Rules ([ref]$rules.Value)
                }
            }
        }

        # Pre-process rules to match settings
        $rules = @{}

        # Find SettingReferences in the first level of the EnhancedDetectionMethod
        $xmlDocument.EnhancedDetectionMethod.Rule.Expression.Operands.Expression | ForEach-Object {
            Find-SettingReferences -Node $_ -Rules ([ref]$rules)
        }
        
        # Create an array to store the settings
        $settingsNodes = $xmlDocument.DocumentElement.SelectNodes("//def:Settings/*", $namespaceManager)
        foreach ($node in $settingsNodes) {
            $logicalName = $node.LogicalName

            $setting = [PSCustomObject]@{
                Type        = $node.LocalName
                LogicalName = $logicalName
            }
            
            # Populate specific properties based on the type of setting
            switch ($node.LocalName) {
                "SimpleSetting" {
                    $setting | Add-Member -NotePropertyName "DataType" -NotePropertyValue $node.DataType
                    $setting | Add-Member -NotePropertyName "Is64Bit" -NotePropertyValue $node.RegistryDiscoverySource.Is64Bit
                    $setting | Add-Member -NotePropertyName "Depth" -NotePropertyValue $node.RegistryDiscoverySource.Depth
                    $setting | Add-Member -NotePropertyName "Hive" -NotePropertyValue $node.RegistryDiscoverySource.Hive
                    $setting | Add-Member -NotePropertyName "Key" -NotePropertyValue $node.RegistryDiscoverySource.Key
                    $setting | Add-Member -NotePropertyName "ValueName" -NotePropertyValue $node.RegistryDiscoverySource.ValueName
                }
                "File" {
                    $setting | Add-Member -NotePropertyName "Is64Bit" -NotePropertyValue $node.Is64Bit
                    $setting | Add-Member -NotePropertyName "Path" -NotePropertyValue $node.Path
                    $setting | Add-Member -NotePropertyName "Filter" -NotePropertyValue $node.Filter
                }
                "MSI" {
                    $setting | Add-Member -NotePropertyName "ProductCode" -NotePropertyValue $node.ProductCode
                }
            }

            # Dynamically add rules as additional properties
            if ($rules.ContainsKey($logicalName)) {
                foreach ($rule in $rules[$logicalName]) {
                    $setting | Add-Member -NotePropertyName "Rules_Operator" -NotePropertyValue $rule.Operator
                    $setting | Add-Member -NotePropertyName "Rules_ConstantValue" -NotePropertyValue $rule.ConstantValue
                    $setting | Add-Member -NotePropertyName "Rules_ConstantDataType" -NotePropertyValue $rule.ConstantDataType
                }
            }
            
            # Add the setting to the settings array
            $settings += $setting
        }

        return $settings
    }
}