AppLockerFoundry.psm1

<#
.SYNOPSIS
    Exports an AppLocker Foundry object to an AppLocker XML file.
.DESCRIPTION
    Exports an AppLocker Foundry object to an AppLocker XML file.
.PARAMETER Rsop
    The AppLocker Foundry object to export.
.PARAMETER Path
    The directory path to export the AppLocker XML file to.
.EXAMPLE
    Export-AlfXml -Rsop $Rsop -Path C:\AppLocker.xml
 
    Exports the AppLocker Foundry object $Rsop to C:\AppLocker
.EXAMPLE
    Get-DatumRsop $datum (Get-DatumNodesRecursive -AllDatumNodes $Datum.AllNodes) | Export-AlfXml -Path C:\AppLocker
 
    Calculate policy objects from Datum and export them to C:\AppLocker
#>

function Export-AlfXml
{
    [OutputType([System.IO.FileInfo])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [System.Collections.Specialized.OrderedDictionary]
        $Rsop,

        [Parameter(Mandatory = $true)]
        [string]
        $Path
    )

    process
    {
        $xmlDoc = [System.Xml.XmlDocument]::new()
        $null = $xmlDoc.AppendChild($xmlDoc.CreateXmlDeclaration('1.0', 'UTF-8', $null))
        $policyNode = $xmlDoc.CreateElement('AppLockerPolicy')
        $versionAttr = $xmlDoc.CreateAttribute('Version')
        $versionAttr.InnerText = '1'
        $null = $policyNode.Attributes.Append($versionAttr)
        Write-PSFMessage -Level Debug -Message ($Rsop | ConvertTo-Yaml)
        $domainPath = Join-Path -Path $Path -ChildPath $Rsop['Domain']
        if (-not (Test-Path -Path $domainPath))
        {
            Write-PSFMessage -Level Verbose -Message "Creating directory $domainPath"
            $null = New-Item -Path $domainPath -ItemType Directory
        }

        $policyPath = Join-Path -Path $domainPath -ChildPath ('{0}.xml' -f $Rsop['PolicyName'])
        Write-PSFMessage -Level Verbose -Message "Will export $($Rsop['PolicyName']) to $policyPath"

        foreach ($ruleCollection in $Rsop['RuleCollections'].GetEnumerator())
        {
            $ruleCollectionNode = $xmlDoc.CreateElement('RuleCollection')
            $typeAttr = $xmlDoc.CreateAttribute('Type')
            $typeAttr.InnerText = $ruleCollection.Key
            $enforcementAttr = $xmlDoc.CreateAttribute('EnforcementMode')
            $enforcementAttr.InnerText = $ruleCollection.Value.EnforcementMode
            $null = $ruleCollectionNode.Attributes.Append($typeAttr)
            $null = $ruleCollectionNode.Attributes.Append($enforcementAttr)

            foreach ($rule in $ruleCollection.Value.Rules)
            {
                Write-PSFMessage -Level Verbose -Message "Adding rule $($rule.Name)"
                $guidAttr = $xmlDoc.CreateAttribute('Id')
                $guidAttr.InnerText = [System.Guid]::NewGuid().ToString()
                $nameAttr = $xmlDoc.CreateAttribute('Name')
                $nameAttr.InnerText = $rule.Name
                $descriptionAttr = $xmlDoc.CreateAttribute('Description')
                $descriptionAttr.InnerText = $rule.Description
                $userOrGroupAttr = $xmlDoc.CreateAttribute('UserOrGroupSid')
                $userOrGroupAttr.InnerText = $rule.UserOrGroupSid
                $actionAttr = $xmlDoc.CreateAttribute('Action')
                $actionAttr.InnerText = $rule.Action

                if ($rule.Contains('Path'))
                {
                    # Path Rule
                    $ruleNode = $xmlDoc.CreateElement('FilePathRule')
                    $conditionNode = $xmlDoc.CreateElement('Conditions')
                    foreach ($rulePath in $rule.Path)
                    {
                        $pathConditionNode = $xmlDoc.CreateElement('FilePathCondition')
                        $pathAttr = $xmlDoc.CreateAttribute('Path')
                        $pathAttr.InnerText = $rulePath
                        $null = $pathConditionNode.Attributes.Append($pathAttr)
                        $null = $conditionNode.AppendChild($pathConditionNode)
                    }
                    $null = $ruleNode.AppendChild($conditionNode)

                    $exceptionsNode = $xmlDoc.CreateElement('Exceptions')
                    foreach ($exception in $rule.Exceptions)
                    {
                        $exceptionConditionNode = $xmlDoc.CreateElement('FilePathCondition')
                        $pathAttr = $xmlDoc.CreateAttribute('Path')
                        $pathAttr.InnerText = $exception
                        $null = $exceptionConditionNode.Attributes.Append($pathAttr)
                        $null = $exceptionsNode.AppendChild($exceptionConditionNode)
                    }
                    $null = $ruleNode.AppendChild($exceptionsNode)
                }

                if ($rule.Contains('Data'))
                {
                    # FileHash Rule
                    $ruleNode = $xmlDoc.CreateElement('FileHashRule')
                    $conditionNode = $xmlDoc.CreateElement('Conditions')
                    $hashConditionNode = $xmlDoc.CreateElement('FileHashCondition')
                    $hashNode = $xmlDoc.CreateElement('FileHash')
                    $dataAttr = $xmlDoc.CreateAttribute('Data')
                    $dataAttr.InnerText = $rule.Data
                    $null = $hashNode.Attributes.Append($dataAttr)
                    $hashTypeAttr = $xmlDoc.CreateAttribute('Type')
                    $hashTypeAttr.InnerText = $rule.Type
                    $null = $hashNode.Attributes.Append($hashTypeAttr)
                    $sourceFileNameAttr = $xmlDoc.CreateAttribute('SourceFileName')
                    $sourceFileNameAttr.InnerText = $rule.SourceFileName
                    $null = $hashNode.Attributes.Append($sourceFileNameAttr)
                    $sourceFileLengthAttr = $xmlDoc.CreateAttribute('SourceFileLength')
                    $sourceFileLengthAttr.InnerText = $rule.SourceFileLength
                    $null = $hashNode.Attributes.Append($sourceFileLengthAttr)
                    $null = $hashConditionNode.AppendChild($hashNode)
                    $null = $conditionNode.AppendChild($hashConditionNode)
                    $null = $ruleNode.AppendChild($conditionNode)
                }

                if ($rule.Contains('PublisherName'))
                {
                    # Publisher Rule
                    $ruleNode = $xmlDoc.CreateElement('FilePublisherRule')
                    $conditionNode = $xmlDoc.CreateElement('Conditions')
                    $publisherConditionNode = $xmlDoc.CreateElement('FilePublisherCondition')
                    $publisherNameAttr = $xmlDoc.CreateAttribute('PublisherName')
                    $publisherNameAttr.InnerText = $rule.PublisherName
                    $null = $publisherConditionNode.Attributes.Append($publisherNameAttr)
                    $productNameAttr = $xmlDoc.CreateAttribute('ProductName')
                    $productNameAttr.InnerText = $rule.ProductName
                    $null = $publisherConditionNode.Attributes.Append($productNameAttr)
                    $binaryNameAttr = $xmlDoc.CreateAttribute('BinaryName')
                    $binaryNameAttr.InnerText = $rule.BinaryName
                    $null = $publisherConditionNode.Attributes.Append($binaryNameAttr)
                    $binaryVersionRangeNode = $xmlDoc.CreateElement('BinaryVersionRange')
                    $binaryLowSectionAttr = $xmlDoc.CreateAttribute('LowSection')
                    $binaryLowSectionAttr.InnerText = $rule.BinaryVersionRange.LowSection
                    $null = $binaryVersionRangeNode.Attributes.Append($binaryLowSectionAttr)
                    $binaryHighSectionAttr = $xmlDoc.CreateAttribute('HighSection')
                    $binaryHighSectionAttr.InnerText = $rule.BinaryVersionRange.HighSection
                    $null = $binaryVersionRangeNode.Attributes.Append($binaryHighSectionAttr)
                    $null = $publisherConditionNode.AppendChild($binaryVersionRangeNode)
                    $null = $conditionNode.AppendChild($publisherConditionNode)
                    $null = $ruleNode.AppendChild($conditionNode)
                }
                
                $null = $ruleNode.Attributes.Append($guidAttr)
                $null = $ruleNode.Attributes.Append($nameAttr)
                $null = $ruleNode.Attributes.Append($descriptionAttr)
                $null = $ruleNode.Attributes.Append($userOrGroupAttr)
                $null = $ruleNode.Attributes.Append($actionAttr)
                $null = $ruleCollectionNode.AppendChild($ruleNode)
                $null = $policyNode.AppendChild($ruleCollectionNode)
            }
        }
        
        Write-PSFMessage -Level Verbose -Message "Exporting $($Rsop['PolicyName']) to $policyPath"
        Write-PSFMessage -Level Debug -Message $xmlDoc.ToString()
        $null = $xmlDoc.AppendChild($policyNode)
        $xmlDoc.Save($policyPath)
        Get-Item -Path $policyPath
    }
}


<#
.SYNOPSIS
    Get AppLocker file information and convert it to YAML
.DESCRIPTIOn
    Get AppLocker file information and convert it to YAML
.PARAMETER Path
    The path to the file or directory to get AppLocker file information for.
.EXAMPLE
    Get-ChildItem -Path "C:\Program Files" -Recurse -Filter *.exe | Get-AlfYamlFileInfo
 
    Get AppLocker file information for all files in "C:\Program Files" and convert it to YAML
#>

function Get-AlfYamlFileInfo
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [string[]]
        $Path
    )

    begin
    {
        $yamlObject = @{
            RuleCollections = @{}
        }
    }

    process
    {
        $fileInfos = Get-AppLockerFileInformation -Path $Path

        :files foreach ($fileInfo in $fileInfos)
        {
            <#
            Exe: .exe and .com
            Msi: .msi, .msp, and .mst
            Script: .ps1, .bat, .cmd, .vbs, and .js
            StoreApps: .appx
            DLL: .dll and .ocx
            #>

            $ruleType = switch ([IO.Path]::GetExtension($fileInfo.Path.Path))
            {
                { $_ -in '.exe', '.com' } { 'Exe' }
                { $_ -in '.msi', '.msp', '.mst' } { 'Msi' }
                { $_ -in '.ps1', '.bat', '.cmd', '.vbs', '.js' } { 'Script' }
                { $_ -in '.appx' } { 'StoreApps' }
                { $_ -in '.dll', '.ocx' } { 'Dll' }
                default
                {
                    Write-Error -Message "Invalid file extension for AppLocker: $_"
                    continue files
                }
            }

            if (-not $yamlObject['RuleCollections'].Contains($ruleType))
            {
                $yamlObject['RuleCollections'][$ruleType] = @{
                    EnforcementMode = 'AuditOnly'
                    Rules           = [System.Collections.ArrayList]::new()
                }
            }

            $null = $yamlObject['RuleCollections'][$ruleType].Rules.Add(
                @{
                    Name           = $fileInfo.Path.Path
                    Description    = $fileInfo.Path.Path
                    Path           = $fileInfo.Path.Path
                    UserOrGroupSid = 'S-1-1-0'
                    Action         = 'Allow'
                }
            )

            if ($fileInfo.Publisher)
            {
                # Ensure all those unnecessary custom types are converted
                $obj = @{
                    Name               = '{0} - {1}' -f $fileInfo.Publisher.PublisherName, $fileInfo.Publisher.ProductName
                    Description        = '{0} - {1}' -f $fileInfo.Publisher.PublisherName, $fileInfo.Publisher.ProductName
                    PublisherName      = $fileInfo.Publisher.PublisherName
                    ProductName        = $fileInfo.Publisher.ProductName
                    BinaryName         = $fileInfo.Publisher.BinaryName
                    BinaryVersionRange = @{
                        LowSection  = $fileInfo.Publisher.BinaryVersion.ToString()
                        HighSection = $fileInfo.Publisher.BinaryVersion.ToString()
                    }
                }
                $null = $yamlObject['RuleCollections'][$ruleType].Rules.Add($obj)
            }

            if ($fileInfo.Hash)
            {
                $obj = @{
                    Name             = '{0} - {1}' -f $fileInfo.Hash.SourceFileName, $fileInfo.Hash.HashType.ToString()
                    Description      = '{0} - {1}' -f $fileInfo.Hash.SourceFileName, $fileInfo.Hash.HashType.ToString()
                    HashType         = $fileInfo.Hash.HashType.ToString()
                    HashDataString   = $fileInfo.Hash.HashDataString
                    SourceFileName   = $fileInfo.Hash.SourceFileName
                    SourceFileLength = $fileInfo.Hash.SourceFileLength
                }
                $null = $yamlObject['RuleCollections'][$ruleType].Rules.Add($obj)
            }
        }
    }

    end
    {
        $yamlObject | ConvertTo-Yaml
    }
}