resourcesAfter.ps1

<#
Registers the cmdlets published by this module.
Necessary for full hybrid module support.
#>

$commonParam = @{
    HelpFile  = (Resolve-Path "$($script:ModuleRoot)\en-us\PSFramework.dll-Help.xml")
    Module = $ExecutionContext.SessionState.Module
}

Import-PSFCmdlet @commonParam -Name Remove-PSFNull -Type ([PSFramework.Commands.RemovePSFNullCommand])
Import-PSFCmdlet @commonParam -Name Select-PSFObject -Type ([PSFramework.Commands.SelectPSFObjectCommand])
Import-PSFCmdlet @commonParam -Name Set-PSFConfig -Type ([PSFramework.Commands.SetPSFConfigCommand])
Import-PSFCmdlet @commonParam -Name Test-PSFShouldProcess -Type ([PSFramework.Commands.TestPSFShouldProcessCommand])
Import-PSFCmdlet @commonParam -Name Write-PSFMessage -Type ([PSFramework.Commands.WritePSFMessageCommand])

Register-PSFConfigValidation -Name "bool" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    try
    {
        if ($Value.GetType().FullName -ne "System.Boolean")
        {
            $Result.Message = "Not a boolean: $Value"
            $Result.Success = $False
            return $Result
        }
    }
    catch
    {
        $Result.Message = "Not a boolean: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $Value
    
    return $Result
}

Register-PSFConfigValidation -Name "consolecolor" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    
    try { [System.ConsoleColor]$color = $Value }
    catch
    {
        $Result.Message = "Not a console color: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $color
    
    return $Result
}

Register-PSFConfigValidation -Name "datetime" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    
    try { [DateTime]$DateTime = $Value }
    catch
    {
        $Result.Message = "Not a DateTime: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $DateTime
    
    return $Result
}

Register-PSFConfigValidation -Name "double" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    
    try { [double]$number = $Value }
    catch
    {
        $Result.Message = "Not a double: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $number
    
    return $Result
}

Register-PSFConfigValidation -Name "integer" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value = $null
        Message = ""
    }
    
    try { [int]$number = $Value }
    catch
    {
        $Result.Message = "Not an integer: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $number
    
    return $Result
}

Register-PSFConfigValidation -Name "integer0to9" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value = $null
        Message = ""
    }
    
    try { [int]$number = $Value }
    catch
    {
        $Result.Message = "Not an integer: $Value"
        $Result.Success = $False
        return $Result
    }
    
    if (($number -lt 0) -or ($number -gt 9))
    {
        $Result.Message = "Out of range. Specify a number ranging from 0 to 9"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $Number
    
    return $Result
}

Register-PSFConfigValidation -Name "integerarray" -ScriptBlock {
    param (
        $var
    )
    
    $test = $true
    try { [int[]]$res = $var }
    catch { $test = $false }
    
    [pscustomobject]@{
        Success = $test
        Value   = $res
        Message = "Casting $var as [int[]] failure. Input is being identified as $($var.GetType())"
    }
}

Register-PSFConfigValidation -Name "integerpositive" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    
    try { [int]$number = $Value }
    catch
    {
        $Result.Message = "Not an integer: $Value"
        $Result.Success = $False
        return $Result
    }
    
    if ($number -lt 0)
    {
        $Result.Message = "Negative value: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $number
    
    return $Result
}

Register-PSFConfigValidation -Name "psframework.logfilefiletype" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSObject -Property @{
        Success  = $True
        Value    = $null
        Message  = ""
    }
    
    try { [PSFramework.Logging.LogFileFileType]$type = $Value }
    catch
    {
        $Result.Message = "Not a logfile file type: $Value . Specify one of these values: $(([enum]::GetNames([PSFramework.Logging.LogFileFileType])) -join ", ")"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $type
    
    return $Result
}

Register-PSFConfigValidation -Name "long" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSOBject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    
    try { [long]$number = $Value }
    catch
    {
        $Result.Message = "Not a long: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $number
    
    return $Result
}

Register-PSFConfigValidation -Name "string" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    
    try
    {
        # Seriously, this should work for almost anybody and anything
        [string]$data = $Value
    }
    catch
    {
        $Result.Message = "Not a string: $Value"
        $Result.Success = $False
        return $Result
    }
    
    if ([string]::IsNullOrEmpty($data))
    {
        $Result.Message = "Is an empty string: $Value"
        $Result.Success = $False
        return $Result
    }
    
    if ($data -eq $Value.GetType().FullName)
    {
        $Result.Message = "Is an object with no proper string representation: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $data
    
    return $Result
}

Register-PSFConfigValidation -Name "stringarray" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSObject -Property @{
        Success  = $True
        Value    = $null
        Message  = ""
    }
    
    try
    {
        $data = @()
        # Seriously, this should work for almost anybody and anything
        foreach ($item in $Value)
        {
            $data += [string]$item
        }
    }
    catch
    {
        $Result.Message = "Not a string array: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $data
    
    return $Result
}

Register-PSFConfigValidation -Name "timespan" -ScriptBlock {
    Param (
        $Value
    )
    
    $Result = New-Object PSObject -Property @{
        Success = $True
        Value   = $null
        Message = ""
    }
    
    try { [timespan]$timespan = $Value }
    catch
    {
        $Result.Message = "Not a Timespan: $Value"
        $Result.Success = $False
        return $Result
    }
    
    $Result.Value = $timespan
    
    return $Result
}

Set-PSFConfig -Module 'PSFramework' -Name 'ComputerManagement.PSSession.IdleTimeout' -Value (New-TimeSpan -Minutes 15) -Initialize -Validation 'timespan' -Handler { [PSFramework.ComputerManagement.ComputerManagementHost]::PSSessionIdleTimeout = $args[0] } -Description "The idle timeout for cached pssessions. When using Invoke-PSFCommand, it will remember sessions for up to this time after last using them, before cleaning them up."

# Unattended mode, so there is a central flag scripts & moduls can detect
Set-PSFConfig -Module PSFramework -Name 'System.Unattended' -Value $false -Initialize -Validation "bool" -Handler { [PSFramework.PSFCore.PSFCoreHost]::Unattended = $args[0] } -Description "Central setting, showing whether the current execution is unattended or not. This allows scripts/moduls to react to whether there is a user at the controls or not."

Set-PSFConfig -Module PSFramework -Name 'SupportPackage.ContactMessage' -Value ' ' -Initialize -Validation 'string' -Description 'Message shown when using New-PSFSUpportPackage. This allows an organization to tie information on how to submit a support package into the command that generates it'

# Encoding Settings
Set-PSFConfig -Module PSFramework -Name 'Text.Encoding.FullTabCompletion' -Value $false -Initialize -Validation 'bool' -Description 'Whether all eoncodings should be part of the tab completion for encodings. By default, only a manageable subset is shown.'
Set-PSFConfig -Module PSFramework -Name 'Text.Encoding.DefaultWrite' -Value 'utf-8' -Initialize -Validation 'string' -Description 'The default encoding to use when writing to file. Only applied by implementing commands.'
Set-PSFConfig -Module PSFramework -Name 'Text.Encoding.DefaultRead' -Value 'utf-8' -Initialize -Validation 'string' -Description 'The default encoding to use when reading from file. Only applied by implementing commands.'

#region Setting the configuration
Set-PSFConfig -Module PSFramework -Name 'Logging.MaxErrorCount' -Value 128 -Initialize -Validation "integerpositive" -Handler { [PSFramework.Message.LogHost]::MaxErrorCount = $args[0] } -Description "The maximum number of error records maintained in-memory. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-PSFConfig -Module PSFramework -Name 'Logging.MaxMessageCount' -Value 1024 -Initialize -Validation "integerpositive" -Handler { [PSFramework.Message.LogHost]::MaxMessageCount = $args[0] } -Description "The maximum number of messages that can be maintained in the in-memory message queue. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-PSFConfig -Module PSFramework -Name 'Logging.MessageLogEnabled' -Value $true -Initialize -Validation "bool" -Handler { [PSFramework.Message.LogHost]::MessageLogEnabled = $args[0] } -Description "Governs, whether a log of recent messages is kept in memory. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-PSFConfig -Module PSFramework -Name 'Logging.ErrorLogEnabled' -Value $true -Initialize -Validation "bool" -Handler { [PSFramework.Message.LogHost]::ErrorLogEnabled = $args[0] } -Description "Governs, whether a log of recent errors is kept in memory. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
Set-PSFConfig -Module PSFramework -Name 'Logging.DisableLogFlush' -Value $false -Initialize -Validation "bool" -Handler { } -Description "When shutting down the process, PSFramework will by default flush the log. This ensures that all events are properly logged. If this is not desired, it can be turned off with this setting."
#endregion Setting the configuration


Set-PSFConfig -Module PSFramework -Name 'message.info.minimum' -Value 1 -Initialize -Validation "integer0to9" -Handler { [PSFramework.Message.MessageHost]::MinimumInformation = $args[0] } -Description "The minimum required message level for messages that will be shown to the user."
Set-PSFConfig -Module PSFramework -Name 'message.info.maximum' -Value 3 -Initialize -Validation "integer0to9" -Handler { [PSFramework.Message.MessageHost]::MaximumInformation = $args[0] } -Description "The maximum message level to still display to the user directly."
Set-PSFConfig -Module PSFramework -Name 'message.verbose.minimum' -Value 4 -Initialize -Validation "integer0to9" -Handler { [PSFramework.Message.MessageHost]::MinimumVerbose = $args[0] } -Description "The minimum required message level where verbose information is written."
Set-PSFConfig -Module PSFramework -Name 'message.verbose.maximum' -Value 6 -Initialize -Validation "integer0to9" -Handler { [PSFramework.Message.MessageHost]::MaximumVerbose = $args[0] } -Description "The maxium message level where verbose information is still written."
Set-PSFConfig -Module PSFramework -Name 'message.debug.minimum' -Value 1 -Initialize -Validation "integer0to9" -Handler { [PSFramework.Message.MessageHost]::MinimumDebug = $args[0] } -Description "The minimum required message level where debug information is written."
Set-PSFConfig -Module PSFramework -Name 'message.debug.maximum' -Value 9 -Initialize -Validation "integer0to9" -Handler { [PSFramework.Message.MessageHost]::MaximumDebug = $args[0] } -Description "The maximum message level where debug information is still written."
Set-PSFConfig -Module PSFramework -Name 'message.info.color' -Value 'Cyan' -Initialize -Validation "consolecolor" -Handler { [PSFramework.Message.MessageHost]::InfoColor = $args[0] } -Description "The color to use when writing text to the screen on PowerShell."
Set-PSFConfig -Module PSFramework -Name 'message.info.color.emphasis' -Value 'green' -Initialize -Validation "consolecolor" -Handler { [PSFramework.Message.MessageHost]::InfoColorEmphasis = $args[0] } -Description "The color to use when emphasizing written text to the screen on PowerShell."
Set-PSFConfig -Module PSFramework -Name 'message.info.color.subtle' -Value 'gray' -Initialize -Validation "consolecolor" -Handler { [PSFramework.Message.MessageHost]::InfoColorSubtle = $args[0] } -Description "The color to use when making writing text to the screen on PowerShell appear subtle."
Set-PSFConfig -Module PSFramework -Name 'message.developercolor' -Value 'Gray' -Initialize -Validation "consolecolor" -Handler { [PSFramework.Message.MessageHost]::DeveloperColor = $args[0] } -Description "The color to use when writing text with developer specific additional information to the screen on PowerShell."
Set-PSFConfig -Module PSFramework -Name 'message.consoleoutput.disable' -Value $false -Initialize -Validation "bool" -Handler { [PSFramework.Message.MessageHost]::DisableVerbosity = $args[0] } -Description "Global toggle that allows disabling all regular messages to screen. Messages from '-Verbose' and '-Debug' are unaffected"
Set-PSFConfig -Module PSFramework -Name 'message.transform.errorqueuesize' -Value 512 -Initialize -Validation "integerpositive" -Handler { [PSFramework.Message.MessageHost]::TransformErrorQueueSize = $args[0] } -Description "The size of the queue for transformation errors. May be useful for advanced development, but can be ignored usually."
Set-PSFConfig -Module PSFramework -Name 'message.nestedlevel.decrement' -Value 0 -Initialize -Validation "integer0to9" -Handler { [PSFramework.Message.MessageHost]::NestedLevelDecrement = $args[0] } -Description "How many levels should be reduced per callstack depth. This makes commands less verbose, the more nested they are called"
Set-PSFConfig -Module PSFramework -Name 'developer.mode.enable' -Value $false -Initialize -Validation "bool" -Handler { [PSFramework.Message.MessageHost]::DeveloperMode = $args[0] } -Description "Developermode enables advanced logging and verbosity features. There is little benefit for enabling this as a regular user. but developers can use it to more easily troubleshoot issues."
Set-PSFConfig -Module PSFramework -Name 'message.style.breadcrumbs' -Value $false -Initialize -Validation "bool" -Handler { [PSFramework.Message.MessageHost]::EnableMessageBreadcrumbs = $args[0] } -Description "Controls how messages are displayed. Enables Breadcrumb display, showing the entire callstack. Takes precedence over command name display."
Set-PSFConfig -Module PSFramework -Name 'message.style.functionname' -Value $true -Initialize -Validation "bool" -Handler { [PSFramework.Message.MessageHost]::EnableMessageDisplayCommand = $args[0] } -Description "Controls how messages are displayed. Enables command name, showing the name of the writing command. Is overwritten by enabling breadcrumbs."
Set-PSFConfig -Module PSFramework -Name 'message.style.timestamp' -Value $true -Initialize -Validation "bool" -Handler { [PSFramework.Message.MessageHost]::EnableMessageTimestamp = $args[0] } -Description "Controls how messages are displayed. Enables timestamp display, including a timestamp in each message."

#region Setting the configuration
Set-PSFConfig -Module PSFramework -Name 'Runspace.StopTimeoutSeconds' -Value 30 -Initialize -Validation "integerpositive" -Handler { [PSFramework.Runspace.RunspaceHost]::StopTimeoutSeconds = $args[0] } -Description "Time in seconds that Stop-PSFRunspace will wait for a scriptspace to selfterminate before killing it."
#endregion Setting the configuration

# The path where type-files are stored when registered
Set-PSFConfig -Module PSFramework -Name 'Serialization.WorkingDirectory' -Value $script:path_typedata -Initialize -Validation "string" -Description "The folder in which registered type extension files are placed before import. Relevant for Register-PSFTypeSerializationData."

if (-not [PSFramework.Configuration.ConfigurationHost]::ImportFromRegistryDone)
{
    # Read config from all settings
    $config_hash = Read-PsfConfigPersisted -Scope 127
    
    foreach ($value in $config_hash.Values)
    {
        try
        {
            if (-not $value.KeepPersisted) { Set-PSFConfig -FullName $value.FullName -Value $value.Value -EnableException }
            else { Set-PSFConfig -FullName $value.FullName -PersistedValue $value.Value -PersistedType $value.Type -EnableException }
            [PSFramework.Configuration.ConfigurationHost]::Configurations[$value.FullName.ToLower()].PolicySet = $value.Policy
            [PSFramework.Configuration.ConfigurationHost]::Configurations[$value.FullName.ToLower()].PolicyEnforced = $value.Enforced
        }
        catch { }
    }
    
    [PSFramework.Configuration.ConfigurationHost]::ImportFromRegistryDone = $true
}


# Action that is performed on registration of the provider using Register-PSFLoggingProvider
$registrationEvent = {
    
}

#region Logging Execution
# Action that is performed when starting the logging script (or the very first time if enabled after launching the logging script)
$begin_event = {
    #region Helper Functions
    function Clean-FileSystemErrorXml
    {
        [CmdletBinding()]
        Param (
            $Path
        )
        
        $totalLength = $Null
        $files = Get-ChildItem -Path $Path.FullName -Filter "$($env:ComputerName)_$($pid)_error_*.xml" | Sort-Object LastWriteTime
        $totalLength = $files | Measure-Object Length -Sum | Select-Object -ExpandProperty Sum
        if (([PSFramework.Message.LogHost]::MaxErrorFileBytes) -gt $totalLength) { return }
        
        $removed = 0
        foreach ($file in $files)
        {
            $removed += $file.Length
            Remove-Item -Path $file.FullName -Force -Confirm:$false
            
            if (($totalLength - $removed) -lt ([PSFramework.Message.LogHost]::MaxErrorFileBytes)) { break }
        }
    }
    
    function Clean-FileSystemMessageLog
    {
        [CmdletBinding()]
        Param (
            $Path
        )
        
        if ([PSFramework.Message.LogHost]::MaxMessagefileCount -eq 0) { return }
        
        $files = Get-ChildItem -Path $Path.FullName -Filter "$($env:ComputerName)_$($pid)_message_*.log" | Sort-Object LastWriteTime
        if (([PSFramework.Message.LogHost]::MaxMessagefileCount) -ge $files.Count) { return }
        
        $removed = 0
        foreach ($file in $files)
        {
            $removed++
            Remove-Item -Path $file.FullName -Force -Confirm:$false
            
            if (($files.Count - $removed) -le ([PSFramework.Message.LogHost]::MaxMessagefileCount)) { break }
        }
    }
    
    function Clean-FileSystemGlobalLog
    {
        [CmdletBinding()]
        Param (
            $Path
        )
        
        # Kill too old files
        Get-ChildItem -Path $Path.FullName | Where-Object Name -Match "^$([regex]::Escape($env:ComputerName))_.+" | Where-Object LastWriteTime -LT ((Get-Date) - ([PSFramework.Message.LogHost]::MaxLogFileAge)) | Remove-Item -Force -Confirm:$false
        
        # Handle the global overcrowding
        $files = Get-ChildItem -Path $Path.FullName | Where-Object Name -Match "^$([regex]::Escape($env:ComputerName))_.+" | Sort-Object LastWriteTime
        if (-not ($files)) { return }
        $totalLength = $files | Measure-Object Length -Sum | Select-Object -ExpandProperty Sum
        
        if (([PSFramework.Message.LogHost]::MaxTotalFolderSize) -gt $totalLength) { return }
        
        $removed = 0
        foreach ($file in $files)
        {
            $removed += $file.Length
            Remove-Item -Path $file.FullName -Force -Confirm:$false
            
            if (($totalLength - $removed) -lt ([PSFramework.Message.LogHost]::MaxTotalFolderSize)) { break }
        }
    }
    #endregion Helper Functions
}

# Action that is performed at the beginning of each logging cycle
$start_event = {
    $filesystem_path = [PSFramework.Message.LogHost]::LoggingPath
    if (-not (Test-Path $filesystem_path))
    {
        $filesystem_root = New-Item $filesystem_path -ItemType Directory -Force -ErrorAction Stop
    }
    else { $filesystem_root = Get-Item -Path $filesystem_path }
    
    try { [int]$filesystem_num_Error = (Get-ChildItem -Path $filesystem_path.FullName -Filter "$($env:ComputerName)_$($pid)_error_*.xml" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty Name | Select-String -Pattern "(\d+)" -AllMatches).Matches[1].Value }
    catch { }
    try { [int]$filesystem_num_Message = (Get-ChildItem -Path $filesystem_path.FullName -Filter "$($env:ComputerName)_$($pid)_message_*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty Name | Select-String -Pattern "(\d+)" -AllMatches).Matches[1].Value }
    catch { }
    if (-not ($filesystem_num_Error)) { $filesystem_num_Error = 0 }
    if (-not ($filesystem_num_Message)) { $filesystem_num_Message = 0 }
}

# Action that is performed for each message item that is being logged
$message_Event = {
    Param (
        $Message
    )
    
    $filesystem_CurrentFile = Join-Path $filesystem_root.FullName "$($env:ComputerName)_$($pid)_message_$($filesystem_num_Message).log"
    if (Test-Path $filesystem_CurrentFile)
    {
        $filesystem_item = Get-Item $filesystem_CurrentFile
        if ($filesystem_item.Length -gt ([PSFramework.Message.LogHost]::MaxMessagefileBytes))
        {
            $filesystem_num_Message++
            $filesystem_CurrentFile = Join-Path $($filesystem_root.FullName) "$($env:ComputerName)_$($pid)_message_$($filesystem_num_Message).log"
        }
    }
    
    if ($Message)
    {
        Add-Content -Path $filesystem_CurrentFile -Value (ConvertTo-Csv ($Message | Select-Object ComputerName, Timestamp, Level, Message, Type, FunctionName, ModuleName, File, Line, @{ n = "Tags"; e = { $_.Tags -join "," } }, TargetObject, Runspace) -NoTypeInformation)[1]
    }
}

# Action that is performed for each error item that is being logged
$error_Event = {
    Param (
        $ErrorItem
    )
    
    if ($ErrorItem)
    {
        $ErrorItem | Export-Clixml -Path (Join-Path $filesystem_root.FullName "$($env:ComputerName)_$($pid)_error_$($filesystem_num_Error).xml") -Depth 3
        $filesystem_num_Error++
    }
    
    Clean-FileSystemErrorXml -Path $filesystem_root
}

# Action that is performed at the end of each logging cycle
$end_event = {
    Clean-FileSystemMessageLog -Path $filesystem_root
    Clean-FileSystemGlobalLog -Path $filesystem_root
}

# Action that is performed when stopping the logging script
$final_event = {
    
}
#endregion Logging Execution

#region Function Extension / Integration
# Script that generates the necessary dynamic parameter for Set-PSFLoggingProvider
$configurationParameters = {
    $configroot = "psframework.logging.filesystem"
    
    $configurations = Get-PSFConfig -FullName "$configroot.*"
    
    $RuntimeParamDic = New-Object  System.Management.Automation.RuntimeDefinedParameterDictionary
    
    foreach ($config in $configurations)
    {
        $ParamAttrib = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttrib.ParameterSetName = '__AllParameterSets'
        $AttribColl = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $AttribColl.Add($ParamAttrib)
        $RuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter(($config.FullName.Replace($configroot, "").Trim(".")), $config.Value.GetType(), $AttribColl)
        
        $RuntimeParamDic.Add(($config.FullName.Replace($configroot, "").Trim(".")), $RuntimeParam)
    }
    return $RuntimeParamDic
}

# Script that is executes when configuring the provider using Set-PSFLoggingProvider
$configurationScript = {
    $configroot = "psframework.logging.filesystem"
    
    $configurations = Get-PSFConfig -FullName "$configroot.*"
    
    foreach ($config in $configurations)
    {
        if ($PSBoundParameters.ContainsKey(($config.FullName.Replace($configroot, "").Trim("."))))
        {
            Set-PSFConfig -Module $config.Module -Name $config.Name -Value $PSBoundParameters[($config.FullName.Replace($configroot, "").Trim("."))]
        }
    }
}

# Script that returns a boolean value. "True" if all prerequisites are installed, "False" if installation is required
$isInstalledScript = {
    return $true
}

# Script that provides dynamic parameter for Install-PSFLoggingProvider
$installationParameters = {
    # None needed
}

# Script that performs the actual installation, based on the parameters (if any) specified in the $installationParameters script
$installationScript = {
    # Nothing to be done - if you need to install your filesystem, you probably have other issues you need to deal with first ;)
}
#endregion Function Extension / Integration

# Configuration settings to initialize
$configuration_Settings = {
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.MaxMessagefileBytes' -Value 5MB -Initialize -Validation "long" -Handler { [PSFramework.Message.LogHost]::MaxMessagefileBytes = $args[0] } -Description "The maximum size of a given logfile. When reaching this limit, the file will be abandoned and a new log created. Set to 0 to not limit the size. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.MaxMessagefileCount' -Value 5 -Initialize -Validation "integerpositive" -Handler { [PSFramework.Message.LogHost]::MaxMessagefileCount = $args[0] } -Description "The maximum number of logfiles maintained at a time. Exceeding this number will cause the oldest to be culled. Set to 0 to disable the limit. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.MaxErrorFileBytes' -Value 20MB -Initialize -Validation "long" -Handler { [PSFramework.Message.LogHost]::MaxErrorFileBytes = $args[0] } -Description "The maximum size all error files combined may have. When this number is exceeded, the oldest entry is culled. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.MaxTotalFolderSize' -Value 100MB -Initialize -Validation "long" -Handler { [PSFramework.Message.LogHost]::MaxTotalFolderSize = $args[0] } -Description "This is the upper limit of length all items in the log folder may have combined across all processes."
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.MaxLogFileAge' -Value (New-TimeSpan -Days 7) -Initialize -Validation "timespan" -Handler { [PSFramework.Message.LogHost]::MaxLogFileAge = $args[0] } -Description "Any logfile older than this will automatically be cleansed. This setting is global."
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.MessageLogFileEnabled' -Value $true -Initialize -Validation "bool" -Handler { [PSFramework.Message.LogHost]::MessageLogFileEnabled = $args[0] } -Description "Governs, whether a log file for the system messages is written. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.ErrorLogFileEnabled' -Value $true -Initialize -Validation "bool" -Handler { [PSFramework.Message.LogHost]::ErrorLogFileEnabled = $args[0] } -Description "Governs, whether log files for errors are written. This setting is on a per-Process basis. Runspaces share, jobs or other consoles counted separately."
    Set-PSFConfig -Module PSFramework -Name 'Logging.FileSystem.LogPath' -Value $script:path_Logging -Initialize -Validation "string" -Handler { [PSFramework.Message.LogHost]::LoggingPath = $args[0] } -Description "The path where the PSFramework writes all its logs and debugging information."
    
    Set-PSFConfig -Module LoggingProvider -Name 'FileSystem.Enabled' -Value $true -Initialize -Validation "bool" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['filesystem']) { [PSFramework.Logging.ProviderHost]::Providers['filesystem'].Enabled = $args[0] } } -Description "Whether the logging provider should be enabled on registration"
    Set-PSFConfig -Module LoggingProvider -Name 'FileSystem.AutoInstall' -Value $false -Initialize -Validation "bool" -Handler { } -Description "Whether the logging provider should be installed on registration"
    Set-PSFConfig -Module LoggingProvider -Name 'FileSystem.InstallOptional' -Value $true -Initialize -Validation "bool" -Handler { } -Description "Whether installing the logging provider is mandatory, in order for it to be enabled"
    Set-PSFConfig -Module LoggingProvider -Name 'FileSystem.IncludeModules' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['filesystem']) { [PSFramework.Logging.ProviderHost]::Providers['filesystem'].IncludeModules = $args[0] } } -Description "Module whitelist. Only messages from listed modules will be logged"
    Set-PSFConfig -Module LoggingProvider -Name 'FileSystem.ExcludeModules' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['filesystem']) { [PSFramework.Logging.ProviderHost]::Providers['filesystem'].ExcludeModules = $args[0] } } -Description "Module blacklist. Messages from listed modules will not be logged"
    Set-PSFConfig -Module LoggingProvider -Name 'FileSystem.IncludeTags' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['filesystem']) { [PSFramework.Logging.ProviderHost]::Providers['filesystem'].IncludeTags = $args[0] } } -Description "Tag whitelist. Only messages with these tags will be logged"
    Set-PSFConfig -Module LoggingProvider -Name 'FileSystem.ExcludeTags' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['filesystem']) { [PSFramework.Logging.ProviderHost]::Providers['filesystem'].ExcludeTags = $args[0] } } -Description "Tag blacklist. Messages with these tags will not be logged"
}

Register-PSFLoggingProvider -Name "filesystem" -RegistrationEvent $registrationEvent -BeginEvent $begin_event -StartEvent $start_event -MessageEvent $message_Event -ErrorEvent $error_Event -EndEvent $end_event -FinalEvent $final_event -ConfigurationParameters $configurationParameters -ConfigurationScript $configurationScript -IsInstalledScript $isInstalledScript -InstallationScript $installationScript -InstallationParameters $installationParameters -ConfigurationSettings $configuration_Settings

# Action that is performed on registration of the provider using Register-PSFLoggingProvider
$registrationEvent = {
    
}

#region Logging Execution
# Action that is performed when starting the logging script (or the very first time if enabled after launching the logging script)
$begin_event = {
    function Get-LogFilePath
    {
        [CmdletBinding()]
        param (
            
        )
        
        $path = Get-PSFConfigValue -FullName 'PSFramework.Logging.LogFile.FilePath'
        $logname = Get-PSFConfigValue -FullName 'PSFramework.Logging.LogFile.LogName'
        
        $scriptBlock = {
            param (
                [string]
                $Match
            )
            
            $hash = @{
                '%date%'  = (Get-Date -Format 'yyyy-MM-dd')
                '%dayofweek%' = (Get-Date).DayOfWeek
                '%day%' = (Get-Date).Day
                '%hour%'   = (Get-Date).Hour
                '%minute%' = (Get-Date).Minute
                '%username%' = $env:USERNAME
                '%userdomain%' = $env:USERDOMAIN
                '%computername%' = $env:COMPUTERNAME
                '%processid%' = $PID
                '%logname%' = $logname
            }
            
            $hash.$Match
        }
        
        [regex]::Replace($path, '%day%|%computername%|%hour%|%processid%|%date%|%username%|%dayofweek%|%minute%|%userdomain%|%logname%', $scriptBlock)
    }
    
    function Write-LogFileMessage
    {
        [CmdletBinding()]
        param (
            [Parameter(ValueFromPipeline = $true)]
            $Message,
            
            [bool]
            $IncludeHeader,
            
            [string]
            $FileType,
            
            [string]
            $Path,
            
            [string]
            $CsvDelimiter,
            
            [string[]]
            $Headers
        )
        
        $parent = Split-Path $Path
        if (-not (Test-Path $parent))
        {
            $null = New-Item $parent -ItemType Directory -Force
        }
        $fileExists = Test-Path $Path
        
        #region Type-Based Output
        switch ($FileType)
        {
            #region Csv
            "Csv"
            {
                if ((-not $fileExists) -and $IncludeHeader) { $Message | ConvertTo-Csv -NoTypeInformation -Delimiter $CsvDelimiter | Set-Content -Path $Path -Encoding UTF8 }
                else { $Message | ConvertTo-Csv -NoTypeInformation -Delimiter $CsvDelimiter | Select-Object -Skip 1 | Add-Content -Path $Path -Encoding UTF8 }
            }
            #endregion Csv
            #region Json
            "Json"
            {
                if ($fileExists) { Add-Content -Path $Path -Value "," -Encoding UTF8 }
                $Message | ConvertTo-Json | Add-Content -Path $Path -NoNewline -Encoding UTF8
            }
            #endregion Json
            #region XML
            "XML"
            {
                [xml]$xml = $message | ConvertTo-Xml -NoTypeInformation
                $xml.Objects.InnerXml | Add-Content -Path $Path -Encoding UTF8
            }
            #endregion XML
            #region Html
            "Html"
            {
                [xml]$xml = $message | ConvertTo-Html -Fragment
                
                if ((-not $fileExists) -and $IncludeHeader)
                {
                    $xml.table.tr[0].OuterXml | Add-Content -Path $Path -Encoding UTF8
                }
                
                $xml.table.tr[1].OuterXml | Add-Content -Path $Path -Encoding UTF8
            }
            #endregion Html
        }
        #endregion Type-Based Output
    }
    
    $logfile_includeheader = Get-PSFConfigValue -FullName 'PSFramework.Logging.LogFile.IncludeHeader'
    $logfile_headers = Get-PSFConfigValue -FullName 'PSFramework.Logging.LogFile.Headers'
    $logfile_filetype = Get-PSFConfigValue -FullName 'PSFramework.Logging.LogFile.FileType'
    $logfile_CsvDelimiter = Get-PSFConfigValue -FullName 'PSFramework.Logging.LogFile.CsvDelimiter'
    
    if ($logfile_headers -contains 'Tags')
    {
        $logfile_headers = $logfile_headers | ForEach-Object {
            if ($_ -ne 'Tags') { $_ }
            else
            {
                @{
                    Name  = 'Tags'
                    Expression  = { $_.Tags -join "," }
                }
            }
        }
    }
    
    $logfile_paramWriteLogFileMessage = @{
        IncludeHeader    = $logfile_includeheader
        FileType         = $logfile_filetype
        CsvDelimiter     = $logfile_CsvDelimiter
        Headers             = $logfile_headers
    }
}

# Action that is performed at the beginning of each logging cycle
$start_event = {
    $logfile_paramWriteLogFileMessage["Path"] = Get-LogFilePath
}

# Action that is performed for each message item that is being logged
$message_Event = {
    Param (
        $Message
    )
    
    $Message | Select-Object $logfile_headers | Write-LogFileMessage @logfile_paramWriteLogFileMessage
}

# Action that is performed for each error item that is being logged
$error_Event = {
    Param (
        $ErrorItem
    )
    
    
}

# Action that is performed at the end of each logging cycle
$end_event = {
    
}

# Action that is performed when stopping the logging script
$final_event = {
    
}
#endregion Logging Execution

#region Function Extension / Integration
# Script that generates the necessary dynamic parameter for Set-PSFLoggingProvider
$configurationParameters = {
    $configroot = "psframework.logging.logfile"
    
    $configurations = Get-PSFConfig -FullName "$configroot.*"
    
    $RuntimeParamDic = New-Object  System.Management.Automation.RuntimeDefinedParameterDictionary
    
    foreach ($config in $configurations)
    {
        $ParamAttrib = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttrib.ParameterSetName = '__AllParameterSets'
        $AttribColl = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $AttribColl.Add($ParamAttrib)
        $RuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter(($config.FullName.Replace($configroot, "").Trim(".")), $config.Value.GetType(), $AttribColl)
        
        $RuntimeParamDic.Add(($config.FullName.Replace($configroot, "").Trim(".")), $RuntimeParam)
    }
    return $RuntimeParamDic
}

# Script that is executes when configuring the provider using Set-PSFLoggingProvider
$configurationScript = {
    $configroot = "psframework.logging.logfile"
    
    $configurations = Get-PSFConfig -FullName "$configroot.*"
    
    foreach ($config in $configurations)
    {
        if ($PSBoundParameters.ContainsKey(($config.FullName.Replace($configroot, "").Trim("."))))
        {
            Set-PSFConfig -Module $config.Module -Name $config.Name -Value $PSBoundParameters[($config.FullName.Replace($configroot, "").Trim("."))]
        }
    }
}

# Script that returns a boolean value. "True" if all prerequisites are installed, "False" if installation is required
$isInstalledScript = {
    return $true
}

# Script that provides dynamic parameter for Install-PSFLoggingProvider
$installationParameters = {
    # None needed
}

# Script that performs the actual installation, based on the parameters (if any) specified in the $installationParameters script
$installationScript = {
    # Nothing to be done - if you need to install your filesystem, you probably have other issues you need to deal with first ;)
}
#endregion Function Extension / Integration

# Configuration settings to initialize
$configuration_Settings = {
    Set-PSFConfig -Module PSFramework -Name 'Logging.LogFile.FilePath' -Value "" -Initialize -Validation string -Handler { } -Description "The path to where the logfile is written. Supports some placeholders such as %Date% to allow for timestamp in the name. For full documentation on the supported wildcards, see the documentation on https://psframework.org"
    Set-PSFConfig -Module PSFramework -Name 'Logging.LogFile.Logname' -Value "" -Initialize -Validation string -Handler { } -Description "A special string you can use as a placeholder in the logfile path (by using '%logname%' as placeholder)"
    Set-PSFConfig -Module PSFramework -Name 'Logging.LogFile.IncludeHeader' -Value $true -Initialize -Validation bool -Handler { } -Description "Whether a written csv file will include headers"
    Set-PSFConfig -Module PSFramework -Name 'Logging.LogFile.Headers' -Value @('ComputerName', 'File', 'FunctionName', 'Level', 'Line', 'Message', 'ModuleName', 'Runspace', 'Tags', 'TargetObject', 'Timestamp', 'Type', 'Username') -Initialize -Validation stringarray -Handler { } -Description "The properties to export, in the order to select them."
    Set-PSFConfig -Module PSFramework -Name 'Logging.LogFile.FileType' -Value "CSV" -Initialize -Validation psframework.logfilefiletype -Handler { } -Description "In what format to write the logfile. Supported styles: CSV, XML, Html or Json. Html, XML and Json will be written as fragments."
    Set-PSFConfig -Module PSFramework -Name 'Logging.LogFile.CsvDelimiter' -Value "," -Initialize -Validation string -Handler { } -Description "The delimiter to use when writing to csv."
    
    Set-PSFConfig -Module LoggingProvider -Name 'LogFile.Enabled' -Value $false -Initialize -Validation "bool" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['logfile']) { [PSFramework.Logging.ProviderHost]::Providers['logfile'].Enabled = $args[0] } } -Description "Whether the logging provider should be enabled on registration"
    Set-PSFConfig -Module LoggingProvider -Name 'LogFile.AutoInstall' -Value $false -Initialize -Validation "bool" -Handler { } -Description "Whether the logging provider should be installed on registration"
    Set-PSFConfig -Module LoggingProvider -Name 'LogFile.InstallOptional' -Value $true -Initialize -Validation "bool" -Handler { } -Description "Whether installing the logging provider is mandatory, in order for it to be enabled"
    Set-PSFConfig -Module LoggingProvider -Name 'LogFile.IncludeModules' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['logfile']) { [PSFramework.Logging.ProviderHost]::Providers['logfile'].IncludeModules = $args[0] } } -Description "Module whitelist. Only messages from listed modules will be logged"
    Set-PSFConfig -Module LoggingProvider -Name 'LogFile.ExcludeModules' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['logfile']) { [PSFramework.Logging.ProviderHost]::Providers['logfile'].ExcludeModules = $args[0] } } -Description "Module blacklist. Messages from listed modules will not be logged"
    Set-PSFConfig -Module LoggingProvider -Name 'LogFile.IncludeTags' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['logfile']) { [PSFramework.Logging.ProviderHost]::Providers['logfile'].IncludeTags = $args[0] } } -Description "Tag whitelist. Only messages with these tags will be logged"
    Set-PSFConfig -Module LoggingProvider -Name 'LogFile.ExcludeTags' -Value @() -Initialize -Validation "stringarray" -Handler { if ([PSFramework.Logging.ProviderHost]::Providers['logfile']) { [PSFramework.Logging.ProviderHost]::Providers['logfile'].ExcludeTags = $args[0] } } -Description "Tag blacklist. Messages with these tags will not be logged"
}

Register-PSFLoggingProvider -Name "logfile" -RegistrationEvent $registrationEvent -BeginEvent $begin_event -StartEvent $start_event -MessageEvent $message_Event -ErrorEvent $error_Event -EndEvent $end_event -FinalEvent $final_event -ConfigurationParameters $configurationParameters -ConfigurationScript $configurationScript -IsInstalledScript $isInstalledScript -InstallationScript $installationScript -InstallationParameters $installationParameters -ConfigurationSettings $configuration_Settings

$scriptBlock = {
    try
    {
        $script:___ScriptName = 'PSFramework.Logging'
        
        while ($true)
        {
            # This portion is critical to gracefully closing the script
            if ([PSFramework.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].State -notlike "Running")
            {
                break
            }
            
            #region Manage Begin Event
            foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetEnabled())
            {
                if (-not $___provider.Initialized)
                {
                    try
                    {
                        $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.BeginEvent)), $null, $null)
                        $___provider.Initialized = $true
                    }
                    catch { $___provider.Errors.Push($_) }
                }
            }
            #endregion Manage Begin Event
            
            #region Start Event
            foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
            {
                try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.StartEvent)), $null, $null) }
                catch { $___provider.Errors.Push($_) }
            }
            #endregion Start Event
            
            #region Message Event
            while ([PSFramework.Message.LogHost]::OutQueueLog.Count -gt 0)
            {
                $Entry = $null
                [PSFramework.Message.LogHost]::OutQueueLog.TryDequeue([ref]$Entry)
                if ($Entry)
                {
                    foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
                    {
                        if ($___provider.MessageApplies($Entry))
                        {
                            try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.MessageEvent)), $null, $Entry) }
                            catch { $___provider.Errors.Push($_) }
                        }
                    }
                }
            }
            #endregion Message Event
            
            #region Error Event
            while ([PSFramework.Message.LogHost]::OutQueueError.Count -gt 0)
            {
                $Record = $null
                [PSFramework.Message.LogHost]::OutQueueError.TryDequeue([ref]$Record)
                
                if ($Record)
                {
                    foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
                    {
                        if ($___provider.MessageApplies($Record))
                        {
                            try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.ErrorEvent)), $null, $Record) }
                            catch { $___provider.Errors.Push($_) }
                        }
                    }
                }
            }
            #endregion Error Event
            
            #region End Event
            foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
            {
                try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.EndEvent)), $null, $null) }
                catch { $___provider.Errors.Push($_) }
            }
            #endregion End Event
            
            Start-Sleep -Seconds 1
        }
    }
    catch
    {
        
    }
    finally
    {
        #region Flush log on exit
        if (([PSFramework.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].State -like "Running") -and ([PSFramework.Configuration.ConfigurationHost]::Configurations["psframework.logging.disablelogflush"].Value))
        {
            #region Start Event
            foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
            {
                try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.StartEvent)), $null, $null) }
                catch { $___provider.Errors.Push($_) }
            }
            #endregion Start Event
            
            #region Message Event
            while ([PSFramework.Message.LogHost]::OutQueueLog.Count -gt 0)
            {
                $Entry = $null
                [PSFramework.Message.LogHost]::OutQueueLog.TryDequeue([ref]$Entry)
                if ($Entry)
                {
                    foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
                    {
                        if ($___provider.MessageApplies($Entry))
                        {
                            try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.MessageEvent)), $null, $Entry) }
                            catch { $___provider.Errors.Push($_) }
                        }
                    }
                }
            }
            #endregion Message Event
            
            #region Error Event
            while ([PSFramework.Message.LogHost]::OutQueueError.Count -gt 0)
            {
                $Record = $null
                [PSFramework.Message.LogHost]::OutQueueError.TryDequeue([ref]$Record)
                
                if ($Record)
                {
                    foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
                    {
                        if ($___provider.MessageApplies($Record))
                        {
                            try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.MessageEvent)), $null, $Record) }
                            catch { $___provider.Errors.Push($_) }
                        }
                    }
                }
            }
            #endregion Error Event
            
            #region End Event
            foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
            {
                try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.EndEvent)), $null, $null) }
                catch { $___provider.Errors.Push($_) }
            }
            #endregion End Event
        }
        #endregion Flush log on exit
        
        #region Final Event
        foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
        {
            try { $ExecutionContext.InvokeCommand.InvokeScript($false, ([System.Management.Automation.ScriptBlock]::Create($___provider.FinalEvent)), $null, $null) }
            catch { $___provider.Errors.Push($_) }
        }
        
        foreach ($___provider in [PSFramework.Logging.ProviderHost]::GetInitialized())
        {
            $___provider.Initialized = $false
        }
        #endregion Final Event
        
        [PSFramework.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].SignalStopped()
    }
}

Register-PSFRunspace -ScriptBlock $scriptBlock -Name 'PSFramework.Logging' -NoMessage
Start-PSFRunspace -Name 'PSFramework.Logging' -NoMessage

Register-PSFTeppScriptblock -Name "PSFramework-config-fullname" -ScriptBlock {
    [PSFramework.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { -not $_.Hidden } | Select-Object -ExpandProperty FullName
}

Register-PSFTeppScriptblock -Name "PSFramework-config-module" -ScriptBlock {
    [PSFramework.Configuration.ConfigurationHost]::Configurations.Values.Module | Select-Object -Unique
}

Register-PSFTeppScriptblock -Name "PSFramework-config-name" -ScriptBlock {
    $moduleName = "*"
    if ($fakeBoundParameter.Module) { $moduleName = $fakeBoundParameter.Module }
    [PSFramework.Configuration.ConfigurationHost]::Configurations.Values | Where-Object { -not $_.Hidden -and ($_.Module -like $moduleName) } | Select-Object -ExpandProperty Name
}

Register-PSFTeppScriptblock -Name 'PSFramework-config-validation' -ScriptBlock { [PSFramework.Configuration.ConfigurationHost]::Validation.Keys }

Register-PSFTeppScriptblock -Name 'PSFramework-dynamiccontentobject-name' -ScriptBlock {
    [PSFramework.Utility.DynamicContentObject]::List
}

Register-PSFTeppScriptblock -Name "PSFramework-Encoding" -ScriptBlock {
    'Unicode'
    'BigEndianUnicode'
    'UTF8'
    'UTF8Bom'
    'UTF8NoBom'
    'UTF7'
    'UTF32'
    'Ascii'
    'Default'
    'BigEndianUTF32'
    if (Get-PSFConfigValue -FullName 'PSFramework.Text.Encoding.FullTabCompletion')
    {
        [System.Text.Encoding]::GetEncodings().BodyName
    }
}

Register-PSFTeppScriptblock -Name 'PSFramework-license-name' -ScriptBlock {
    (Get-PSFLicense).Product
}

Register-PSFTeppScriptblock -Name 'PSFramework-logging-provider' -ScriptBlock {
    (Get-PSFLoggingProvider).Name
}

Register-PSFTeppScriptblock -Name 'PSFramework.Message.Module' -ScriptBlock {
    Get-PSFMessage | Select-Object -ExpandProperty ModuleName | Select-Object -Unique
}

Register-PSFTeppScriptblock -Name 'PSFramework.Message.Function' -ScriptBlock {
    Get-PSFMessage | Select-Object -ExpandProperty FunctionName | Select-Object -Unique
}

Register-PSFTeppScriptblock -Name 'PSFramework.Message.Tags' -ScriptBlock {
    Get-PSFMessage | Select-Object -ExpandProperty Tags | Remove-PSFNull -Enumerate | Select-Object -Unique
}

Register-PSFTeppScriptblock -Name 'PSFramework.Message.Runspace' -ScriptBlock {
    Get-PSFMessage | Select-Object -ExpandProperty Runspace | Select-Object -Unique
}

Register-PSFTeppScriptblock -Name 'PSFramework.Message.Level' -ScriptBlock {
    Get-PSFMessage | Select-Object -ExpandProperty Level | Select-Object -Unique
}

Register-PSFTeppScriptblock -Name 'PSFramework-utility-psprovider' -ScriptBlock {
    (Get-PSProvider).Name
}

Register-PSFTeppScriptblock -Name 'PSFramework-runspace-name' -ScriptBlock {
    (Get-PSFRunspace).Name
}

Register-PSFTeppScriptblock -Name 'PSFramework-tepp-scriptblockname' -ScriptBlock {
    [PSFramework.TabExpansion.TabExpansionHost]::Scripts.Keys
}

Register-PSFTeppScriptblock -Name "PSFramework-Unregister-PSFConfig-FullName" -ScriptBlock {
    switch ("$($fakeBoundParameter.Scope)")
    {
        "UserDefault" { $path = "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\PSFramework\Config\Default" }
        "UserMandatory" { $path = "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\PSFramework\Config\Enforced" }
        "SystemDefault" { $path = "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\PSFramework\Config\Default" }
        "SystemMandatory" { $path = "HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\PSFramework\Config\Enforced" }
        default { $path = "HKCU:\SOFTWARE\Microsoft\WindowsPowerShell\PSFramework\Config\Default" }
    }
    
    if (Test-Path $path)
    {
        $properties = Get-ItemProperty -Path $path
        $common = 'PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider'
        $properties.PSObject.Properties.Name | Where-Object { $_ -notin $common }
    }
}

Register-PSFTeppScriptblock -Name "PSFramework-Unregister-PSFConfig-Module" -ScriptBlock {
    [PSFramework.Configuration.ConfigurationHost]::Configurations.Values.Module | Select-Object -Unique
}

#region Configuration
Register-PSFTeppArgumentCompleter -Command Export-PSFConfig -Parameter FullName -Name 'PSFramework-config-fullname'
Register-PSFTeppArgumentCompleter -Command Export-PSFConfig -Parameter Module -Name 'PSFramework-config-module'
Register-PSFTeppArgumentCompleter -Command Export-PSFConfig -Parameter Name -Name 'PSFramework-config-name'

Register-PSFTeppArgumentCompleter -Command Get-PSFConfig -Parameter FullName -Name 'PSFramework-config-fullname'
Register-PSFTeppArgumentCompleter -Command Get-PSFConfig -Parameter Module -Name 'PSFramework-config-module'
Register-PSFTeppArgumentCompleter -Command Get-PSFConfig -Parameter Name -Name 'PSFramework-config-name'

Register-PSFTeppArgumentCompleter -Command Set-PSFConfig -Parameter FullName -Name 'PSFramework-config-fullname'
Register-PSFTeppArgumentCompleter -Command Set-PSFConfig -Parameter Module -Name 'PSFramework-config-module'
Register-PSFTeppArgumentCompleter -Command Set-PSFConfig -Parameter Name -Name 'PSFramework-config-name'
Register-PSFTeppArgumentCompleter -Command Set-PSFConfig -Parameter Validation -Name 'PSFramework-config-validation'

Register-PSFTeppArgumentCompleter -Command Register-PSFConfig -Parameter FullName -Name 'PSFramework-config-fullname'
Register-PSFTeppArgumentCompleter -Command Register-PSFConfig -Parameter Module -Name 'PSFramework-config-module'
Register-PSFTeppArgumentCompleter -Command Register-PSFConfig -Parameter Name -Name 'PSFramework-config-name'

Register-PSFTeppArgumentCompleter -Command Get-PSFConfigValue -Parameter FullName -Name 'PSFramework-config-fullname'

Register-PSFTeppArgumentCompleter -Command Unregister-PSFConfig -Parameter FullName -Name 'PSFramework-Unregister-PSFConfig-FullName'
Register-PSFTeppArgumentCompleter -Command Unregister-PSFConfig -Parameter Module -Name 'PSFramework-Unregister-PSFConfig-Module'
#endregion Configuration

#region License
Register-PSFTeppArgumentCompleter -Command Get-PSFLicense -Parameter Filter -Name 'PSFramework-license-name'
#endregion License

#region Logging
Register-PSFTeppArgumentCompleter -Command Get-PSFLoggingProvider -Parameter Name -Name 'PSFramework-logging-provider'
Register-PSFTeppArgumentCompleter -Command Install-PSFLoggingProvider -Parameter Name -Name 'PSFramework-logging-provider'
Register-PSFTeppArgumentCompleter -Command Set-PSFLoggingProvider -Parameter Name -Name 'PSFramework-logging-provider'
#endregion Logging

#region Message
Register-PSFTeppArgumentCompleter -Command Get-PSFMessage -Parameter ModuleName -Name 'PSFramework.Message.Module'
Register-PSFTeppArgumentCompleter -Command Get-PSFMessage -Parameter FunctionName -Name 'PSFramework.Message.Function'
Register-PSFTeppArgumentCompleter -Command Get-PSFMessage -Parameter Tag -Name 'PSFramework.Message.Tags'
Register-PSFTeppArgumentCompleter -Command Get-PSFMessage -Parameter Runspace -Name 'PSFramework.Message.Runspace'
Register-PSFTeppArgumentCompleter -Command Get-PSFMessage -Parameter Level -Name 'PSFramework.Message.Level'
#endregion Message

#region Runspace
Register-PSFTeppArgumentCompleter -Command Get-PSFRunspace -Parameter Name -Name 'PSFramework-runspace-name'
Register-PSFTeppArgumentCompleter -Command Register-PSFRunspace -Parameter Name -Name 'PSFramework-runspace-name'
Register-PSFTeppArgumentCompleter -Command Stop-PSFRunspace -Parameter Name -Name 'PSFramework-runspace-name'
Register-PSFTeppArgumentCompleter -Command Start-PSFRunspace -Parameter Name -Name 'PSFramework-runspace-name'

Register-PSFTeppArgumentCompleter -Command Get-PSFDynamicContentObject -Parameter Name -Name 'PSFramework-dynamiccontentobject-name'
Register-PSFTeppArgumentCompleter -Command Set-PSFDynamicContentObject -Parameter Name -Name 'PSFramework-dynamiccontentobject-name'
#endregion Runspace

#region Serialization
Register-PSFTeppArgumentCompleter -Command Export-PSFClixml -Parameter Encoding -Name 'PSFramework-Encoding'
Register-PSFTeppArgumentCompleter -Command Import-PSFClixml -Parameter Encoding -Name 'PSFramework-Encoding'
#endregion Serialization

#region Tab Completion
Register-PSFTeppArgumentCompleter -Command Set-PSFTeppResult -Parameter TabCompletion -Name 'PSFramework-tepp-scriptblockname'
#endregion Tab Completion

#region Utility
Register-PSFTeppArgumentCompleter -Command Resolve-PSFPath -Parameter Provider -Name 'PSFramework-utility-psprovider'
#endregion Utility


$mappings = @{
    "microsoft.sqlserver.management.smo.server"            = @("NetName", "DomainInstanceName")
    "deserialized.microsoft.sqlserver.management.smo.server" = @("NetName", "DomainInstanceName")
    "microsoft.sqlserver.management.smo.linkedserver"   = @("Name")
    "deserialized.microsoft.sqlserver.management.smo.linkedserver" = @("Name")
    "microsoft.activedirectory.management.adcomputer"   = @("DNSHostName", "Name")
    "deserialized.microsoft.activedirectory.management.adcomputer" = @("DNSHostName", "Name")
    "Microsoft.DnsClient.Commands.DnsRecord_A"            = @("Name", "IPAddress")
    "Deserialized.Microsoft.DnsClient.Commands.DnsRecord_A" = @("Name", "IPAddress")
    "Microsoft.DnsClient.Commands.DnsRecord_AAAA"        = @("Name", "IPAddress")
    "Deserialized.Microsoft.DnsClient.Commands.DnsRecord_AAAA" = @("Name", "IPAddress")
}


foreach ($key in $mappings.Keys)
{
    Register-PSFParameterClassMapping -ParameterClass 'Computer' -TypeName $key -Properties $mappings[$key]
}

# Define our type aliases
$TypeAliasTable = @{
    PSFComputer               = "PSFramework.Parameter.ComputerParameter"
    PSFComputerParameter   = "PSFramework.Parameter.ComputerParameter"
    PSFDateTime               = "PSFramework.Parameter.DateTimeParameter"
    PSFDateTimeParameter   = "PSFramework.Parameter.DateTimeParameter"
    PSFEncoding            = "PSFramework.Parameter.EncodingParameter"
    PSFEncodingParameter   = "PSFramework.Parameter.EncodingParameter"
    psfrgx                   = "PSFramework.Utility.RegexHelper"
    PSFTimeSpan               = "PSFramework.Parameter.TimeSpanParameter"
    PSFTimeSpanParameter   = "PSFramework.Parameter.TimeSpanParameter"
    PSFValidatePattern     = "PSFramework.Validation.PsfValidatePatternAttribute"
    PSFValidateScript      = "PSFramework.Validation.PsfValidateScriptAttribute"
    PSFValidateSet         = "PSFramework.Validation.PsfValidateSetAttribute"
}

Set-PSFTypeAlias -Mapping $TypeAliasTable

$scriptBlock = {
    $script:___ScriptName = 'psframework.taskengine'
    
    try
    {
        #region Main Execution
        while ($true)
        {
            # This portion is critical to gracefully closing the script
            if ([PSFramework.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].State -notlike "Running")
            {
                break
            }
            
            $task = $null
            $tasksDone = @()
            while ($task = [PSFramework.TaskEngine.TaskHost]::GetNextTask($tasksDone))
            {
                try { ([ScriptBlock]::Create($task.ScriptBlock.ToString())).Invoke() }
                catch { Write-PSFMessage -EnableException $false -Level Warning -Message "[Maintenance] Task '$($task.Name)' failed to execute: $_" -ErrorRecord $_ -FunctionName "task:TaskEngine" -Target $task }
                $task.LastExecution = Get-Date
                $tasksDone += $task.Name
            }
            
            # If there will no more tasks need executing in the future, might as well kill the runspace
            if (-not ([PSFramework.TaskEngine.TaskHost]::HasPendingTasks)) { break }
            
            Start-Sleep -Seconds 5
        }
        #endregion Main Execution
    }
    catch {  }
    finally
    {
        [PSFramework.Runspace.RunspaceHost]::Runspaces[$___ScriptName.ToLower()].SignalStopped()
    }
}

Register-PSFRunspace -ScriptBlock $scriptBlock -Name 'psframework.taskengine' -NoMessage

#region Handle Module Removal
$PSF_OnRemoveScript = {
    # Stop all managed runspaces ONLY on the main runspace's termination
    if ([runspace]::DefaultRunspace.Id -eq 1)
    {
        Get-PSFRunspace | Stop-PSFRunspace
    }
    
    # Properly disconnect all remote sessions still held open
    $psframework_pssessions.Values | Remove-PSSession
}
$ExecutionContext.SessionState.Module.OnRemove += $PSF_OnRemoveScript
Register-EngineEvent -SourceIdentifier ([System.Management.Automation.PsEngineEvent]::Exiting) -Action $PSF_OnRemoveScript
#endregion Handle Module Removal

#region Declare runtime variable for the flow control component
$paramNewVariable = @{
    Name  = "psframework_killqueue"
    Value = (New-Object PSFramework.Utility.LimitedConcurrentQueue[int](25))
    Option = 'ReadOnly'
    Scope = 'Script'
    Description = 'Variable that is used to maintain the list of commands to kill. This is used by Test-PSFFunctionInterrupt. Note: The value tested is the hashcade from the callstack item.'
}

New-Variable @paramNewVariable
#endregion Declare runtime variable for the flow control component

#region Declare PSSession Cache
$paramNewVariable2 = @{
    Name  = "psframework_pssessions"
    Value = (New-Object PSFramework.ComputerManagement.PSSessionContainer)
    Option = 'ReadOnly'
    Scope = 'Script'
    Description = 'Variable containing the list of established powershell remoting sessions. This is used by Invoke-PSFCommand to track connections, disconnect expired sessions and reconnect sessions by name.'
}

New-Variable @paramNewVariable2
#endregion Declare PSSession Cache

$license = New-PSFLicense -Product 'PSFramework' -Manufacturer 'Friedrich Weinmann' -ProductVersion $ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date -Year 2017 -Month 04 -Day 27 -Hour 0 -Minute 0 -Second 0) -Text @"
Copyright (c) Friedrich Weinmann
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"@


#region Chris Dent
$null = New-PSFLicense -Product 'Import-PSCmdlet' -Manufacturer 'Chris Dent' -ProductVersion '1.0.0.0' -ProductType Script -Name MIT -Version '1.0.0.0' -Date (Get-Date -Year 2018 -Month 05 -Day 16).Date -Text @"
Copyright (c) Chris Dent
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"@
 -Description @"
The PSFramework is happy to publish the Import-PSFCmdlet command, based on the
original work of Chris Dent's, 'Import-PSCmdlet'
 
Thank you for allowing its use :)
- Original Source: https://www.indented.co.uk/cmdlets-without-a-dll/
- Author blog: https://www.indented.co.uk/
"@
 -Parent $license
#endregion Chris Dent

#region Joel Bennet
$null = New-PSFLicense -Product 'Configuration-ExportPaths' -Manufacturer 'Joel Bennet' -ProductVersion '1.3.0' -ProductType Script -Name MIT -Version '1.0.0.0' -Date (Get-Date -Year 2018 -Month 05 -Day 16).Date -Text @"
Copyright (c) 2015 Joel Bennett
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"@
 -Description @"
The PSFramework is happy to base its internal path selection for configuration
exports on the original work of Joel Bennet's, 'Configuration' module.
Its implementation can be found in the internal script file:
internal/scripts/loadConfigurationPersisted.ps1
 
Thank you for allowing its use :)
- Original Source: https://github.com/PoshCode/Configuration
- Author blog: http://huddledmasses.org/blog/
- Author Twitter: https://twitter.com/jaykul?lang=en
"@
 -Parent $license
#endregion Joel Bennet