PSSymantecCloud.psm1

#Region '.\Private\ConvertTo-FlatObject.ps1' 0
# From https://github.com/EvotecIT/PSSharedGoods/tree/master/Public/Converts
Function ConvertTo-FlatObject {
    <#
    .SYNOPSIS
    Flattends a nested object into a single level object.
 
    .DESCRIPTION
    Flattends a nested object into a single level object.
 
    .PARAMETER Objects
    The object (or objects) to be flatten.
 
    .PARAMETER Separator
    The separator used between the recursive property names
 
    .PARAMETER Base
    The first index name of an embedded array:
    - 1, arrays will be 1 based: <Parent>.1, <Parent>.2, <Parent>.3, …
    - 0, arrays will be 0 based: <Parent>.0, <Parent>.1, <Parent>.2, …
    - "", the first item in an array will be unnamed and than followed with 1: <Parent>, <Parent>.1, <Parent>.2, …
 
    .PARAMETER Depth
    The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop.
 
    .PARAMETER Uncut
    The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop.
 
    .PARAMETER ExcludeProperty
    The propertys to be excluded from the output.
 
    .EXAMPLE
    $Object3 = [PSCustomObject] @{
        "Name" = "Przemyslaw Klys"
        "Age" = "30"
        "Address" = @{
            "Street" = "Kwiatowa"
            "City" = "Warszawa"
 
            "Country" = [ordered] @{
                "Name" = "Poland"
            }
            List = @(
                [PSCustomObject] @{
                    "Name" = "Adam Klys"
                    "Age" = "32"
                }
                [PSCustomObject] @{
                    "Name" = "Justyna Klys"
                    "Age" = "33"
                }
                [PSCustomObject] @{
                    "Name" = "Justyna Klys"
                    "Age" = 30
                }
                [PSCustomObject] @{
                    "Name" = "Justyna Klys"
                    "Age" = $null
                }
            )
        }
        ListTest = @(
            [PSCustomObject] @{
                "Name" = "Sława Klys"
                "Age" = "33"
            }
        )
    }
 
    $Object3 | ConvertTo-FlatObject
 
    .NOTES
    Based on https://powersnippets.com/convertto-flatobject/
    #>

    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeLine)][Object[]]$Objects,
        [String]$Separator = ".",
        [ValidateSet("", 0, 1)]$Base = 1,
        [int]$Depth = 5,
        [string[]] $ExcludeProperty,
        [Parameter(DontShow)][String[]]$Path,
        [Parameter(DontShow)][System.Collections.IDictionary] $OutputObject
    )
    Begin {
        $InputObjects = [System.Collections.Generic.List[Object]]::new()
    }
    Process {
        foreach ($O in $Objects) {
            if ($null -ne $O) {
                $InputObjects.Add($O)
            }
        }
    }
    End {
        If ($PSBoundParameters.ContainsKey("OutputObject")) {
            $Object = $InputObjects[0]
            $Iterate = [ordered] @{}
            if ($null -eq $Object) {
                #Write-Verbose -Message "ConvertTo-FlatObject - Object is null"
            } elseif ($Object.GetType().Name -in 'String', 'DateTime', 'TimeSpan', 'Version', 'Enum') {
                $Object = $Object.ToString()
            } elseif ($Depth) {
                $Depth--
                If ($Object -is [System.Collections.IDictionary]) {
                    $Iterate = $Object
                } elseif ($Object -is [Array] -or $Object -is [System.Collections.IEnumerable]) {
                    $i = $Base
                    foreach ($Item in $Object.GetEnumerator()) {
                        $NewObject = [ordered] @{}
                        If ($Item -is [System.Collections.IDictionary]) {
                            foreach ($Key in $Item.Keys) {
                                if ($Key -notin $ExcludeProperty) {
                                    $NewObject[$Key] = $Item[$Key]
                                }
                            }
                        } elseif ($Item -isnot [Array] -and $Item -isnot [System.Collections.IEnumerable]) {
                            foreach ($Prop in $Item.PSObject.Properties) {
                                if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) {
                                    $NewObject["$($Prop.Name)"] = $Item.$($Prop.Name)
                                }
                            }
                        } else {
                            $NewObject = $Item
                        }
                        $Iterate["$i"] = $NewObject
                        $i += 1
                    }
                } else {
                    foreach ($Prop in $Object.PSObject.Properties) {
                        if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) {
                            $Iterate["$($Prop.Name)"] = $Object.$($Prop.Name)
                        }
                    }
                }
            }
            If ($Iterate.Keys.Count) {
                foreach ($Key in $Iterate.Keys) {
                    if ($Key -notin $ExcludeProperty) {
                        ConvertTo-FlatObject -Objects @(, $Iterate["$Key"]) -Separator $Separator -Base $Base -Depth $Depth -Path ($Path + $Key) -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty
                    }
                }
            } else {
                $Property = $Path -Join $Separator
                if ($Property) {
                    # We only care if property is not empty
                    if ($Object -is [System.Collections.IDictionary] -and $Object.Keys.Count -eq 0) {
                        $OutputObject[$Property] = $null
                    } else {
                        $OutputObject[$Property] = $Object
                    }
                }
            }
        } elseif ($InputObjects.Count -gt 0) {
            foreach ($ItemObject in $InputObjects) {
                $OutputObject = [ordered]@{}
                ConvertTo-FlatObject -Objects @(, $ItemObject) -Separator $Separator -Base $Base -Depth $Depth -Path $Path -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty
                [PSCustomObject] $OutputObject
            }
        }
    }
}
#EndRegion '.\Private\ConvertTo-FlatObject.ps1' 162
#Region '.\Private\Get-ConfigurationPath.ps1' 0
function Get-ConfigurationPath {
    <#
    .SYNOPSIS
        returns hashtable object with the BaseURL, SepCloudCreds, SepCloudToken full path
    .DESCRIPTION
    .INPUTS
        None
    .OUTPUTS
        Hashtable
    #>


    @{
        BaseUrl       = "api.sep.securitycloud.symantec.com"
        SepCloudCreds = "$env:TEMP\SepCloudOAuthCredentials.xml"
        SepCloudToken = "$env:TEMP\SepCloudToken.xml"
    }

}
#EndRegion '.\Private\Get-ConfigurationPath.ps1' 19
#Region '.\Private\Get-ExcelAllowListObject.ps1' 0
function Get-ExcelAllowListObject {
    <# TODO fill description
    .SYNOPSIS
        Imports excel allow list report from its file path as a PSObject
    .DESCRIPTION
        Imports excel allow list report as a PSObject.
        Same structure that Get-SepCloudPolicyDetails uses to compare Excel allow list and SEP Cloud allow list policy
    .EXAMPLE
        Get-ExcelAllowListObject -Path "WorkstationsAllowListPolicy.xlsx"
        Imports the excel file and returns a structured PSObject
    .INPUTS
        Excel path of allow list policy previously generated from Export-SepCloudAllowListPolicyToExcel CmdLet
    .OUTPUTS
        Custom PSObject
    #>


    param (
        # excel path
        [Parameter(
            ValueFromPipeline
        )]
        [string[]]
        [Alias("Excel")]
        [Alias("Path")]
        $excel_path
    )

    process {

        # List all excel tabs
        $AllSheets = Get-ExcelSheetInfo $excel_path
        $SheetsInfo = @{}
        # Import all Excel info in $SheetsInfo hashtable
        $AllSheets | ForEach-Object { $SheetsInfo[$_.Name] = Import-Excel $_.Path -WorksheetName $_.Name }

        # Get Object from ExceptionStructure Class
        $obj_policy_excel = [ExceptionStructure]::new()

        # Populates $obj_policy_excel

        # Add Applications
        foreach ($line in $SheetsInfo['Applications']) {
            $obj_policy_excel.AddProcessFile(
                $line.sha2,
                $line.Name
            )
        }

        # Add Files
        foreach ($line in $SheetsInfo['Files']) {
            # Parse "features.X" properties to gather the feature_names in an array
            [array]$feature_names = @()
            [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature
            $i = 0
            foreach ($feat in $nb_features) {
                if ($null -ne $line.($nb_features[$i])) {
                    $feature_names += $line.($nb_features[$i])
                }
                $i++
            }
            # Use AddWindowsFiles
            $obj_policy_excel.AddWindowsFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $feature_names
            )
        }

        # Add Directories
        foreach ($line in $SheetsInfo['Directories']) {
            # Parse "features.X" properties to gather the feature names in an array
            [array]$feature_names = @()
            [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature
            $i = 0
            foreach ($feat in $nb_features) {
                if ($null -ne $line.($nb_features[$i])) {
                    $feature_names += $line.($nb_features[$i])
                }
                $i++
            }
            # Use AddWindowsDirectories
            $obj_policy_excel.AddWindowsDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $feature_names
            )
        }

        # Add Extensions
        # no loop required, whole array needed
        $obj_policy_excel.AddExtensions(@{
                names     = $sheetsInfo['Extensions'].extensions
                scheduled = $true
                features  = 'AUTO_PROTECT'
            }
        )

        # Add WebDomains
        foreach ($line in $SheetsInfo['Webdomains']) {
            $obj_policy_excel.AddWebDomains(
                $line.domain
            )
        }

        # Add IPS Hosts
        foreach ($line in $SheetsInfo['Ips_Hosts']) {
            $obj_policy_excel.AddIpsHostsIpv4Address(
                $line.ip
            )
        }

        # Add IPS Subnet v4
        foreach ($line in $SheetsInfo['Ips_Hosts_subnet_v4']) {
            $obj_policy_excel.AddIpsHostsIpv4Subnet(
                $line.ip,
                $line.mask
            )
        }

        # Add IPS Subnet v6
        foreach ($line in $SheetsInfo['Ips_Hosts_subnet_v6']) {
            $obj_policy_excel.AddIpsHostsIpv6Subnet(
                $line.ipv6_subnet
            )
        }

        # Add IPs ranges (includes both IPv4 & v6)
        foreach ($line in $SheetsInfo['Ips_range']) {
            $obj_policy_excel.AddIpsRange(
                $line.ip_start,
                $line.ip_end
            )
        }

        # Add Certificates
        foreach ($line in $SheetsInfo['Certificates']) {
            $obj_policy_excel.AddCertificates(
                $line.signature_issuer,
                $line.signature_company_name,
                $line."signature_fingerprint.algorithm",
                $line."signature_fingerprint.value"
            )
        }

        # Add Linux Files
        foreach ($line in $SheetsInfo['Linux Files']) {
            # Parse "features.X" properties to gather the feature_names in an array
            [array]$feature_names = @()
            [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature
            $i = 0
            foreach ($feat in $nb_features) {
                if ($null -ne $line.($nb_features[$i])) {
                    $feature_names += $line.($nb_features[$i])
                }
                $i++
            }
            # Use AddLinuxFiles
            $obj_policy_excel.AddLinuxFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $feature_names
            )
        }

        # Add Mac Files
        foreach ($line in $SheetsInfo['Mac Files']) {
            # Parse "features.X" properties to gather the feature_names in an array
            [array]$feature_names = @()
            [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature
            $i = 0
            foreach ($feat in $nb_features) {
                if ($null -ne $line.($nb_features[$i])) {
                    $feature_names += $line.($nb_features[$i])
                }
                $i++
            }
            # Use AddMacFiles
            $obj_policy_excel.AddMacFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $feature_names
            )
        }



        # Add Linux Directories
        foreach ($line in $SheetsInfo['Linux Directories']) {
            # Parse "features.X" properties to gather the feature names in an array
            [array]$feature_names = @()
            [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature
            $i = 0
            foreach ($feat in $nb_features) {
                if ($null -ne $line.($nb_features[$i])) {
                    $feature_names += $line.($nb_features[$i])
                }
                $i++
            }
            # Use AddLinuxDirectories
            $obj_policy_excel.AddLinuxDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $feature_names
            )
        }

        # Add Mac Directories
        foreach ($line in $SheetsInfo['Mac Directories']) {
            # Parse "features.X" properties to gather the feature names in an array
            [array]$feature_names = @()
            [array]$nb_features = $line.PSObject.properties.name | Select-String -Pattern feature
            $i = 0
            foreach ($feat in $nb_features) {
                if ($null -ne $line.($nb_features[$i])) {
                    $feature_names += $line.($nb_features[$i])
                }
                $i++
            }
            # Use AddMacDirectories
            $obj_policy_excel.AddMacDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $feature_names
            )
        }

        return $obj_policy_excel
    }
}
#EndRegion '.\Private\Get-ExcelAllowListObject.ps1' 239
#Region '.\Private\Get-SEPCloudToken.ps1' 0
function Get-SEPCloudToken {
    <#
    .SYNOPSIS
    Generates an authenticated Token from the SEP Cloud API
 
    .DESCRIPTION
    Gathers Bearer Token from the SEP Cloud console to interact with the authenticated API
    Securely stores credentials or valid token locally (By default on TEMP location)
    Connection information available here : https://sep.securitycloud.symantec.com/v2/integration/client-applications
 
    .PARAMETER ClientID
    ClientID parameter required to generate a token
 
    .PARAMETER Secret
    Secret parameter required in combinaison to ClientID to generate a token
 
    .EXAMPLE
    Get-SEPCloudToken
 
    .EXAMPLE
    Get-SEPCloudToken(ClientID,Secret)
 
    .NOTES
    Function logic
    1. Test locally stored encrypted token
    2. Test locally stored encrypted Client/Secret to generate a token
    3. Requests Client/Secret to generate token
    #>


    [CmdletBinding()]
    param (
        # ClientID from SEP Cloud Connection App
        [Parameter()]
        [string]
        $ClientID,

        # Secret from SEP Cloud Connection App
        [Parameter()]
        [string]
        $Secret
    )

    # init
    $BaseURL = (Get-ConfigurationPath).BaseUrl
    $SepCloudCreds = (Get-ConfigurationPath).SepCloudCreds
    $SepCloudToken = (Get-ConfigurationPath).SepCloudToken
    $URI_Tokens = 'https://' + $BaseURL + '/v1/oauth2/tokens'
    $URI_Features = 'https://' + $BaseURL + '/v1/devices/enums'

    # Test if we have a token locally stored
    if (Test-Path -Path $SepCloudToken) {
        <# If true, test it against the API #>
        $Token = Import-Clixml -Path $SepCloudToken
        $Headers = @{
            Host          = $BaseURL
            Accept        = "application/json"
            Authorization = $Token
        }

        try {
            $response = Invoke-RestMethod -Method POST -Uri $URI_Features -Headers $Headers
            # Valid token, returning it
            Write-Verbose "Local token - valid"
            return $Token
        } catch {
            $StatusCode = $_.Exception.Response.StatusCode
            Write-Verbose "Authentication error - From locally stored token - Expected HTTP 200, got $([int]$StatusCode) - Continue ..."
            # Invalid token, deleting local token file
            Remove-Item $SepCloudToken
        }
    }

    # Test if OAuth cred present on the disk
    if (Test-Path -Path "$SepCloudCreds") {
        <# If true, Attempt to get a token #>
        $OAuth = Import-Clixml -Path $SepCloudCreds
        $OAuth_Basic = "Basic " + $OAuth
        $Headers = @{
            Host          = $BaseURL
            Accept        = "application/json"
            Authorization = $OAuth_Basic
        }

        try {
            $response = Invoke-RestMethod -Method POST -Uri $URI_Tokens -Headers $Headers
            # Get the auth token from the response & store it locally
            Write-Verbose "Valid credentials - returning valid token"
            $null = $Bearer_Token = "Bearer " + $response.access_token
            $Bearer_Token | Export-Clixml -Path $SepCloudToken
            return $Bearer_Token

        } catch {
            $StatusCode = $_.Exception.Response.StatusCode
            Write-Verbose "Authentication error - From locally stored credentials - Expected HTTP 200, got $([int]$StatusCode) - Continue..."
            # Invalid Credentials, deleting local credentials file
            Remove-Item $SepCloudCreds
        }
    }

    <# If no token nor OAuth creds available locally
    # Encode ClientID and Secret to create Basic Auth string
    # Authentication requires the following "Basic + encoded CliendID:ClientSecret" #>

    if ($clientID -eq "" -or $Secret -eq "") {
        Write-Host "No local credentials found"
        $ClientID = Read-Host -Prompt "Enter ClientID"
        $Secret = Read-Host -Prompt "Enter Secret"
    }
    $Encoded_Creds = [convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($ClientID + ':' + $Secret)))
    $Encoded_Creds | Export-Clixml -Path $SepCloudCreds

    # Create Basic Auth string
    $BasicAuth = "Basic " + $Encoded_Creds
    $Headers = @{
        Host          = $BaseURL
        Accept        = "application/json"
        Authorization = $BasicAuth
    }
    $Response = Invoke-RestMethod -Method POST -Uri $URI_Tokens -Headers $Headers -UseBasicParsing

    # Get the auth token from the response and store it
    $Bearer_Token = "Bearer " + $Response.access_token
    $Bearer_Token | Export-Clixml -Path $SepCloudToken
    return $Bearer_Token
}
#EndRegion '.\Private\Get-SEPCloudToken.ps1' 125
#Region '.\Private\InternalClass.ps1' 0
# https://stackoverflow.com/a/74901407/2552996
class Extensions {
    [Collections.Generic.List[string]] $names = [Collections.Generic.List[string]]::new()
    [bool] $scheduled
    [Collections.Generic.List[string]] $features = [Collections.Generic.List[string]]::new()
}

class UpdateAllowlist {
    [object] $add
    [object] $remove
    UpdateAllowlist() {
        $ExceptionStructureAdd = [ExceptionStructure]::new()
        $ExceptionStructureRemove = [ExceptionStructure]::new()
        $this.add = $ExceptionStructureAdd
        $this.remove = $ExceptionStructureRemove
    }

}

class ExceptionStructure {
    [object] $Applications
    [object] $Certificates
    [object] $webdomains
    [object] $ips_hosts
    [Extensions] $Extensions
    [object] $windows
    [object] $linux
    [object] $mac
    # Setting up the PSCustomObject structure from the JSON example : https://pastebin.com/FaKYpgw3
    # TODO finish obj structure
    ExceptionStructure() {
        $this.applications = [System.Collections.Generic.List[object]]::new()
        $this.Certificates = [System.Collections.Generic.List[object]]::new()
        $this.webdomains = [System.Collections.Generic.List[object]]::new()
        $this.ips_hosts = [System.Collections.Generic.List[object]]::new()
        $this.extensions = [Extensions]::new()
        # TODO Extensions obj be hashtable. Converting to JSON will not be incorrect format (list instead of k/v pair)
        $this.windows = [PSCustomObject]@{
            files       = [System.Collections.Generic.List[object]]::new()
            directories = [System.Collections.Generic.List[object]]::new()
        }
        $this.Linux = [PSCustomObject]@{
            files       = [System.Collections.Generic.List[object]]::new()
            directories = [System.Collections.Generic.List[object]]::new()
        }
        $this.mac = [PSCustomObject]@{
            files       = [System.Collections.Generic.List[object]]::new()
            directories = [System.Collections.Generic.List[object]]::new()
        }
    }

    # method to add APPLICATIONS tab to the main obj
    [void] AddProcessFile(
        [string] $sha2,
        [string] $name
    ) {
        $this.applications.Add([pscustomobject]@{
                processfile = [pscustomobject]@{
                    sha2 = $sha2
                    name = $name
                }
            })
    }

    # Method to add CERTIFICATES tab to the main obj
    [void] AddCertificates(
        [string] $signature_issuer,
        [string] $signature_company_name,
        # [string] $signature_fingerprint,
        [string] $algorithm,
        [string] $value
    ) {
        # $this.certificates.Add()
        $this.certificates.Add([pscustomobject]@{
                signature_issuer       = $signature_issuer
                signature_company_name = $signature_company_name
                signature_fingerprint  = [pscustomobject]@{
                    algorithm = $algorithm
                    value     = $value
                }
            })
    }

    # Method to add WEBDOMAINS to the main obj
    [void] AddWebDomains(
        [string] $domain
    ) {
        $this.webdomains.add([PSCustomObject]@{
                domain = $domain
            })
    }

    # Method to add IPv4 addresses IPS_HOSTS to the main obj
    [void] AddIpsHostsIpv4Address(
        [string] $ip
    ) {
        $this.ips_hosts.add([PSCustomObject]@{
                ip = $ip
            })
    }

    # Method to add IPv4 subnet IPS_HOSTS to the main obj
    [void] AddIpsHostsIpv4Subnet(
        [string] $ip,
        [string] $mask
    ) {
        $this.ips_hosts.add([pscustomobject]@{
                ipv4_subnet = [pscustomobject]@{
                    ip   = $ip
                    mask = $mask
                }
            })
    }

    # method to add IPv6 subnet IPS_HOSTS to the main obj
    [void] AddIpsHostsIpv6Subnet(
        [string] $ipv6_subnet
    ) {
        $this.ips_hosts.add([pscustomobject]@{
                ipv6_subnet = $ipv6_subnet
            })
    }

    #method to add ip ranges to the main obj
    [void] AddIpsRange(
        [string] $ip_start,
        [string] $ip_end
    ) {
        $this.ips_hosts.add([pscustomobject]@{
                ip_range = [pscustomobject]@{
                    ip_start = $ip_start
                    ip_end   = $ip_end
                }
            })
    }

    # Method to add EXTENSIONS tab to the main obj
    [void] AddExtensions([Extensions] $Extension) {
        $this.Extensions = $Extension
    }

    # Method to add Windows FILES excel tab to obj
    [void] AddWindowsFiles(
        [string] $pathvariable,
        [string] $path,
        [bool] $scheduled,
        [array] $features
    ) {
        $this.windows.files.add([pscustomobject]@{
                pathvariable = $pathvariable
                path         = $path
                scheduled    = $scheduled
                features     = $features
            })
    }

    # Method to add Linux FILES excel tab to obj
    [void] AddLinuxFiles(
        [string] $pathvariable,
        [string] $path,
        [bool] $scheduled,
        [array] $features
    ) {
        $this.linux.files.add([pscustomobject]@{
                pathvariable = $pathvariable
                path         = $path
                scheduled    = $scheduled
                features     = $features
            })
    }

    # Method to add Mac FILES excel tab to obj
    [void] AddMacFiles(
        [string] $pathvariable,
        [string] $path,
        [bool] $scheduled,
        [array] $features
    ) {
        $this.mac.files.add([pscustomobject]@{
                pathvariable = $pathvariable
                path         = $path
                scheduled    = $scheduled
                features     = $features
            })
    }

    # Method to add Windows DIRECTORIES excel tab to obj
    [void] AddWindowsDirectories(
        [string] $pathvariable,
        [string] $directory,
        [bool] $recursive,
        [bool] $scheduled,
        [array] $features
    ) {
        $this.windows.directories.add([pscustomobject]@{
                pathvariable = $pathvariable
                directory    = $directory
                recursive    = $recursive
                scheduled    = $scheduled
                features     = $features
            })
    }

    # Method to add Linux DIRECTORIES excel tab to obj
    [void] AddLinuxDirectories(
        [string] $pathvariable,
        [string] $directory,
        [bool] $recursive,
        [bool] $scheduled,
        [array] $features
    ) {
        $this.linux.directories.add([pscustomobject]@{
                pathvariable = $pathvariable
                directory    = $directory
                recursive    = $recursive
                scheduled    = $scheduled
                features     = $features
            })
    }

    # Method to add Mac DIRECTORIES excel tab to obj
    [void] AddMacDirectories(
        [string] $pathvariable,
        [string] $directory,
        [bool] $recursive,
        [bool] $scheduled,
        [array] $features
    ) {
        $this.mac.directories.add([pscustomobject]@{
                pathvariable = $pathvariable
                directory    = $directory
                recursive    = $recursive
                scheduled    = $scheduled
                features     = $features
            })
    }
}
#EndRegion '.\Private\InternalClass.ps1' 238
#Region '.\Private\Merge-SepCloudAllowList.ps1' 0
function Merge-SepCloudAllowList {
    <#
    .SYNOPSIS
        Merges 2 SEP Cloud allow list policy to a single PSObject
    .DESCRIPTION
        Returns a custom PSObject ready to be converted in json as HTTP Body for Update-SepCloudAllowlistPolicy CmdLet
        Excel file takes precedence in case of conflicts. It is the main "source of truth".
        Logic goes as below
        - If SEP exception present in both excel & policy : no changes
        - If SEP exception present only in Excel : add exception
        - If SEP exception present only in policy (so not in Excel) : remove exception
    .NOTES
        Excel file takes precedence in case of conflicts
    .INPUTS
        - SEP cloud allow list policy PSObject
        - Excel report file path (previously generated from Export-SepCloudAllowListPolicyToExcel CmdLet)
    .OUTPUTS
        - Custom PSObject
    .EXAMPLE
        Merge-SepCloudAllowList -Policy_Name "My Allow List Policy For Servers" -Excel ".\Data\Centralized_exceptions_for_servers.xlsx" | Update-SepCloudAllowlistPolicy
    #>


    param (
        # Policy version
        [Parameter(
        )]
        [string]
        [Alias("Version")]
        $Policy_Version,

        # Exact policy name
        [Parameter(
            Mandatory
        )]
        [string]
        [Alias("PolicyName")]
        $Policy_Name,

        # excel path
        [Parameter(
            Mandatory
        )]
        [string]
        [Alias("Excel")]
        [Alias("Path")]
        $excel_path
    )

    # Get policy details to compare with Excel file
    # Use specific version or by default latest version
    switch ($PSBoundParameters.Keys) {
        'Policy_Version' {
            $obj_policy = Get-SepCloudPolicyDetails -Policy_Name $Policy_Name -Policy_Version $Policy_Version
        }
        Default {}
    }

    if ($null -eq $PSBoundParameters['Policy_Version']) {
        $obj_policy = Get-SepCloudPolicyDetails -Policy_Name $Policy_Name
    }

    # Import excel report as a structured object with
    $obj_policy_excel = Get-ExcelAllowListObject -Path $excel_path

    # Initialize structured obj that will be later converted
    # to HTTP JSON Body with "add" and "remove" hive
    $obj_body = [UpdateAllowlist]::new()

    ###########################
    # Comparison starts here #
    ###########################

    # "Applications" tab
    # Parsing excel object first
    $policy_sha2 = $obj_policy.features.configuration.applications.processfile
    $excel_sha2 = $obj_policy_excel.Applications.processfile
    # Parsing first excel object
    foreach ($line in $excel_sha2) {
        # if sha2 appears in both lists
        if ($policy_sha2.sha2.contains($line.sha2)) {
            # No changes needed
            continue
        } else {
            # if sha2 only in excel list, set the sha to the "add" hive
            $obj_body.add.AddProcessFile(
                $line.sha2,
                $line.name
            )
        }
    }
    # Parsing then policy object
    foreach ($line in $policy_sha2) {
        # if sha2 appears only in policy (so not in Excel)
        if (-not $excel_sha2.sha2.contains($line.sha2)) {
            # set the sha to the "remove" hive
            $obj_body.remove.AddProcessFile(
                $line.sha2,
                $line.name
            )
        }
    }

    # "Files" tab
    # Parsing excel object first
    $policy_files = $obj_policy.features.configuration.windows.files
    $excel_files = $obj_policy_excel.windows.files
    foreach ($line in $excel_files) {
        # If file appears in both lists
        if ($policy_files.path.contains($line.Path)) {
            # No changes needed
            continue
        } else {
            # if file only in excel list, set the file to the "add" hive
            $obj_body.add.AddWindowsFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $line.features
            )
        }
    }
    # Parsing then policy object
    foreach ($line in $policy_files) {
        # if file appears only in policy (so not in Excel)
        if (-not $excel_files.path.contains($line.path)) {
            # set the file to the "remove" hive
            $obj_body.remove.AddWindowsFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $line.features
            )
        }
    }

    # "Directories" tab
    # Parsing excel object first
    $policy_directories = $obj_policy.features.configuration.windows.directories
    $excel_directories = $obj_policy_excel.windows.directories
    foreach ($line in $excel_directories) {
        # If directory appears in both lists
        if ($policy_directories.directory.contains($line.directory)) {
            # No changes needed
            continue
        } else {
            # if directory only in excel list, set the directory to the "add" hive
            $obj_body.add.AddWindowsDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $line.features
            )
        }
    }
    # parsing then policy object
    foreach ($line in $policy_directories) {
        # if directory appears only in policy (so not in Excel)
        if (-not $excel_directories.directory.contains($line.directory)) {
            # set the directory to the "remove" hive
            $obj_body.remove.AddWindowsDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $line.features
            )
        }
    }

    # "Certificates" tab
    # Parsing excel object first
    # TODO confirm this is the right way to compare certificates
    $policy_certs = $obj_policy.features.configuration.certificates
    $excel_certs = $obj_policy_excel.certificates
    foreach ($line in $excel_certs) {
        # If certs appears in both lists
        if ($policy_certs.signature_fingerprint.value.contains($line.signature_fingerprint.value)) {
            # No changes needed
            continue
        } else {
            # if cert only in excel list, set the cert to the "add" hive
            $obj_body.add.AddCertificates(
                $line.signature_issuer,
                $line.signature_company_name,
                $line.signature_fingerprint.algorithm,
                $line.signature_fingerprint.value
            )
        }
    }

    # Parsing then policy object
    foreach ($line in $policy_certs) {
        # if cert appears only in policy (so not in Excel)
        if (-not $excel_certs.signature_fingerprint.value.contains($line.signature_fingerprint.value)) {
            # set the cert to the "remove" hive
            $obj_body.remove.AddCertificates(
                $line.signature_issuer,
                $line.signature_company_name,
                $line.signature_fingerprint.algorithm,
                $line.signature_fingerprint.value
            )
        }
    }

    # "Webdomains" tab
    # Parsing excel object first
    $policy_webdomains = $obj_policy.features.configuration.webdomains
    $excel_webdomains = $obj_policy_excel.webdomains
    foreach ($line in $excel_webdomains) {
        # If webdomain appears in both lists
        if ($policy_webdomains.domain.contains($line.domain)) {
            # No changes needed
            continue
        } else {
            # if webdomain only in excel list, set the webdomain to the "add" hive
            $obj_body.add.AddWebDomains(
                $line.domain
            )
        }
    }

    # Parsing then policy object
    foreach ($line in $policy_webdomains) {
        # if webdomain appears only in policy (so not in Excel)
        if (-not $excel_webdomains.domain.contains($line.domain)) {
            # set the webdomain to the "remove" hive
            $obj_body.remove.AddWebDomains(
                $line.domain
            )
        }
    }

    # "Ips_hosts" tab
    # Parsing excel object first
    $policy_ips_hosts = $obj_policy.features.configuration.ips_hosts
    $excel_ips_hosts = $obj_policy_excel.ips_hosts
    foreach ($line in $excel_ips_hosts) {
        # If Ips_hosts appears in both lists
        if ($policy_ips_hosts.ip.contains($line.ip)) {
            # No changes needed
            continue
        } else {
            # if Ips_hosts only in excel list, set the Ips_hosts to the "add" hive
            $obj_body.add.AddIpsHostsIpv4Address(
                $line.ip
            )
        }
    }

    # Parsing then policy object
    foreach ($line in $policy_ips_hosts) {
        # if Ips_hosts appears only in policy (so not in Excel)
        if (-not $excel_ips_hosts.ip.contains($line.ip)) {
            # set the Ips_hosts to the "remove" hive
            $obj_body.remove.AddIpsHostsIpv4Address(
                $line.ip
            )
        }
    }

    # "Ips_Hosts_subnet_v6" tab
    # Parsing excel object first
    $policy_ips_hosts_subnet_v6 = $obj_policy.features.configuration.ips_hosts.ipv6_subnet | Where-Object { $_ }
    $excel_ips_hosts_subnet_v6 = $obj_policy_excel.ips_hosts.ipv6_subnet | Where-Object { $_ }
    foreach ($line in $excel_ips_hosts_subnet_v6) {
        # if subnet appears in both lists
        if ($policy_ips_hosts_subnet_v6.contains($line)) {
            # no changes
            continue
        } else {
            # if subnet only in excel list, set the subnet to the "add" hive
            $obj_body.add.AddIpsHostsIpv6Subnet(
                $line
            )
        }
        # }
    }

    # parsing then policy object
    foreach ($line in $policy_ips_hosts_subnet_v6) {
        # if subnet appears only in policy (so not in Excel)
        if (-not $excel_ips_hosts_subnet_v6.contains($line)) {
            # set the subnet to the "remove" hive
            $obj_body.remove.AddIpsHostsIpv6Subnet(
                $line
            )
        }
    }

    # "ip ranges" tab
    # Parsing excel object first
    $policy_ip_range = $obj_policy.features.configuration.ips_hosts.ip_range | Where-Object { $_ }
    $excel_ip_range = $obj_policy_excel.ips_hosts.ip_range | Where-Object { $_ }
    foreach ($line in $excel_ip_range) {
        # If ip_start appears in both lists
        if ($policy_ip_range.ip_start.contains($line.ip_start)) {
            # find the index of the ip_start in the policy list
            $policy_index = $policy_ip_range.ip_start.IndexOf($line.ip_start)
            # use index to find the corresponding ip_end
            $policy_ip_end = $policy_ip_range.ip_end[$policy_index]
            # if policy_ip_end is the same as in excel list, no changes needed
            if ($policy_ip_end -eq $line.ip_end) {
                continue
            } else {
                # if policy_ip_end is different, remove the ip_start & ip_end from policy and ...
                $obj_body.remove.AddIpsRange(
                    $policy_ip_range.ip_start[$policy_index],
                    $policy_ip_range.ip_end[$policy_index]
                )
                # ... set the ip range from excel to the "add" hive
                $obj_body.add.AddIpsRange(
                    $line.ip_start,
                    $line.ip_end
                )
            }
        }
        # if ip_start appears only in excel list
        else {
            # set the ip range to the "add" hive
            $obj_body.add.AddIpsRange(
                $line.ip_start,
                $line.ip_end
            )
        }
    }

    # then parsing policy object
    foreach ($line in $policy_ip_range) {
        # if ip_start appears only in policy (so not in Excel)
        if (-not $excel_ip_range.ip_start.contains($line.ip_start)) {
            # set the ip range to the "remove" hive
            $obj_body.remove.AddIpsRange(
                $line.ip_start,
                $line.ip_end
            )
        }
    }

    # "Ips_Hosts_subnet_v4" tab
    # Parsing excel object first
    $policy_ips_hosts_subnet_v4 = $obj_policy.features.configuration.ips_hosts.ipv4_subnet | Where-Object { $_ }
    $excel_ips_hosts_subnet_v4 = $obj_policy_excel.ips_hosts.ipv4_subnet | Where-Object { $_ }
    foreach ($line in $excel_ips_hosts_subnet_v4) {
        # If ip appears in both lists
        if ($policy_ips_hosts_subnet_v4.ip.contains($line.ip)) {
            # find the index of the ip in the policy list
            $policy_index = $policy_ips_hosts_subnet_v4.ip.IndexOf($line.ip)
            # use index to find the corresponding mask
            $policy_mask = $policy_ips_hosts_subnet_v4.mask[$policy_index]
            # if policy_mask is the same as in excel list, no changes needed
            if ($policy_mask -eq $line.mask) {
                continue
            } else {
                # if policy_mask is different, remove the ip and mask from policy and ...
                $obj_body.remove.AddIpsHostsIpv4Subnet(
                    $policy_ips_hosts_subnet_v4.ip[$policy_index],
                    $policy_ips_hosts_subnet_v4.mask[$policy_index]
                )
                # ... set the ip from excel to the "add" hive
                $obj_body.add.AddIpsHostsIpv4Subnet(
                    $line.ip,
                    $line.mask
                )
            }
        }
    }

    # then parsing policy object
    foreach ($line in $policy_ips_hosts_subnet_v4) {
        # if ip appears only in policy (so not in Excel)
        if (-not $excel_ips_hosts_subnet_v4.ip.contains($line.ip)) {
            # set the ip to the "remove" hive
            $obj_body.remove.AddIpsHostsIpv4Subnet(
                $line.ip,
                $line.mask
            )
        }
    }

    # "Extensions" tab
    # Parsing excel object first
    $policy_extensions = $obj_policy.features.configuration.extensions
    $excel_extensions = $obj_policy_excel.extensions
    $extensions_list_to_add = @()
    foreach ($line in $excel_extensions.names) {
        # If extension appears in both lists
        if ($policy_extensions.names.contains($line)) {
            # No changes needed
            continue
        } else {
            # if extension only in excel list, set the extension to the "add" hive
            # Adding it to $extensions_list_to_add
            $extensions_list_to_add += $line
        }
    }
    # If extensions to add not empty
    if ($null -ne $extensions_list_to_add) {
        [PSCustomObject]$ext = @{
            Names     = $extensions_list_to_add
            scheduled = $true
            features  = 'AUTO_PROTECT'
        }
        $obj_body.add.AddExtensions(
            $ext
        )
    }

    # Parsing then policy object
    $extensions_list_to_remove = @()
    foreach ($line in $policy_extensions.names) {
        # if extension appears only in policy (so not in Excel)
        # Adding it to the $extensions_list_to_remove
        if (-not $excel_extensions.names.contains($line)) {
            $extensions_list_to_remove += $line
        }
    }
    # If extensions to remove not empty
    if ($null -ne $extensions_list_to_remove) {
        # set the extension to the "remove" hive
        [PSCustomObject]$ext = @{
            Names     = $extensions_list_to_remove
            scheduled = $true
            features  = 'AUTO_PROTECT'
        }
        $obj_body.remove.AddExtensions(
            $ext
        )
    }

    # "Linux Files" tab
    # Parsing excel object first
    $policy_linux_files = $obj_policy.features.configuration.linux.files
    $excel_linux_files = $obj_policy_excel.linux.files
    foreach ($line in $excel_linux_files) {
        # If file appears in both lists
        if ($policy_linux_files.contains($line.Path)) {
            # No changes needed
            continue
        } else {
            # if file only in excel list, set the file to the "add" hive
            $obj_body.add.AddLinuxFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $line.features
            )
        }
    }

    # Parsing then policy object
    foreach ($line in $policy_linux_files) {
        # if file appears only in policy (so not in Excel)
        if (-not $excel_linux_files.path.contains($line.path)) {
            # set the file to the "remove" hive
            $obj_body.remove.AddLinuxFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $line.features
            )
        }
    }

    # "Linux Directories" tab
    # Parsing excel object first
    $policy_linux_directories = $obj_policy.features.configuration.linux.directories
    $excel_linux_directories = $obj_policy_excel.linux.directories
    foreach ($line in $excel_linux_directories) {
        # If directory appears in both lists
        if ($policy_linux_directories.contains($line.directory)) {
            # No changes needed
            continue
        } else {
            # if directory only in excel list, set the directory to the "add" hive
            $obj_body.add.AddLinuxDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $line.features
            )
        }
    }

    # Parsing then policy object
    foreach ($line in $policy_linux_directories) {
        # if directory appears only in policy (so not in Excel)
        if (-not $excel_linux_directories.directory.contains($line.directory)) {
            # set the directory to the "remove" hive
            $obj_body.remove.AddLinuxDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $line.features
            )
        }
    }

    # "Mac Files" tab
    # Parsing excel object first
    $policy_mac_files = $obj_policy.features.configuration.mac.files
    $excel_mac_files = $obj_policy_excel.mac.files
    foreach ($line in $excel_mac_files) {
        # If file appears in both lists
        if ($policy_mac_files.contains($line.Path)) {
            # No changes needed
            continue
        } else {
            # if file only in excel list, set the file to the "add" hive
            $obj_body.add.AddMacFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $line.features
            )
        }
    }

    # Parsing then policy object
    foreach ($line in $policy_mac_files) {
        # if file appears only in policy (so not in Excel)
        if (-not $excel_mac_files.path.contains($line.path)) {
            # set the file to the "remove" hive
            $obj_body.remove.AddMacFiles(
                $line.pathvariable,
                $line.path,
                $line.scheduled,
                $line.features
            )
        }
    }

    # "Mac Directories" tab
    # Parsing excel object first
    $policy_mac_directories = $obj_policy.features.configuration.mac.directories
    $excel_mac_directories = $obj_policy_excel.mac.directories
    foreach ($line in $excel_mac_directories) {
        # If directory appears in both lists
        if ($policy_mac_directories.contains($line.directory)) {
            # No changes needed
            continue
        } else {
            # if directory only in excel list, set the directory to the "add" hive
            $obj_body.add.AddMacDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $line.features
            )
        }
    }

    # Parsing then policy object
    foreach ($line in $policy_mac_directories) {
        # if directory appears only in policy (so not in Excel)
        if (-not $excel_mac_directories.directory.contains($line.directory)) {
            # set the directory to the "remove" hive
            $obj_body.remove.AddMacDirectories(
                $line.pathvariable,
                $line.directory,
                $line.recursive,
                $line.scheduled,
                $line.features
            )
        }
    }


    return $obj_body
}
#EndRegion '.\Private\Merge-SepCloudAllowList.ps1' 574
#Region '.\Public\Export-SepCloudAllowListPolicyToExcel.ps1' 0
function Export-SepCloudAllowListPolicyToExcel {
    <#
    .SYNOPSIS
        Export an Allow List policy to a human readable excel report
    .INPUTS
        Policy name
        Policy version
        Excel path
    .OUTPUTS
        Excel file
    .DESCRIPTION
        Exports an allow list policy object it to an Excel file, with one tab per allow type (filename/file hash/directory etc...)
        Supports pipeline input with allowlist policy object
    .EXAMPLE
        Export-SepCloudAllowListPolicyToExcel -Name "My Allow list Policy" -Version 1 -Path "allow_list.xlsx"
        Exports the policy with name "My Allow list Policy" and version 1 to an excel file named "allow_list.xlsx"
    .EXAMPLE
        Get-SepCloudPolicyDetails -Name "My Allow list Policy" | Export-SepCloudAllowListPolicyToExcel -Path "allow_list.xlsx"
        Gathers policy in an object, pipes the output to Export-SepCloudAllowListPolicyToExcel to export in excel format
    #>


    param (
        # Path of Export
        [Parameter(Mandatory)]
        [Alias("Path")]
        [Alias("Excel")]
        [string]
        $excel_path,

        # Policy version
        [Parameter()]
        [string]
        [Alias("Version")]
        $Policy_Version,

        # Exact policy name
        [Parameter()]
        [string]
        [Alias("Name")]
        $Policy_Name,

        # Policy Obj to work with
        [Parameter(
            ValueFromPipeline
        )]
        [pscustomobject]
        $obj_policy
    )
    <#
    Using as a template the following command
    Get-SepCloudPolicyDetails -Name "MyAllowListPolicy" -Policy_version 1 | Export-SepCloudAllowListPolicyToExcel -Path "C:\Test\test5.xlsx"
    Parsing the custom object to get the list of
    $obj_policy.features.configuration.applications
    $obj_policy.features.configuration.applications.processfile
    $obj_policy.features.configuration.applications.processfile.name
    $obj_policy.features.configuration.applications.processfile.sha2
    $obj_policy.features.configuration.certificates
    $obj_policy.features.configuration.webdomains
    $obj_policy.features.configuration.ips_hosts
    $obj_policy.features.configuration.extensions
    $obj_policy.features.configuration.extensions.names
    $obj_policy.features.configuration.windows.files
    $obj_policy.features.configuration.windows.directories
    #>


    # If no PSObject is provided, get it from Get-SepCloudPolicyDetails
    if ($null -eq $obj_policy) {
        # Use specific version or by default latest
        if ($Policy_version -ne "") {
            $obj_policy = Get-SepCloudPolicyDetails -Name $Policy_Name -Policy_Version $Policy_Version
        } else {
            $obj_policy = Get-SepCloudPolicyDetails -Name $Policy_Name
        }
    }

    # Init
    $Applications = $obj_policy.features.configuration.applications.processfile
    $Certificates = $obj_policy.features.configuration.certificates
    $Webdomains = $obj_policy.features.configuration.webdomains
    $Extensions_list = $obj_policy.features.configuration.extensions.names
    $Files = $obj_policy.features.configuration.windows.files
    $Directories = $obj_policy.features.configuration.windows.directories
    $linux_Files = $obj_policy.features.configuration.linux.files
    $linux_Directories = $obj_policy.features.configuration.linux.directories
    $mac_Files = $obj_policy.features.configuration.mac.files
    $mac_Directories = $obj_policy.features.configuration.mac.directories

    # Split IPS ipv4 addresses & subnet in different arrays to export in different excel sheets
    $Ips_Hosts = $obj_policy.features.configuration.ips_hosts | Where-Object { $null -ne $_.ip } # removing empty strings
    $Ips_Hosts_subnet_v4 = $obj_policy.features.configuration.ips_hosts.ipv4_subnet | Where-Object { $_ } # removing empty strings
    $Ips_Hosts_subnet_v6_list = $obj_policy.features.configuration.ips_hosts.ipv6_subnet | Where-Object { $_ } # removing empty strings
    $Ips_range = $obj_policy.features.configuration.ips_hosts.ip_range | Where-Object { $_ } # removing empty strings

    # Split Extensions in an array of objects for correct formating
    $Extensions = @()
    foreach ($line in $Extensions_list) {
        $obj = New-Object -TypeName PSObject
        $obj | Add-Member -MemberType NoteProperty -Name Extensions -Value $line
        $Extensions += $obj
    }

    # formating Ips_Hosts_subnet_v6
    $Ips_Hosts_subnet_v6 = @()
    foreach ($line in $Ips_Hosts_subnet_v6_list) {
        $obj = New-Object -TypeName PSObject
        $obj | Add-Member -MemberType NoteProperty -Name ipv6_subnet -Value $line
        $Ips_Hosts_subnet_v6 += $obj
    }

    # Exporting data to Excel
    Import-Module -Name ImportExcel
    $Applications | Export-Excel $excel_path -WorksheetName "Applications" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Files | ConvertTo-FlatObject | Export-Excel $excel_path -WorksheetName "Files" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Directories | ConvertTo-FlatObject | Export-Excel $excel_path -WorksheetName "Directories" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Extensions |  Export-Excel $excel_path -WorksheetName "Extensions" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Webdomains | Export-Excel $excel_path -WorksheetName "Webdomains" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Ips_Hosts | Export-Excel $excel_path -WorksheetName "Ips_Hosts" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Ips_Hosts_subnet_v4 | Export-Excel $excel_path -WorksheetName "Ips_Hosts_subnet_v4" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Ips_Hosts_subnet_v6 | Export-Excel $excel_path -WorksheetName "Ips_Hosts_subnet_v6" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Ips_range | Export-Excel $excel_path -WorksheetName "Ips_range" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $Certificates | ConvertTo-FlatObject | Export-Excel $excel_path -WorksheetName "Certificates" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $linux_Files | ConvertTo-FlatObject | Export-Excel $excel_path -WorksheetName "Linux Files" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $linux_Directories | ConvertTo-FlatObject | Export-Excel $excel_path -WorksheetName "Linux Directories" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $mac_Files | ConvertTo-FlatObject | Export-Excel $excel_path -WorksheetName "Mac Files" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
    $mac_Directories | ConvertTo-FlatObject | Export-Excel $excel_path -WorksheetName "Mac Directories" -ClearSheet -BoldTopRow -AutoSize -FreezeTopRow -AutoFilter
}
#EndRegion '.\Public\Export-SepCloudAllowListPolicyToExcel.ps1' 127
#Region '.\Public\Get-SepCloudDeviceDetails.ps1' 0
function Get-SepCloudDeviceDetails {
    # TODO add documentation
    <#
    .SYNOPSIS
        Gathers device details from the SEP Cloud console
    .DESCRIPTION
        Gathers device details from the SEP Cloud console
    .PARAMETER Device_ID
        id used to lookup a unique computer
    .NOTES
        Information or caveats about the function e.g. 'This function is not supported in Linux'
    .EXAMPLE
        Get-SepCloudDeviceDetails -id wduiKXDDSr2CVrRaqrFKNx
    .EXAMPLE
        Get-SepCloudDeviceDetails -computername MyComputer
    #>


    [CmdletBinding()]
    param (
        # device_ID parameter
        [Parameter(
            ValueFromPipelineByPropertyName = $true)]
        [Alias("id")]
        [string]
        $Device_ID,

        # Computer Name
        [Parameter(
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("Computer")]
        [Alias("Device")]
        [Alias("host")]
        [string]
        $Computername
    )

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        # Get token
        $Token = Get-SEPCloudToken
    }

    process {
        # Get Device_ID from the computername
        if ($null -ne $Computername) {
            $Device_ID = (Get-SepCloudDevices -Computername $Computername).id
        }

        # Setup URI
        $URI = 'https://' + $BaseURL + "/v1/devices/$Device_ID"

        if ($null -ne $Token) {
            $Body = @{}
            $Headers = @{
                Host          = $BaseURL
                Accept        = "application/json"
                Authorization = $Token
                Body          = $Body
            }
            $Response = Invoke-RestMethod -Method GET -Uri $URI -Headers $Headers -Body $Body -UseBasicParsing
            return $Response
        }

    }
}
#EndRegion '.\Public\Get-SepCloudDeviceDetails.ps1' 69
#Region '.\Public\Get-SepCloudDevices.ps1' 0
function Get-SepCloudDevices {
    <# TODO fill up description
    .SYNOPSIS
        Gathers list of devices from the SEP Cloud console
    .PARAMETER Computername
    Specify one or many computer names. Accepts pipeline (up to 10 devices per query)
    Supports partial match
    .PARAMETER is_online
    Switch to lookup only online machines
    .PARAMETER Device_status
    Lookup devices per security status. Accepts only "SECURE", "AT_RISK", "COMPROMISED", "NOT_COMPUTED"
    .EXAMPLE
    Get-SepCloudDevices
    .EXAMPLE
    Get-SepCloudDevices -Computername MyComputer
    .EXAMPLE
    Get-SepCloudDevices -is_online -Device_status AT_RISK
    #>


    [CmdletBinding()]
    param (
        <# Optional ComputerName parameter
        TODO work to allow multiple values from Computername
        More info https://apidocs.securitycloud.symantec.com/#
        name query name of the device. [NOTE] Provide comma seperated values in case of multiple name search
        Note : seems to be limited to 10 values max
        #>

        [Parameter(
            ValueFromPipeline = $true
        )]
        [string]
        $Computername,

        # Optional Is_Online parameter
        [Parameter()]
        [Alias("Online")]
        [switch]
        $is_online,

        # Optional include_details parameter
        [Parameter()]
        [Alias("Details")]
        [switch]
        $include_details,

        # Device Group
        [Parameter()]
        [Alias("Group")]
        [string]
        $Device_group,

        # Optional Device_Status parameter
        [Parameter()]
        [Alias("DeviceStatus")]
        [ValidateSet("SECURE", "AT_RISK", "COMPROMISED", "NOT_COMPUTED")]
        $Device_status
    )

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $URI_Tokens = 'https://' + $BaseURL + "/v1/devices"
        $ArrayResponse = @()
    }

    process {
        # Get token
        $Token = Get-SEPCloudToken

        if ($null -ne $Token ) {
            # HTTP body content containing all the queries
            $Body = @{}

            # Iterating through all parameter and add them to the HTTP body
            if ($Computername -ne "") {
                $Body.Add("name", "$Computername")
            }
            if ($is_online -eq $true ) {
                $body.add("is_online", "true")
            }
            if ($include_details -eq $true) {
                $Body.Add("include_details", "true")
            }
            if ($Device_status -ne "") {
                $Body.Add("device_status", "$Device_status")
            }
            if ($Device_group -ne "") {
                $Body.Add("device_group", "$Device_group")
            }
            $Headers = @{
                Host          = $BaseURL
                Accept        = "application/json"
                Authorization = $Token
            }

            try {
                $Response = Invoke-RestMethod -Method GET -Uri $URI_Tokens -Headers $Headers -Body $Body -UseBasicParsing
                $ArrayResponse += $Response.devices
                $Devices_count_gathered = (($ArrayResponse | Measure-Object).count)
                <# If pagination #>
                if ($Response.total -gt $Devices_count_gathered) {
                    <# Loop through via Offset parameter as there is no "next" parameter for /devices/ API call #>
                    do {
                        # change the "offset" parameter for next query
                        $Body.Remove("offset")
                        $Body.Add("offset", $Devices_count_gathered)
                        # Run query, add it to the array, increment counter
                        $Response = Invoke-RestMethod -Method GET -Uri $URI_Tokens -Headers $Headers -Body $Body -UseBasicParsing
                        $ArrayResponse += $Response.devices
                        $Devices_count_gathered = (($ArrayResponse | Measure-Object).count)
                    } until (
                        $Devices_count_gathered -ge $Response.total
                    )
                }

            } catch {
                $StatusCode = $_
                $StatusCode
            }
        }
    }

    end {
        return $ArrayResponse
    }
}
#EndRegion '.\Public\Get-SepCloudDevices.ps1' 127
#Region '.\Public\Get-SepCloudEventSearch.ps1' 0
function Get-SepCloudEventSearch {
    <# TODO fill description for Get-SepCloudEventSearch
    .SYNOPSIS
        Get list of SEP Cloud Events. By default, every events for the past 30 days
    .DESCRIPTION
        A longer description of the function, its purpose, common use cases, etc.
    .PARAMETER FileDetection
        Runs the following query under the hood "feature_name:MALWARE_PROTECTION AND ( type_id:8031 OR type_id:8032 OR type_id:8033 OR type_id:8027 OR type_id:8028 ) AND ( id:12 OR id:11 AND type_id:8031 )"
    .LINK
        https://github.com/Douda/PSSymantecCloud
    .EXAMPLE
        Get-SepCloudEventSearch -FileDetection
    .EXAMPLE
        Get-SepCloudEventSearch - Query "type_id:8031 OR type_id:8032 OR type_id:8033"
    #>


    param (
        # file Detection
        [Parameter()]
        [switch]
        $FileDetection,

        # Custom query to run
        [Parameter()]
        [string]
        $Query
    )

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $URI_Tokens = 'https://' + $BaseURL + "/v1/event-search"
        $ArrayResponse = @()
    }

    process {
        # Get token
        $Token = Get-SEPCloudToken

        if ($null -ne $Token) {
            # HTTP body content containing all mandatory info to start a query
            $Body = @{
                "product"      = "SAEP"
                "feature_name" = "ALL"
            }
            <# Setting dates for the query Date Format required : -UFormat "%Y-%m-%dT%T.000+00:00"
            Example :
            "start_date": "2022-10-16T00:00:00.000+00:00",
            "end_date": "2022-11-16T00:00:00.000+00:00" #>


            $end_date = Get-Date -Format "yyyy-MM-ddTHH:mm:ss.fffK"
            $start_date = ((Get-Date).addDays(-29) | Get-Date -Format "yyyy-MM-ddTHH:mm:ss.fffK")
            $Body.Add("start_date", $start_date)
            $Body.Add("end_date", $end_date)

            # Iterating through all parameter and adding them to the HTTP body
            switch ($PSBoundParameters.Keys) {
                'FileDetection' {
                    $Body.Add("query", '( feature_name:MALWARE_PROTECTION AND ( type_id:8031 OR type_id:8032 OR type_id:8033 OR type_id:8027 OR type_id:8028 ) AND ( id:12 OR id:11 AND type_id:8031 ) )')
                }
                'Query' {
                    $Body.Add("query", "$Query")
                }
                Default {
                }
            }

            # Convert body to Json after adding potential query with parameters
            $Body_Json = ConvertTo-Json $Body

            $Headers = @{
                Host           = $BaseURL
                Accept         = "application/json"
                "Content-Type" = "application/json"
                Authorization  = $Token
            }

            try {
                $Response = Invoke-RestMethod -Method POST -Uri $URI_Tokens -Headers $Headers -Body $Body_Json -UseBasicParsing
                $ArrayResponse += $Response
                if ($null -ne $Response.next) {
                    <# If pagination #>
                    do {
                        # change the "next" offset for next query
                        $Body.Remove("next")
                        $Body.Add("next", $Response.next)
                        $Body_Json = ConvertTo-Json $Body
                        # Run query & add it to the array
                        $Response = Invoke-RestMethod -Method POST -Uri $URI_Tokens -Headers $Headers -Body $Body_Json -UseBasicParsing
                        $ArrayResponse += $Response
                    } until (
                        ($null -eq $Response.next)
                    )
                }
            } catch {
                $StatusCode = $_
                $StatusCode
            }
        }
    }

    end {
        return $ArrayResponse.events
    }
}
#EndRegion '.\Public\Get-SepCloudEventSearch.ps1' 106
#Region '.\Public\Get-SepCloudFeatureList.ps1' 0
function Get-SepCloudFeatureList {
    <# TODO : fill in description Get-SepCloudFeatureList
    .SYNOPSIS
        A short one-line action-based description, e.g. 'Tests if a function is valid'
    .DESCRIPTION
        A longer description of the function, its purpose, common use cases, etc.
    .NOTES
        Information or caveats about the function e.g. 'This function is not supported in Linux'
    .LINK
        Specify a URI to a help page, this will show when Get-Help -Online is used.
    .EXAMPLE
        Test-MyTestFunction -Verbose
        Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
    #>


    param (
    )

    # Init
    $BaseURL = (Get-ConfigurationPath).BaseUrl
    $URI_Tokens = 'https://' + $BaseURL + "/v1/devices/enums"
    # Get token
    $Token = Get-SEPCloudToken

    if ($null -ne $Token) {
        # HTTP body content containing all the queries
        $Body = @{}
        $Headers = @{
            Host          = $BaseURL
            Accept        = "application/json"
            Authorization = $Token
            Body          = $Body
        }
        $Response = Invoke-RestMethod -Method GET -Uri $URI_Tokens -Headers $Headers -Body $Body -UseBasicParsing
        return $Response
    }
}
#EndRegion '.\Public\Get-SepCloudFeatureList.ps1' 38
#Region '.\Public\Get-SepCloudIncidentDetails.ps1' 0
function Get-SepCloudIncidentDetails {

    <# TODO fill description
    TODO finish up the API query from incident_number and not UID for ease of use
.SYNOPSIS
    A short one-line action-based description, e.g. 'Tests if a function is valid'
.DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
.NOTES
    Information or caveats about the function e.g. 'This function is not supported in Linux'
.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    Test-MyTestFunction -Verbose
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>


    param (
        # Incident GUID
        [Parameter(
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        [Alias("incident_uid")]
        $Incident_ID,

        # Incident number
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [string[]]
        [Alias("Name")]
        $Incident_number
    )

    begin {
        # Init
        $Token = Get-SEPCloudToken
        # Get list of all SEP Cloud policies
        $obj_policies = (Get-SepCloudPolices).policies
    }

    process {
        # Filter only the policy with the correct name
        $obj_policy = ($obj_policies | Where-Object { $_.name -eq "$Policy_Name" })

        $Policy_UUID = ($obj_policy).policy_uid
        $Policy_Version = ($obj_policy).policy_version
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $URI = 'https://' + $BaseURL + "/v1/policies/$Policy_UUID/versions/$Policy_Version"

        $Body = @{}
        $Headers = @{
            Host          = $BaseURL
            Accept        = "application/json"
            Authorization = $Token
            Body          = $Body
        }
        $Resp = Invoke-RestMethod -Method GET -Uri $URI -Headers $Headers -Body $Body -UseBasicParsing

        $Resp
    }
}
#EndRegion '.\Public\Get-SepCloudIncidentDetails.ps1' 65
#Region '.\Public\Get-SepCloudIncidents.ps1' 0
function Get-SepCloudIncidents {

    <# TODO fill description for Get-SepCloudIncidents
    .SYNOPSIS
        Get list of SEP Cloud incidents. By default, shows opened incidents
    .DESCRIPTION
        Get list of SEP Cloud incidents. Using the LUCENE query syntax, you can customize which incidents to gather. More information : https://techdocs.broadcom.com/us/en/symantec-security-software/endpoint-security-and-management/endpoint-security/sescloud/Endpoint-Detection-and-Response/investigation-page-overview-v134374740-d38e87486/Cloud-Database-Search/query-and-filter-operators-by-data-type-v134689952-d38e88796.html
    .PARAMETER Open
        filters only opened incidents. Simulates a query "state_id: [0 TO 3]" which represents incidents with the following states <0 Unknown | 1 New | 2 In Progress | 3 On Hold>
    .PARAMETER Include_events
        Includes every events that both are part of the context & triggered incident events
    .PARAMETER Query
        Type your customer Lucene query to pass to the API
    .OUTPUTS
        PSObject containing all SEP incidents
    .EXAMPLE
        Get-SepCloudIncidents -Open -Include_Events
    .EXAMPLE
        Get-SepCloudIncidents -Query "state_id: [0 TO 5]"
        This query a list of every possible incidents (opened, closed and with "Unknown" status)
    .LINK
        https://github.com/Douda/PSSymantecCloud
    #>

    [CmdletBinding(DefaultParameterSetName = 'QueryOpen')]
    param (
        # Opened incidents
        [Parameter(
            ParameterSetName = "QueryOpen"
        )]
        [switch]
        $Open,

        # Include events
        [Parameter()]
        [switch]
        $Include_events,

        # Custom query to run
        [Parameter(
            ParameterSetName = "QueryCustom"
        )]
        [string]
        $Query

    )
    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $URI_Tokens = 'https://' + $BaseURL + "/v1/incidents"
        $ArrayResponse = @()
    }

    process {
        # Get token
        $Token = Get-SEPCloudToken

        if ($null -ne $Token) {
            # HTTP body content containing all the queries
            $Body = @{}

            # Settings dates
            $end_date = Get-Date -Format "yyyy-MM-ddTHH:mm:ss.fffK"
            $start_date = ((Get-Date).addDays(-29)  | Get-Date -Format "yyyy-MM-ddTHH:mm:ss.fffK")
            $Body.Add("start_date", $start_date)
            $Body.Add("end_date", $end_date)

            # Iterating through all parameter and adding them to the HTTP body
            switch ($PSBoundParameters.Keys) {
                'Query' {
                    $Body.Add("query", "$Query")
                }
                'Open' {
                    $Body.Add("query", "state_id: [0 TO 3]")
                }
                'Include_events' {
                    $Body.Add("include_events", "true")
                }
                Default {
                }
            }

            $Body_Json = ConvertTo-Json $Body
            $Headers = @{
                Host           = $BaseURL
                Accept         = "application/json"
                "Content-Type" = "application/json"
                Authorization  = $Token
            }

            try {
                $Response = Invoke-RestMethod -Method POST -Uri $URI_Tokens -Headers $Headers -Body $Body_Json -UseBasicParsing
                $ArrayResponse += $Response
                if ($null -ne $Response.next) {
                    <# If pagination #>
                    do {
                        # change the "next" offset for next query
                        $Body.Remove("next")
                        $Body.Add("next", $Response.next)
                        $Body_Json = ConvertTo-Json $Body
                        # Run query & add it to the array
                        $Response = Invoke-RestMethod -Method POST -Uri $URI_Tokens -Headers $Headers -Body $Body_Json -UseBasicParsing
                        $ArrayResponse += $Response
                    } until (
                        ($null -eq $Response.next)
                    )
                }
            } catch {
                $StatusCode = $_
                $StatusCode
            }
        }
    }
    end {
        return $ArrayResponse.incidents
    }
}
#EndRegion '.\Public\Get-SepCloudIncidents.ps1' 117
#Region '.\Public\Get-SepCloudPolices.ps1' 0
function Get-SepCloudPolices {
    # TODO to finish; test cmd-let

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $URI_Tokens = 'https://' + $BaseURL + "/v1/policies"
        $Token = Get-SEPCloudToken
        $Body = @{}
        $Headers = @{
            Host          = $BaseURL
            Accept        = "application/json"
            Authorization = $Token
            Body          = $Body
        }
    }

    process {
        try {
            $Response = Invoke-RestMethod -Method GET -Uri $URI_Tokens -Headers $Headers -Body $Body -UseBasicParsing
        }

        catch {
            $StatusCode = $_
            $StatusCode
        }

        $Response
    }
}
#EndRegion '.\Public\Get-SepCloudPolices.ps1' 31
#Region '.\Public\Get-SepCloudPolicyDetails.ps1' 0
function Get-SepCloudPolicyDetails {

    <# TODO finish Get-SepCloudPolicyDetails description
    .SYNOPSIS
        A short one-line action-based description, e.g. 'Tests if a function is valid'
    .DESCRIPTION
        A longer description of the function, its purpose, common use cases, etc.
    .NOTES
        Information or caveats about the function e.g. 'This function is not supported in Linux'
    .LINK
        Specify a URI to a help page, this will show when Get-Help -Online is used.
    .EXAMPLE
        Test-MyTestFunction -Verbose
        Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
    #>



    param (
        # Policy UUID
        [Parameter(
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        [Alias("policy_uid")]
        $Policy_UUID,

        # Policy version
        [Parameter()]
        [string]
        [Alias("Version")]
        $Policy_Version,

        # Exact policy name
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [string[]]
        [Alias("Name")]
        $Policy_Name
    )

    begin {
        # Init
        $Token = Get-SEPCloudToken
        $obj_policies = (Get-SepCloudPolices).policies
    }

    process {
        # Get list of all SEP Cloud policies and get only the one with the correct name
        $obj_policy = ($obj_policies | Where-Object { $_.name -eq "$Policy_Name" })

        # Use specific version or by default latest
        if ($Policy_version -ne "") {
            $obj_policy = $obj_policy | Where-Object {
                $_.name -eq "$Policy_Name" -and $_.policy_version -eq $Policy_Version
            }
        } else {
            $obj_policy = ($obj_policy | Sort-Object -Property policy_version -Descending | Select-Object -First 1)
        }

        $Policy_UUID = ($obj_policy).policy_uid
        $Policy_Version = ($obj_policy).policy_version
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $URI = 'https://' + $BaseURL + "/v1/policies/$Policy_UUID/versions/$Policy_Version"

        if ($null -ne $Token) {
            $Body = @{}
            $Headers = @{
                Host          = $BaseURL
                Accept        = "application/json"
                Authorization = $Token
                Body          = $Body
            }
            $Resp = Invoke-RestMethod -Method GET -Uri $URI -Headers $Headers -Body $Body -UseBasicParsing
        }

        $Resp
    }
}
#EndRegion '.\Public\Get-SepCloudPolicyDetails.ps1' 81
#Region '.\Public\Get-SepCloudTargetRules.ps1' 0
function Get-SepCloudTargetRules {
    # TODO to finish; test cmd-let

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $URI_Tokens = 'https://' + $BaseURL + "/v1/policies/target-rules"
        $Token = Get-SEPCloudToken
        $Body = @{}
        $Headers = @{
            Host          = $BaseURL
            Accept        = "application/json"
            Authorization = $Token
            Body          = $Body
        }
    }

    process {
        try {
            $Response = Invoke-RestMethod -Method GET -Uri $URI_Tokens -Headers $Headers -Body $Body -UseBasicParsing
        }

        catch {
            $StatusCode = $_
            $StatusCode
        }

        $Response
    }
}
#EndRegion '.\Public\Get-SepCloudTargetRules.ps1' 31
#Region '.\Public\Get-SepThreatIntelCveProtection.ps1' 0
function Get-SepThreatIntelCveProtection {
    <#
    .SYNOPSIS
        Provide information whether a given CVE has been blocked by any of Symantec technologies
    .DESCRIPTION
        Provide information whether a given URL/domain has been blocked by any of Symantec technologies.
        These technologies include Antivirus (AV), Intrusion Prevention System (IPS) and Behavioral Analysis & System Heuristics (BASH).
 
    .PARAMETER network
        Specify one domain
 
    .EXAMPLE
        Get-SepThreatIntelCveProtection -cve CVE-2023-35311
    #>


    [CmdletBinding()]
    param (
        # Mandatory cve
        [Parameter(
            Mandatory,
            ValueFromPipeline = $true)]
        [Alias('vuln')]
        [Alias('vulnerability')]
        [string[]]
        $cve
    )

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $Token = Get-SEPCloudToken
    }

    process {
        # $URI in the process block for pipeline support
        $URI = 'https://' + $BaseURL + "/v1/threat-intel/protection/cve/$cve"

        if ($null -ne $Token) {
            # HTTP body content containing all the queries
            $Body = @{}
            $Headers = @{
                Host          = $BaseURL
                Accept        = "application/json"
                Authorization = $Token
                Body          = $Body
            }
            $Response = Invoke-RestMethod -Method GET -Uri $URI -Headers $Headers -Body $Body -UseBasicParsing
            $Response
        }
    }
}
#EndRegion '.\Public\Get-SepThreatIntelCveProtection.ps1' 52
#Region '.\Public\Get-SepThreatIntelFileProtection.ps1' 0
function Get-SepThreatIntelFileProtection {
    <#
    .SYNOPSIS
        Provide information whether a given file has been blocked by any of Symantec technologies
    .DESCRIPTION
        Provide information whether a given file has been blocked by any of Symantec technologies.
        These technologies include Antivirus (AV), Intrusion Prevention System (IPS) and Behavioral Analysis & System Heuristics (BASH).
 
    .PARAMETER file_sha256
        Specify one sha256 hash
 
    .EXAMPLE
        Get-SepThreatIntelFileProtection -file_sha256 eec3f761f7eabe9ed569f39e896be24c9bbb8861b15dbde1b3d539505cd9dd8d
    #>


    [CmdletBinding()]
    param (
        # Mandatory file sha256
        [Parameter(
            Mandatory,
            ValueFromPipeline = $true)]
        [Alias('sha256')]
        [string[]]
        $file_sha256
    )

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $Token = Get-SEPCloudToken
    }

    process {
        # $URI in the process block for pipeline support
        $URI = 'https://' + $BaseURL + "/v1/threat-intel/protection/file/$file_sha256"

        if ($null -ne $Token) {
            # HTTP body content containing all the queries
            $Body = @{}
            $Headers = @{
                Host          = $BaseURL
                Accept        = "application/json"
                Authorization = $Token
                Body          = $Body
            }
            $Response = Invoke-RestMethod -Method GET -Uri $URI -Headers $Headers -Body $Body -UseBasicParsing
            $Response
        }
    }
}
#EndRegion '.\Public\Get-SepThreatIntelFileProtection.ps1' 51
#Region '.\Public\Get-SepThreatIntelNetworkProtection.ps1' 0
function Get-SepThreatIntelNetworkProtection {
    <#
    .SYNOPSIS
        Provide information whether a given URL/domain has been blocked by any of Symantec technologies
    .DESCRIPTION
        Provide information whether a given URL/domain has been blocked by any of Symantec technologies.
        These technologies include Antivirus (AV), Intrusion Prevention System (IPS) and Behavioral Analysis & System Heuristics (BASH).
 
    .PARAMETER network
        Specify one domain
 
    .EXAMPLE
        Get-SepThreatIntelNetworkProtection -domain nicolascoolman.eu
    #>


    [CmdletBinding()]
    param (
        # Mandatory domain name
        [Parameter(
            Mandatory,
            ValueFromPipeline = $true)]
        [Alias('domain')]
        [Alias('URL')]
        [string[]]
        $network
    )

    begin {
        # Init
        $BaseURL = (Get-ConfigurationPath).BaseUrl
        $Token = Get-SEPCloudToken
    }

    process {
        # $URI in the process block for pipeline support
        $URI = 'https://' + $BaseURL + "/v1/threat-intel/protection/network/$network"

        if ($null -ne $Token) {
            # HTTP body content containing all the queries
            $Body = @{}
            $Headers = @{
                Host          = $BaseURL
                Accept        = "application/json"
                Authorization = $Token
                Body          = $Body
            }
            $Response = Invoke-RestMethod -Method GET -Uri $URI -Headers $Headers -Body $Body -UseBasicParsing
            $Response
        }
    }
}
#EndRegion '.\Public\Get-SepThreatIntelNetworkProtection.ps1' 52
#Region '.\Public\Test-SepCloudConnectivity.ps1' 0
function Test-SepCloudConnectivity {
    param (
    )

    if (Get-SEPCloudToken) {
        Write-Host "Authentication OK"
        return $true
        else {
            Write-Host "Authentication failed"
            return $false
        }
    }
}
#EndRegion '.\Public\Test-SepCloudConnectivity.ps1' 14
#Region '.\Public\Update-SepCloudAllowlistPolicy.ps1' 0
function Update-SepCloudAllowlistPolicy {
    <# TODO verify description
    .SYNOPSIS
        Updates Symantec Allow List policy using an excel file
    .DESCRIPTION
        Gathers Allow List policy information from an Excel file generated from Export-SepCloudAllowListPolicyToExcel function
        You can manually add lines to the Excel file, and the updated Excel will be used to add new exceptions to the Allow list policy of your choice
    .INPUTS
        - Excel file generated from Export-SepCloudAllowListPolicyToExcel function
        - Policy name to update
        - OPTIONAL : policy version (default latest version)
    .PARAMETER Policy_UUID
        Optional parameter - GUID of the policy. Optional. The function can gathers the UUID from the policy name
    .PARAMETER Policy_Version
        Optional parameter - Version of the policy to update. By default, latest version selected
    .PARAMETER Policy_Name
        Exact name of the policy to update
    .PARAMETER ExcelFile
        Path fo the Excel file that contains updated information on Allow list to update
        Takes Excel template from Export-SepCloudAllowListPolicyToExcel function
    .NOTES
        Currently supports only filehash/filename
        TODO update NOTES when more options will be supported
    .EXAMPLE
        TODO review & add more examples
        Get-SepCloudPolicyDetails
        Update-SepCloudAllowlistPolicy -policy "My Policy" -ExcelFile .\WorkstationsAllowList.xlsx
        the file MyAllowList.xlsx can be generated from : get-sepcloudpolicyDetails -name "Workstations Allow List Policy" | Export-SepCloudAllowListPolicyToExcel -Path .\Data\WorkstationsAllowList.xlsx
    #>



    # TODO to finish; test ParameterSetName Policy
    param (
        # Policy UUID
        [Parameter(
            ParameterSetName = 'Policy'
        )]
        [string]
        $Policy_UUID,

        # Policy version
        [Parameter(
            ParameterSetName = 'Policy'
        )]
        [string]
        [Alias("Version")]
        $Policy_Version,

        # Exact policy name
        [Parameter(
            ValueFromPipeline
            # Mandatory
        )]
        [string]
        [Alias("PolicyName")]
        # TODO remove hardcoded value
        $Policy_Name = "AB - Testing - Servers - Core - Allow List Policy",

        # Excel file to import data from
        [Parameter(
            # Mandatory
            # TODO add this parameter as mandatory once development is done
        )]
        [string]
        [Alias("Excel")]
        # TODO remove hardcoded excel path for dev
        $excel_path = "C:\Amcor\Module\Workstations_allowlist.xlsx"
    )

    # Verify parameters
    switch ($PSBoundParameters.Keys) {
        'Policy_Version' {
            # Merge cloud policy with excel file with specified version
            $obj_policy = Merge-SepCloudAllowList -Excel $excel_path -Policy_Name $Policy_Name -Policy_Version $Policy_Version
        }
        Default {}
    }

    if ($null -eq $PSBoundParameters['Policy_Version']) {
        # Merge cloud policy with excel file with latest version
        $obj_policy = Merge-SepCloudAllowList -Excel $excel_path -Policy_Name $Policy_Name
    }


    # Converting PSObj to json
    $Body = $obj_policy | ConvertTo-Json -Depth 10

    # Get token for API query
    $Token = Get-SEPCloudToken

    # API query
    if ($null -ne $Token) {
        $Headers = @{
            Host           = $BaseURL
            "Content-Type" = "application/json"
            Accept         = "application/json"
            Authorization  = $Token
        }
        $Response = Invoke-RestMethod -Method PATCH -Uri $URI -Headers $Headers -Body $Body -UseBasicParsing
        # TODO uncomment API query
    } else {
        Write-Error "Invalid or empty token - exit"
        break
    }
    # TODO See if we need to remove return once finished
    return $Response
}
#EndRegion '.\Public\Update-SepCloudAllowlistPolicy.ps1' 108