Atempo.Lina.psm1

<#
=========== MODULE STRUCTURE ===========
 
    This module contains the following file structure
        -- Atempo.Lina.psm1 : Global variables + Loading of functions (Common,Get,Set,New,Remove)
        -- Atempo.Lina.psd1 : Lina PowerShell Module manifest (description, release notes, icon, author etc)
        -- help.html : HTML Help file (usually opened using Get-LinaHelp)
        -- Functions/ : Folder containing all the functions/cmdlets
                |-- Common.ps1 : Connect/Disconnect and all internal functions
                |-- Get.ps1 : Get-Lina* / Listing elements
                |-- New.ps1 : New-Lina* / Creating elements
                |-- Remove.ps1 : Remove-Lina* / Deleting elements
                |-- Set.ps1 : Set-Lina* / Modifying elements
                |-- Copy.ps1 : Copy-Lina* / Cloning elements
 
=========== TODO ===========
    6.1 Compat : Get-LinaUserProfiles (ADM/list_profile.json => 404)
    Remove-CrossToken
    QA Script for Cross Restore + Get-Sysinfo ?
    Enhance AgentDetails in Get-LinaCrossRestoreToken
     
    Try & Catch error on connect => Exit
    Error returns
    Check global enable ssl certification validation
     
    AutoDoc in common => needs to be finalized or removed
    New-LinaUserProfile from scratch ?
    AppVeyor Continuous integration + Docker on premise for testing ?
    Write-host green/red/orange
    All LinaUser cmdlet
    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
#>


# Needed for URLEncode
Add-Type -AssemblyName System.Web

# Can be set to $False from calling script to enforce certificate checking
$global:GLOBAL_IGNORE_CERTIFICATES = $True

# Use TLS v1.2 By default to avoid connection issues and increase security
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# Can be set to $True from calling script to enable verbose logging
$global:LINA_DEBUG_MODE=$False
$global:LINA_DEBUG_RESPONSE_MODE=$False
# Can be set to $True from calling script to enable auto-documenting
$global:LINA_DOC_MODE=$False
$global:LINA_DOC_OUTPUT=""
$global:LINA_DOC_URL_DONE=@()



# ============ Internal Variables =============== #

# Path to the Functions dir containing all functions
$global:FUNCTIONS_DIR  = @( Get-ChildItem -Path $PSScriptRoot\Functions\*.ps1 -ErrorAction SilentlyContinue )

$global:LoggedSession = $null
$global:GLOBAL_LINA_SERVER = $null
$global:GLOBAL_LINA_SERVERTYPE = $null
$global:GLOBAL_LINA_CONNECTED=$False
$global:GLOBAL_DISCONNNECTED_MESSAGE="You are disconnected from Lina Server. Please reconnect using Connect-LinaServer."

$global:LINA_TRANSLATIONS = $null
$global:LINA_VERSION = $null
$global:INT_LINA_API_VERSION = 0

# Mapping PowerShell Lina Strategies Properties to Lina REST API properties
$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"
}


# <ParamRetentionTimeRepl>30</ParamRetentionTimeRepl>
# <ParamBackupRestrictionActivated>0</ParamBackupRestrictionActivated>
# <ParamBackupRestrictionArray></ParamBackupRestrictionArray>
# <ParamBackupRestrictionSchedule></ParamBackupRestrictionSchedule>
# <ParamWanGWArray></ParamWanGWArray>
# <ParamCacheMode>0</ParamCacheMode>
# <ParamCacheSize>0</ParamCacheSize>
# <ParamAllowWebAccess>0</ParamAllowWebAccess>
# <ParamAllowFullBrowse>1</ParamAllowFullBrowse>
# <ParamAllowVolumesMgmt>0</ParamAllowVolumesMgmt>
# <ParamAllowRestore>0</ParamAllowRestore>
# <ParamAllowRemoteServer>0</ParamAllowRemoteServer>
# <ParamAllowCrossRestore>0</ParamAllowCrossRestore>
# <ParamRepliTargetID>0</ParamRepliTargetID>

$global:INT_BLOCK_STATUS= @{ 
    0 = "UNBLOCKED_AGENT_LABEL"; # aka "Normal"
    1 = "AGENT_DELETION_LABEL";
    2 = "AGENT_ARCHIVED_LABEL";
    3 = "AGENT_END_OF_LIFE_LABEL"; # aka Keep only last version
    4 = "AGENT_VACATION_LABEL"; 
    5 = "AGENT_DISABLED_LABEL";
    6 = "AGENT_PAUSED_LABEL"
}

$global:INT_BLOCK_STATUS_NAME= @{ 
    0 = "Active";
    1 = "Deleted";
    2 = "Archived";
    3 = "EndOfLife"; # aka Keep only last version
    4 = "OnVacation"; 
    5 = "Disabled";
    6 = "Paused"
}


# Possible values for User Types (0, 3, 4 may not exist but it's to avoid crashes just in case)
$global:INT_USER_TYPES= @{ 
    1 = "Local";
    2 = "LDAP";
    3 = "LDAP Group";
    4 = "Unknown"
}

$global:INT_API_ERRORCODES= @(
        (0, "ASM_OK", "[0] - OK"),
        (1, "ASM_ERR",  "[1] - General error"),
        (2, "ASM_ERR_MEM", "[2] - Out of memory"),
        (3, "ASM_ERR_EOF", "[3] - End of file or end of stream"),
        (4, "ASM_ERR_READ", "[4] - Read error"),
        (5, "ASM_ERR_WRITE", "[5] - Write error"),
        (6, "ASM_ERR_RANGE", "[6] - Value out of range"),
        (7, "ASM_ERR_FORMAT", "[7] - Corrupt format"),
        (8, "ASM_ERR_PERM", "[8] - Permission denied"),
        (9, "ASM_ERR_ABORT", "[9] - Abort"),
        (10, "ASM_ERR_FNF", "[10] - File/Object not found"),
        (11, "ASM_ERR_EXIST", "[11] - Object/entity already exists"),
        (12, "ASM_ERR_NOT_EXIST", "[12] - Object/entity does not exist"),
        (13, "ASM_ERR_NO_ADMIN", "[13] - Access denied. Administration rights are required"),
        (14, "ASM_ERR_COMM", "[14] - Network error or broken connection"),
        (15, "ASM_ERR_BAD_CPR", "[15] - Block already compressed"),
        (16, "ASM_ERR_BAD_STREAM", "[16] - Stream is corrupt"),
        (17, "ASM_ERR_DELETED_STREAM", "[17] - Stream is not available because it is deleted"),
        (18, "ASM_ERR_HEADER_CORRUPTED", "[18] - Corrupt header"),
        (19, "ASM_ERR_TRAILER_CORRUPTED", "[19] - Corrupt trailer"),
        (20, "ASM_ERR_CHECKSUM", "[20] - Bad checksum/digest"),
        (21, "ASM_ERR_NOT_IMPLEMENTED", "[21] - Operation not yet implemented"),
        (22, "ASM_ERR_ILLEGAL_CASE", "[22] - Illegal case"),
        (23, "ASM_ERR_CREATE_THREAD", "[23] - Could not start a thread"),
        (24, "ASM_ERR_CPR", "[24] - Compress error"),
        (25, "ASM_ERR_UNCPR", "[25] - Uncompress error"),
        (26, "ASM_ERR_CANT_OPEN_FILE", "[26] - Could not open file (bad path or permission)"),
        (27, "ASM_ERR_PROG_INCONS", "[27] - Coding error"),
        (28, "ASM_ERR_HOTBACKUP_IN_PROGRESS", "[28] - Hot backup in progress. Operation will be delayed"),
        (29, "ASM_ERR_GET_INTEGER", "[29] - Receive an integer"),
        (30, "ASM_ERR_EXIT", "[30] - Exit"),
        (31, "ASM_ERR_CONFIG", "[31] - Configuration error"),
        (32, "ASM_ERR_LOG", "[32] - Fail to build or log event"),
        (33, "ASM_ERR_TIME_OUT", "[33] - No respond or operation takes too long time"),
        (34, "ASM_ERR_GO", "[34] - Begins the task"),
        (35, "ASM_ERR_INVALID_PARAM", "[35] - Invalid parameter"),
        (36, "ASM_ERR_DB", "[36] - Database operation failure"),
        (37, "ASM_ERR_DB_EXEC", "[37] - SQL request failed"),
        (38, "ASM_ERR_BAD_PASSWORD", "[38] - Wrong login or password"),
        (39, "ASM_ERR_FS_FULL", "[39] - Filesystem is full"),
        (40, "ASM_ERR_FS_NEAR_FULL", "[40] - Filesystem is nearly full"),
        (41, "ASM_ERR_NOT_REACHABLE", "[41] - Server not reachable"),
        (42, "ASM_ERR_NOTHING_DONE", "[42] - Nothing was done"),
        (43, "ASM_ERR_SERVICE_NOT_AVAILABLE", "[43] - Service not available or not enabled"),
        (44, "ASM_ERR_OLD_VERSION", "[44] - Client version is not compatible with server version"),
        (45, "ASM_ERR_FULL", "[45] - Container full or upper limit reached"),
        (46, "ASM_ERR_REQ", "[46] - Unable to perform request"),
        (47, "ASM_ERR_OS_NOT_SUPPORTED", "[47] - OS version not supported"),
        (48, "ASM_ERR_RETRY", "[48] - Operation can't be done now"),
        (49, "ASM_ERR_EMPTY", "[49] - Empty"),
        (50, "ASM_ERR_LICENSE", "[50] - License error"),
        (51, "ASM_ERR_INVALID_SESS", "[51] - Invalid session"),
        (52, "ASM_ERR_MAINTENANCE_MODE", "[52] - Maintenance mode"),
        (53, "ASM_ERR_BUFFER_TOO_SMALL", "[53] - Buffer too small"),
        (54, "ASM_ERR_SHUTDOWN", "[54] - Server is stopping"),
        (55, "ASM_ERR_CORRUPTED", "[55] - Block is corrupted"),
        (56, "ASM_ERR_REMOVED", "[56] - Removed"),
        (57, "ASM_ERR_REPLI_IN_PROGRESS", "[57] - Stream replication in progress"),
        (58, "ASM_ERR_INMEM_FULL", "[58] - InMemory DB is full"),
        (59, "ASM_ERR_PRODUCT_DISABLED", "[59] - Product is disabled"),
        (60, "ASM_ERR_ASK_ACK", "[60] - Need an ACK"),
        (61, "ASM_ERR_BLOCK_NOT_FOUND", "[61] - Block not found"),
        (62, "ASM_ERR_DEPRECATED", "[62] - Deprecated"),
        (63, "ASM_ERR_USER_DISABLED", "[63] - User disabled"),
        (64, "ASM_ERR_NO_COOKIE", "[64] - No cookie available"),
        (65, "ASM_ERR_PERM_ENFORCEMENT", "[65] - Forbidden to give higher permission to someone"),
        (66, "ASM_ERR_PERM_OWNER", "[66] - Forbidden to alter his own permission"),
        (67, "ASM_ERR_PERM_READONLY_TENANT", "[67] - Forbidden to alter somethings his this tenant"),
        (68, "ASM_ERR_NOT_ENOUGH_SPACE", "[68] - Not enough free space on disks"),
        (69, "ASM_ERR_NOT_EMPTY", "[69] - Not empty"),
        (70, "ASM_ERR_IN_USE", "[70] - In use"),
        (71, "ASM_ERR_BLOCK_SKIPPED", "[71] - Block out of format, skipped"),
        (72, "ASM_ERR_NEED_2FA", "[72] - Need double authentication code"),
        (73, "ASM_ERR_BAD_TENANT", "[73] - Attempt to access a resource available in another tenant"),
        (74, "ASM_ERR_INFO_NOT_AVAILABLE", "[74] - Information not available"),
        (75, "ASM_ERR_PERM_NODE", "[75] - Permission denied for master/slave/standalone node"),
        (76, "ASM_ERR_WEAK_PASSWORD", "[76] - Weak password (does not conform to password policy)"),
        (77, "ASM_ERR_EXPIRED_PASSWORD", "[77] - Expired password"),
        (78, "ASM_ERR_FORCE_2FA", "[78] - Force user to create a 2FA config"),
        (79, "ASM_ERR_CACHE_FULL", "[79] - S3 cache is full"),
        (80, "ASM_ERR_S3_LOCK_NOT_SUPPORTED", "[80] - S3 lock not supported"),
        (81, "ASM_ERR_S3_BUCKET_NOT_CHANGED", "[81] - S3 bucket can not be changed"),
        (82, "ASM_ERR_S3_REGION_NOT_PROVIDED", "[82] - S3 region must be provided"),
        (83, "ASM_ERR_S3_BUCKET_CREATION", "[83] - S3 bucket can not be created"),
        (84, "ASM_ERR_S3_BUCKET_LOCKED", "[84] - S3 lock activated on current bucket")
    )

# Checking PowerShell version because Get-LinaUserProfile / Get-LinaUserGroup is using Enum variables and requires PowerShell >= 5
if ($PSVersionTable.PSVersion.Major -ge 5) {
    $global:ROLES1_LIST = [flags()] Enum ROLES1_LIST {
        ViewAlerts = 0x1                # access to the alerts view
        SetAlerts  = 0x2                # ability to modify alerts parameters (notification, recipients)
        ViewSupport = 0x4               # access to the Support view (maintenance)

        ViewUserProfiles   = 0x8        # deprecated in 6.1+
        CreateUserProfiles = 0x10       # deprecated in 6.1+
        DeleteUserProfiles = 0x20       # deprecated in 6.1+
        SetUserProfiles    = 0x40       # deprecated in 6.1+

        ViewUsers   = 0x80              # access to the users view
        CreateUsers = 0x100             # deprecated in 6.1+
        DeleteUsers = 0x200             # deprecated in 6.1+
        SetUsers    = 0x400             # deprecated in 6.1+

        ViewServices = 0x800            # access to the services view
        SetServices  = 0x1000           # ability to modify services

        ViewReplications   = 0x2000     # access to the replication servers view
        CreateReplications = 0x4000     # ability to create a replication server
        DeleteReplications = 0x8000     # ability to delete a replication server
        SetReplications    = 0x10000    # ability to modify a replication server

        AllowCrossResto = 0x200000      # ability to do a cross resto
        AllowWebResto   = 0x400000      # ability to do a web resto

        ViewBMR     = 0x800000          # access to the BMR view (server side)
        SetBMR      = 0x1000000         # ability to modify BMR parameters (server side)
        AllowBMRResto   = 0x2000000     # ability to do a BMR resto (agent side)
        All              = 0xffffffff   # All current and future roles
    }
    $global:ROLES2_LIST = [flags()] Enum ROLES2_LIST {
        ViewFiles = 0x1                 # access to the files view
        ViewLicense = 0x2               # access to the license view
        SetLicense  = 0x4               # ability to modify license parameters (products activation and license)
        ViewSmtp = 0x8                  # view the smtp configuration
        SetSmtp  = 0x10                 # ability to modify smtp configuration
        ViewLdap = 0x20                 # view the ldap configuration
        SetLdap  = 0x40                 # ability to modify ldap configuration
        ViewTunables         = 0x80     # access to the tunables view [NOT USED => everyone may access, because it's needed by guest user]
        SetTunables          = 0x100    # ability to modify tunables
        ViewAdvancedTunables = 0x200    # access to the advanced tunables view [NOT USED]
        ViewStreams   = 0x400           # access to the streams view
        DeleteStreams = 0x800           # access to the streams view
        ViewStatistics      = 0x1000    # access to the statistics view
        RecomputeStatistics = 0x2000    # ability to recompute detailled statistics
        ViewConfig   = 0x4000           # view the Config
        SetConfig    = 0x8000           # ability to modify the Config
        RestartService   = 0x10000      # ability to restart the service itself
        ViewSnapshot   = 0x20000        # list all snapshots
        CreateSnapshot = 0x40000        # create a new snapshot
        DeleteSnapshot = 0x80000        # delete a snapshot
        All            = 0xffffffff     # All current and futur roles
    }
    $global:ROLES3_LIST = [flags()] Enum ROLES3_LIST {
        # agents
        ViewAlnAgent   = 0x1                    # View all agent (else view only those with a relation with us)
        CreateAlnAgent = 0x2                    # ability to create an agent
        DeleteAlnAgent = 0x4                    # ability to delete an agent
        SetAlnAgent    = 0x8                    # ability to modify agent parameters
        # strategies
        ViewAlnStrategy   = 0x10                # access to the strategies view
        CreateAlnStrategy = 0x20                # ability to create a strategy
        DeleteAlnStrategy = 0x40                # ability to delete a strategy
        SetAlnStrategy    = 0x80                # ability to modify strategy parameters
        # protections
        ViewAlnProtection   = 0x100             # access to the protections view
        CreateAlnProtection = 0x200             # ability to create a protection
        DeleteAlnProtection = 0x400             # ability to delete a protection
        SetAlnProtection    = 0x800             # ability to modify protection parameters
        # protection rules
        ViewAlnProtectionRule   = 0x1000        # deprecated
        CreateAlnProtectionRule = 0x2000        # deprecated
        DeleteAlnProtectionRule = 0x4000        # deprecated
        SetAlnProtectionRule    = 0x8000        # deprecated

        All                     = 0xffffffff    # All current and future roles
    }
    $global:ROLES4_LIST = [flags()] Enum ROLES4_LIST {
        # protection zones
        ViewAlnProtectionZone   = 0x1                   # deprecated
        CreateAlnProtectionZone = 0x2                   # deprecated
        DeleteAlnProtectionZone = 0x4                   # deprecated
        SetAlnProtectionZone    = 0x8                   # deprecated
        # file categories
        ViewAlnFileCategory   = 0x10                    # deprecated
        CreateAlnFileCategory = 0x20                    # deprecated
        DeleteAlnFileCategory = 0x40                    # deprecated
        SetAlnFileCategory    = 0x80                    # deprecated
        # default configuration
        SetAlnDefaultConfiguration = 0x100              # ability to modify aln default configuration (per tenant)
        # domain
        ViewDomains = 0x200                             # access to the domain view
        CreateDomains = 0x400                           # ability to create a domain
        DeleteDomains = 0x800                           # ability to delete a domain
        SetDomains = 0x1000                             # ability to modify domain parameters

        SetAlnGlobalDefaultConfiguration = 0x2000       # ability to modify aln default configuration (global / shared by all tenants)
        isSuperadmin  = 0x4000                          # special role for "Superadmin" profile (user can have any name, not only "superadmin")
        isAdmin     = 0x8000                            # special role for "Admin" profile (user can have any name, not only "admin")
        All  = 0xffffffff                               # All current and future roles
    }

    # Calculating max values for each role, used for builtin accounts with negative values
    $global:ROLES1_MAX=0  
    [enum]::GetNames([ROLES1_LIST]) | ForEach-Object {$ROLES1_MAX+=$([ROLES1_LIST]::$_.value__)}
    $global:ROLES2_MAX=0
    [enum]::GetNames([ROLES2_LIST]) | ForEach-Object {$ROLES2_MAX+=$([ROLES2_LIST]::$_.value__)}
    $global:ROLES3_MAX=0 
    [enum]::GetNames([ROLES3_LIST]) | ForEach-Object {$ROLES3_MAX+=$([ROLES3_LIST]::$_.value__)}
    $global:ROLES4_MAX=0  
    [enum]::GetNames([ROLES4_LIST]) | ForEach-Object {$ROLES4_MAX+=$([ROLES4_LIST]::$_.value__)}


}else {
    # Declaring them for PS < 5.0 but won't work. Needs a workaround to avoid using Enum + flags (bitmasks)
    # Trying to avoid some crashes
    $global:ROLES2_LIST=@()
    $global:ROLES3_LIST=@()
    $global:ROLES4_LIST=@()
    $global:ROLES1_MAX=0
    $global:ROLES2_MAX=0  
    $global:ROLES3_MAX=0  
    $global:ROLES4_MAX=0 
}

# Loading dynamically all scripts containing the functions in Functions/
Foreach($import in @($FUNCTIONS_DIR))
{
    Try
    {
        . $import.fullname
    }
    Catch
    {
        Write-Error -Message "Failed to import function $($import.fullname): $_"
    }
}