FSLogix.PowerShell.Rules.psm1

#Requires -Version 5.0
function Add-FslAssignment {

    <#
        .SYNOPSIS
            Adds to the content of a FSLogix Rule assignment file.
 
        .DESCRIPTION
            This function can add to FSLogix assignment file contents, the assignment file should have the same basename as the matching rule file.
            This will not overwrite the contents of an existing file.
 
        .PARAMETER Path
            The Target file path to set the assignment within
        .PARAMETER RuleSetApplies
            This determines whether a ruleset does or does not apply to users/groups/processes etc. For instance when using a Hiding rule, applying that hiding rule to users will hide the file from the users assigned to it when applied.
        .PARAMETER UserName
            If you wish to tie down the rule to an individual user use theier unsername in this parameter. Groupname is more usual for assignments however
        .PARAMETER GroupName
            Use this to tie the assignment of the rule to a specific group
        .PARAMETER WellKnownSID
            The Well Known SID for groups such as Domain Admins are useful for cross-language assignments, if you use a group with a well known SID in the groupname parameter this will be automatically filled out, so mostly useful for pipeline input.
        .PARAMETER ADDistinguisedName
            Full Distinguished name of AD component
        .PARAMETER ProcessName
            Process name for the rule assignment, mostly used for redirect rules
        .PARAMETER IncludeChildProcess
            If Process name is stated you can optionally include chile prcesses (recommended)
        .PARAMETER IPAddress
            Enter the IPv4 or IPv6 address. Partial strings are allowed. For example, if you enter 192.168, an address of 192.168.0.1 will be considered to match.
        .PARAMETER ComputerName
            Enter the Full Distinguished Name of the computer object, or the computer name (wildcards accepted). Must be in the format ComputerName@Domain
        .PARAMETER OU
            You can specify an Active Directory Container and the assignment will be effective for all of the objects in that container. Enter the Full Distinguished Name of the container.
        .PARAMETER EnvironmentVariable
            By Specifying an environment variable, you can customize rules in various other ways. A very useful example for this option is when using it with RDSH, XenApp, or other remote sessions. You can use the Environment Variable CLIENTNAME to limit visibility to the device being used to access the RDSH or XenApp system.
            The environment variables that are supported are the ones that are present when the user's session is created. Environment variables set during logon are not supported.
        .PARAMETER AssignedTime
            Only used for pipeline input
        .PARAMETER UnAssignedTime
            Only used for pipeline input
        .EXAMPLE
            A sample command that uses the function or script, optionaly followed
            by sample output and a description. Repeat this keyword for each example.
    #>


    [CmdletBinding()]
    Param (

        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [Alias('AssignmentFilePath')]
        [System.String]$Path,

        [Parameter(
            ParameterSetName = 'User',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Executable',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Network',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Computer',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'OU',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'EnvironmentVariable',
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$RuleSetApplies,

        [Parameter(
            ParameterSetName = 'User',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$UserName,

        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$GroupName,

        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$WellKnownSID,

        [Parameter(
            ParameterSetName = 'User',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$ADDistinguisedName,

        [Parameter(
            ParameterSetName = 'Executable',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$ProcessName,

        [Parameter(
            ParameterSetName = 'Executable',
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$IncludeChildProcess,

        [Parameter(
            ParameterSetName = 'Network',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$IPAddress,

        [Parameter(
            ParameterSetName = 'Computer',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [ValidatePattern(".*@.*")]
        [System.String]$ComputerName,

        [Parameter(
            ParameterSetName = 'OU',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$OU,

        [Parameter(
            ParameterSetName = 'EnvironmentVariable',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [ValidatePattern(".*=.*")]
        [System.String]$EnvironmentVariable,

        [Parameter(
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$PassThru,

        [Parameter(
            ParameterSetName = 'AssignmentObjectPipeline',
            ValuefromPipeline = $true,
            ValuefromPipelineByPropertyName = $true
        )]
        [PSTypeName('FSLogix.Assignment')]$InputObject
    )

    BEGIN {
        Set-StrictMode -Version Latest
        #check file has correct filename extension
        if ($Path -notlike "*.fxa") {
            Write-Warning 'Assignment files should have an fxa extension'
        }
        if ( -not ( Test-Path $Path )) {
            $version = 1
            $minimumLicenseAssignedTime = 0
            Set-Content -Path $Path -Value "$version`t$minimumLicenseAssignedTime" -Encoding Unicode -ErrorAction Stop
        }

    } # Begin
    PROCESS {

        $convertToFslAssignmentCodeParams = @{ }

        $assignmentCode = $null
        $idString = $null
        $DistinguishedName = $null
        $FriendlyName = $null

        if ($PSCmdlet.ParameterSetName -eq 'AssignmentObjectPipeline') {
            $allFields = $InputObject
        }
        else {
            $allFields = [PSCustomObject]@{

                RuleSetApplies      = $RuleSetApplies
                UserName            = $UserName
                GroupName           = $GroupName
                ADDistinguisedName  = $ADDistinguisedName
                WellKnownSID        = $WellKnownSID
                ProcessName         = $ProcessName
                IncludeChildProcess = $IncludeChildProcess
                IPAddress           = $IPAddress
                ComputerName        = $ComputerName
                OU                  = $OU
                EnvironmentVariable = $EnvironmentVariable
                AssignedTime        = 0
                UnAssignedTime      = 0

            }
        }

        if ($allFields.RuleSetApplies) {
            $convertToFslAssignmentCodeParams += @{ 'Apply' = $true }
        }
        else {
            $convertToFslAssignmentCodeParams += @{ 'Remove' = $true }
        }


        if ($allFields.UserName) {

            $convertToFslAssignmentCodeParams += @{ 'User' = $true }

            if ($allFields.ADDistinguisedName) {
                $convertToFslAssignmentCodeParams += @{ 'ADDistinguishedName' = $true }
                $distinguishedName = $allFields.ADDistinguisedName
            }

            $idString = $allFields.UserName
            $friendlyName = $allFields.UserName
        }

        if ( $allFields.GroupName ) {

            $convertToFslAssignmentCodeParams += @{ 'Group' = $true }

            if ( $allFields.ADDistinguisedName ) {
                $convertToFslAssignmentCodeParams += @{ 'ADDistinguishedName' = $true }
                $distinguishedName = $allFields.ADDistinguisedName
            }

            #Determine if the group has a Well Known SID
            $wellknownSids = [Enum]::GetValues([System.Security.Principal.WellKnownSidType])
            $account = New-Object System.Security.Principal.NTAccount($allFields.GroupName)
            try {
                $sid = $account.Translate([System.Security.Principal.SecurityIdentifier])
                $result = foreach ($s in $wellknownSids) { $sid.IsWellKnown($s) }

                if ( $result -contains $true ) {
                    $idString = $sid.Value
                }
                else {
                    $idString = $allFields.GroupName
                }
            }
            catch {
                $idString = $allFields.GroupName
            }
            
            $friendlyName = $allFields.GroupName
        }

        if ( $allFields.ProcessName ) {

            $convertToFslAssignmentCodeParams += @{ 'Process' = $true }

            if ($allFields.IncludeChildProcess) {
                $convertToFslAssignmentCodeParams += @{ 'ApplyToProcessChildren' = $true }
            }

            $idString = $allFields.ProcessName

        }

        if ( $allFields.IPAddress ) {
            $convertToFslAssignmentCodeParams += @{ 'Network' = $true }
            $idString = $allFields.IPAddress
        }

        if ( $allFields.ComputerName ) {
            $convertToFslAssignmentCodeParams += @{ 'Computer' = $true }
            $idString = $allFields.ComputerName
        }

        if ( $allFields.OU ) {
            $convertToFslAssignmentCodeParams += @{ 'ADDistinguishedName' = $true }
            $idString = $allFields.OU
        }

        if ( $allFields.EnvironmentVariable ) {
            $convertToFslAssignmentCodeParams += @{ 'EnvironmentVariable' = $true }
            $idString = $allFields.EnvironmentVariable
            if ( $allFields.AssignedTime -eq 0 -and $convertToFslAssignmentCodeParams.Remove -eq $true ) {
                $allFields.AssignedTime = (Get-Date).ToFileTime()
            }
        }


        if ( $allFields.AssignedTime -is [DateTime] ) {
            $AssignedTime = $allFields.AssignedTime.ToFileTime()
        }
        else {
            $AssignedTime = $allFields.AssignedTime
        }

        if ( $allFields.UnAssignedTime -is [DateTime] ) {
            $UnAssignedTime = $allFields.UnAssignedTime.ToFileTime()
        }
        else {
            $UnAssignedTime = $allFields.UnAssignedTime
        }

        if ( -not (Test-Path variable:script:DistinguishedName) ) {
            $DistinguishedName = ''
        }

        $assignmentCode = ConvertTo-FslAssignmentCode @convertToFslAssignmentCodeParams

        $message = "$assignmentCode`t$idString`t$DistinguishedName`t$FriendlyName`t$AssignedTime`t$UnAssignedTime"

        $addContentParams = @{
            'Path'     = $Path
            'Encoding' = 'Unicode'
            'Value'    = $message
            'WhatIf'   = $false
        }

        Add-Content @addContentParams

        Write-Verbose -Message "Written $message to $Path"

        if ($passThru) {
            $passThruObject = [pscustomobject]@{
                AssignmentCode    = $assignmentCode
                IdString          = $idString
                DistinguishedName = $DistinguishedName
                FriendlyName      = $FriendlyName
                AssignedTime      = $AssignedTime
                UnAssignedTime    = $UnAssignedTime
            }
            Write-Output $passThruObject
        }
    } #Process
    END { } #End
}  #function Add-FslAssignment

function Add-FslRule {
    [CmdletBinding()]

    Param (

        [Parameter(
            Position = 1,
            Mandatory = $true,
            ValuefromPipelineByPropertyName = $true
        )]
        [Alias('RuleFilePath')]
        [System.String]$Path,

        [Parameter(
            ParameterSetName = 'Hiding',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = 'Redirect',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = 'AppContainer',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = 'SpecifyValue',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Alias('Name')]
        [System.String]$FullName,

        [Parameter(
            ParameterSetName = 'Hiding',
            Mandatory = $true,
            Position = 3,
            ValuefromPipelineByPropertyName = $true
        )]
        [ValidateSet('FolderOrKey', 'FileOrValue', 'Font', 'Printer')]
        [System.String]$HidingType,

        [Parameter(
            ParameterSetName = 'Redirect',
            Mandatory = $true,
            Position = 6,
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$RedirectDestPath,

        [Parameter(
            ParameterSetName = 'Redirect',
            Mandatory = $true,
            Position = 7,
            ValuefromPipelineByPropertyName = $true
        )]
        [ValidateSet('FolderOrKey', 'FileOrValue')]
        [string]$RedirectType,

        [Parameter(
            ParameterSetName = 'Redirect',
            Position = 8,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$CopyObject,

        [Parameter(
            ParameterSetName = 'AppContainer',
            Mandatory = $true,
            Position = 9,
            ValuefromPipelineByPropertyName = $true
        )]
        [string]$DiskFile,

        [Parameter(
            ParameterSetName = 'SpecifyValue',
            Mandatory = $true,
            Position = 10,
            ValuefromPipelineByPropertyName = $true
        )]
        [string[]]$ValueData,

        [Parameter(
            ParameterSetName = 'SpecifyValue',
            Mandatory = $false,
            ValuefromPipelineByPropertyName = $true
        )]
        [ValidateSet('String', 'DWORD', 'QWORD', 'Multi-String', 'ExpandableString')]
        [string]$RegValueType = 'String',

        [Parameter(
            Position = 11,
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$Comment = 'Created By PowerShell Script',

        [Parameter(
            Position = 13,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Passthru,

        [Parameter(
            ParameterSetName = 'RuleObjectPipeline',
            ValuefromPipeline = $true,
            ValuefromPipelineByPropertyName = $true
        )]
        [PSTypeName('FSLogix.Rule')]$RuleObject
    )

    BEGIN {
        Set-StrictMode -Version Latest

        $FRX_RULE_SRC_IS_A_FILE_OR_VALUE = 0x00000002
        $FRX_RULE_TYPE_REDIRECT = 0x00000100
        $FRX_RULE_TYPE_SPECIFIC_DATA = 0x00000800

    } # Begin
    PROCESS {

        if ( -not ( Test-Path $Path )) {
            $version = 1
            Set-Content -Path $Path -Value $version -Encoding Unicode -ErrorAction Stop
        }
        #check file has correct filename extension
        if ($Path -notlike "*.fxr") {
            Write-Warning 'Rule files should have an fxr extension'
        }

        $convertToFslRuleCodeParams = @{ }

        #This switch statement sets up the function parameters for ConvertT-FslRuleCode
        switch ($PSCmdlet.ParameterSetName) {

            Hiding {
                switch ($true) {
                    { $HidingType -eq 'Font' } { $convertToFslRuleCodeParams += @{ 'HideFont' = $true }
                    }
                    { $HidingType -eq 'Printer' } { $convertToFslRuleCodeParams += @{ 'Printer' = $true }
                    }
                    { $HidingType -eq 'FileOrValue' } { $convertToFslRuleCodeParams += @{ 'FileOrValue' = $true }
                    }
                    { $HidingType -eq 'FolderOrKey' } { $convertToFslRuleCodeParams += @{ 'FolderOrKey' = $true }
                    }
                    { $HidingType -ne 'Font' -and $HidingType -ne 'Printer' } { $convertToFslRuleCodeParams += @{ 'Hiding' = $true }
                    }
                }
                break
            }
            Redirect {
                $convertToFslRuleCodeParams += @{ 'Redirect' = $true }

                switch ($true) {
                    { $RedirectType -eq 'FileOrValue' } { $convertToFslRuleCodeParams += @{ 'FileOrValue' = $true }
                    }
                    { $RedirectType -eq 'FolderOrKey' } { $convertToFslRuleCodeParams += @{ 'FolderOrKey' = $true }
                    }
                }
                $convertToFslRuleCodeParams += @{
                    'CopyObject' = $CopyObject
                }

                break
            }
            AppContainer {
                $convertToFslRuleCodeParams += @{ 'VolumeAutomount' = $true }
                break
            }
            SpecifyValue {
                $convertToFslRuleCodeParams += @{ 'SpecificData' = $true }
                $convertToFslRuleCodeParams += @{ 'FileOrValue' = $true }
                break
            }
            RuleObjectPipeline {
                if ($RuleObject.HidingType) {
                    switch ($true) {
                        { $RuleObject.HidingType -eq 'Font' } { $convertToFslRuleCodeParams += @{ 'HideFont' = $true }
                        }
                        { $RuleObject.HidingType -eq 'Printer' } { $convertToFslRuleCodeParams += @{ 'Printer' = $true }
                        }
                        { $RuleObject.HidingType -eq 'FileOrValue' } { $convertToFslRuleCodeParams += @{ 'FileOrValue' = $true }
                        }
                        { $RuleObject.HidingType -eq 'FolderOrKey' } { $convertToFslRuleCodeParams += @{ 'FolderOrKey' = $true }
                        }
                        { $RuleObject.HidingType -ne 'Font' -and $RuleObject.HidingType -ne 'Printer' } { $convertToFslRuleCodeParams += @{ 'Hiding' = $true }
                        }
                    }
                }
                if ($RuleObject.RedirectType) {
                    $convertToFslRuleCodeParams += @{ 'Redirect' = $true }
                    switch ($true) {
                        { $RuleObject.RedirectType -eq 'FileOrValue' } { $convertToFslRuleCodeParams += @{ 'FileOrValue' = $true }
                        }
                        { $RuleObject.RedirectType -eq 'FolderOrKey' } { $convertToFslRuleCodeParams += @{ 'FolderOrKey' = $true }
                        }
                    }
                }
                if ($RuleObject.DiskFile) {
                    $convertToFslRuleCodeParams += @{ 'VolumeAutomount' = $true }
                }
                if ($RuleObject.Data) {
                    $convertToFslRuleCodeParams += @{ 'SpecificData' = $true }
                    $convertToFslRuleCodeParams += @{ 'FileOrValue' = $true }
                    $RegValueType = $RuleObject.RegValueType
                    $ValueData = $RuleObject.Data
                }
                if ($RuleObject.CopyObject) {
                    $convertToFslRuleCodeParams += @{ 'CopyObject' = $true }
                }
                $FullName = $RuleObject.FullName
                $RedirectDestPath = $RuleObject.RedirectDestPath
            }

        }

        $flags = ConvertTo-FslRuleCode @convertToFslRuleCodeParams

        switch ($true) {
            (($flags -band  $FRX_RULE_TYPE_SPECIFIC_DATA) -eq 2048) {
                $sourceParent = Split-Path $FullName -Parent
                $source = Split-Path $FullName -Leaf

                #get rid of array, when not using multi-string
                if ($RegValueType -ne 'Multi-String') {
                    $RegData = $ValueData[0]
                }
                else {
                    $RegData = $ValueData
                }

                switch ($RegValueType) {
                    String {
                        try {
                            $hex = ConvertTo-FslHexString -RegData $RegData -ErrorAction Stop
                        }
                        catch {
                            Write-Error "$Error[0]"
                            exit
                        }
        
                        $binary = '01' + $hex.ToString() + '0000'
                        break
                    }
                    DWORD {
 
                        try {
                            $hex = ConvertTo-FslHexDword -RegData $RegData -ErrorAction Stop
                        }
                        catch {
                            Write-Error "$Error[0]"
                            exit
                        }
        
                        $binary = '04' + $hex.ToString()
                        
                        break
                    }
                    QWORD {
                        try {
                            $hex = ConvertTo-FslHexQword -RegData $RegData -ErrorAction Stop
                        }
                        catch {
                            Write-Error "Unable to convert $Regdata to a QWORD Unsigned 64 bit Integer"
                            exit
                        }
        
                        $binary = '0B' + $hex.ToString()
                        break
                    }
                    Multi-String {
                        try {
                            $hex = ConvertTo-FslHexMultiString -RegData $RegData -ErrorAction Stop
                        }
                        catch {
                            Write-Error $error[0]
                            exit
                        }
                        $binary = '07' + $hex + '000000'
                        break
                    }
                    ExpandableString {
                        try {
                            $hex = ConvertTo-FslHexString -RegData $RegData -ErrorAction Stop
                        }
                        catch {
                            Write-Error "$Error[0]"
                            exit
                        }
        
                        $binary = '02' + $hex.ToString() + '0000'    
                        break
                    }
                }
                if ($Comment -eq 'Created By PowerShell Script') {
                    $Comment = "Created by Script: $RegValueType $($ValueData.ToString())"
                }
                break
            }
            (($flags -band  $FRX_RULE_SRC_IS_A_FILE_OR_VALUE) -eq 2) {
                $sourceParent = Split-Path $FullName -Parent
                $source = Split-Path $FullName -Leaf
                $binary = $null
                break
            }
            Default {
                $sourceParent = $FullName
                $source = $null
                $binary = $null
            }
        }

        if ($flags -band $FRX_RULE_SRC_IS_A_FILE_OR_VALUE -and
            $flags -band $FRX_RULE_TYPE_REDIRECT) {
            $destParent = Split-Path $RedirectDestPath -Parent
            $dest = Split-Path $RedirectDestPath -Leaf
        }
        else {
            $destParent = $RedirectDestPath
            $dest = $null
        }

        $addContentParams = @{
            'Path'     = $Path
            'Encoding' = 'Unicode'
            'WhatIf'   = $false
        }

        Add-Content @addContentParams -Value "##$Comment"
        Write-Verbose -Message "Written $Comment to $Path"

        If ($convertToFslRuleCodeParams.ContainsKey( 'CopyObject' ) -and
            $convertToFslRuleCodeParams.ContainsKey( 'Redirect' ) -and
            $convertToFslRuleCodeParams.ContainsKey( 'FolderOrKey' ) ) {
            if ( $convertToFslRuleCodeParams.CopyObject -and
                $convertToFslRuleCodeParams.Redirect -and
                $convertToFslRuleCodeParams.FolderOrKey ) {
                    $SourceParent = $SourceParent.TrimEnd('\') + '\'
                    $destParent = $destParent.TrimEnd('\') + '\'
            }
        
        }
        else {
            $destParent = $destParent.TrimEnd('\')
        }

        $message = "$SourceParent`t$Source`t$DestParent`t$Dest`t$Flags`t$binary"

        Add-Content @addContentParams -Value $message

        Write-Verbose -Message "Written $message to $Path"

        If ($passThru) {
            $passThruObject = [pscustomobject]@{
                SourceParent = $SourceParent
                Source       = $Source
                DestParent   = $DestParent
                Dest         = $Dest
                Flags        = $Flags
                binary       = $binary
                Comment      = $Comment
            }
            Write-Output $passThruObject
        }
    } #Process
    END { } #End
}  #function Add-FslRule

function Compare-FslFilePath {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [System.Array[]]$Files,

        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$OutputPath = "$PSScriptRoot"
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {

        foreach ($filepath in $Files) {
            if (-not (Test-Path $filepath)){
                Write-Error "$filepath does not exist"
                exit
            }
        }

        $allFiles = @()
        foreach ($filepath in $Files){
            $appFiles = ( Import-Clixml $filepath ).FullName
            $allfiles += $appFiles
        }

        $dupes = $allFiles | Group-Object | Where-Object { $_.Count -gt 1 } | Select-Object -ExpandProperty Name

        $uniqueFiles = @{}

        foreach ($filepath in $Files){

            $baseFileName = $filepath | Get-ChildItem | Select-Object -ExpandProperty BaseName

            $newFileName = "$($baseFileName)_UniqueHiding.fxr"

            $currentAppFiles = ( Import-Clixml $filepath ).FullName

            $uniqueFiles =  $currentAppFiles | Where-Object { $dupes -notcontains $_ }

            $uniqueFiles | Set-FslRule -HidingType FileOrValue -RuleFilePath ( Join-Path $OutputPath $newFileName )

        }

    } #Process
    END {} #End
}  #function Compare-FslFilePath

function Compare-FslRuleFile {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [System.Array]$Files,

        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$OutputPath = "$PSScriptRoot"
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {

        foreach ($filepath in $Files) {
            if (-not (Test-Path $filepath)){
                Throw "$filepath does not exist"
            }
        }

        foreach ($filepath in $Files) {
            $diffRule = @()

            $referenceFile = $filepath
            $baseFileName = $filepath | Get-ChildItem | Select-Object -ExpandProperty BaseName
            $rules = Get-FslRule -Path $filepath
            #Get hiding rules (only concerned with hiding rules that are registry keys)
            $refRule = $rules | Where-Object { $_.HidingType -eq 'FolderOrKey' -and $_.FullName -like "HKLM*"} | Select-Object -ExpandProperty FullName

            foreach ($filepath in $Files){
                if ($filepath -ne $referenceFile){
                    $notRefRule = Get-FslRule $filepath
                     #Get hiding rules (only concerned with hiding rules that are registry keys)
                    $notRefHideRules = $notRefRule | Where-Object { $_.HidingType -eq 'FolderOrKey' -and $_.FullName -like "HKLM*" } | Select-Object -ExpandProperty FullName
                    $diffRule += $notRefHideRules
                }
            }

            #get rid of dupes between the rest of the files
            $uniqueDiffRule = $diffRule | Group-Object | Select-Object -ExpandProperty Name

            #Add all together
            $refAndDiff = $refRule + $uniqueDiffRule

            #Get Dupes between current file and rest of files
            $dupes = $refAndDiff  | Group-Object | Where-Object { $_.Count -gt 1 } | Select-Object -ExpandProperty Name

            #remove dupes from old rule list
            $newRules = $rules | Where-Object {$dupes -notcontains $_.FullName }

            $newRuleFileName = Join-Path $OutputPath ($baseFileName + '_Hiding' + '.fxr')

            $newRedirectFileName = Join-Path $OutputPath ($baseFileName + '_Redirect' + '.fxr')

            $newRules | Set-FslRule -RuleFilePath $newRuleFileName

            $newRedirect = $dupes | Select-Object -Property @{n = 'FullName'; e = {$_}},
            @{n = 'RedirectDestPath'; e = { "HKLM\Software\FSLogix\Redirect\$($baseFileName)\$($_.TrimStart('HKLM\'))"}},
            @{n = 'RedirectType'; e = {'FolderOrKey'}}

            $newRedirect | Set-FslRule -RuleFilePath $newRedirectFileName -RedirectType FolderOrKey


        }

    } #Process
    END {} #End
}  #function Compare-FslRuleFile

function Get-FslAssignment {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [System.String]$Path
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {
        if (-not (Test-Path $Path)) {
            Write-Error "$Path not found."
            exit
        }

        #Grab txt file contents apart from first line
        $lines = Get-Content -Path $Path | Select-Object -Skip 1

        foreach ($line in $lines) {

            #If line matches tab separated data with 5 columns.
            if ( $line -match "([^\t]*\t){5}" ) {
                #Create a powershell object from the columns
                $lineObj = $line | ConvertFrom-String -Delimiter `t -PropertyNames FlagsDec, IdString, DistinguishedName, FriendlyName, AssignedTime, UnAssignedTime
                #ConvertFrom-String converts the hex value in flag to decimal, need to convert back to a hex string. Add in the comment and output it.
                $assignment = $lineObj | Select-Object -Property  IdString, DistinguishedName, FriendlyName, AssignedTime, UnAssignedTime, @{n = 'Flags'; e = { '0x' + "{0:X8}" -f $lineObj.FlagsDec } }

                $poshFlags = $assignment.Flags | ConvertFrom-FslAssignmentCode

                if ($poshFlags.PSObject.Properties -contains 'java') {
                    Write-Error 'Please use the cmdlet Get-FslJavaAssignment to get assignments for java files'
                    exit
                }

                $output = [PSCustomObject]@{
                    PSTypeName          = "FSLogix.Assignment"
                    RuleSetApplies      = switch ( $true ) {
                        $poshFlags.Remove { $false }
                        $poshFlags.Apply { $true }
                    }
                    UserName            = if ( $poshFlags.User ) { $assignment.IdString } else { $null }
                    GroupName           = if ( $poshFlags.Group ) { $assignment.FriendlyName } else { $null }
                    ADDistinguisedName  = if ( $poshFlags.Group ) { $assignment.DistinguishedName } else { $null }
                    WellKnownSID        = if ( $poshFlags.Group ) { $assignment.IdString } else { $null }
                    ProcessName         = if ( $poshFlags.Process ) { $assignment.IdString } else { $null }
                    IncludeChildProcess = if ( $poshFlags.Process ) { $poshFlags.ApplyToProcessChildren } else { $null }
                    IPAddress           = if ( $poshFlags.Network ) { $assignment.IdString } else { $null }
                    ComputerName        = if ( $poshFlags.Computer ) { $assignment.IdString } else { $null }
                    OU                  = if ( $poshFlags.ADDistinguishedName ) { $assignment.IdString } else { $null }
                    EnvironmentVariable = if ( $poshFlags.EnvironmentVariable ) { $assignment.IdString } else { $null }
                    AssignedTime        = if ( $poshFlags.EnvironmentVariable ) {
                        if ($assignment.AssignedTime -ne 0) {
                            [DateTime]::FromFileTime($assignment.AssignedTime)
                        }
                        else {
                            0
                        }
                    }
                    else { 0 }
                    UnAssignedTime      = if ( $poshFlags.EnvironmentVariable ) {
                        if ($assignment.UnAssignedTime -ne 0) {
                            [DateTime]::FromFileTime($assignment.UnAssignedTime)
                        }
                        else {
                            0
                        }
                    }
                    else { 0 }
                }

                Write-Output $output
            } #if
        } #foreach
    } #Process
    END { } #End
}  #function Get-FslAssignment

function Get-FslLicenseDay {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [Alias('AssignmentFilePath')]
        [System.String]$Path
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {
        if (-not (Test-Path $Path)) {
            Write-Error "$Path not found."
            break
        }

        If ((Get-ChildItem -Path $Path).Extension -ne '.fxa') {
            Write-Warning 'Assignment file extension should be .fxa'
        }

        $firstLine = Get-Content -Path $Path -TotalCount 1

        try {
            [int]$licenseDay = $firstLine.Split("`t")[-1]
        }
        catch {
            Write-Error "Bad data on first line of $Path"
            break
        }

        $output = [pscustomobject]@{
            LicenseDay = $licenseDay
        }

        Write-Output $output

    } #Process
    END { } #End
}  #function Get-FslLicenseDay

function Get-FslRule {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [System.String]$Path
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {
        if (-not (Test-Path $Path)) {
            Write-Error "$Path not found."
            exit
        }
        #Grab txt file contents apart from first line
        $lines = Get-Content -Path $Path | Select-Object -Skip 1

        foreach ($line in $lines) {
            switch ($true) {
                #Grab comment if this line is one.
                $line.StartsWith('##') {
                    $comment = $line.TrimStart('#')
                    break
                }
                #If line matches tab separated data with 5 columns.
                { $line -match "([^\t]*\t){5}" } {
                    #Create a powershell object from the columns only works on full PowerShell, not core
                    $lineObj = $line | ConvertFrom-String -Delimiter `t -PropertyNames SrcParent, Src, DestParent, Dest, FlagsDec, Binary
                    #ConvertFrom-String converts the hex value in flag to decimal, need to convert back to a hex string. Add in the comment and output it.
                    $rulePlusComment = $lineObj | Select-Object -Property SrcParent, Src, DestParent, Dest, @{n = 'Flags'; e = { '0x' + "{0:X8}" -f $lineObj.FlagsDec } }, Binary, @{n = 'Comment'; e = { $comment } }

                    $poshFlags = $rulePlusComment.Flags | ConvertFrom-FslRuleCode
                    if ($rulePlusComment.DestParent) {
                        $destPath = try {
                            (Join-Path $rulePlusComment.DestParent $rulePlusComment.Dest -ErrorAction Stop).TrimEnd('\')
                        }
                        catch {
                            [system.io.fileinfo]($rulePlusComment.DestParent.TrimEnd('\', '/') + '\' + $rulePlusComment.Dest.TrimStart('\', '/').TrimEnd('\'))
                        }
                    }
                    $fullnameJoin = try {
                        (Join-Path $rulePlusComment.SrcParent $rulePlusComment.Src -ErrorAction Stop).TrimEnd('\')
                    }
                    catch {
                        [system.io.fileinfo]($rulePlusComment.SrcParent.TrimEnd('\', '/') + '\' + $rulePlusComment.Src.TrimStart('\', '/')).TrimEnd('\')
                    }

                    if ($rulePlusComment.Binary) {
                        $SpecificData = ConvertFrom-FslRegHex -HexString $rulePlusComment.Binary
                    }
                    else{
                        $SpecificData = [PSCustomObject]@{
                            Data = $null
                            RegValueType = $null
                        }
                    }

                    $output = [PSCustomObject]@{
                        PSTypeName       = "FSLogix.Rule"
                        FullName         = $fullnameJoin

                        HidingType       = if ($poshFlags.Hiding -or $poshFlags.HideFont -or $poshFlags.Printer) {
                            switch ( $true ) {
                                $poshFlags.HideFont { 'Font'; break }
                                $poshFlags.Printer { 'Printer'; break }
                                $poshFlags.FolderOrKey { 'FolderOrKey'; break }
                                $poshFlags.FileOrValue { 'FileOrValue'; break }
                            }
                        }
                        else { $null }
                        RedirectDestPath = if ($poshFlags.Redirect) { $destPath } else { $null }
                        RedirectType     = if ($poshFlags.Redirect) {
                            switch ( $true ) {
                                $poshFlags.FolderOrKey { 'FolderOrKey'; break }
                                $poshFlags.FileOrValue { 'FileOrValue'; break }
                            }
                        }
                        else { $null }

                        CopyObject       = if ($poshFlags.CopyObject) { $poshFlags.CopyObject } else { $null }
                        DiskFile         = if ($poshFlags.VolumeAutoMount) { $destPath } else { $null }
                        #Binary = $rulePlusComment.Binary
                        Data             = $SpecificData.Data
                        RegValueType     = $SpecificData.RegValueType
                        Comment          = $rulePlusComment.Comment
                        #Flags = $rulePlusComment.Flags
                    }

                    Write-Output $output
                    break

                }
                Default {
                    Write-Error "Rule file element: $line Does not match a comment or a rule format"
                }
            }
        }
    } #Process
    END { } #End
}  #function Get-FslRule

function Remove-FslAssignment {
    [CmdletBinding(SupportsShouldProcess = $true)]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [alias('AssignmentFilePath')]
        [System.String]$Path,

        [Parameter(
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [alias('FullName')]
        [System.String]$Name,


        [Parameter(
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Force
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {

        If (-not (Test-Path -Path $Path)) {
            Write-Error "$Path Not found"
            break
        }

        if ($Path -notlike "*.fxa") {
            Write-Warning 'Assignment files should have an fxa filename extension'
        }

        $licenceDay = (Get-FslLicenseDay -Path $Path).LicenseDay

        $assignments = Get-FslAssignment -Path $Path

        switch ($true) {
            {$assignments.UserName -contains $Name} {
                $lines = $assignments | Where-Object {$_.Username -eq $Name}
                foreach ($line in $lines) {
                    If ($PSCmdlet.ShouldProcess("UserName Assignment $Name")) {
                        Remove-FslLine -Path $Path -Category Username -Name $Name -Type Assignment
                    }
                }
            }
            {$assignments.GroupName -contains $Name} {
                $lines = $assignments | Where-Object {$_.GroupName -eq $Name}
                foreach ($line in $lines) {
                    If ($PSCmdlet.ShouldProcess("GroupName Assignment $Name")) {
                        Remove-FslLine -Path $Path -Category GroupName -Name $Name -Type Assignment
                    }
                }
            }
            {$assignments.ProcessName -contains $Name} {
                $lines = $assignments | Where-Object {$_.ProcessName -eq $Name}
                foreach ($line in $lines) {
                    If ($PSCmdlet.ShouldProcess("ProcessName Assignment $Name")) {
                        Remove-FslLine -Path $Path -Category ProcessName -Name $Name -Type Assignment
                    }
                }
            }
            {$assignments.IPAddress -contains $Name} {
                $lines = $assignments | Where-Object {$_.IPAddress -eq $Name}
                foreach ($line in $lines) {
                    If ($PSCmdlet.ShouldProcess("IPAddress Assignment $Name")) {
                        Remove-FslLine -Path $Path -Category IPAddress -Name $Name -Type Assignment
                    }
                }
            }
            {$assignments.ComputerName -contains $Name} {
                $lines = $assignments | Where-Object {$_.ComputerName -eq $Name}
                foreach ($line in $lines) {
                    If ($PSCmdlet.ShouldProcess("ComputerName Assignment $Name")) {
                        Remove-FslLine -Path $Path -Category ComputerName -Name $Name -Type Assignment
                    }
                }
            }
            {$assignments.OU -contains $Name} {
                $lines = $assignments | Where-Object {$_.OU -eq $Name}
                foreach ($line in $lines) {
                    If ($PSCmdlet.ShouldProcess("OU Assignment $Name")) {
                        Remove-FslLine -Path $Path -Category OU -Name $Name -Type Assignment
                    }
                }
            }
            {$assignments.EnvironmentVariable -contains $Name} {
                $lines = $assignments | Where-Object {$_.EnvironmentVariable -eq $Name}

                foreach ($line in $lines) {

                    if (-not $line.AssignedTime -eq 0) {
                        $unassignMinimum = $line.AssignedTime.AddDays($licenceDay)
                    }
                    $now = Get-Date

                    switch ($true) {

                        {$line.AssignedTime -eq 0} {
                            If ($PSCmdlet.ShouldProcess("Environment Variable Assignment $Name")) {
                                Remove-FslLine -Path $Path -Category EnvironmentVariable -Name $Name -Type Assignment
                            }
                            break
                        }
                        {$licenceDay -ne 0 -and
                            $line.AssignedTime -ne 0 -and
                            $unassignMinimum -gt $now -and
                            $Force -eq $false
                        } {
                            #If check for license time has failed and force isn't present, throw an error.
                            $daysLeft = ($unassignMinimum - $line.AssignedTime).Days
                            Write-Error "License agreement violation detected $daysLeft days left out of $licenceDay days before license can be reassigned."
                            break
                        }

                        Default {
                            If ($PSCmdlet.ShouldProcess("Environment Variable Assignment $Name")) {
                                Remove-FslLine -Path $Path -Category EnvironmentVariable -Name $Name -Type Assignment
                                $line.UnAssignedTime = Get-Date
                                $line | Add-FslAssignment -Path $Path
                            }
                        }
                    }
                }
            }

            Default {}
        }

        $licenceDay | Set-FslLicenseDay -Path $Path

    } #Process
    END {} #End
}  #function Remove-FslAssignment

function Remove-FslRule {
    [CmdletBinding(SupportsShouldProcess = $true)]

    Param (
        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [alias('RuleFilePath')]
        [System.String]$Path,

        [Parameter(
            Position = 2,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [alias('FullName')]
        [System.String]$Name
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {

        If (-not (Test-Path -Path $Path)) {
            Write-Error "$Path Not found"
            break
        }

        if ($Path -notlike "*.fxr") {
            Write-Warning 'Rule files should have an fxr filename extension'
        }

        $rules = Get-FslRule -Path $Path

        if ( $rules.FullName -notcontains $Name ) {
            Write-Error "Could not find rule with name $Name in file $Path"
            break
        }
        else {
            $lines = $rules | Where-Object {$_.FullName -eq $Name}
            foreach ($line in $lines) {
                If ($PSCmdlet.ShouldProcess("Rule $Name")) {
                    Remove-FslLine -Path $Path -Category FullName -Name $Name -Type Rule
                }
            }
        }
    } #Process
    END {} #End
}  #function Remove-FslRule

function Set-FslAssignment {

    <#
        .SYNOPSIS
            Sets the content of a FSLogix Rule assignment file.
 
        .DESCRIPTION
            This function can set an FSLogix assignment file contents, the assignment file should have the same basename as the matching rule file.
            This will overwrite the contents of an existing file.
 
        .PARAMETER AssignmentFilePath
            The Target file path to set the assignment within
        .PARAMETER RuleSetApplies
            This determines whether a ruleset does or does not apply to users/groups/processes etc. For instance when using a Hiding rule, applying that hiding rule to users will hide the file from the users assigned to it when applied.
        .PARAMETER UserName
            If you wish to tie down the rule to an individual user use theier unsername in this parameter. Groupname is more usual for assignments however
        .PARAMETER GroupName
            Use this to tie the assignment of the rule to a specific group
        .PARAMETER WellKnownSID
            The Well Known SID for groups such as Domain Admins are useful for cross-language assignments, if you use a group with a well known SID in the group name parameter this will be automatically filled out, so mostly useful for pipeline input.
        .PARAMETER ADDistinguisedName
            Full Distinguished name of AD component
        .PARAMETER ProcessName
            Process name for the rule assignment, mostly used for redirect rules
        .PARAMETER IncludeChildProcess
            If Process name is stated you can optionally include chile prcesses (recommended)
        .PARAMETER ProcessId
            If you know process ID, but not name, used for troubleshooting mainly
        .PARAMETER IPAddress
            Enter the IPv4 or IPv6 address. Partial strings are allowed. For example, if you enter 192.168, an address of 192.168.0.1 will be considered to match.
        .PARAMETER ComputerName
            Enter the Full Distinguished Name of the computer object, or the computer name (wildcards accepted). Must be in the format ComputerName@Domain
        .PARAMETER OU
            You can specify an Active Directory Container and the assignment will be effective for all of the objects in that container. Enter the Full Distinguished Name of the container.
        .PARAMETER EnvironmentVariable
            By Specifying an environment variable, you can customize rules in various other ways. A very useful example for this option is when using it with RDSH, XenApp, or other remote sessions. You can use the Environment Variable CLIENTNAME to limit visibility to the device being used to access the RDSH or XenApp system.
            The environment variables that are supported are the ones that are present when the user's session is created. Environment variables set during logon are not supported.
        .EXAMPLE
            A sample command that uses the function or script, optionaly followed
            by sample output and a description. Repeat this keyword for each example.
    #>


    [CmdletBinding()]
    Param (

        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [Alias('AssignmentFilePath')]
        [System.String]$Path,

        [Parameter(
            ParameterSetName = 'User',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Executable',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Network',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Computer',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'OU',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'EnvironmentVariable',
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$RuleSetApplies,

        [Parameter(
            ParameterSetName = 'User',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$UserName,

        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$GroupName,

        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$WellKnownSID,

        [Parameter(
            ParameterSetName = 'User',
            ValuefromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = 'Group',
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$ADDistinguisedName,

        [Parameter(
            ParameterSetName = 'Executable',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$ProcessName,

        [Parameter(
            ParameterSetName = 'Executable',
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$IncludeChildProcess,

        [Parameter(
            ParameterSetName = 'Network',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$IPAddress,

        [Parameter(
            ParameterSetName = 'Computer',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [ValidatePattern(".*@.*")]
        [System.String]$ComputerName,

        [Parameter(
            ParameterSetName = 'OU',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$OU,

        [Parameter(
            ParameterSetName = 'EnvironmentVariable',
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [ValidatePattern(".*=.*")]
        [System.String]$EnvironmentVariable,

        [Parameter(
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$PassThru,

        [Parameter(
            ParameterSetName = 'AssignmentObjectPipeline',
            ValuefromPipeline = $true,
            ValuefromPipelineByPropertyName = $true
        )]
        [PSTypeName('FSLogix.Assignment')]$InputObject
    )
    BEGIN {
        Set-StrictMode -Version Latest
        $version = 1
        $minimumLicenseAssignedTime = 0
        $setContent = $true

    } # Begin
    PROCESS {

        #Add first line if pipeline input
        If ($setContent) {
            Set-Content -Path $Path -Value "$version`t$minimumLicenseAssignedTime" -Encoding Unicode -ErrorAction Stop -WhatIf:$false
            Write-Verbose "Setting assignment file $Path contents"
            Add-FslAssignment @PSBoundParameters
            $setContent = $false
        }
        else {
            Add-FslAssignment @PSBoundParameters
        }

    } #Process
    END {
    } #End
}  #function Set-FslAssignment

function Set-FslLicenseDay {

    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [Alias('AssignmentFilePath')]
        [System.String]$Path,

        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [int]$LicenseDay

    )

    BEGIN {
        Set-StrictMode -Version Latest
        $version = 1
    } # Begin
    PROCESS {

        if (-not (Test-Path $Path)) {
            Write-Error "$Path not found."
            break
        }

        If ((Get-ChildItem -Path $Path).Extension -ne '.fxa') {
            Write-Warning 'Assignment file extension should be .fxa'
        }

        $content = Get-Content -Path $Path | Select-Object -Skip 1

        Set-Content -Path $Path -Value "$version`t$LicenseDay" -Encoding Unicode -WhatIf:$false

        Add-Content -Path $Path -Value $content -Encoding Unicode -WhatIf:$false

    } #Process
    END {} #End
}  #function Set-FslLicenseDay

function Set-FslRule {
    [CmdletBinding()]

    Param (

        [Parameter(
            Position = 1,
            Mandatory = $true,
            ValuefromPipelineByPropertyName = $true
        )]
        [Alias('RuleFilePath')]
        [System.String]$Path,

        [Parameter(
            ParameterSetName = 'Hiding',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = 'Redirect',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = 'AppContainer',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = 'SpecifyValue',
            Position = 2,
            ValueFromPipeline = $true,
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [Alias('Name')]
        [System.String]$FullName,

        [Parameter(
            ParameterSetName = 'Hiding',
            Mandatory = $true,
            Position = 3,
            ValuefromPipelineByPropertyName = $true
        )]
        [ValidateSet('FolderOrKey', 'FileOrValue', 'Font', 'Printer')]
        [System.String]$HidingType,

        [Parameter(
            ParameterSetName = 'Redirect',
            Mandatory = $true,
            Position = 6,
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$RedirectDestPath,

        [Parameter(
            ParameterSetName = 'Redirect',
            Mandatory = $true,
            Position = 7,
            ValuefromPipelineByPropertyName = $true
        )]
        [ValidateSet('FolderOrKey', 'FileOrValue')]
        [string]$RedirectType,

        [Parameter(
            ParameterSetName = 'Redirect',
            Position = 8,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$CopyObject,

        [Parameter(
            ParameterSetName = 'AppContainer',
            Mandatory = $true,
            Position = 9,
            ValuefromPipelineByPropertyName = $true
        )]
        [string]$DiskFile,

        [Parameter(
            ParameterSetName = 'SpecifyValue',
            Mandatory = $true,
            Position = 10,
            ValuefromPipelineByPropertyName = $true
        )]
        [Alias('Binary')]
        [string]$Data,

        [Parameter(
            Position = 11,
            ValuefromPipelineByPropertyName = $true
        )]
        [System.String]$Comment = 'Created By PowerShell Script',

        [Parameter(
            Position = 13,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Passthru,

        [Parameter(
            ParameterSetName = 'RuleObjectPipeline',
            Position = 14,
            ValuefromPipeline = $true,
            ValuefromPipelineByPropertyName = $true
        )]
        [PSTypeName('FSLogix.Rule')]$RuleObject
    )


    BEGIN {
        Set-StrictMode -Version Latest
        $version = 1
        $setContent = $true
    } # Begin
    PROCESS {

        #check file has correct filename extension
        if ($Path -notlike "*.fxr") {
            Write-Warning 'The Rule file should have an fxr extension'
        }

        #Add first line if pipeline input
        If ($setContent) {
            Set-Content -Path $Path -Value $version -Encoding Unicode -ErrorAction Stop
            Add-FslRule @PSBoundParameters
            $setContent = $false
        }
        else {
            Add-FslRule @PSBoundParameters
        }
    } #Process
    END {} #End
}  #function Set-FslRule

function ConvertFrom-FslAssignmentCode {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [Int]$AssignmentCode
    )

    BEGIN {
        Set-StrictMode -Version Latest
        $Apply = 0x0001
        $Remove = 0x0002
        $User = 0x0004
        $Process = 0x0008
        $Group = 0x0010
        $Network = 0x0020
        $Computer = 0x0040
        $ADDistinguishedName = 0x0080
        $ApplyToProcessChildren = 0x0100
        #$ProcessID = 0x0200
        $EnvironmentVariable = 0x2000
        #$MandatoryLevelShift = 10
        #$MandatoryLevelMask = 0x1C00

    } # Begin
    PROCESS {
        $output = [PSCustomObject]@{
            'Apply'                  = if ( $AssignmentCode -band $Apply ) { $true } else { $false }
            'Remove'                 = if ( $AssignmentCode -band $Remove ) { $true } else { $false }
            'User'                   = if ( $AssignmentCode -band $User ) { $true } else { $false }
            'Process'                = if ( $AssignmentCode -band $Process ) { $true } else { $false }
            'Group'                  = if ( $AssignmentCode -band $Group ) { $true } else { $false }
            'Network'                = if ( $AssignmentCode -band $Network ) { $true } else { $false }
            'Computer'               = if ( $AssignmentCode -band $Computer ) { $true } else { $false }
            'ADDistinguishedName'    = if ( $AssignmentCode -band $ADDistinguishedName ) { $true } else { $false }
            'ApplyToProcessChildren' = if ( $AssignmentCode -band $ApplyToProcessChildren ) { $true } else { $false }
            #'ProcessId' = if ( $AssignmentCode -band $ProcessID ) { $true } else { $false } #Can't get the GUI to produce a pid code
            'EnvironmentVariable'    = if ( $AssignmentCode -band $EnvironmentVariable ) { $true } else { $false }

            #The Mandatory bits are in the original code, but not used
            #'MandatoryLevelShift' = if ( $AssignmentCode -band $MandatoryLevelShift ) { $true } else { $false }
            #'MandatoryLevelMask' = if ( $AssignmentCode -band $MandatoryLevelMask ) { $true } else { $false }
        }

        Write-Output $output

    } #Process
    END { } #End
}  #function ConvertFrom-FslAssignmentCode

function ConvertFrom-FslRegHex {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [System.String]$HexString
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {
        $outputData = $null

        switch ($HexString.Substring(0, 2)) {
            '01' {
                $regValueType = 'String'
                $hexLong = $HexString.substring(2, $HexString.length - 6)
                $hex = $hexLong -Split '(.{4})'
                $hex | ForEach-Object {
                    if ($_ -ne '') {
                        $byte = $_.substring(0, 2)
                        $outputData += [char]([convert]::toint16($byte, 16))
                    }
                }
                break
            }
            '04' {
                $regValueType = 'DWORD'
                #Grab relevant characters
                $hexLong = $HexString.substring(2, 8)
                #Split into bytes
                $hex = $hexLong -Split '(..)'
                #Need to make current little endian into big endian in order for [convert] to work
                [System.Array]::Reverse($hex)
                $bEndian = $hex -join ''
                $int32 = [convert]::ToUInt32($bEndian, 16)
                #everything is a string in output - maybe change
                $outputData = $int32.ToString()
                break
            }
            '0B' {
                $regValueType = 'QWORD'
                #Grab relevant characters
                $hexLong = $HexString.substring(2, 16)
                #Split into bytes
                $hex = $hexLong -Split '(..)'
                #Need to make current little endian into big endian in order for [convert] to work
                [System.Array]::Reverse($hex)
                $bEndian = $hex -join ''
                $int64 = [convert]::ToUInt64($bEndian, 16)
                #everything is a string in output - maybe change
                $outputData = $int64.ToString()
                break
            }
            '07' {
                $regValueType = 'Multi-String'
                $outputData = @()
                
                $splitStrings = $HexString.substring(2, $HexString.length - 10) -split '000000'

                foreach ($string in $splitStrings) {
                    $outputLine = @()
                    $string = $string + '00'
                    $hex = $string -Split '(.{4})'
                    $hex | ForEach-Object {
                        if ($_ -ne '') {
                            $byte = $_.substring(0, 2)
                            $outputLine += [char]([convert]::toint16($byte, 16))
                        }
                        
                    }
                    $outputData += $outputLine -Join ''
                }
                break
            }
            Default {
                Write-Error "Could not determine the type of registry value form the Hex Code $($HexString.Substring(0,2))"
                exit
            }
        }

        $output = [PSCustomObject]@{
            Data         = $outputData
            RegValueType = $regValueType
        }
        Write-Output $output
    } #Process
    END { } #End
}  #function ConvertFrom-FslRegHex

function ConvertFrom-FslRuleCode {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [Int]$RuleCode
    )

    BEGIN {
        Set-StrictMode -Version Latest
        $FRX_RULE_SRC_IS_A_DIR_OR_KEY = 0x00000001
        $FRX_RULE_SRC_IS_A_FILE_OR_VALUE = 0x00000002
        $FRX_RULE_SHOULD_COPY_FILE = 0x00000010
        $FRX_RULE_TYPE_REDIRECT = 0x00000100
        $FRX_RULE_TYPE_HIDING = 0x00000200
        $FRX_RULE_TYPE_HIDE_PRINTER = 0x00000400
        $FRX_RULE_TYPE_SPECIFIC_DATA = 0x00000800 #Specific Value Rule
        $FRX_RULE_TYPE_JAVA = 0x00001000
        $FRX_RULE_TYPE_VOLUME_AUTOMOUNT = 0x00002000
        $FRX_RULE_TYPE_HIDE_FONT = 0x00004000
    } # Begin

    PROCESS {

        switch ($true) {
            { $RuleCode -band $FRX_RULE_SRC_IS_A_DIR_OR_KEY } { $folderOrKey = $true }
            { -not ( $RuleCode -band $FRX_RULE_SRC_IS_A_DIR_OR_KEY ) } { $folderOrKey = $false }
            { $RuleCode -band $FRX_RULE_SRC_IS_A_FILE_OR_VALUE } { $fileOrValue = $true }
            { -not ( $RuleCode -band $FRX_RULE_SRC_IS_A_FILE_OR_VALUE ) } { $fileOrValue = $false }
            { $RuleCode -band $FRX_RULE_SHOULD_COPY_FILE } { $copyObject = $true }
            { -not ( $RuleCode -band $FRX_RULE_SHOULD_COPY_FILE ) } { $copyObject = $false }
            { $RuleCode -band $FRX_RULE_TYPE_REDIRECT } { $redirect = $true }
            { -not ( $RuleCode -band $FRX_RULE_TYPE_REDIRECT ) } { $redirect = $false }
            { $RuleCode -band $FRX_RULE_TYPE_HIDING } { $hiding = $true }
            { -not ( $RuleCode -band $FRX_RULE_TYPE_HIDING ) } { $hiding = $false }
            { $RuleCode -band $FRX_RULE_TYPE_HIDE_PRINTER } { $hidePrinter = $true }
            { -not ( $RuleCode -band $FRX_RULE_TYPE_HIDE_PRINTER ) } { $hidePrinter = $false }
            { $RuleCode -band $FRX_RULE_TYPE_SPECIFIC_DATA } { $specificData = $true }
            { -not ( $RuleCode -band $FRX_RULE_TYPE_SPECIFIC_DATA ) } { $specificData = $false }
            { $RuleCode -band $FRX_RULE_TYPE_JAVA } { $java = $true }
            { -not ( $RuleCode -band $FRX_RULE_TYPE_JAVA ) } { $java = $false }
            { $RuleCode -band $FRX_RULE_TYPE_VOLUME_AUTOMOUNT } { $volumeAutoMount = $true }
            { -not ( $RuleCode -band $FRX_RULE_TYPE_VOLUME_AUTOMOUNT ) } { $volumeAutoMount = $false }
            { $RuleCode -band $FRX_RULE_TYPE_HIDE_FONT } { $font = $true }
            { -not ( $RuleCode -band $FRX_RULE_TYPE_HIDE_FONT ) } { $font = $false }
            default { }
        } #Switch

        $outObject = [PSCustomObject]@{
            'FolderOrKey'     = $folderOrKey
            'FileOrValue'     = $fileOrValue
            'CopyObject'      = $copyObject
            'Redirect'        = $redirect
            'Hiding'          = $hiding
            'Printer'         = $hidePrinter
            'SpecificData'    = $specificData
            'Java'            = $java
            'VolumeAutoMount' = $volumeAutoMount
            'HideFont'        = $font
        }
        Write-Output $outObject
    } #Process
    END { } #End
}  #function ConvertFrom-FslRuleCode

function ConvertTo-FslAssignmentCode {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Apply,

        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Remove,

        [Parameter(
            Position = 2,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$User,

        [Parameter(
            Position = 3,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Process,

        [Parameter(
            Position = 4,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Group,

        [Parameter(
            Position = 5,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Network,

        [Parameter(
            Position = 6,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Computer,

        [Parameter(
            Position = 7,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$ADDistinguishedName,

        [Parameter(
            Position = 8,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$ApplyToProcessChildren,

        [Parameter(
            Position = 9,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$ProcessId,

        [Parameter(
            Position = 10,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$EnvironmentVariable
    )

    BEGIN {
        Set-StrictMode -Version Latest
        $ApplyBit = 0x0001
        $RemoveBit = 0x0002
        $UserBit = 0x0004
        $ProcessBit = 0x0008
        $GroupBit = 0x0010
        $NetworkBit = 0x0020
        $ComputerBit = 0x0040
        $ADDistinguishedNameBit = 0x0080
        $ApplyToProcessChildrenBit = 0x0100
        #$PidBit = 0x0200
        $EnvironmentVariableBit = 0x2000

        #$MandatoryLevelMaskBit = 0x1C00
        #$MandatoryLevelShiftBit = 10
    } # Begin
    PROCESS {
        $codeToOutput = 0
        switch ($true) {
            $Apply { $codeToOutput = $codeToOutput -bor $ApplyBit }
            $Remove { $codeToOutput = $codeToOutput -bor $RemoveBit }
            $User { $codeToOutput = $codeToOutput -bor $UserBit }
            $Process { $codeToOutput = $codeToOutput -bor $ProcessBit }
            $Group { $codeToOutput = $codeToOutput -bor $GroupBit }
            $Network { $codeToOutput = $codeToOutput -bor $NetworkBit }
            $Computer { $codeToOutput = $codeToOutput -bor $ComputerBit }
            $ADDistinguishedName { $codeToOutput = $codeToOutput -bor $ADDistinguishedNameBit }
            $ApplyToProcessChildren { $codeToOutput = $codeToOutput -bor $ApplyToProcessChildrenBit }
            #$ProcessId { $codeToOutput = $codeToOutput -bor $PidBit } #Can't get the GUI to produce a pid code
            $EnvironmentVariable { $codeToOutput = $codeToOutput -bor $EnvironmentVariableBit }

            #The Mandatory bits are in the original code, but not used
            #$MandatoryLevelMask { $codeToOutput = $codeToOutput -bor $MandatoryLevelMaskBit }
            #$MandatoryLevelShift { $codeToOutput = $codeToOutput -bor $MandatoryLevelShiftBit }
        }

        #convert code to hex string so it doesn't get outputted as an integer
        $formattedCode = "0x{0:X8}" -f $codeToOutput

        Write-Output $formattedCode.ToLower()

    } #Process
    END {} #End
}  #function ConvertTo-FslAssignmentCode

function ConvertTo-FslHexDword {
    [CmdletBinding()]
    param (
        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [uInt32]$RegData
    )
    
    begin {
    }
    
    process {
        $hex = $null
        try {
            $hex = [convert]::ToString($RegData, 16)

            while ($hex.length -lt 8) {
                $hex = '0' + $hex
            }

            $hexArray = $hex -split "(..)"
            [array]::Reverse($hexArray)

            $output = $hexArray -join ''
        }
        catch {
            Write-Error "Unable to convert $Regdata from uInt64 to Hex"
            exit
        }

        Write-Output $output
    }
    
    end {
    }
}

function ConvertTo-FslHexMultiString {
    [CmdletBinding()]
    param (
        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [String[]]$RegData
    )
    
    begin {
    }
    
    process {
        $hex = $null
        $combinedHex = $null
        foreach ($string in $RegData) {
            $regDataChars = $string.ToCharArray()
            foreach ($character in $regDataChars) { 
                $hex = $hex + [System.String]::Format("{0:X4}", [System.Convert]::ToUInt16($character))
            }
            $hexWithZeros = $hex.Substring(2) + '000000'
            $hex = $null
            $combinedHex = $combinedHex + $hexWithZeros
            
        }
        $output = $combinedHex -join ''
        Write-Output $output
    }
    
    end {
    }
}

function ConvertTo-FslHexQword {
    [CmdletBinding()]
    param (
        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [uInt64]$RegData
    )
    
    begin {
    }
    
    process {
        $hex = $null
        try {
            $hex = [String]::Format("{0:x}", $regdata)

            while ($hex.length -lt 16) {
                $hex = '0' + $hex
            }

            $hexArray = $hex -split "(..)"
            [array]::Reverse($hexArray)              

        }
        catch {
            Write-Error "Unable to convert $Regdata to Hex"
            exit
        }
        $output = $hexArray -join ''
        
        Write-Output $output
    }
    
    end {
    }
}

function ConvertTo-FslHexString {
    [CmdletBinding()]
    param (
        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true,
            ValuefromPipeline = $true,
            Mandatory = $true
        )]
        [System.String]$RegData
    )
    
    begin {
    }
    
    process {
        $hex = $null
        $regDataChars = $RegData.ToCharArray()

        foreach ($character in $regDataChars) { 
            $hex = $hex + [System.String]::Format("{0:X4}", [System.Convert]::ToUInt16($character))
        }

        $hexJoined = $hex -join ''
        $output = $hexJoined.SubString(2) + '00'

        Write-Output $output
    }
    
    end {
    }
}

function ConvertTo-FslRuleCode {
    [CmdletBinding()]

    Param (
        [Parameter(
            Position = 0,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$FolderOrKey,

        [Parameter(
            Position = 1,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$FileOrValue,

        <#
        [Parameter(
            Position = 2,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$ContainsUserVar,
        #>


        [Parameter(
            Position = 3,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$CopyObject,

        <#
        [Parameter(
            Position = 4,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Persistent,
        #>


        [Parameter(
            Position = 5,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Redirect,

        [Parameter(
            Position = 6,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Hiding,

        [Parameter(
            Position = 7,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Printer,

        [Parameter(
            Position = 8,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$SpecificData,

        [Parameter(
            Position = 9,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Java,

        [Parameter(
            Position = 10,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$VolumeAutoMount,

        [Parameter(
            Position = 11,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$HideFont

        <#
        [Parameter(
            Position = 12,
            ValuefromPipelineByPropertyName = $true
        )]
        [Switch]$Mask
        #>

    )

    BEGIN {
        Set-StrictMode -Version Latest
        $FRX_RULE_SRC_IS_A_DIR_OR_KEY = 0x00000001
        $FRX_RULE_SRC_IS_A_FILE_OR_VALUE = 0x00000002
        #$FRX_RULE_CONTAINS_USER_VARS = 0x00000008
        $FRX_RULE_SHOULD_COPY_FILE = 0x00000010
        $FRX_RULE_IS_PERSISTANT = 0x00000020
        $FRX_RULE_TYPE_REDIRECT = 0x00000100
        $FRX_RULE_TYPE_HIDING = 0x00000200
        $FRX_RULE_TYPE_HIDE_PRINTER = 0x00000400
        $FRX_RULE_TYPE_SPECIFIC_DATA = 0x00000800
        $FRX_RULE_TYPE_JAVA = 0x00001000
        $FRX_RULE_TYPE_VOLUME_AUTOMOUNT = 0x00002000
        $FRX_RULE_TYPE_HIDE_FONT = 0x00004000
    } # Begin
    PROCESS {
        $codeToOutput = 0
        #Persistent is always true except if Java is present so no need to pass in a parameter
        if ($java) {
            $persistent = $false
        }
        else {
            $persistent = $true
        }

        switch ($true) {
            $FolderOrKey { $codeToOutput = $codeToOutput -bor $FRX_RULE_SRC_IS_A_DIR_OR_KEY }
            $FileOrValue { $codeToOutput = $codeToOutput -bor $FRX_RULE_SRC_IS_A_FILE_OR_VALUE }
            $CopyObject { $codeToOutput = $codeToOutput -bor $FRX_RULE_SHOULD_COPY_FILE }
            $Persistent { $codeToOutput = $codeToOutput -bor $FRX_RULE_IS_PERSISTANT }
            $Redirect { $codeToOutput = $codeToOutput -bor $FRX_RULE_TYPE_REDIRECT }
            $Hiding { $codeToOutput = $codeToOutput -bor $FRX_RULE_TYPE_HIDING }
            $Printer { $codeToOutput = $codeToOutput -bor $FRX_RULE_TYPE_HIDE_PRINTER }
            $SpecificData { $codeToOutput = $codeToOutput -bor $FRX_RULE_TYPE_SPECIFIC_DATA }
            $Java { $codeToOutput = $codeToOutput -bor $FRX_RULE_TYPE_JAVA }
            $VolumeAutomount { $codeToOutput = $codeToOutput -bor $FRX_RULE_TYPE_VOLUME_AUTOMOUNT }
            $HideFont { $codeToOutput = $codeToOutput -bor $FRX_RULE_TYPE_HIDE_FONT }
        }

        #convert code to hex string so it doesn't get outputted as an integer
        $formattedCode = "0x{0:X8}" -f $codeToOutput

        Write-Output $formattedCode
    } #Process
    END {} #End
}  #function ConvertTo-FslRuleCode

function Remove-FslLine {
    [CmdletBinding()]

    Param (
        [Parameter(
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$Path,
        [Parameter(
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$Category,

        [Parameter(
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [System.String]$Name,

        [Parameter(
            ValuefromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [ValidateSet('Assignment', 'Rule')]
        [System.String]$Type
    )

    BEGIN {
        Set-StrictMode -Version Latest
    } # Begin
    PROCESS {

        switch ($Type) {
            Assignment {
                Get-FslAssignment $Path | Where-Object {$_.$Category -ne $Name} | Set-FslAssignment $Path
            }
            Rule {
                Get-FslRule $Path | Where-Object {$_.$Category -ne $Name} | Set-FslRule $Path
            }
            Default {}
        }

    } #Process
    END {} #End
}  #function Remove-FslLine