Atempo.Lina.psm1

<#
 .Synopsis
  Provides basic management CLI for Atempo Lina continuous data protection solution
 
 .Description
  Provides basic management CLI for Atempo Lina continuous data protection solution
 
 .Example
    # You may have to set the execution policy before using the module
    Set-ExecutionPolicy Unrestricted
#>


<# TODO
<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>SET</Func><ServiceProfile><ID>136</ID><ParamSchedule>300</ParamSchedule><ParamAlertTime>259200</ParamAlertTime><ParamDataAging>7</ParamDataAging><ParamThroughputLimit>700</ParamThroughputLimit><ParamBackupRestrictionActivated>0</ParamBackupRestrictionActivated><ParamBackupRestrictionArray></ParamBackupRestrictionArray><ParamEncryptionActivated>0</ParamEncryptionActivated><ParamEncryptionGWArray></ParamEncryptionGWArray><ParamWanActivated>0</ParamWanActivated><ParamWanGWArray></ParamWanGWArray><ParamCompression>BROTLI</ParamCompression><ParamCacheMode>0</ParamCacheMode><ParamCacheSize>0</ParamCacheSize><ParamQuotaMaxProtSize>0</ParamQuotaMaxProtSize><ParamQuotaMaxProtObjs>0</ParamQuotaMaxProtObjs><ParamQuotaMaxHistSize>0</ParamQuotaMaxHistSize><ParamQuotaMaxHistObjs>0</ParamQuotaMaxHistObjs><ParamAllowClientRules>0</ParamAllowClientRules><ParamAllowClientPause>0</ParamAllowClientPause><ParamAllowClientBoost>0</ParamAllowClientBoost><ParamAllowClientNetworkParams>0</ParamAllowClientNetworkParams><ParamAllowClientRPO>0</ParamAllowClientRPO><ParamAllowWebAccess>0</ParamAllowWebAccess><ParamAllowFullBrowse>1</ParamAllowFullBrowse><ParamRepliTargetID>0</ParamRepliTargetID></ServiceProfile></HNConfig>
    Manage disconnections error
    Use Invoke-WebRequest instead of RestMethod => uniformize returns inside functions and improve debug like in Miria module
    Use SecurePassword
    Clone a strategy ?
    Creation of Protections ?
    Managing LinaTenant auto-assignment rules
    manage list of replications
    managing all missing objects (paths, rules etc)
    Ability to pipe commands => should be done everywhere
#>


<# Other URLS :
Global stats / info.xml : Objects in infolist
Protection / lst_dataprofiles.xml : Objects in HNConfig.DataProfileArray.DataProfile
Protection rules / lst_filters.xml : Objects in HNConfig.FilterRuleArray.FilterRule
Paths / lst_predefinedpaths.xml : Objects in HNConfig.PredefinedPathArray.PredefinedPath
File Types / lst_filetypes.xml : Objects in HNConfig.FileTypeArray.FileType
Agent groups / lst_hierarchies.xml : Objects in HNConfig.HierarchyArray.Hierarchy
User groups / ADM/list_user_group.json : Objects in user_groups
Groups to users / ADM/list_prof_ug_relation.json : Objects in relations
#>


# Needed for URLEncode
Add-Type -AssemblyName System.Web
$global:GLOBAL_IGNORE_CERTIFICATES = $True
$global:LINA_DEBUG_MODE=$False


# ============ Internal Variables =============== #
$global:LoggedSession = $null
$global:GLOBAL_LINA_SERVER = $null
$global:LINA_TRANSLATIONS = $null
$global:LINA_VERSION = $null
$global:INT_LINA_API_VERSION = $null

$global:INT_STRATEGIES_MAPPINGS= @{
    Name                        = "Name" ;
    RPOInMinutes                = "ParamSchedule" ;
    RetentionInDays             = "ParamDataAging" ;
    AlertAfterDays              = "ParamAlertTime" ;
    ThroughputLimitKBps         = "ParamThroughputLimit" ;
    AlwaysEncrypt               = "ParamEncryptionActivated" ;
    WanMode                     = "ParamWanActivated" ;
    CompressionAlgo             = "ParamCompression" ;
    ReplicationTargetID         = "ParamRepliTargetID";
    AllowClientRPO              = "ParamAllowClientRPO" ;
    AllowClientRules            = "ParamAllowClientRules" ;
    AllowClientPause            = "ParamAllowClientPause" ;
    AllowClientBoost            = "ParamAllowClientBoost" ;
    AllowClientNetworkParams    = "ParamAllowClientNetworkParams" ;
    AllowWebAccess              = "ParamAllowWebAccess" ;
    QuotaMaxProtSizeMB          = "ParamQuotaMaxProtSize" ;
    QuotaMaxProtObjs            = "ParamQuotaMaxProtObjs" ;
    QuotaMaxHistSizeMB          = "ParamQuotaMaxHistSize" ;
    QuotaMaxHistObjs            = "ParamQuotaMaxHistObjs" ;
    ReplicationTargets          = "RepliTarget" ;
    TenantID                    = "DomainID"
}


function Get-LinaHelp() {
<#
.SYNOPSIS
Opens the HTML help of this module.
.DESCRIPTION
Opens the HTML help file located in the module folder with default browser.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Get-LinaHelp
Opens the HTML help file located in the module folder with default browser.
#>

    $current_modulepath = Split-Path $script:MyInvocation.MyCommand.Path
    Start-Process "$current_modulepath\help.html"
}

function Disable-SslVerification{
    if (-not ([System.Management.Automation.PSTypeName]"TrustEverything").Type)
    {
        Add-Type -TypeDefinition  @"
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public static class TrustEverything
{
    private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain,
        SslPolicyErrors sslPolicyErrors) { return true; }
    public static void SetCallback() { System.Net.ServicePointManager.ServerCertificateValidationCallback = ValidationCallback; }
    public static void UnsetCallback() { System.Net.ServicePointManager.ServerCertificateValidationCallback = null; }
}
"@

    }
    [TrustEverything]::SetCallback()
}
function Enable-SslVerification {
    if (([System.Management.Automation.PSTypeName]"TrustEverything").Type)
    {
        [TrustEverything]::UnsetCallback()
    }
}


function CallAPI(){
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True,Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,
        [Parameter(Mandatory=$False,Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$ContentType,
        [Parameter(Mandatory=$False,Position=2)]
        [ValidateNotNullOrEmpty()]
        [string]$Body,                  
        [Parameter()]
        [switch]$FirstConnection
    )
    
    if ($Path -like "*.json*") {
        $ContentType="application/json; charset=utf-8"
    }else {
        # most content is XML so this is default
        $ContentType="application/xml; charset=utf-8"
    }

    if ($FirstConnection) {
        if ( $GLOBAL_IGNORE_CERTIFICATES ) {
            # Disable Certificate checking
            if ($PSVersionTable.PSVersion.Major -lt 6) {
                # Pre-PowerShell 6.0
                # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
                Disable-SslVerification
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -DisableKeepAlive
            }else {
                # PowerShell Core (>= 6.0)
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -SkipCertificateCheck
            }
        }else {
            # Enable certificate checking
            if ($PSVersionTable.PSVersion.Major -lt 6) {
                # Pre-PowerShell 6.0
                # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -DisableKeepAlive
            }else {
                # PowerShell Core (>= 6.0)
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession
            }
        }

        Set-Variable -name LoggedSession -Scope global -Value $Currentsession

    } else {
        # Next connections reuse the Session Cookie set globally
        if ($PSVersionTable.PSVersion.Major -ge 6 -AND $GLOBAL_IGNORE_CERTIFICATES) {
            <# Disable Certificate checking for WebRequest (PowerShell >= 6.0) #>
            $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -ContentType $ContentType -Method "Post" -Body $Body -WebSession $LoggedSession -SkipCertificateCheck
        }else {
            # Same request if enabled or not. Checking is disabled globally on PowerShell < 6.0
            # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
            $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -ContentType $ContentType -Method "Post" -Body $Body -WebSession $LoggedSession -DisableKeepAlive
        }
    }
    if ($LINA_DEBUG_MODE -AND $Path -notlike "*locale/*.js*") { Write-Host "Call API $Path : $request / Body : $Body" }
    Return $request
}
function LinaToLocalTime ($lina_time=0) {
    <# Lina Times are UTC based ? Not GMT ?#>
    if (!$lina_time -OR $lina_time -eq 0 ) {
        Return $null
    }

    $date=(Get-Date 01.01.1970)+([System.TimeSpan]::fromseconds($lina_time/1000))
    $oFromTimeZone = [System.TimeZoneInfo]::FindSystemTimeZoneById("UTC")
    $oToTimeZone = [System.TimeZoneInfo]::FindSystemTimeZoneById([System.TimeZoneInfo]::Local.Id)
    $utc = [System.TimeZoneInfo]::ConvertTimeToUtc($date, $oFromTimeZone)
    $newTime = [System.TimeZoneInfo]::ConvertTime($utc,$oToTimeZone)
    return $newTime
}

function HumanFriendlyTimespan ($last_backup_time=0) { 
    if (!$last_backup_time -OR $last_backup_time -eq 0 ) {
        Return $null
    }

    if ((Get-Date $last_backup_time -format "yyyy") -eq 1970) {
        return "Never"
    }

    $since = New-TimeSpan -Start $last_backup_time
    $since_d=[math]::Round($since.TotalDays)
    $since_h=[math]::Round($since.TotalHours)
    $since_m=[math]::Round($since.TotalMinutes)
    $since_s=[math]::Round($since.TotalSeconds)
    
    if ($since_d -ge 1) {
        if ($since_d -gt 1) { $plural="s"}
        return "$since_d day$plural ago"
    }elseif ($since_h -ge 1) {
        if ($since_h -gt 1) { $plural="s"}
        return "$since_h hour$plural ago"
    }elseif ($since_m -ge 1) {
        if ($since_m -gt 1) { $plural="s"}
        return "$since_m minute$plural ago"
    }else {
        if ($since_s -gt 1) { $plural="s"}
        return "$since_s second$plural ago"
    }
}

function TranslateErrorCode() {
    Param(
        [Parameter(Mandatory=$True,Position=0)]
        [string]$LinaError,
        [Parameter(Mandatory=$True,Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$FailedAction
    )

    $ERROR_CODES = @{"0"="OK";"1"="Undefined ID";"2"="Bad syntax (XML)";"3"="Cannot delete referenced object";"4"="Name already exists";"5"="Undefined referenced ID";"6"="Bad parameter";"7"="Cannot modify immutable object";"8"="Bad path syntax / path too long";"9"="Bad extension syntax / extension too long";"10"="Bad Name syntax / name too long";"11"="Buffer too short (internal)";"12"="Unexpected XML tag";"13"="Permission error, no admin session found";"101"="Conversion error (internal)";"102"="Duplicate ID (internal)";"103"="XML error (internal)";"104"="XML data error (internal)";"105"="Memory allocation error (internal)";"107"="Bad data header (internal)";"108"="Bad command (internal)";"199"="Generic internal";"999"="Unidentified error"}
    $error_clean=$LinaError.Replace("[","").Replace("]","").Trim()
    $translated_error = $ERROR_CODES[$error_clean]

    if ($translated_error) {
        $found_error = $translated_error
    }else {
        $found_error = "Unknown Error # $translated_error"
    }
    Write-Host "ERROR occurred trying to $FailedAction : $found_error / Error $LinaError" -ForegroundColor Red
}

function InitTranslations() {
    Param(
        [Parameter(Mandatory=$True,Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]$Lang
    )   
    $request = CallAPI "/Admin/locale/$lang.js"

    $temp_utf = FixEncoding($request.ToString())
    <#
    if ($PSVersionTable.PSVersion.Major -ge 6) {
        $temp_utf = $request.ToString()
    }else {
        $temp_utf = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($request.ToString()))
    }
    #>



    $temp_split = $temp_utf -split '\n'
    $lines = ($temp_split | Select-String -Pattern "SERVER_NAME_","UNCAT_AGENTS_LABEL" ).line
    $translations = @{}
    foreach ($line in $lines) {
        $splitted = $line -split ': "'
        $key = $splitted[0].Trim().Replace('SERVER_NAME_','').Replace("__","_")
        $value = $splitted[1].Replace('",','').Trim()
        $translations.add($key,$value)
    }
    Set-Variable -name LINA_TRANSLATIONS -Scope global -Value $translations
}

function TranslateSystemCategories($ToTranslate=0) {
    if (!$ToTranslate -OR $ToTranslate -eq 0 ) {
        return $null
    }
    $system_categories = @{"256"="Windows";"512"="Linux";"768"="macOS";"272"="Windows Server";"528"="Linux Server";"784"="macOS Server"}
    $translated=$system_categories[$ToTranslate]
    if ($translated) {
        return $translated
    }else {
        return $ToTranslate
    }
}
function Translate($ToTranslate=0,$FixEncoding=1) {
    # FixEncoding = 0 is used for JSON returns that ara already in UTF-8 and do not need fixing
    if (!$ToTranslate -OR $ToTranslate -eq 0 ) {
        return $null
    }

    $ToTranslate = $ToTranslate.TrimStart("_").TrimEnd("_")

    if ($ToTranslate -eq "UNCAT") { $ToTranslate = "UNCAT_AGENTS_LABEL"; }
    $translated = $LINA_TRANSLATIONS[$ToTranslate.Replace("__","_")]

    if ($translated) {
        return $translated
    }else {
        # No translation available. Use original text.
        if ($ToTranslate -AND $FixEncoding -eq 1) {
            return [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($ToTranslate))
            #return FixEncoding($ToTranslate)
        }elseif ($ToTranslate -AND $FixEncoding -eq 0) {
            return FixEncoding($ToTranslate)
        }else {
            return $null
        }
    }
}

function FixEncoding($text) {
    if ($PSVersionTable.PSVersion.Major -ge 6) {
        Return $text
    }else {
        Return [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($text))
    }
    
}

function LinaTimestamp {
    return [math]::Round((New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get-Date)).TotalSeconds*1000)
}
function Connect-LinaServer {
<#
.SYNOPSIS
Connects to an Atempo Lina server.
.DESCRIPTION
Connects to an Atempo Lina server 5.0+ using superadmin credentials.
Select your locale using the -Locale switch. Locale is used only to display the default elements (strategies, protections etc) with localized names.
Please note : there is no support for multiple connections to different or same server.
By default certificate checking is not enabled. If you want to enable it use $GLOBAL_IGNORE_CERTIFICATES= $False
.INPUTS
None
.OUTPUTS
None
.PARAMETER Server
Specify the URL of the Lina server you want to connect to.
You need to specify the protocol and ports. For example : https://10.0.0.1:8181 or http://10.0.0.1:8181
.PARAMETER User
Specify the user name you want to use for authenticating with the server. It must be superadmin.
.PARAMETER Password
Specify the password
.PARAMETER Locale
Specify the language that will be used for default elements names.
Optional. Default value is English.
Possible values are : "en","fr","es","de"
.PARAMETER Tenant
Select a specific tenant for actions (listing, creation will be limited to this tenant)
Optional. Default value is -1 (All tenant / Global view)
.EXAMPLE
Connect-LinaServer -Server "https://mylinaserver.domain.com:8181" -User "superadmin" -Password "mypassword"
Connection to mylinaserver.domain.com using default locale and global view (no tenant).
.EXAMPLE
Connect-LinaServer -Server "https://mylinaserver.domain.com:8181" -User "superadmin" -Password "mypassword" -Locale "fr" -Tenant "MyTenant"
Connection to mylinaserver.domain.com using french language and filtered on the tenant MyTenant.
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$True,Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]$Server,
        [Parameter(Mandatory=$True,Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$User,
        [Parameter(Mandatory=$True,Position=2)]
        [ValidateNotNullOrEmpty()]
        [string]$Password,
        [Parameter(Mandatory=$false,Position=3)]
        [ValidateSet("en","fr","es","de")] 
        [string]$Locale="en",
        [Parameter(Mandatory=$false,Position=4)]
        [ValidateNotNullOrEmpty()]
        [string]$Tenant
    )
    if ($LINA_DEBUG_MODE -eq $true) {$PSDefaultParameterValues['*:Verbose'] = $true}

    Set-Variable -name GLOBAL_LINA_SERVER -Scope global -Value $Server
    $userenc=[System.Web.HttpUtility]::UrlEncode($User)
    $passenc=[System.Web.HttpUtility]::UrlEncode($Password)
    Write-Host "Connecting to Lina server $GLOBAL_LINA_SERVER : " -NoNewline
    $request = CallAPI -Path "/ADE/login.html?user=$userenc&password=$passenc" -FirstConnection
    if ([string]$request -like "*- OK*") {
        
        $global:LINA_VERSION = (Get-LinaGlobalStats).LinaVersion
        if ($LINA_VERSION.Major -eq 5) {
                if ($LINA_VERSION.Minor -eq 1) {
                    $global:INT_LINA_API_VERSION=51
                }else {
                    $global:INT_LINA_API_VERSION=50
                }
                InitTranslations($Locale)
                Write-Host "Successfully connected (Lina version $LINA_VERSION)"
                Write-Verbose "API version used $INT_LINA_API_VERSION"
        }else {
            Write-Host "Error : cannot connect to server with Lina version below 5.0"
            Disconnect-LinaServer
        }
       
    } else {
        Write-Error "Error occurred trying to connect : \"TranslateErrorCode($request)"\"
    }    
}

function Disconnect-LinaServer {
<#
.SYNOPSIS
Disconnects from an Atempo Lina server.
.DESCRIPTION
Disconnects current session.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Disconnect-LinaServer
Disconnects current session.
#>

    Write-Host "Disconnecting from Lina server $GLOBAL_LINA_SERVER : " -NoNewline
    $request = CallAPI -Path "/ADE/logout.json"
    if ([string]$request -like "*- OK*" -OR [string]$request -like "*ASM_OK*") {
        Write-Host "Successfully disconnected."
    } else {
        TranslateErrorCode -LinaError $request -FailedAction "Disconnecting from server"
    }
}

function Get-LinaGlobalStats {
<#
.SYNOPSIS
Retrieves the global stats and configuration of the current Lina server.
.DESCRIPTION
Retrieves the global stats and configuration of the current Lina server such as the server version deduplication ratio, volume stored etc
.INPUTS
None
.OUTPUTS
LinaStats Object
.EXAMPLE
Get-LinaGlobalStats
Retrieve global infos of current Lina Server
.EXAMPLE
$lina_version = (Get-LinaGlobalStats).ALNVersion
Get current installed version of Lina sofware.
#>

    Write-Verbose "Getting global stats"
    $request = CallAPI -Path "/info.xml"
    $stats = ([xml]$request).infolist

    if ($stats.aln_version -like "* DEV") {
        $LinaVersion=[version]($stats.aln_version -replace " DEV","")
        $LinaVersionType="Beta"
    }else {
        $LinaVersion=[version]$stats.aln_version
        $LinaVersionType="Stable"
    }

    $LinaStats = [PSCustomObject]@{
        PSTypeName                  = 'LinaStats'
        LinaVersion                 = $LinaVersion
        LinaVersionType             = $LinaVersionType

        PercentDiskUse              = $stats.disk_use
        DedupRatio                  = $stats.dedup_ratio
        CompressionRatio            = $stats.cpr_ratio
        DiskAvailableGB             = [math]::Round(($stats.disk_avail)/(1024*1024*1024),2)

        DiskSavingGB                = [math]::Round(($stats.vol_protected-$stats.vol_stored)/(1024*1024*1024),1)
        VolumeProtectedGB           = [math]::Round(($stats.vol_protected)/(1024*1024*1024),1)
        VolumeRestoredGB            = [math]::Round(($stats.vol_restored)/(1024*1024*1024),1)
        VolumeStoredGB              = [math]::Round(($stats.vol_stored)/(1024*1024*1024),1)

        VolumeProtectedMB           = [math]::Round(($stats.vol_protected)/(1024*1024))
        VolumeRestoredMB            = [math]::Round(($stats.vol_restored)/(1024*1024))
        VolumeStoredMB              = [math]::Round(($stats.vol_stored)/(1024*1024))
    }
    Return $LinaStats
}

function Get-LinaAgent {
<#
.SYNOPSIS
Retrieves the agents on a Lina server.
.DESCRIPTION
Retrieves the agents on a Lina server. Returns a set of agents that correspond to the filter criteria provided
.INPUTS
None
.OUTPUTS
Array of LinaAgent Objects
.PARAMETER Name
Name of the agent(s) to retrieve. Wilcards are allowed. For example : *DESKTOP*
.PARAMETER ID
ID of the agent to retrieve. For example : 134.
.EXAMPLE
Get-LinaAgent -Name "TEST"
Retrieves the agent named "TEST" and display its properties.
.EXAMPLE
$agents_desktop = Get-LinaAgent -Name "*DESKTOP*"
Retrieves all the agents with DESKTOP inside their names. The list returned can be passed to other functions.
.EXAMPLE
Get-LinaAgent | Where {$_.LastSyncTime -le ((Get-Date).AddDays(-14)) } | Select Name,Email,LastBackup,LastSyncTime | ft
Retrieves all the agents without backups since last 14 days.
.EXAMPLE
Get-LinaAgent -Name "TEST" | Remove-LinaAgent
Will get the agent named TEST then pass it to the next command for deletion.
#>

    [cmdletbinding(DefaultParameterSetName="ByName")]
    Param(
        [Parameter(ParameterSetName="ByName",Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName="ByID")]
        [ValidateRange(0,2147483647)]
        [int]$ID
    )
    Write-Verbose "Getting list of agents"
    $timestamp = LinaTimestamp
    $Tenants = Get-LinaTenant
    $LinaItems = @()

    if ($global:INT_LINA_API_VERSION -le 50 ) {
        
        $agentstats = Get-LinaAgentStats

        # Using API 5.0 (XML and dissociated agent and AgentStats)
        $request = CallAPI -Path "/lst_clients.xml?timestamp=$timestamp&type=agent"
        $Items = ([xml]$request).HNConfig.ClientArray.Client

        foreach ($Item in $Items) {
            <# Filtering on ID or Name (only if provided) #>
            $Name_encoded = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($Item.Name))
            if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR $Name_encoded -like "$Name" -OR $Item.ID -eq $ID)) {
                $current_agentstat =  $agentstats | where-object {$_.ID -eq $Item.ID}

                $CurrentLinaItem = [PSCustomObject]@{
                    PSTypeName                  = 'LinaAgent'
                    Name                        = $Name_encoded
                    ID                          = $Item.ID
                    Tenant                      = $Tenants | where-object {$_.ID -eq $Item.DomainID} | Select-Object -ExpandProperty Name
                    AgentGroup                  = Translate($Item.AttachementArray.Attachement.Group.Name)
                    Strategy                    = Translate($Item.ServiceProfile.Name)
                    Protection                  = Translate($Item.DataProfile.Name)

                    ComputerName                = $current_agentstat.ComputerName
                    System                      = $current_agentstat.System
                    SystemCategory              = TranslateSystemCategories($current_agentstat.SystemCategory)
                    AgentVersion                = $current_agentstat.AgentVersion
                    LastLogon                   = $current_agentstat.LastLogon
                    Email                       = $current_agentstat.Email
                    CdpType                     = $current_agentstat.CdpType
                    Alert                       = $current_agentstat.Alert

                    # LastBackup = LastSync. LastCompleted session update even when there are some errors
                    LastBackup                  = $current_agentstat.LastBackup

                    LastSessionStart            = $current_agentstat.LastSessionStart
                    LastSessionComplete         = $current_agentstat.LastSessionComplete
                    LastConnection              = $current_agentstat.LastConnection
                    LastSync                    = $current_agentstat.LastSync

                    LastStartedSession          = $current_agentstat.LastStartedSession
                    LastCompletedSession        = $current_agentstat.LastCompletedSession
                    LastConnectionTime          = $current_agentstat.LastConnectionTime
                    LastSyncTime                = $current_agentstat.LastSyncTime

                    TenantID                    = $Item.DomainID
                    AgentGroupID                = $Item.AttachementArray.Attachement.Group.ID
                    StrategyID                  = $Item.ServiceProfile.ID
                    ProtectionID                = $Item.DataProfile.ID
                    
                    StrategyInternalName        = $Item.ServiceProfile.Name
                    ProtectionInternalName      = $Item.DataProfile.Name

                }
                $LinaItems += $CurrentLinaItem
            }
        }
    } else {
        
        # Using API 5.1+ (JSON containing agent and agentStats)
        $request = CallAPI -Path "/stats_clients.json?timestamp=$timestamp"
        $Items = $request.ClientArray

            foreach ($Item in $Items) {
                #$Name_encoded = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($Item.AdminInfos.Name))
                $Name_encoded = FixEncoding($Item.AdminInfos.Name)
                <# Filtering on ID or Name (only if provided) #>
                if ((!$Name -AND !$ID) -OR ( $Item.AdminInfos.Name -like "$Name" -OR $Name_encoded -like "$Name" -OR $Item.AdminInfos.ID -eq $ID)) {


                    # Avoid divisions by zero
                    if ($Item.AdminInfos.Quotas.MaxProtSize -gt 0) { $MaxProtSize=(($Item.ServerInfos.Backup.ProtSize)/($Item.AdminInfos.Quotas.MaxProtSize))*100 } else { $MaxProtSize=$null }
                    if ($Item.AdminInfos.Quotas.MaxHistSize -gt 0) { $MaxHistSize=(($Item.ServerInfos.Backup.HistSize)/($Item.AdminInfos.Quotas.MaxHistSize))*100 } else { $MaxHistSize=$null }
                    if ($Item.AdminInfos.Quotas.MaxProtObjs -gt 0) { $MaxProtObjs=(($Item.ServerInfos.Backup.ProtObjs)/($Item.AdminInfos.Quotas.MaxProtObjs))*100 } else { $MaxProtObjs=$null }
                    if ($Item.AdminInfos.Quotas.MaxHistObjs -gt 0) { $MaxHistObjs=(($Item.ServerInfos.Backup.HistObjs)/($Item.AdminInfos.Quotas.MaxHistObjs))*100 } else { $MaxHistObjs=$null }
                    
                    $CurrentLinaItem = [PSCustomObject]@{
                        Name                        = $Name_encoded
                        ID                          = $Item.AdminInfos.ID
                        Tenant                      = $Tenants | where-object {$_.ID -eq $Item.AdminInfos.DomainID} | Select-Object -ExpandProperty Name
                        AgentGroup                  = Translate $Item.AdminInfos.AttachementArray.Group.Name 0 #Param 0 means encoding is already OK (UTF-8 because of JSON)
                        Strategy                    = Translate $Item.AdminInfos.ServiceProfile.Name 0 #Param 0 means encoding is already OK (UTF-8 because of JSON)
                        Protection                  = Translate $Item.AdminInfos.DataProfile.Name 0 #Param 0 means encoding is already OK (UTF-8 because of JSON)

                        ComputerName                = $Item.AgentInfos.ComputerName
                        System                      = $Item.AgentInfos.System
                        SystemCategory              = TranslateSystemCategories($Item.AgentInfos.SystemCategory)
                        AgentVersion                = $Item.AgentInfos.AgentVersion
                        LastLogon                   = $Item.AgentInfos.LastLogon
                        Email                       = $Item.AgentInfos.Email
                        CdpType                     = $Item.AgentInfos.CdpType
                        Alert                       = $Item.ServerInfos.Alerts.Alert
                        
                        ProtectedSizeMB             = [math]::Round(($Item.ServerInfos.Backup.ProtSize)/(1024*1024))
                        HistorySizeMB               = [math]::Round(($Item.ServerInfos.Backup.HistSize)/(1024*1024))
                        ProtectedObjects            = $Item.ServerInfos.Backup.ProtObjs
                        HistoryObjects              = $Item.ServerInfos.Backup.HistObjs

                        QuotaMaxProtSize            = $Item.AdminInfos.Quotas.MaxProtSize
                        QuotaMaxHistSize            = $Item.AdminInfos.Quotas.MaxHistSize
                        QuotaMaxProtObjs            = $Item.AdminInfos.Quotas.MaxProtObjs
                        QuotaMaxHistObjs            = $Item.AdminInfos.Quotas.MaxHistObjs

                        QuotaPercentUsedProtSize          =  [math]::Round($MaxProtSize)
                        QuotaPercentUsedHistSize          =  [math]::Round($MaxHistSize)
                        QuotaPercentUsedProtObjs          =  [math]::Round($MaxProtObjs)
                        QuotaPercentUsedHistObjs          =  [math]::Round($MaxHistObjs)
                        
                        # LastBackup = LastSync. LastCompleted session update even when there are some errors
                        LastBackup                  = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.SyncTime)

                        LastSessionStart            = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.LastStartedSession)
                        LastSessionComplete         = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.LastCompletedSession)
                        LastConnection              = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.LastConnectionTime)
                        LastSync                    = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.SyncTime)

                        LastStartedSession          = LinaToLocalTime $Item.NetworkInfos.LastStartedSession
                        LastCompletedSession        = LinaToLocalTime $Item.NetworkInfos.LastCompletedSession
                        LastConnectionTime          = LinaToLocalTime $Item.NetworkInfos.LastConnectionTime
                        LastSyncTime                = LinaToLocalTime $Item.NetworkInfos.SyncTime

                        TenantID                    = $Item.AdminInfos.DomainID
                        AgentGroupID                = $Item.AdminInfos.AttachementArray.Group.ID
                        StrategyID                  = $Item.AdminInfos.ServiceProfile.ID
                        ProtectionID                = $Item.AdminInfos.DataProfile.ID
                        
                        StrategyInternalName        = $Item.AdminInfos.ServiceProfile.Name
                        ProtectionInternalName      = $Item.AdminInfos.DataProfile.Name  
                    }
                    $LinaItems += $CurrentLinaItem
                }   
            }    
    
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host "No agent found."
    }
    Return $LinaItems
}

function Get-LinaStrategy {
<#
.SYNOPSIS
Retrieves the strategies on a Lina server.
.DESCRIPTION
Retrieves the strategies on a Lina server. Returns a set of strategies that correspond to the filter criteria provided.
If TenantID = 0, it is a global strategy across tenants.
.INPUTS
None
.OUTPUTS
Array of LinaStrategy Objects
.PARAMETER Name
Optional : Name of the strategy to retrieve. Wilcards are allowed. For example : *STRAT*
.PARAMETER ID
Optional : ID of the strategy to retrieve.
.EXAMPLE
Get-LinaStrategy
Retrieves all the strategies.
.EXAMPLE
Get-LinaStrategy -Name "*STRAT*"
Retrieves all the strategies with STRAT inside their names.
.EXAMPLE
Get-LinaStrategy -Name "*STRAT*" | Select-Object -Property Name,RPOInMinutes
Displays the list of strategies and their configured RPO in minutes.
#>

    [cmdletbinding(DefaultParameterSetName="ByName")]
    Param(
        [Parameter(ParameterSetName="ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName="ByID")]
        [ValidateRange(0,2147483647)]
        [int]$ID
    )    
    Write-Verbose "Getting list of strategies"
    $timestamp = LinaTimestamp
    $request = CallAPI -Path "/lst_serviceprofiles.xml?timestamp=$timestamp"
    $Items = ([xml]$request).HNConfig.ServiceProfileArray.ServiceProfile
    $Tenants = LinaTenantShort
    $LinaItems = @()
    foreach ($Item in $Items) {
        $translated_name = Translate($Item.Name)
        if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR  $Item.ID -eq $ID -OR $translated_name -like "$Name") ) {        
            
            if ([int]$Item.RepliTarget.ID -eq 0) {
                $replitarget = "None"
            }else {
                $replitarget = $Item.RepliTarget.Name
            }
            
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName                  = 'LinaStrategy'
                Name                        = $translated_name
                ID                          = $Item.ID

                Tenant                      = ($Tenants | where-object {$_.ID -eq $Item.DomainID}).Name
                RPOInMinutes                = ($Item.ParamSchedule/60)
                RetentionInDays             = $Item.ParamDataAging
                AlertAfterDays              = ($Item.ParamAlertTime/(24*3600))
                ThroughputLimitKBps         = $Item.ParamThroughputLimit
                ReplicationTarget           = $replitarget
                AlwaysEncrypt               = ![bool][int]$Item.ParamEncryptionActivated
                WanMode                     = [bool][int]$Item.ParamWanActivated
                CompressionAlgo             = $Item.ParamCompression
                AllowClientRPO              = [bool][int]$Item.ParamAllowClientRPO
                AllowClientRules            = [bool][int]$Item.ParamAllowClientRules
                AllowClientPause            = [bool][int]$Item.ParamAllowClientPause
                AllowClientBoost            = [bool][int]$Item.ParamAllowClientBoost
                AllowClientNetworkParams    = [bool][int]$Item.ParamAllowClientNetworkParams
                AllowWebAccess              = [bool][int]$Item.ParamAllowWebAccess
                QuotaMaxProtSizeMB          = [math]::Round(($Item.ParamQuotaMaxProtSize)/(1024*1024))
                QuotaMaxProtObjs            = $Item.ParamQuotaMaxProtObjs
                QuotaMaxHistSizeMB          = [math]::Round(($Item.ParamQuotaMaxHistSize)/(1024*1024))
                QuotaMaxHistObjs            = $Item.ParamQuotaMaxHistObjs
                ReplicationTargetID         = $Item.RepliTarget.ID
                
                TenantID                    = $Item.DomainID
                InternalName                = $Item.Name
            }
            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host "No strategy found."
    }
    Return $LinaItems
}

function Get-LinaProtection {
    <#
    .SYNOPSIS
    Retrieves the protection policies on a Lina server.
    .DESCRIPTION
    Retrieves the protection policies on a Lina server. Returns a set of protections that correspond to the filter criteria provided.
    .INPUTS
    None
    .OUTPUTS
    Array of LinaProtection Objects
    .PARAMETER Name
    Optional : Name of the protection to retrieve. Wilcards are allowed. For example : *PROT*
    .PARAMETER ID
    Optional : ID of the protection to retrieve.
    .PARAMETER ProtectionZone
    Optional : Filter by Protection zone name used in subrules. No wildcard allowed.
    .PARAMETER ProtectionRule
    Optional : Filter by Protection rule name used in subrules. No wildcard allowed.
    .EXAMPLE
    Get-LinaProtection
    Retrieves all the protections.
    .EXAMPLE
    Get-LinaProtection -Name "*PROT*"
    Retrieves all the protection with PROT inside their names.
    .EXAMPLE
    Get-LinaProtection -ID 15
    Retrieves the protection with ID 15
    .EXAMPLE
    Get-LinaProtection -ProtectionZone "The entire computer"
    Retrieves the protection with a subrule containing the protection zone named "MyProtection"
    .EXAMPLE
    Get-LinaProtection -ProtectionRule "Protect all objects"
    Retrieves the protection with a subrule containing the protection rule named "MyProtection"
    #>

        [cmdletbinding(DefaultParameterSetName="ByName")]
        Param(
            [Parameter(ParameterSetName="ByName")]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
            [Parameter(ParameterSetName="ByID")]
            [ValidateRange(0,2147483647)]
            [int]$ID,
            [Parameter(ParameterSetName="ByProtRule")]
            [ValidateNotNullOrEmpty()]
            [string]$ProtectionRule,
            [Parameter(ParameterSetName="ByProtZone")]
            [ValidateNotNullOrEmpty()]
            [string]$ProtectionZone           
            
        )    
        Write-Verbose "Getting list of protection policies"
        $timestamp = LinaTimestamp
        $request = CallAPI -Path "/lst_dataprofiles.xml?timestamp=$timestamp"
        $Items = ([xml]$request).HNConfig.DataProfileArray.DataProfile
        $Tenants = LinaTenantShort
        $protection_rules = Get-LinaProtectionRule
        $protection_zones = Get-LinaProtectionZone       
        $LinaItems = @()

        if ($ProtectionRule) {
            $protection_rule_id_filter=($protection_rules | Where-Object { $_.Name -eq $ProtectionRule}).ID
            if (!$protection_rule_id_filter) {
                Write-Host "No protection rule found with name $ProtectionRule. Please provide the exact name"
                return
            }
        }else {
            $protection_rule_id_filter=-1
        } 

        if ($ProtectionZone) {
            $protection_zone_id_filter=($protection_zones | Where-Object { $_.Name -eq $ProtectionZone}).ID
            if (!$protection_zone_id_filter) {
                Write-Host "No protection zone found with name $ProtectionZone. Please provide the exact name"
                return
            }            
        }else {
            $protection_zone_id_filter=-1
        }  

        foreach ($Item in $Items) {

            if ((!$Name -AND !$ID -AND !$ProtectionRule -AND !$ProtectionZone ) -OR ( $Item.Name -like "$Name" -OR  $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" -OR  $Item.RuleArray.RulePredef.FilterRuleID -contains $protection_rule_id_filter -OR $Item.RuleArray.RulePredef.PredefinedPathID -contains $protection_zone_id_filter) ) {        
                $CurrentLinaItem = [PSCustomObject]@{
                    PSTypeName                  = 'LinaProtection'
                    Name                        = Translate($Item.Name)
                    ID                          = $Item.ID

                    Tenant                      = ($Tenants | where-object {$_.ID -eq $Item.DomainID}).Name
                    OS                          = $Item.OS
                    BareMetalRestore            = [bool][int]$Item.BMR
                    BuiltIn                     = [bool]($null -eq $Item.CustomRef)
                    TenantID                    = $Item.DomainID
                    InternalName                = $Item.Name
                }
                $i=1
                foreach ($subrule in $Item.RuleArray.RulePredef) {
                    $subrule_protection = $protection_rules | where-object { $_.ID -eq $subrule.FilterRuleID}
                    $subrule_path = $protection_zones | where-object { $_.ID -eq $subrule.PredefinedPathID}

                    $CurrentLinaItem | add-member -membertype noteproperty -name "Rule#$i" -value ($subrule_path.Name+" ( "+$subrule_path.Path+" ) >> "+$subrule_protection.Name)
                    $i++
                }

                $LinaItems += $CurrentLinaItem
            }
        }
        if ($LinaItems.Count -eq 0) {
            Write-Host "No protection found."
        }        
        Return $LinaItems
    }
    


function Get-LinaProtectionZone {
            <#
            .SYNOPSIS
            Retrieves the protection zones on a Lina server.
            .DESCRIPTION
            Retrieves the protection zones on a Lina server. Returns a set of protection zones that correspond to the filter criteria provided.
            .INPUTS
            None
            .OUTPUTS
            Array of LinaProtectionZone Objects
            .PARAMETER Name
            Optional : Name of the protection zone to retrieve. Wilcards are allowed. For example : *Mac*
            .PARAMETER ID
            Optional : ID of the protection zone to retrieve.
            .PARAMETER Path
            Optional : Return protection containing this exact path. Wildcards character (*) will be considered only as stars.
            .PARAMETER MatchingPath
            Optional : Return protection matching this path. Wilcards are allowed.
            .EXAMPLE
            Get-LinaProtectionZone
            Retrieves all the protection zones.
            .EXAMPLE
            Get-LinaProtectionZone -Name "*Mac*"
            Retrieves all the protection zones with Mac inside their names.
            .EXAMPLE
            Get-LinaProtectionZone -Path "\*\Users\*\"
            Retrieves the protection zones matching this exact path.
            .EXAMPLE
            Get-LinaProtectionZone -MatchingPath "\*\Users\*\"
            Retrieves all the protection zones matching this path (stars are considered as wildcards)
            #>

                [cmdletbinding(DefaultParameterSetName="ByName")]
                Param(
                    [Parameter(ParameterSetName="ByName")]
                    [ValidateNotNullOrEmpty()]
                    [string]$Name,
                    [Parameter(ParameterSetName="ByID")]
                    [ValidateRange(0,2147483647)]
                    [int]$ID,
                    [Parameter(ParameterSetName="ByPath")]
                    [ValidateNotNullOrEmpty()]
                    [string]$Path,
                    [Parameter(ParameterSetName="ByMatchingPath")]
                    [ValidateNotNullOrEmpty()]
                    [string]$MatchingPath                   
                )    
                Write-Verbose "Getting list of protection zones"
                $timestamp = LinaTimestamp
                $request = CallAPI -Path "/lst_predefinedpaths.xml?timestamp=$timestamp"
                $Items = ([xml]$request).HNConfig.PredefinedPathArray.PredefinedPath
                $Tenants = LinaTenantShort
                $LinaItems = @()
                foreach ($Item in $Items) {

                    if ((!$Name -AND !$ID -AND !$Path -AND !$MatchingPath) -OR ( $Item.Name -like "$Name" -OR  $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" -OR $Item.Path -eq "$Path" -OR $Item.Path -like "$MatchingPath" ) ) {        
                        $CurrentLinaItem = [PSCustomObject]@{
                            PSTypeName                  = 'LinaProtectionZone'
                            Name                        = Translate($Item.Name)
                            ID                          = $Item.ID
        
                            Tenant                      = ($Tenants | where-object {$_.ID -eq $Item.DomainID}).Name
                            OS                          = $Item.OS
                            Path                        = $Item.Path
                            BuiltIn                     = [bool]($null -eq $Item.CustomRef)

                            TenantID                    = $Item.DomainID
    
                        }
                        $LinaItems += $CurrentLinaItem
                    }
                }
                if ($LinaItems.Count -eq 0) {
                    Write-Host "No protection zone found."
                }        
                Return $LinaItems
            }

function Get-LinaFileCategory {
        <#
        .SYNOPSIS
        Retrieves the file categories on a Lina server.
        .DESCRIPTION
        Retrieves the file categories on a Lina server. Returns a set of file categories that correspond to the filter criteria provided.
        .INPUTS
        None
        .OUTPUTS
        Array of LinaFileCategory Objects
        .PARAMETER Name
        Optional : Name of the file category to retrieve. Wilcards are allowed. For example : *FILECAT*
        .PARAMETER ID
        Optional : ID of the file category to retrieve.
        .EXAMPLE
        Get-LinaFileCategory
        Retrieves all the file categories.
        .EXAMPLE
        Get-LinaProtection -Name "*Text*"
        Retrieves all the file categories with Text inside their names.
        #>

            [cmdletbinding(DefaultParameterSetName="ByName")]
            Param(
                [Parameter(ParameterSetName="ByName")]
                [ValidateNotNullOrEmpty()]
                [string]$Name,
                [Parameter(ParameterSetName="ByID")]
                [ValidateRange(0,2147483647)]
                [int]$ID
            )    
            Write-Verbose "Getting list of file categories"
            $timestamp = LinaTimestamp
            $request = CallAPI -Path "/lst_filetypes.xml?timestamp=$timestamp"
            $Items = ([xml]$request).HNConfig.FileTypeArray.FileType
            $Tenants = LinaTenantShort
            $LinaItems = @()
            foreach ($Item in $Items) {
                if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR  $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" ) ) {        
                    $CurrentLinaItem = [PSCustomObject]@{
                        PSTypeName                  = 'LinaFileCategory'
                        Name                        = Translate($Item.Name)
                        ID                          = $Item.ID
    
                        Tenant                      = ($Tenants | where-object {$_.ID -eq $Item.DomainID}).Name
                        FileExtensions              = $Item.ExtArray.Ext
                        BuiltIn                     = [bool]($null -eq $Item.CustomRef)
    
                        TenantID                    = $Item.DomainID

                    }
                    $LinaItems += $CurrentLinaItem
                }
            }
            if ($LinaItems.Count -eq 0) {
                Write-Host "No file category found."
            }        
            Return $LinaItems
        }

function TranslateBehaviors ($to_translate) {
    switch ( $to_translate ) {
        "INCL" { $result = "Include / No Agent modification allowed" }
        "EXCL" { $result = "Exclude / No Agent modification allowed" }
        "ADVINCL" { $result = "Include / Allow agents modifications" }
        "ADVEXCL" { $result = "Exclude / Allow agents modifications" }
    }
    return $result
}

function Get-LinaProtectionRule {
            <#
            .SYNOPSIS
            Retrieves the protection rules on a Lina server.
            .DESCRIPTION
            Retrieves the protection rules on a Lina server. Returns a set of file categories that correspond to the filter criteria provided.
            .INPUTS
            None
            .OUTPUTS
            Array of LinaProtectionRules Objects
            .PARAMETER Name
            Optional : Name of the protection rule to retrieve. Wilcards are allowed. For example : *FILECAT*
            .PARAMETER ID
            Optional : ID of the protection rule to retrieve.
            .EXAMPLE
            Get-LinaProtectionRule
            Retrieves all the protection rules.
            .EXAMPLE
            Get-LinaProtectionRule -Name "*Office*"
            Retrieves all the protection rules with Office inside their names.
            #>

                [cmdletbinding(DefaultParameterSetName="ByName")]
                Param(
                    [Parameter(ParameterSetName="ByName")]
                    [ValidateNotNullOrEmpty()]
                    [string]$Name,
                    [Parameter(ParameterSetName="ByID")]
                    [ValidateRange(0,2147483647)]
                    [int]$ID
                )    
                Write-Verbose "Getting list of protection rules"
                $timestamp = LinaTimestamp
                $request = CallAPI -Path "/lst_filters.xml?timestamp=$timestamp"
                $Items = ([xml]$request).HNConfig.FilterRuleArray.FilterRule
                $Tenants = LinaTenantShort
                $FileCategories = Get-LinaFileCategory
                $LinaItems = @()
                foreach ($Item in $Items) {
                    if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR  $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" ) ) {        

                        
                        $CurrentLinaItem = [PSCustomObject]@{
                            PSTypeName                  = 'LinaProtectionRule'
                            Name                        = Translate($Item.Name)
                            ID                          = $Item.ID
                            Tenant                              = ($Tenants | where-object {$_.ID -eq $Item.DomainID}).Name
                            DefaultBehavior                     = TranslateBehaviors($Item.DefaultBehavior)
                            BuiltIn                             = [bool]($null -eq $Item.CustomRef)
                            TenantID                            = $Item.DomainID
    
                        }
                        $i=1
                        foreach ($subrule in $Item.SubRuleArray.SubRule) {
                            $filecat_name = ($FileCategories | where-object { $_.ID -eq $subrule.FileType.ID}).Name
                            $rule_behavior = TranslateBehaviors($subrule.Behavior)

                            $CurrentLinaItem | add-member -membertype noteproperty -name "Rule#$i" -value ($rule_behavior+' : '+$filecat_name)
                            $i++
                        }

                        $LinaItems += $CurrentLinaItem
                    }
                }
                if ($LinaItems.Count -eq 0) {
                    Write-Host "No protection rule found."
                }        
                Return $LinaItems
            }
function Get-LinaTenant {
<#
.SYNOPSIS
Retrieves the tenants (entities) on a Lina server.
.DESCRIPTION
Retrieves the tenants (entities) on a Lina server. Returns a set of tenants that correspond to the filter criteria provided.
.INPUTS
None
.OUTPUTS
Array of LinaTenant Objects
.PARAMETER Name
Optional : Name of the tenant to retrieve. Wilcards are allowed. For example : *Tenant*
.PARAMETER ID
Optional : ID of the tenant to retrieve.
.PARAMETER Default
Get the default tenant if any
.EXAMPLE
Get-LinaTenant
Retrieve all tenants
.EXAMPLE
Get-LinaTenant -Name "BaasCustomer1"
Retrieve Tenant named BaasCustomer1
.EXAMPLE
Get-LinaTenant -ID 2
Retrieve Tenant with ID 2
.EXAMPLE
Get-LinaTenant -Default
Retrieve the default tenant (if any)
#>

    [cmdletbinding(DefaultParameterSetName="ByName")]
    Param(
        [Parameter(Mandatory=$false,Position=0,ParameterSetName="ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(Mandatory=$True,ParameterSetName="ByID")]
        [ValidateRange(0,2147483647)]
        [int]$ID,
        [Parameter(Mandatory=$True,ParameterSetName="Default")]
        [switch]$Default
    )
    $timestamp = LinaTimestamp
    $request = CallAPI -Path "/ADE/check_session.xml?timestamp=$timestamp"
    $current_tenant_id = [int](([xml]$request).session.effective_domain_id)

    if ($current_tenant_id -gt 1) {
        Write-Host "WARNING : to list tenants you should be logged as superadmin and be in the global view (using Set-LinaCurrentTenant -All). Current tenant ID is : $current_tenant_id"
        return ""
    }

    Write-Verbose "Getting list of tenants"
    $timestamp = LinaTimestamp
    $request = CallAPI -Path "/ADM/list_domain.json?timestamp=$timestamp"
    $Items = $request.domains
  
    $request2 = CallAPI -Path "/lst_globals.xml?domain_id=0"
    $prefs_global = ([xml]$request2).HNConfig.Globals
    $prefs_tenants = ([xml]$request2).HNConfig.Globals.DomainGlobalsArray.DomainGlobals

    
    $cache_strategies=Get-LinaStrategy
    $cache_protections=Get-LinaProtection


    $LinaItems = @()
    foreach ($Item in $Items) {
        $name_encoded = FixEncoding($Item.name)
        if ((!$Name -AND !$ID) -OR $Item.name -like "$Name" -OR $name_encoded -like "$Name" -OR $Item.id -eq $ID ) {
            if ($prefs_tenants.DomainID -contains [int]$Item.id) {
                $DefaultStrategyID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).WinDefaultServiceProfileID
                $WindowsDefaultProtectionID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).WinDefaultDataProfileID
                $MacDefaultProtectionID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).MacDefaultDataProfileID
                $LinuxDefaultProtectionID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).LinuxDefaultDataProfileID
            }else {
                $DefaultStrategyID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).WinDefaultServiceProfileID
                $WindowsDefaultProtectionID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).WinDefaultDataProfileID
                $MacDefaultProtectionID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).MacDefaultDataProfileID
                $LinuxDefaultProtectionID=($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id}).LinuxDefaultDataProfileID
            }
            # These ones are always shared across tenants
            $AutoCreateClients = [bool][int]$prefs_global.AutoCreateClients

            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName                  = 'LinaTenant'
                Name                        = $name_encoded
                ID                          = $Item.id

                # Had to reencode to UTF-8 because of a bug or maybe to bad return type
                Comment                     = FixEncoding($Item.comment)
                IsDefault                   = [bool][int]$Item.def_domain

                AutoCreateClients             = $AutoCreateClients
                WindowsDefaultProtection    = ($cache_protections | Where-Object {$WindowsDefaultProtectionID -eq $_.ID}).Name
                MacDefaultProtection        = ($cache_protections | Where-Object {$MacDefaultProtectionID -eq $_.ID}).Name
                LinuxDefaultProtection      = ($cache_protections | Where-Object {$LinuxDefaultProtectionID -eq $_.ID}).Name
                DefaultStrategy             = ($cache_strategies | Where-Object {$DefaultStrategyID -eq $_.ID}).Name
                
                # Need to check if this one is always global or not
                # WebRestoreEnabled = $tenant_config.WebRestoreEnabled

                WindowsDefaultProtectionID    = $WindowsDefaultProtectionID
                MacDefaultProtectionID        = $MacDefaultProtectionID
                LinuxDefaultProtectionID      = $LinuxDefaultProtectionID   
                DefaultStrategyID             = $DefaultStrategyID

                UUID                        = $Item.uuid
            }
            $LinaItems += $CurrentLinaItem
        }
        
    }

    if ($LinaItems.Count -eq 0) {
        Write-Host "No tenant found."
    }
    
    if ($Default) {
        Return ($LinaItems | Where-Object {$_.IsDefault})
    }else {
        Return $LinaItems
    }
}

function LinaTenantShort {
    <#
    .SYNOPSIS
    Retrieves the tenants (entities) on a Lina server.
    .DESCRIPTION
    Retrieves the tenants (entities) on a Lina server. Returns a set of tenants that correspond to the filter criteria provided.
    .INPUTS
    None
    .OUTPUTS
    Array of LinaTenant Objects
    .PARAMETER Name
    Optional : Name of the tenant to retrieve. Wilcards are allowed. For example : *Tenant*
    .PARAMETER ID
    Optional : ID of the tenant to retrieve.
    .PARAMETER Default
    Get the default tenant if any
    .EXAMPLE
    Get-LinaTenant
    Retrieve all tenants
    .EXAMPLE
    Get-LinaTenant -Name "BaasCustomer1"
    Retrieve Tenant named BaasCustomer1
    .EXAMPLE
    Get-LinaTenant -ID 2
    Retrieve Tenant with ID 2
    .EXAMPLE
    Get-LinaTenant -Default
    Retrieve the default tenant (if any)
    #>

        [cmdletbinding(DefaultParameterSetName="ByName")]
        Param(
            [Parameter(Mandatory=$false,Position=0,ParameterSetName="ByName")]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
            [Parameter(Mandatory=$True,ParameterSetName="ByID")]
            [ValidateRange(-1,2147483647)]
            [int]$ID,
            [Parameter(Mandatory=$True,ParameterSetName="Default")]
            [switch]$Default
    
        )
        Write-Verbose "Getting list of tenants (short)"
        $request = CallAPI -Path "/ADM/list_domain.json?timestamp=$timestamp"
        $Items = $request.domains
        $LinaItems = @()
        foreach ($Item in $Items) {
            $name_encoded = FixEncoding($Item.name)
            if ((!$Name -AND !$ID) -OR $Item.name -like "$Name" -OR $name_encoded -like "$Name" -OR $Item.id -eq $ID ) {
    
                $CurrentLinaItem = [PSCustomObject]@{
                    PSTypeName                  = 'LinaTenant'
                    Name                        = FixEncoding($Item.name)
                    ID                          = $Item.id
    
                    # Had to reencode to UTF-8 because of a bug or maybe to bad return type
                    Comment                     = FixEncoding($Item.comment)
                    IsDefault                   = [bool][int]$Item.def_domain
    
                    UUID                        = $Item.uuid
                }
                $LinaItems += $CurrentLinaItem
            }
            
        }
        if ($LinaItems.Count -eq 0) {
            Write-Host "No tenant found."
        }
        if ($Default) {
            Return ($LinaItems | Where-Object {$_.IsDefault})
        }else {
            Return $LinaItems
        }
    }


function Get-LinaAgentStats {
<#
.SYNOPSIS
Retrieves the agents statistics and advanced information on a Lina server.
.DESCRIPTION
Retrieves the agents statistics and advanced information on a Lina server. Statistics are only available for agent that have been online at least one time.
.INPUTS
None
.OUTPUTS
Array of LinaAgentStats Objects
.PARAMETER Name
Name of the agent(s) to retrieve. Wilcards are allowed. For example : *DESKTOP*
.PARAMETER ID
ID of the agent to retrieve. For example : 134.
.EXAMPLE
Get-LinaAgentStats
Retrieve all agent stats
.EXAMPLE
Get-LinaAgentStats | where {$_.LastLogon -like "*mike*"}
Retrieve agent info on agents where mike is logged in.
.EXAMPLE
Get-LinaAgentStats | where {$_.LastCompletedSession -le (Get-Date).AddDays(-7)}
Retrieve agent info on agents with no completed backups in last 7 days
#>

    [cmdletbinding(DefaultParameterSetName="ByName")]
    Param(
        [Parameter(ValueFromPipeline)]
        [pscustomobject]$lina_agent,
        [Parameter(ParameterSetName="ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName="ByID")]
        [ValidateRange(0,2147483647)]
        [int]$ID
    )
    if ($global:INT_LINA_API_VERSION -ge 51) {
        Write-Host "WARNING : Get-LinaAgentStats is deprecated with Lina 5.1, use Get-LinaAgent instead. Commands have been merged"
        return 
    }
    Write-Verbose "Getting agents Statistics (only available if has been online)"
    $timestamp = LinaTimestamp
    $request = CallAPI -Path "/stats_clients.xml?timestamp=$timestamp"
    $Items = ([xml]$request).Stats.ClientArray.Client
    $LinaItems = @()
    foreach ($Item in $Items) {
        <# Filtering on ID or Name (only if provided) #>
        if ((!$Name -AND !$ID -AND !$lina_agent) -OR ($Item.AdminName -like "$Name" -OR  $Item.AdminID -eq $ID -OR $lina_agent.ID -eq $Item.AdminID ) ) {
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName                  = 'LinaAgentStats'
                Name                        = $Item.AdminName
                ID                          = $Item.AdminID

                ComputerName                = $Item.ComputerName
                System                      = $Item.System
                SystemCategory              = TranslateSystemCategories($Item.SystemCategory)
                AgentVersion                = $Item.AgentVersion
                Strategy                    = Translate($Item.AdminServiceProfileName)
                Protection                  = Translate($Item.AdminDataProfileName)
                LastLogon                   = $Item.LastLogon
                Email                       = $Item.Email
                CdpType                     = $Item.CdpType
                Alert                       = $Item.Alert
                
                LastBackup                  = HumanFriendlyTimeSpan (LinaToLocalTime $Item.SyncTime)

                LastSessionStart            = HumanFriendlyTimeSpan (LinaToLocalTime $Item.LastStartedSession)
                LastSessionComplete         = HumanFriendlyTimeSpan (LinaToLocalTime $Item.LastCompletedSession)
                LastConnection              = HumanFriendlyTimeSpan (LinaToLocalTime $Item.LastConnectionTime)
                LastSync                    = HumanFriendlyTimeSpan (LinaToLocalTime $Item.SyncTime)

                LastStartedSession          = LinaToLocalTime $Item.LastStartedSession
                LastCompletedSession        = LinaToLocalTime $Item.LastCompletedSession
                LastConnectionTime          = LinaToLocalTime $Item.LastConnectionTime
                LastSyncTime                = LinaToLocalTime $Item.SyncTime
                
                StrategyInternalName           = $Item.AdminServiceProfileName
                ProtectionInternalName         = $Item.AdminDataProfileName
            }
            $LinaItems += $CurrentLinaItem
        }
        
    }
    if ($LinaItems.Count -eq 0) {
        Write-Verbose "No agent statistics found."
    }        
    Return $LinaItems
}


function Get-LinaAgentGroup {
<#
.SYNOPSIS
Retrieves the agent groups
.DESCRIPTION
Retrieves the agent groups (Main view of agents)
.INPUTS
None
.OUTPUTS
Array of LinaAgentGroup Objects
.PARAMETER Name
Name of the Group to retrieve. Wilcards are allowed. For example : *UNCATEGOR*
.PARAMETER ID
ID of the group retrieve. For example : 4.
.PARAMETER Tenant
Name of the Tenant hosting the groups
Can also be used in conjunction with -Name to filter by name AND Tenant.
Can be useful if multiple groups have the same name in multiple tenants.
.EXAMPLE
Get-LinaAgentGroup
Retrieve all agent groups
.EXAMPLE
Get-LinaAgentGroup -ID 131
Retrieve agent group by it ID. Will return a unique object.
.EXAMPLE
Get-LinaAgentGroup -Name "Group1"
Retrieve agent group by its Name. Wilcards are allowed.
Warning : result may not be unique even without using wildcards. Group names may not be unique across tenants.
.EXAMPLE
Get-LinaAgentGroup -Tenant "MyTenant"
Retrieve agent groups of the Tenant MyTenant
.EXAMPLE
Get-LinaAgentGroup -Name "Group1" -Tenant "BaasCustomer1"
Filter agent group by name and by Tenant MyTenant. If not using wildcards in name, return will be unique.
#>

        [cmdletbinding(DefaultParameterSetName='ByNothing')]
        Param(
            [Parameter(Mandatory=$true,ParameterSetName="ByName")]
            [Parameter(Mandatory=$True,ParameterSetName="ByNameAndTenant")]
            [Parameter(Mandatory=$false,ParameterSetName="ByNothing")]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
            [Parameter(Mandatory=$true,ParameterSetName="ByID")]
            [ValidateRange(0,2147483647)]
            [int]$ID,
            [Parameter(Mandatory=$True,ParameterSetName="ByTenant")]
            [Parameter(Mandatory=$True,ParameterSetName="ByNameAndTenant")]
            [Parameter(Mandatory=$false,ParameterSetName="ByNothing")]
            [ValidateNotNullOrEmpty()]
            [string]$Tenant
        )
        if ($Tenant) {
            $TenantID=(LinaTenantShort -Name $Tenant).ID
            Write-Verbose "Getting agent groups for Tenant $Tenant (ID $TenantID)"
        }else {
            Write-Verbose "Getting agent groups"
        }
        
        $timestamp = LinaTimestamp
        $request = CallAPI -Path "/lst_hierarchies.xml?timestamp=$timestamp"
        $Items = ([xml]$request).HNConfig.HierarchyArray.Hierarchy.GroupArray.Group
        $Tenants = LinaTenantShort
        $LinaItems = @()
        foreach ($Item in $Items) {
            # PreFiltering By Tenant (Groups in tenant 0 are for all Tenants, example : Unsorted agents)
            if (!$TenantID -OR $Item.DomainID -eq $TenantID -OR $Item.DomainID -eq 0 ) {
            <# Filtering on ID or Name (only if provided) #>
                $name_translation = Translate($Item.Name) 
                if ((!$Name -AND !$ID) -OR ( $name_translation -like "$Name" -OR  $Item.ID -eq $ID) ) {
                    $CurrentLinaItem = [PSCustomObject]@{
                        PSTypeName                  = 'LinaAgentGroup'
                        Name                        = $name_translation
                        ID                          = $Item.ID
                        Tenant                      = $Tenants | where-object {$_.ID -eq $Item.DomainID} | Select-Object -ExpandProperty Name
                        NbAgents                    = $Item.NbRefs 

                        InternalName                = $Item.Name
                        TenantID                    = $Item.DomainID
                    }
                    $LinaItems += $CurrentLinaItem
                }
            }
            
        }
        if ($LinaItems.Count -eq 0) {
            Write-Host "No agent group found."
        }            
        Return $LinaItems
}


function New-LinaAgent {
<#
.SYNOPSIS
Creates a new agent.
.DESCRIPTION
Creates a new agent with the provided name.
.INPUTS
None
.OUTPUTS
LinaAgent Object
.PARAMETER Name
Name of the agent to create
.PARAMETER TenantName
Optional : Name of the tenant where the agent should be created.
If not provided, agent will be created in the current tenant.
In global view (no tenant selected), agent will be created in the default tenant.
.EXAMPLE
New-LinaAgent -Name "TEST-CREATE"
Creates a new agent in current tenant.
.EXAMPLE
New-LinaAgent -Name "TEST-CREATE2" -Tenant "MyTenant"
Creates a new agent in tenant MyTenant
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$true,ParameterSetName="Name",Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(Mandatory=$false,ParameterSetName="Name",Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$Tenant        
    )
    Write-Host "Creating a Lina agent named $Name"

    $current_tenant_id = Get-LinaCurrentTenantID 6>$null
    if (!$Tenant) {
        if ($current_tenant_id -le 1) {
            # No current tenant (global view) => using default tenant
            $domain =  LinaTenantShort 6>$null | Where-Object {$_.IsDefault} 
            if ($domain.ID -le 1) {
                # No default Tenant ....
                Write-Host "ERROR : There is no default tenant. Please select a tenant."
                return 
            }
            $domain_id=[int]$domain.ID
            $domain_name=[string]$domain.Name
            Write-Host "No tenant selected, agent will be created in default tenant $domain_name (ID $domain_id)"
        } else {
            # Using Current tenant
            $domain =  Get-LinaTenant -ID (Get-LinaCurrentTenantID) 6>$null
            $domain_id=[int]$domain.ID
            $domain_name=[string]$domain.Name            
            Write-Host "No tenant selected, agent will be created in current tenant $domain_name (ID $domain_id)"
        }
    }else {
        $domain =  LinaTenantShort 6>$null | Where-Object {$_.Name -eq $Tenant}
        $domain_id=[int]$domain.ID
        $domain_name=[string]$domain.Name
        if ( !($domain_id -gt 0) ) {
            Write-Host "Error : No tenant found with name $Tenant. Please input the exact name of the Tenant"
            Return
        }
    }
    
    $void = Set-LinaCurrentTenant -ID $domain_id 6>$null

    $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>ADD</Func><Client><Name>'+$Name+'</Name></Client></HNConfig>'
    $request = CallAPI -Path "/mng_client.html" -ContentType "application/xml" -Body $body
    
    # Setting back the current tenant to what it was before the New-LinaAgent command
   $void =  Set-LinaCurrentTenant -ID $current_tenant_id 6>$null
    if ([string]$request -like "*[0]*") {
        Write-Host "Agent $Name successfully created in Tenant $domain_name"
        $agent_created = Get-LinaAgent -Name $Name
        Return $agent_created
    } else {
        TranslateErrorCode -LinaError $request -FailedAction "create agent $Name"
        Return $null
    }    
}

function New-LinaStrategy {
    <#
    .SYNOPSIS
    Creates a new strategy from scrath or from another one (cloning).
    .DESCRIPTION
    Creates a new strategy with the provided name and configuration.
    Current limitations : two parameters (gateways where encryption is disabled or WAN mode disabled) are not kept when cloning/creating
    .INPUTS
    Single LinaStrategy Object or Array of parameters
    .OUTPUTS
    LinaStrategy Object
    .PARAMETER Name
    Name of the strategy to create
    .PARAMETER Tenant
    Optional : Name of the tenant where the new strategy should be created.
    If not provided, strategy will be created in the same tenant as the original (global if not provided)
    .EXAMPLE
    Get-LinaStrategy -Name "MyStrat" | New-LinaStrategy -Name "Strat2"
    Clone the strategy MyStrat to Strat2 in same tenant and keep all settings
    .EXAMPLE
    Get-LinaStrategy -Name "MyStrat" | New-LinaStrategy -Name "Strat2" -Tenant "Customer2"
    Clone the strategy MyStrat to Strat2 in tenant Customer2 and keep all settings
   .EXAMPLE
    @{ RPOInMinutes = 1 } | New-LinaStrategy -Name "Strat4"
    Creates a new strategy with default parameters except for RPO.
    #>

        [cmdletbinding()]
        Param(
            [Parameter(ValueFromPipeline=$True,ParameterSetName="ByObject")]
            [pscustomobject]$lina_strategy,
            [Parameter(Mandatory=$true,ParameterSetName="ByObject",Position=0)]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
            [Parameter(Mandatory=$false,ParameterSetName="ByObject",Position=1)]
            [ValidateNotNullOrEmpty()]
            [string]$Tenant
        )
        BEGIN{}
        PROCESS{
            $current_tenant_id = Get-LinaCurrentTenantID 6>$null
            
            if ($Tenant) {
                
                $domain =  LinaTenantShort -Name $Tenant 6>$null
                $domain_id=[int]$domain.ID
                if ( !($domain_id -gt 0) ) {
                    Write-Host "Error : No tenant found with name $Tenant. Please input the exact name of the Tenant"
                    Return
                }
                Write-Host "Creating strategy $Name in Tenant $Tenant"
            } elseif ($current_tenant_id -gt 1 ) {
                $tenant_name = (LinaTenantShort -ID $current_tenant_id 6>$null).Name
                Write-Host "Creating strategy $Name in current Tenant $tenant_name (ID = $current_tenant_id)"
            }elseif ($current_tenant_id -le 1) {
                        # No current tenant (global view) => using default tenant instead
                        $domain =  LinaTenantShort 6>$null | Where-Object {$_.IsDefault} 
                        if ($domain.ID -le 1) {
                            # No default Tenant ....
                            Write-Host "ERROR : There is no default tenant. Please select a tenant to create this strategy."
                            return 
                        }
                        $domain_id=[int]$domain.ID
                        $domain_name=[string]$domain.Name
                        Write-Host "No tenant selected, strategy will be created in default tenant $domain_name (ID $domain_id)"
            } 
           
            $xml='<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>ADD</Func><ServiceProfile><Name>'+$Name+'</Name>'
            if ($lina_strategy) {
                if ([string]$lina_strategy.GetType() -eq "System.Management.Automation.PSCustomObject") {
                    $items = $lina_strategy.PSObject.Properties
                }else {
                    $items = $lina_strategy.GetEnumerator()
                }
            }
            foreach ($param in $items) {
                if ([string]$lina_strategy.GetType() -eq "System.Management.Automation.PSCustomObject") {
                    $current_key = $($param.Name)
                    $current_value = $($param.Value)
                }else {
                    $current_key = $param.Key
                    $current_value = $param.Value
                }                
                    switch ($current_key) {
                        RPOInMinutes { $current_value = $current_value*60}
                        AlwaysEncrypt { $current_value = !$current_value}
                        AlertAfterDays { $current_value = $current_value*24*3600}
                        QuotaMaxProtSizeMB { $current_value = $current_value*1024*1024}
                        QuotaMaxHistSizeMB { $current_value = $current_value*1024*1024}
                        TenantID { 
                            # Replacing the Tenant with the one provided
                            if ($domain_id -ge 0) {  $current_value = $domain_id } 
                        }                        
                    }
                    # Convert Boolean to 0 or 1
                    If ($current_value -eq $True -OR $current_value -eq $false) { $current_value = [int]$current_value }
                    
                    # Translate Powershell names to XML tags
                    $int_trans = $INT_STRATEGIES_MAPPINGS[$current_key]

                    if ($current_key -notin "Name","ID","InternalName","ReplicationTarget","Tenant" ) {
                        # Filter out some parameters for creation
                        $xml +="<$int_trans>$current_value</$int_trans>"
                    }
            }
            $xml+="</ServiceProfile></HNConfig>"
            $body ="data=$xml"
            $request = CallAPI -Path "/mng_serviceprofile.html" -ContentType "application/xml" -Body $body
            if ([string]$request -like "*[0]*") {
                Write-Host "Strategy $Name successfully created."
                $strategy_created = Get-LinaStrategy -Name $Name
                
                # Setting back the current tenant to what it was before the New-LinaStrategy command
                if ($Tenant) { Set-LinaCurrentTenant -ID $current_tenant_id 6>$null }
                
                Return $strategy_created
            } else {
                TranslateErrorCode -LinaError $request -FailedAction "create strategy $Name"
                Return $null
            }
        }
        END { }
}



function Set-LinaStrategy {
    <#
    .SYNOPSIS
    Changes the configuration of a strategy
    .DESCRIPTION
    Changes the configuration
    Current limitations : two parameters (gateways where encryption is disabled or WAN mode disabled) cannot be modified
    .INPUTS
    Single LinaStrategy Object or Array of parameters
    .OUTPUTS
    LinaStrategy Object
    .PARAMETER Name
    Name of the strategy to modify
    .PARAMETER Tenant
    Optional : Name of the tenant where the strategy to modify is. Needed if multiple strategies have the same name across tenants
    .EXAMPLE
    @{ RPOInMinutes = 1 } | Set-LinaStrategy -Name "MyStrat"
    Modify strategy MyStrat with a RPO of 1 minute
    .EXAMPLE
    Get-LinaStrategy -Name "MyTemplate" | Set-LinaStrategy -Name "MyStrat"
    Clone parameters from strategy MyTemplate to the strategy "MyStrat"
    #>

        [cmdletbinding()]
        Param(
            [Parameter(ValueFromPipeline=$True,ParameterSetName="ByObject")]
            [pscustomobject]$lina_strategy,
            [Parameter(Mandatory=$true,ParameterSetName="ByObject",Position=0)]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
            [Parameter(Mandatory=$false,ParameterSetName="ByObject",Position=1)]
            [ValidateNotNullOrEmpty()]
            [string]$Tenant
        )
        BEGIN{}
        PROCESS{
            $current_tenant_id = Get-LinaCurrentTenantID 6>$null
            
            if ($Tenant) {
                
                $domain =  LinaTenantShort -Name $Tenant 6>$null
                $domain_id=[int]$domain.ID
                if ( !($domain_id -gt 0) ) {
                    Write-Host "Error : No tenant found with name $Tenant. Please input the exact name of the Tenant"
                    Return
                }
                $void = Set-LinaCurrentTenant -ID $domain_id 6>$null
            } 
            
            if ($lina_strategy) {
                if ([string]$lina_strategy.GetType() -eq "System.Management.Automation.PSCustomObject") {
                    $items = $lina_strategy.PSObject.Properties
                }else {
                    # Get all items in the array
                    $items = $lina_strategy.GetEnumerator()
                }

                    $strategy_id = (Get-LinaStrategy -Name $Name).ID
             

                    if (!$strategy_id -OR $strategy_id -le 0) {
                        Write-Host "ERROR : No Strategy found with this name ($Name)"
                        return 
                    }elseif ($strategy_id.Count -gt 1) {
                        Write-Host "ERROR : Multiple strategies are matching this name ($Name). Please select a tenant to identify which one you want to modify."
                        return 
                    }
                   
            }

            $xml='<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>SET</Func><ServiceProfile><ID>'+$strategy_id+'</ID>'

            foreach ($param in $items) {

                if ([string]$lina_strategy.GetType() -eq "System.Management.Automation.PSCustomObject") {
                    $current_key = $($param.Name)
                    $current_value = $($param.Value)
                }else {
                    $current_key = $param.Key
                    $current_value = $param.Value
                }     
                                
                    switch ($current_key) {
                        RPOInMinutes { $current_value = $current_value*60}
                        AlwaysEncrypt { $current_value = !$current_value}
                        AlertAfterDays { $current_value = $current_value*24*3600}
                        QuotaMaxProtSizeMB { $current_value = $current_value*1024*1024}
                        QuotaMaxHistSizeMB { $current_value = $current_value*1024*1024}
                        TenantID { 
                            # Replacing the Tenant with the one provided
                            if ($domain_id -ge 0) {  $current_value = $domain_id } 
                        }                        
                    }
                    # Convert Boolean to 0 or 1
                    If ($current_value -eq $True -OR $current_value -eq $false) { $current_value = [int]$current_value }
                    
                    # Translate Powershell names to XML tags
                    $int_trans = $INT_STRATEGIES_MAPPINGS[$current_key]

                    if ($current_key -notin "Name","ID","InternalName","ReplicationTarget","Tenant" ) {
                        # Filter out some parameters for creation
                        $xml +="<$int_trans>$current_value</$int_trans>"
                    }
            }
            $xml+="</ServiceProfile></HNConfig>"
            $body ="data=$xml"
            $request = CallAPI -Path "/mng_serviceprofile.html" -ContentType "application/xml" -Body $body
            if ([string]$request -like "*[0]*") {
                Write-Host "Strategy $Name successfully modified."
                $strategy_modified = Get-LinaStrategy -Name $Name
                
                # Setting back the current tenant to what it was before the Set-LinaStrategy command
                if ($Tenant) { Set-LinaCurrentTenant -ID $current_tenant_id 6>$null }
                
                Return $strategy_modified
            } else {
                TranslateErrorCode -LinaError $request -FailedAction "modify strategy $Name"
                Return $null
            }
        }
        END { }
}

function New-LinaAgentGroup {
    <#
    .SYNOPSIS
    Creates a new agent group
    .DESCRIPTION
    Creates a new agent group with the provided name.
    .INPUTS
    None
    .OUTPUTS
    LinaAgentGroup Object
    .PARAMETER Name
    Name of the agent group to create
    .PARAMETER Tenant
    Optional : Name of the tenant where the agent group should be created.
    If not provided, agent group will be created in the current tenant.
    In global view (no tenant selected), agent group will be created in the default tenant.
    .EXAMPLE
    New-LinaAgentGroup -Name "Test-Group"
    Creates a new agent group in current tenant.
    .EXAMPLE
    New-LinaAgent -Name "Test-Group" -Tenant "MyTenant"
    Creates a new agent group in tenant MyTenant
    #>

        [cmdletbinding()]
        Param(
            [Parameter(Mandatory=$true,ParameterSetName="Name",Position=0)]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
            [Parameter(Mandatory=$false,ParameterSetName="Name",Position=1)]
            [ValidateNotNullOrEmpty()]
            [string]$Tenant      
        )
        Write-Host "Creating a Lina agent group named $Name"
        $current_tenant_id = Get-LinaCurrentTenantID 6>$null
        if (!$Tenant) {
            if ($current_tenant_id -le 1) {
                # No current tenant (global view) => using default tenant
                $domain =  LinaTenantShort 6>$null | Where-Object {$_.IsDefault} 
                if ($domain.ID -le 1) {
                    # No default Tenant ....
                    Write-Host "ERROR : There is no default tenant. Please select a tenant."
                    return 
                }
                $domain_id=[int]$domain.ID
                $domain_name=[string]$domain.Name
                Write-Host "No tenant selected, agent group will be created in default tenant $domain_name (ID $domain_id)"
            } else {
                # Using Current tenant
                $domain =  LinaTenantShort -ID (Get-LinaCurrentTenantID) 6>$null
                $domain_id=[int]$domain.ID
                $domain_name=[string]$domain.Name            
                Write-Host "No tenant selected, agent group will be created in current tenant $domain_name (ID $domain_id)"
            }
        }else {
            $domain =  LinaTenantShort 6>$null | Where-Object {$_.Name -eq $Tenant}
            $domain_id=[int]$domain.ID
            $domain_name=[string]$domain.Name
            if ( !($domain_id -gt 0) ) {
                Write-Host "Error : No tenant found with name $Tenant. Please input the exact name of the Tenant"
                Return
            }
        }
        
        $void = Set-LinaCurrentTenant -ID $domain_id 6>$null
        # Needs to clarify if ID of hierarchy can be anything else than 1 or not (<ID>1</ID>)
        $body ='data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>ADDG</Func><Hierarchy><ID>1</ID><Group><Name>'+$Name+'</Name></Group></Hierarchy></HNConfig>'
        $request = CallAPI -Path "/mng_hierarchy.html" -ContentType "application/xml" -Body $body
        
        
        # Setting back the current tenant to what it was before the New-LinaAgentGroup command
        $void = Set-LinaCurrentTenant -ID $current_tenant_id 6>$null
        if ([string]$request -like "*[0]*") {
            Write-Host "Agent group $Name successfully created in Tenant $domain_name (ID $domain_id)"
            $agent_created = Get-LinaAgentGroup -Name $Name -Tenant $domain_name
            Return $agent_created
        } else {
            TranslateErrorCode -LinaError $request -FailedAction "create agent group $Name"
            Return $null
        }    
    }

function New-LinaTenant {
        <#
        .SYNOPSIS
        Creates a new tenant
        .DESCRIPTION
        Creates a new tenant with the provided name. Needs to be logged as superadmin in order to work.
        .INPUTS
        None
        .OUTPUTS
        LinaTenant Object
        .PARAMETER Name
        Name of the tenant to create
        .PARAMETER Comments
        Name of the tenant to create
        .PARAMETER Default
        Optional : If set to true, the new tenant will be the default tenant.
        .EXAMPLE
        New-LinaTenant -Name "MyTenant"
        Creates a new tenant named "Mytenant"
        #>

            [cmdletbinding()]
            Param(
                [Parameter(Mandatory=$true,ParameterSetName="Name",Position=0)]
                [ValidateNotNullOrEmpty()]
                [string]$Name,
                [Parameter(Mandatory=$false,ParameterSetName="Name",Position=1)]
                [ValidateNotNullOrEmpty()]
                [string]$Comments
            )

            $request = CallAPI -Path "/ADE/check_session.xml?timestamp=$timestamp"
            $current_tenant_id = [int](([xml]$request).session.effective_domain_id)
        
            if ($current_tenant_id -gt 1) {
                Write-Host "WARNING : to create a tenant you should be logged as superadmin and be in the global view (using Set-LinaCurrentTenant -All). Current tenant ID is : $current_tenant_id"
                return ""
            }
        
            Write-Host "Creating a new Lina Tenant"

            $name_enc=[System.Web.HttpUtility]::UrlEncode($Name)
            $comment_enc=[System.Web.HttpUtility]::UrlEncode($Comments)
            $request = CallAPI -Path "/ADM/add_domain.json?name=$name_enc&comment=$comment_enc" -ContentType "application/json"

            if ([string]$request -like "*[0]*") {
                $tenant_id = $request.id 
                Write-Host "Tenant $Name successfully created (ID $tenant_id)"
                # Going back to global view to be able to list tenants
                Set-LinaCurrentTenant -All 6>$null
                $tenant_created = Get-LinaTenant -ID $tenant_id
                Return $tenant_created
            } else {
                TranslateErrorCode -LinaError $request -FailedAction "create tenant $Name"
                Set-LinaCurrentTenant -All 6>$null
                Return $null
            }    
        }

function Get-LinaCurrentTenantID {
<#
.SYNOPSIS
Get the current tenant
.DESCRIPTION
Get the current tenant. If current tenant is the Global View (no tenant selected), it will return -1.
.INPUTS
None
.OUTPUTS
Current tenant ID
.EXAMPLE
$current_tenant_id = Get-LinaCurrentTenantID
Get the current tenant ID.
#>

    Write-Verbose "Getting current tenant"
    $timestamp = LinaTimestamp
    $request = CallAPI -Path "/ADE/check_session.xml?timestamp=$timestamp"
    $current_tenant_id = [int](([xml]$request).session.effective_domain_id)
    if ($current_tenant_id -eq -1 -OR $current_tenant_id -eq 1) {
        # Notes on tenant numbers : -1 and 0 => Global View, 1 = Root (first level tenant invisible/ contains superadmin and guest). >1 : Second level tenants
        Write-Host "No current tenant selected (ID = -1 or 1). Global view on all tenants."
        Return -1
    }elseif ($current_tenant_id -gt 1) {
        $current_tenant = LinaTenantShort -ID $current_tenant_id
        $current_tenant_name = $current_tenant.Name 
        Write-Host "Current tenant is $current_tenant_name (ID $current_tenant_id)"
        Return $current_tenant_id
    } else {
        TranslateErrorCode -LinaError $request -FailedAction "get current tenant"
        Return $null
    }
}

function Set-LinaCurrentTenant {
<#
.SYNOPSIS
Set the current tenant
.DESCRIPTION
Set the current tenant. When set, all listings and actions will be executed and filtered inside this tenant.
To set the global view (view across all tenants), use the -All switch.
.INPUTS
LinaTenant Object
.OUTPUTS
None
.PARAMETER ID
ID of the tenant to set
.PARAMETER Name
Name of the tenant to set
.PARAMETER All
Switch to set the global view (view across all tenants). Available for superadmin only.
.EXAMPLE
Get-LinaTenant -Name "MyTenant" | Set-LinaCurrentTenant
Set the current tenant to MyTenant
.EXAMPLE
Set-LinaCurrentTenant -All
Enable global view across all tenants (superadmin only)
.EXAMPLE
Set-LinaCurrentTenant -ID 2
Set the current tenant to Tenant with ID 2
#>

    [cmdletbinding(DefaultParameterSetName="ByID")]
    Param(
        [Parameter(ValueFromPipeline=$True,ParameterSetName="ByObject")]
        [pscustomobject]$lina_tenant,
        [Parameter(Mandatory=$true,ParameterSetName="ByID")]
        [ValidateNotNullOrEmpty()]
        [string]$ID,
        [Parameter(Mandatory=$true,ParameterSetName="ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(Mandatory=$true,ParameterSetName="All")]
        [ValidateNotNullOrEmpty()]
        [switch]$All        
    )
    if ($Name) {
        LinaTenantShort -Name $Name | Set-LinaCurrentTenant
        Return
    }elseif ($ID) {
        LinaTenantShort -ID $ID | Set-LinaCurrentTenant
        Return
    }
    # All tenants is 1 or -1 (no tenant selected, global view)
    if ($All) {
        $ID=1
        $tenant_name="Global View / All Tenants"
    }elseif ($lina_tenant -AND $lina_tenant.ID -ge 1) {
        $ID=$lina_tenant.ID
        $tenant_name=$lina_tenant.Name
    }elseif (!$ID) {
        $ID=1
        $tenant_name="Global View / All Tenants"
    }
    Write-Verbose "Setting current tenant to Tenant ID $ID"
    $request = CallAPI -Path "/ADM/domain_set_current.json?domain_id=$ID"
    if ($request.status -eq 0) {
        Write-Verbose "Current tenant has been set to $tenant_name (ID $ID)"
        Return $ID
    } else {
        TranslateErrorCode -LinaError $request.status -FailedAction "to set current tenant to tenant ID $ID"
        Return
    }
}

function Remove-LinaAgent {
<#
.SYNOPSIS
Deletes a Lina Agent from a Lina Server.
.DESCRIPTION
Deletes a Lina Agent. Unique data will be reclaimed automatically by server after some time (configurable).
.INPUTS
Accept pipelining of LinaAgent objects (from Get-LinaAgent for example)
.OUTPUTS
None
.PARAMETER Name
Name of the agent to delete. Wildcards are accepted (don't forget to enable Bulk mode for actions on multiple agents).
.PARAMETER WhatIf
No actual deletion will happen if set. It will only display what agents would be deleted.
Sometimes also called "Dry Run mode"
.PARAMETER Bulk
Security parameter to enable deletion of multiple agents. If not set only one agent will be deleted.
.EXAMPLE
Remove-LinaAgent -Name "AGENT132"
Real deletion of a single agent by name
.EXAMPLE
Get-LinaAgent -Name "AGENT132" | Remove-LinaAgent
Same example as #1 but using piped commands
.EXAMPLE
Get-LinaAgent -Name "AGENT1*" | Remove-LinaAgent -Bulk -WhatIf
Simulates deletion of a multiple agents by name (WhatIf mode)
.EXAMPLE
Get-LinaAgent -Name "AGENT1*" | Remove-LinaAgent -Bulk
Real deletion of a multiple agents by name
.EXAMPLE
Get-LinaAgent -ID 164 | Remove-LinaAgent -WhatIf
Simulates deletion of a single agent by ID
#>

    [cmdletbinding(DefaultParameterSetName="ByName")]
    Param(
        [Parameter(ValueFromPipeline=$True,Mandatory=$True,ParameterSetName="ByPipeline")]
        [pscustomobject[]]$LinaAgents,

        [Parameter(Mandatory=$True,ParameterSetName="ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        # Do not delete, only show will be done
        [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
        [Parameter(Mandatory=$False,ParameterSetName="ByName")]
        [switch]$WhatIf,

        # Enable deleting multiple agents
        [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
        [Parameter(Mandatory=$False,ParameterSetName="ByName")]        
        [switch]$Bulk
    )
    BEGIN {
        $nb_of_agents_deleted=0
        if ($Name) {
            $LinaAgents = Get-LinaAgent -Name $Name 6>$null
        }
    }
    PROCESS {
        foreach ($LinaAgent in $LinaAgents) {
            # Should be useless now
            If(!$LinaAgent.ID) { return }

            $Name = $LinaAgent.Name
            $delete_id = $LinaAgent.ID

            if ($nb_of_agents_deleted -ge 1 -AND !$Bulk) {
                Write-Host "WARNING : Bulk mode has not been enabled. Agent $Name will not be deleted."
                return
            }

            
            if (!$delete_id) {
                Write-Host "WARNING : No agent found with this name."
                return
            }else {
                $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>DEL</Func><Client><ID>'+$delete_id+'</ID></Client></HNConfig>'
                if (!$WhatIf) {
                    #$request = Invoke-WebRequest -Uri "$GLOBAL_LINA_SERVER/mng_client.html" -Method "POST" -ContentType "application/xml" -Body $body -WebSession $LoggedSession
                    $request = CallAPI -Path "/mng_client.html" -ContentType "application/xml" -Body $body 
                }else {
                    # WhatIf mode enabled => does not delete agent
                    $request = "WHATIF"
                }
                if ([string]$request -like "*[0]*") {
                    Write-Host "Agent $Name successfully deleted."
                } elseif ($request -eq "WHATIF") {
                    Write-Host "Agent $Name will be deleted if not using 'WhatIf' mode"
                }else {
                    TranslateErrorCode -LinaError $request -FailedAction "delete agent $Name"
                }
            }
            $nb_of_agents_deleted +=1
 
        }
    }
    END {}

}

function Remove-LinaStrategy {
    <#
    .SYNOPSIS
    Deletes a Lina Strategy from a Lina Server.
    .DESCRIPTION
    Deletes a Lina Strategy.
    .INPUTS
    Accept pipelining of LinaStrategy objects (from Get-LinaStrategy for example)
    .OUTPUTS
    None
    .PARAMETER Name
    Name of the Strategy to delete. Wildcards are accepted (don't forget to enable Bulk mode for actions on multiple agents).
    .PARAMETER WhatIf
    No actual deletion will happen if set. It will only display what strategy would be deleted.
    Sometimes also called "Dry Run mode"
    .PARAMETER Bulk
    Security parameter to enable deletion of multiple strategies. If not set only one strategy will be deleted.
    .EXAMPLE
    Remove-LinaStrategy -Name "MyStrat"
    Real deletion of a single strategy by name
    .EXAMPLE
    Get-LinaStrategy -Name "MyStrat" | Remove-LinaStrategy
    Same example as #1 but using piped commands
    .EXAMPLE
    Get-LinaStrategy -Name "Strat*" | Remove-LinaStrategy -Bulk -WhatIf
    Simulates deletion of a multiple strategies by name (WhatIf mode)
    .EXAMPLE
    Get-LinaStrategy -Name "Strat" | Remove-LinaStrategy -Bulk
    Real deletion of a multiple strategies by name
    #>

        [cmdletbinding(DefaultParameterSetName="ByName")]
        Param(
            [Parameter(ValueFromPipeline=$True,Mandatory=$True,ParameterSetName="ByPipeline")]
            [pscustomobject[]]$LinaStrategies,
    
            [Parameter(Mandatory=$True,ParameterSetName="ByName")]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
    
            # Do not delete, only show will be done
            [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
            [Parameter(Mandatory=$False,ParameterSetName="ByName")]
            [switch]$WhatIf,
    
            # Enable deleting multiple strategies
            [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
            [Parameter(Mandatory=$False,ParameterSetName="ByName")]        
            [switch]$Bulk
        )
        BEGIN {
            $nb_of_strategies_deleted=0
            if ($Name) {
                $LinaStrategies = Get-LinaStrategy -Name $Name
            }
        }
        PROCESS {
            foreach ($LinaStrategy in $LinaStrategies) {
                # Should be useless now
                If(!$LinaStrategy.ID) { return }
    
                $Name = $LinaStrategy.Name
                $delete_id = $LinaStrategy.ID
    
                if ($nb_of_strategies_deleted -ge 1 -AND !$Bulk) {
                    Write-Host "WARNING : Bulk mode has not been enabled. Strategy $Name will not be deleted."
                    return
                }
    
                
                if (!$delete_id) {
                    Write-Host "WARNING : No strategy found with this name."
                    return
                }else {
                    $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>DEL</Func><ServiceProfile><ID>'+$delete_id+'</ID></ServiceProfile></HNConfig>'
                    if (!$WhatIf) {
                       $request = CallAPI -Path "/mng_serviceprofile.html" -ContentType "application/xml" -Body $body 
                    }else {
                        # WhatIf mode enabled => does not delete strategy
                        $request = "WHATIF"
                    }
                    if ([string]$request -like "*[0]*") {
                        Write-Host "Strategy $Name successfully deleted."
                    } elseif ($request -eq "WHATIF") {
                        Write-Host "Strategy $Name will be deleted if not using 'WhatIf' mode"
                    }else {
                        TranslateErrorCode -LinaError $request -FailedAction "delete strategy $Name"
                    }
                }
                $nb_of_strategies_deleted +=1
     
            }
        }
        END {}
    
    }
    
function Remove-LinaAgentGroup {
    <#
    .SYNOPSIS
    Deletes a Lina Agent Group from a Lina Server.
    .DESCRIPTION
    Deletes a Lina Agent Group. Agents in this group will go to the "unsorted" group.
    .INPUTS
    Accept pipelining of LinaAgentGroup objects (from Get-LinaAgentGroup for example)
    .OUTPUTS
    None
    .PARAMETER Name
    Name of the agent group to delete. Wildcards are accepted (don't forget to enable Bulk mode for actions on multiple groups).
    .PARAMETER WhatIf
    No actual deletion will happen if set. It will only display what agent groups would be deleted.
    Sometimes also called "Dry Run mode"
    .PARAMETER Bulk
    Security parameter to enable deletion of multiple agent group. If not set only one group will be deleted.
    .EXAMPLE
    Remove-LinaAgentGroup -Name "Group132"
    Real deletion of a single agent group by name
    .EXAMPLE
    Get-LinaAgentGroup -Name "Group132" | Remove-LinaAgentGroup
    Same example as #1 but using piped commands
    .EXAMPLE
    Get-LinaAgentGroup -Name "Group1*" | Remove-LinaAgentGroup -Bulk -WhatIf
    Simulates deletion of a multiple agent groups by name (WhatIf mode)
    .EXAMPLE
    Get-LinaAgentGroup -Name Group1*" | Remove-LinaAgentGroup -Bulk
    Real deletion of a multiple agent groups by name
    .EXAMPLE
    Get-LinaAgentGroups -ID 164 | Remove-LinaAgentGroups -WhatIf
    Simulates deletion of a single agent group by ID
    #>

        [cmdletbinding(DefaultParameterSetName="ByName")]
        Param(
            [Parameter(ValueFromPipeline=$True,Mandatory=$True,ParameterSetName="ByPipeline")]
            [pscustomobject[]]$LinaAgentGroups,
    
            [Parameter(Mandatory=$True,ParameterSetName="ByName")]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
    
            # Do not delete, only show will be done
            [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
            [Parameter(Mandatory=$False,ParameterSetName="ByName")]
            [switch]$WhatIf,
    
            # Enable deleting multiple groups
            [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
            [Parameter(Mandatory=$False,ParameterSetName="ByName")]        
            [switch]$Bulk
        )
        BEGIN {
            $nb_of_agentgroups_deleted=0
            if ($Name -ne "") {
                $LinaAgentGroups = Get-LinaAgentGroup -Name $Name
            }

        }
        PROCESS {

            foreach ($LinaAgentGroup in $LinaAgentGroups) {
                # Should be useless now
                If(!$LinaAgentGroup.ID) { return }
    
                $Name = $LinaAgentGroup.Name
                $delete_id = $LinaAgentGroup.ID
                
                If($LinaAgentGroup.InternalName -eq "UNCAT") {
                    Write-Host "WARNING : The default agent group ($Name) cannot be removed."
                    return
                }

                if ($nb_of_agentgroups_deleted -ge 1 -AND !$Bulk) {
                    Write-Host "WARNING : Bulk mode has not been enabled. Agent group $Name will not be deleted."
                    return
                }
    
                
                if (!$delete_id) {
                    Write-Host "WARNING : No agent group found with this name."
                    return
                }else {
                    $body='data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>DELG</Func><Hierarchy><ID>1</ID><Group><ID>'+$delete_id+'</ID></Group></Hierarchy></HNConfig>'
                    if (!$WhatIf) {
                        #$request = Invoke-WebRequest -Uri "$GLOBAL_LINA_SERVER/mng_client.html" -Method "POST" -ContentType "application/xml" -Body $body -WebSession $LoggedSession
                        $request = CallAPI -Path "/mng_hierarchy.html" -ContentType "application/xml" -Body $body 
                    }else {
                        # WhatIf mode enabled => does not delete agent
                        $request = "WHATIF"
                    }
                    if ([string]$request -like "*[0]*") {
                        Write-Host "Agent group $Name successfully deleted."
                    } elseif ($request -eq "WHATIF") {
                        Write-Host "Agent group $Name will be deleted if not using 'WhatIf' mode"
                    }else {
                        TranslateErrorCode -LinaError $request -FailedAction "delete agent group $Name"
                    }
                }
                $nb_of_agentgroups_deleted +=1
     
            }
        }
        END {}
}
function Remove-LinaTenant {
    <#
    .SYNOPSIS
    Deletes a Tenant from a Lina Server.
    .DESCRIPTION
    Deletes a Tenant from a Lina Server.
    .INPUTS
    Accept pipelining of LinaTenant objects (from Get-LinaTenant for example)
    .OUTPUTS
    None
    .PARAMETER Name
    Name of the Tenant to delete. Wildcards are accepted (don't forget to enable Bulk mode for actions on multiple tenants).
    .PARAMETER WhatIf
    No actual deletion will happen if set. It will only display what tenants would be deleted.
    Sometimes also called "Dry Run mode"
    .PARAMETER Bulk
    Security parameter to enable deletion of multiple tenants. If not set only one tenant will be deleted.
    .EXAMPLE
    Remove-LinaTenant -Name "MyTenant"
    Real deletion of a single tenant by name
    .EXAMPLE
    Get-LinaTenant -Name "MyTenant" | Remove-LinaTenant
    Same example as #1 but using piped commands
    .EXAMPLE
    Get-LinaTenant -Name "Group1*" | Remove-LinaTenant -Bulk -WhatIf
    Simulates deletion of a multiple Tenants by name (WhatIf mode)
    .EXAMPLE
    Get-LinaTenant -Name Group1*" | Remove-LinaTenant -Bulk
    Real deletion of a multiple Tenants by name
    .EXAMPLE
    Get-LinaTenant -ID 164 | Remove-LinaTenant -WhatIf
    Simulates deletion of a single Tenant by ID
    #>

        [cmdletbinding(DefaultParameterSetName="ByName")]
        Param(
            [Parameter(ValueFromPipeline=$True,Mandatory=$True,ParameterSetName="ByPipeline")]
            [pscustomobject[]]$LinaTenants,
    
            [Parameter(Mandatory=$True,ParameterSetName="ByName")]
            [ValidateNotNullOrEmpty()]
            [string]$Name,
    
            # Do not delete, only show will be done
            [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
            [Parameter(Mandatory=$False,ParameterSetName="ByName")]
            [switch]$WhatIf,
    
            # Enable deleting multiple groups
            [Parameter(Mandatory=$False,ParameterSetName="ByPipeline")]
            [Parameter(Mandatory=$False,ParameterSetName="ByName")]        
            [switch]$Bulk
        )
        BEGIN {
            $nb_of_tenants_deleted=0
            if ($Name) {
                $LinaTenants = LinaTenantShort -Name $Name
            }
        }
        PROCESS {
            foreach ($LinaTenant in $LinaTenants) {
                # Should be useless now
                If(!$LinaTenant.ID) { return }
    
                $Name = $LinaTenant.Name
                $delete_id = $LinaTenant.ID
                
                if ($nb_of_tenants_deleted -ge 1 -AND !$Bulk) {
                    Write-Host "WARNING : Bulk mode has not been enabled. Tenant $Name will not be deleted."
                    return
                }
    
                
                if (!$delete_id) {
                    Write-Host "WARNING : No Tenant found with this name."
                    return
                }else {
                    if (!$WhatIf) {
                        #$request = Invoke-WebRequest -Uri "$GLOBAL_LINA_SERVER/mng_client.html" -Method "POST" -ContentType "application/xml" -Body $body -WebSession $LoggedSession
                        $request = CallAPI -Path "/ADM/del_domain.json?id=$delete_id" -ContentType "application/json"
                    }else {
                        # WhatIf mode enabled => does not delete agent
                        $request = "WHATIF"
                    }
                    if ($request.status -eq 0) {
                        Write-Host "Tenant $Name successfully deleted."
                    } elseif ($request -eq "WHATIF") {
                        Write-Host "Tenant $Name will be deleted if not using 'WhatIf' mode"
                    }else {
                        TranslateErrorCode -LinaError $request.status -FailedAction "delete Tenant $Name"
                    }
                }
                $nb_of_agentgroups_deleted +=1
     
            }
        }
        END {}
}
     
function Set-LinaAgent {
<#
.SYNOPSIS
Changes the configuration of an agent.
.DESCRIPTION
Changes the configuration of an agent for example its name.
.INPUTS
Array of LinaAgent Objects
.OUTPUTS
The modified LinaAgent Objects
.PARAMETER NewName
New name for the agent. If multiple agent are passed, only the first one will be renamed as names have to be unique.
.PARAMETER Strategy
Change the strategy for this agent
.PARAMETER Protection
Change the protection for this agent
.PARAMETER Tenant
Move the agent to this tenant.
.PARAMETER Group
Move the agent to this agent group.
Note : Agent group has to be in the same tenant as the agent or in its new tenant (if Tenant is provided)
 
.EXAMPLE
Get-LinaAgent -Name "TEST1" | Set-LinaAgent -NewName "TEST2"
Changes the name of the agent from TEST1 to TEST2
.EXAMPLE
Get-LinaAgent -Name "TEST1" | Set-LinaAgent -Tenant "MyTenant"
Moves the agent TEST1 to the Tenant MyTenant
.EXAMPLE
Get-LinaAgent -Name "TEST1" | Set-LinaAgent -NewName "TEST2" -Tenant "MyTenant" -Group "MyGroup" -Strategy "MyStrategy" -Protection "MyProtection"
Changes everything at the same time (Name, Tenant, Group, Strategy and Protection)
#>

    [cmdletbinding(DefaultParameterSetName='Update')]
    Param(
        [Parameter(ValueFromPipeline=$True,Mandatory=$true,ParameterSetName="Update")]
        [pscustomobject[]]$LinaAgents,
        [Parameter(Mandatory=$false,ParameterSetName="Update")]
        [ValidateNotNullOrEmpty()]
        [string]$NewName,
        [Parameter(Mandatory=$false,ParameterSetName="Update")]
        [ValidateNotNullOrEmpty()]
        [string]$Tenant,
        [Parameter(Mandatory=$false,ParameterSetName="Update")]
        [ValidateNotNullOrEmpty()]
        [string]$Group,
        [Parameter(Mandatory=$false,ParameterSetName="Update")]
        [ValidateNotNullOrEmpty()]
        [string]$Strategy,
        [Parameter(Mandatory=$false,ParameterSetName="Update")]
        [ValidateNotNullOrEmpty()]
        [string]$Protection
    )
    BEGIN {
        $lina_agents_processed=0
    }
    PROCESS {

        foreach($LinaAgent in $LinaAgents) {
            If(!$LinaAgent.ID) { return }
            $agent_name = $LinaAgent.Name
            $agent_id = $LinaAgent.ID
            $agent_tenantname = $LinaAgent.Tenant
            $timestamp = LinaTimestamp

            if ($NewName) {
                # Renaming Agent => can only be done on one agent at a time
                if ($lina_agents_processed -ge 1) {
                    Write-Host "WARNING : You cannot rename multiple agents with the same name. Agent $agent_name will be ignored."
                    Return
                }else {
                    Write-Host "Renaming Lina agent named $agent_name to new name $NewName"
                    $timestamp = LinaTimestamp
                    $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>REN</Func><Client><ID>'+$agent_id+'</ID><Name>'+$NewName+'</Name></Client></HNConfig>'
                    $request = CallAPI -Path "/mng_client.html?timestamp=$timestamp" -ContentType "application/xml" -Body $body
                    if ([string]$request -like "*[0]*") {
                        Write-Host "Agent $agent_name successfully renamed to $NewName."
                        $agent_name = $NewName
                    }else {
                        TranslateErrorCode -LinaError $request -FailedAction "Renaming agent $agent_name to new name $NewName"
                        Return
                    }
                }
                $lina_agents_processed++
            }

            if ($Tenant) {
                # Moving to new tenant
                $tenant_id = (LinaTenantShort -Name $Tenant).ID
                if ($tenant_id -le 1) { 
                    Write-Error "ERROR : No tenant found with this name."
                    Return
                }
                $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>MOV</Func><Client><ID>'+$agent_id+'</ID><DomainID>'+$tenant_id+'</DomainID><Name>@auto</Name></Client></HNConfig>'
                Write-Host "Moving Lina agent named $agent_name to Tenant $Tenant"
                $timestamp = LinaTimestamp
                $request = CallAPI -Path "/mng_client.html?timestamp=$timestamp" -ContentType "application/xml" -Body $body
                if ([string]$request -like "*[0]*") {
                    Write-Host "Agent $agent_name successfully moved to Tenant $Tenant."
                    $agent_tenantname = $Tenant
                }else {
                    TranslateErrorCode -LinaError $request -FailedAction "Moving agent $Name to Tenant $Tenant"
                    Return
                }
            }

            if ($Strategy) {
                # Setting a new strategy
                $strategy_id = (Get-LinaStrategy -Name $Strategy).ID
                if ($strategy_id -lt 1 ) { 
                    Write-Error "ERROR : No Strategy found with this name."
                    Return
                }
                $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>SET</Func><Client><ID>'+$agent_id+'</ID><ServiceProfileID>'+$strategy_id+'</ServiceProfileID></Client></HNConfig>'
                Write-Host "Assigning strategy $Strategy on Lina agent named $agent_name"
                $timestamp = LinaTimestamp
                $request = CallAPI -Path "/mng_client.html?timestamp=$timestamp" -ContentType "application/xml" -Body $body
                if ([string]$request -like "*[0]*") {
                    Write-Host "Strategy $Strategy assigned successfully to Agent $agent_name."
                }else {
                    TranslateErrorCode -LinaError $request -FailedAction "Assigning strategy $Strategy to agent $Name"
                    Return
                }
            }

            if ($Protection) {
                # Setting a new strategy
                $protection_id = (Get-LinaProtection -Name $Protection).ID
                if ($protection_id -lt 1) { 
                    Write-Error "ERROR : No Protection found with this name."
                    Return
                }
                $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>SET</Func><Client><ID>'+$agent_id+'</ID><DataProfileID>'+$protection_id+'</DataProfileID></Client></HNConfig>'
                Write-Host "Assigning protection $Protection on Lina agent named $agent_name"
                $timestamp = LinaTimestamp
                $request = CallAPI -Path "/mng_client.html?timestamp=$timestamp" -ContentType "application/xml" -Body $body
                if ([string]$request -like "*[0]*") {
                    Write-Host "Protection $Protection assigned successfully to Agent $agent_name."
                }else {
                    TranslateErrorCode -LinaError $request -FailedAction "Assigning protection $Protection to agent $Name"
                    Return
                }
            }

            if ($Group) {
                # Moving to group, filtering to group in the same tenant as the agent to move
                $group_id = (Get-LinaAgentGroup -Name $Group -Tenant $agent_tenantname).ID 
                if ($group_id -le 0) { 
                    Write-Error "ERROR : No group found with this name in the same tenant as the agent."
                    Return
                }
                $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>SETG</Func><Client><ID>'+$agent_id+'</ID><AttachementArray><Attachement><HierarchyID>1</HierarchyID><GroupID>'+$group_id+'</GroupID></Attachement></AttachementArray></Client></HNConfig>'
                Write-Host "Moving Lina agent named $agent_name to agent group $Group"
                $timestamp = LinaTimestamp
                $request = CallAPI -Path "/mng_client.html?timestamp=$timestamp" -ContentType "application/xml" -Body $body
                if ([string]$request -like "*[0]*") {
                    Write-Host "Agent $agent_name successfully moved to agent group $Group in Tenant $agent_tenantname."
                }else {
                    TranslateErrorCode -LinaError $request -FailedAction "Moving agent $Name to Group $Group in Tenant $agent_tenantname."
                    Return
                }
            }
            Return Get-LinaAgent -ID $agent_id

        }
    }
    END{ }
}

function Set-LinaTenant {
    <#
    .SYNOPSIS
    Changes the configuration of a Tenant.
    .DESCRIPTION
    Changes the configuration of a tenant for example its Comments.
    .INPUTS
    LinaTenant Object
    .PARAMETER isDefault
    Set the tenant as the default one
    .OUTPUTS
    The modified LinaTenant Object
    .EXAMPLE
    Get-LinaTenant -ID 2 | Set-LinaTenant -SetDefault
    Set the Tenant with ID 2 as the default tenant
    .EXAMPLE
    Get-LinaTenant -Name "MyTenant" | Set-LinaTenant
    Unset the Tenant MyTenant as the default tenant
    #>

    [cmdletbinding(DefaultParameterSetName='update')]
    Param(
        [Parameter(ValueFromPipeline=$True,Mandatory=$true,ParameterSetName="update")]
        [pscustomobject[]]$LinaTenants,
        [Parameter(Mandatory=$false,ParameterSetName="update")]
        [Nullable[boolean]]$IsDefault,
        [Parameter(Mandatory=$false,ParameterSetName="update")]
        [Nullable[boolean]]$AutoCreateClients,
        [Parameter(Mandatory=$false,ParameterSetName="update")]
        [ValidateNotNullOrEmpty()]
        [string]$WindowsDefaultProtection,
        [Parameter(Mandatory=$false,ParameterSetName="update")]
        [ValidateNotNullOrEmpty()]
        [string]$LinuxDefaultProtection,
        [Parameter(Mandatory=$false,ParameterSetName="update")]
        [ValidateNotNullOrEmpty()]
        [string]$MacDefaultProtection,
        [Parameter(Mandatory=$false,ParameterSetName="update")]
        [ValidateNotNullOrEmpty()]
        [string]$DefaultStrategy
    )
        BEGIN{
            $tenants_modified=0
        }
        PROCESS {

            foreach($Tenant in $LinaTenants) {
                If(!$Tenant.ID) { return }
                $tenant_name = $Tenant.Name
                $tenant_id = $Tenant.ID

                # Batch all configuration (preferences) modification into a single request
                $config_modif=0
                $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>SET</Func><Globals>'

                if ($AutoCreateClients -ne $null) {
                     $body+='<AutoCreateClients>'+([int]$AutoCreateClients)+'</AutoCreateClients>'
                     $config_modif=1
                }
                if ($WindowsDefaultProtection -ne "" ) {
                    $current_item=$WindowsDefaultProtection
                    $item_id=(Get-LinaProtection -Name $current_item).ID
                    if ($null -eq $item_id -OR $item_id -le 0) {
                        Write-Host "Error : No Protection found with name $current_item."
                        Return 
                    }
                    $body+='<WinDefaultDataProfileID>'+$item_id+'</WinDefaultDataProfileID>' 
                    $config_modif=1
                }
                if ($LinuxDefaultProtection -ne "") {
                    $current_item=$LinuxDefaultProtection
                    $item_id=(Get-LinaProtection -Name $current_item).ID
                    if ($null -eq $item_id -OR $item_id -le 0) {
                        Write-Host "Error : No Protection found with name $current_item."
                        Return 
                    }
                    $body+='<LinuxDefaultDataProfileID>'+$item_id+'</LinuxDefaultDataProfileID>' 
                    $config_modif=1
                }
                if ($MacDefaultProtection -ne "") {
                    $current_item=$MacDefaultProtection
                    $item_id=(Get-LinaProtection -Name $current_item).ID
                    if ($null -eq $item_id -OR $item_id -le 0) {
                        Write-Host "Error : No Protection found with name $current_item."
                        Return 
                    }
                    $body+='<MacDefaultDataProfileID>'+$item_id+'</MacDefaultDataProfileID>' 
                    $config_modif=1
                }                                 

                if ($DefaultStrategy -ne "") {
                    $current_item=$DefaultStrategy
                    $item_id=(Get-LinaStrategy -Name $current_item).ID
                    if ($null -eq $item_id -OR $item_id -le 0) {
                        Write-Host "Error : No Strategy found with name $current_item."
                        Return 
                    }
                    $body+='<WinDefaultServiceProfileID>'+$item_id+'</WinDefaultServiceProfileID>' 
                    $config_modif=1
                }    

                if ($config_modif -eq 1 ) {
                    $current_tenant_id = Get-LinaCurrentTenantID 6>$null

                    Write-Host "Modifying configuration of tenant $tenant_name"
                    Set-LinaCurrentTenant -ID $tenant_id 6>$null
                    $body += '</Globals></HNConfig>'
                    $request = CallAPI -Path "/mng_globals.html" -ContentType "application/xml" -Body $body

                    # Setting back the current tenant to what it was before the Set-LinaTenant command
                    Set-LinaCurrentTenant -ID $current_tenant_id 6>$null
                    if ([string]$request -like "*[0]*") {                    
                        Write-Host "Tenant $tenant_name config has been succesfully applied"
                    } else {
                        TranslateErrorCode -LinaError $request.status -FailedAction "applying config on tenant $tenant_name "
                        Return
                    }                    

                }


                # Modifications of default tenant (not the same requests)
                if ($IsDefault -ne $null) {
                    If ($tenants_modified -ge 1) {
                        Write-Host "Warning : Only one tenant can be set as the default one. Only the first one will be set as the default tenant"
                        return
                    }
                    if ($IsDefault -eq $True) {
                        $request = CallAPI -Path "/ADM/domain_set_default.json?id=$tenant_id" -ContentType "application/json"
                        if ($request.status -eq 0) {
                            Write-Host "Tenant $tenant_name is now the default Tenant"
                            $tenants_modified++
                        } else {
                            TranslateErrorCode -LinaError $request.status -FailedAction "set tenant $tenant_name as the default tenant"
                            Return
                        }
                    }elseif ($IsDefault -eq $False) {
                        if (-NOT (LinaTenantShort -Name $tenant_name).IsDefault) {
                            Write-Host "Tenant $tenant_name is not the default => No modification is required"
                        }
                        $request = CallAPI -Path "/ADM/domain_unset_all.json" -ContentType "application/json"
                        if ($request.status -eq 0) {
                            Write-Host "Tenant $tenant_name is not the default Tenant anymore and no Tenant is set as default."
                            $tenants_modified++

                        } else {
                            TranslateErrorCode -LinaError $request.status -FailedAction "unset tenant $tenant_name as the default tenant"
                            Return
                        }
                    }
                }
                Return Get-LinaTenant -ID $tenant_id
            }
            
        }
        END{ }
}