Atempo.Lina.psm1
<#
.Synopsis This Windows PowerShell module contains PowerCLI Cloud Infrastructure Suite cmdlets. .Description Provide basic management of Lina Agents .Example # Getting list of agents Connect-LinaServer -Server "https://10.0.0.1:8181" -User "superadmin" -Password "MyPassword" Get-LinaAgent Disconnect-LinaServer #> <# TODO manage $Body in CallAPI Get locales for default stragies names (locale/en.js for example) Ability to pipe commands Improved error reporting #> <# 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:LoggedSession = $null $global:GLOBAL_LINA_SERVER = $null $global:GLOBAL_IGNORE_CERTIFICATES = $True 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()] [switch]$FirstConnection ) if ($Path -like "*.json*") { $ContentType="application/json" }else { # most content is XML so this is default $ContentType="application/xml" } if ($FirstConnection) { if ($PSVersionTable.PSVersion.Major -lt 6 -AND $GLOBAL_IGNORE_CERTIFICATES) { <# Disable Certificate checking for WebRequest (Pre-PowerShell 6.0) #> Disable-SslVerification $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession } elseif ($GLOBAL_IGNORE_CERTIFICATES) { <# Disable Certificate checking for WebRequest (PowerShell >= 6.0) #> $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -SkipCertificateCheck }else { # Certificate check is enabled $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 -WebSession $LoggedSession -SkipCertificateCheck }else { # Same request if enabled or not. Checkingt is disabled globally on PowerShell > 6.0 $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -ContentType $ContentType -WebSession $LoggedSession } } Return $request } function LinaToLocalTime ($lina_time) { <# Lina Times are UTC based ? Not GMT ?#> $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 LinaTimestamp { return [math]::Round((New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get-Date)).TotalSeconds*1000) } function Connect-LinaServer { [cmdletbinding()] Param( [Parameter(ParameterSetName="Server",Position=0)] [ValidateNotNullOrEmpty()] [string]$Server, [Parameter(ParameterSetName="Server",Position=1)] [ValidateNotNullOrEmpty()] [string]$User, [Parameter(ParameterSetName="Server",Position=2)] [ValidateNotNullOrEmpty()] [string]$Password, [Parameter(Mandatory=$false,ParameterSetName="Server",Position=3)] [ValidateNotNullOrEmpty()] [string]$Tenant ) 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" #$request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/ADE/login.html?user=$userenc&password=$passenc" -SessionVariable Currentsession #Set-Variable -name LoggedSession -Scope global -Value $Currentsession $request = CallAPI -Path "/ADE/login.html?user=$userenc&password=$passenc" -FirstConnection if ([string]$request -like "*- OK*") { Write-Host "Successfully connected." } else { Write-Host "Error occurred trying to connect : \$request\" } } function Disconnect-LinaServer { Write-Host "Disconnecting from Lina server $GLOBAL_LINA_SERVER" #$request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/ADE/logout.json" -WebSession $LoggedSession $request = CallAPI -Path "/ADE/logout.json" if ([string]$request -like "*- OK*") { Write-Host "Successfully disconnected." } else { Write-Host "Error occurred trying to disconnect : \$request\" } } function Get-LinaGlobalStats { Write-Host "Getting global stats" $request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/info.xml" -ContentType "application/xml" -WebSession $LoggedSession $request = CallAPI -Path "/info.xml" $stats = ([xml]$request).infolist $LinaStats = [PSCustomObject]@{ PSTypeName = 'LinaStats' ALNVersion = $stats.aln_version PercentDiskUse = $stats.disk_use DedupRatio = $stats.dedup_ratio DiskAvailableGB = [math]::Round(($stats.disk_avail)/(1024*1024*1024),2) VolumeProtectedGB = [math]::Round(($stats.vol_protected)/(1024*1024*1024),2) VolumeRestoredGB = [math]::Round(($stats.vol_restored)/(1024*1024*1024),2) VolumeStoredGB = [math]::Round(($stats.vol_stored)/(1024*1024*1024),2) 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 { [cmdletbinding(DefaultParameterSetName="ByName")] Param( [Parameter(ParameterSetName="ByName")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(ParameterSetName="ByID")] [ValidateRange(0,2147483647)] [int]$ID ) Write-Host "Getting list of agents" $timestamp = LinaTimestamp #$request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/lst_clients.xml?timestamp=$timestamp&type=agent" -ContentType "application/xml" -WebSession $LoggedSession $request = CallAPI -Path "/lst_clients.xml?timestamp=$timestamp&type=agent" $Items = ([xml]$request).HNConfig.ClientArray.Client $LinaItems = @() foreach ($Item in $Items) { <# Filtering on ID or Name (only if provided) #> if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR $Item.ID -eq $ID)) { $CurrentLinaItem = [PSCustomObject]@{ PSTypeName = 'LinaAgent' Name = $Item.Name ID = $Item.ID TenantID = $Item.DomainID Strategy = $Item.ServiceProfile.Name Protection = $Item.DataProfile.Name } $LinaItems += $CurrentLinaItem } } Return $LinaItems } function Get-LinaStrategy { [cmdletbinding()] Param( [Parameter(ParameterSetName="Name")] [ValidateNotNullOrEmpty()] [string]$Name ) Write-Host "Getting list of strategies" $timestamp = LinaTimestamp #$request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/lst_serviceprofiles.xml?timestamp=$timestamp" -ContentType "application/xml" -WebSession $LoggedSession $request = CallAPI -Path "/lst_serviceprofiles.xml?timestamp=$timestamp" $Items = ([xml]$request).HNConfig.ServiceProfileArray.ServiceProfile $LinaItems = @() foreach ($Item in $Items) { if (!$Name -OR $Item.name -like "$Name") { $CurrentLinaItem = [PSCustomObject]@{ PSTypeName = 'LinaStrategy' ID = $Item.ID Name = $Item.Name RPOInMinutes = ($Item.ParamSchedule/60) RetentionInDays = $Item.ParamDataAging } $LinaItems += $CurrentLinaItem } } Return $LinaItems } function Get-LinaTenant { [cmdletbinding(DefaultParameterSetName="ByName")] Param( [Parameter(ParameterSetName="ByName")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(ParameterSetName="ByID")] [ValidateNotNullOrEmpty()] [int]$ID ) Write-Host "Getting list of tenants" $timestamp = LinaTimestamp #$request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/ADM/list_domain.json?timestamp=$timestamp" -ContentType "application/json" -WebSession $LoggedSession $request = CallAPI -Path "/ADM/list_domain.json?timestamp=$timestamp" $Items = $request.domains $LinaItems = @() foreach ($Item in $Items) { if ((!$Name -AND !$ID) -OR $Item.name -like "$Name" -OR $Item.ID -eq $ID ) { $CurrentLinaItem = [PSCustomObject]@{ PSTypeName = 'LinaTenant' TenantID = $Item.id UUID = $Item.uuid Name = $Item.name Comment = $Item.comment IsDefault = [bool]$Item.def_domain } $LinaItems += $CurrentLinaItem } } Return $LinaItems } function Get-LinaAgentStats { [cmdletbinding(DefaultParameterSetName="ByName")] Param( [Parameter(ValueFromPipeline)] [pscustomobject]$lina_agent, [Parameter(ParameterSetName="ByName")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(ParameterSetName="ByID")] [ValidateRange(0,2147483647)] [int]$ID ) Write-Host "Getting agents info" $timestamp = LinaTimestamp #$request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/stats_clients.xml?timestamp=$timestamp" -ContentType "application/xml" -WebSession $LoggedSession $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 = 'LinaAgentInfos' Name = $Item.AdminName ID = $Item.AdminID ComputerName = $Item.ComputerName System = $Item.System AgentVersion = $Item.AgentVersion Strategy = $Item.AdminServiceProfileName Protection = $Item.AdminDataProfileName LastLogon = $Item.LastLogon Alert = $Item.Alert LastStartedSession = LinaToLocalTime $Item.LastStartedSession LastCompletedSession = LinaToLocalTime $Item.LastCompletedSession LastConnectionTime = LinaToLocalTime $Item.LastConnectionTime LastSyncTime = LinaToLocalTime $Item.LastSyncTime } $LinaItems += $CurrentLinaItem } } Return $LinaItems } function New-LinaAgent { [cmdletbinding()] Param( [Parameter(Mandatory=$true,ParameterSetName="Name")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory=$false,ParameterSetName="Name")] [ValidateNotNullOrEmpty()] [string]$TenantName ) Write-Host "Creating a Lina agent named $Name" $current_tenant = Get-LinaCurrentTenant 6>$null if (!$TenantName) { if (!$current_tenant -OR $current_tenant.TenantID -le 1) { # No current tenant (global view) => using default tenant $domain = Get-LinaTenant 6>$null | Where-Object {$_.IsDefault} $domain_id=[int]$domain.TenantID $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-LinaCurrentTenant 6>$null $domain_id=[int]$domain.TenantID $domain_name=[string]$domain.Name Write-Host "No tenant selected, agent will be created in current tenant $domain_name (ID $domain_id)" } }else { $domain = Get-LinaTenant 6>$null | Where-Object {$_.Name -eq $TenantName} $domain_id=[int]$domain.TenantID $domain_name=[string]$domain.Name if ( !($domain_id -gt 0) ) { Write-Host "Error : No tenant found with name $TenantName. Please input the exact name of the Tenant" Return } } #Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/ADM/domain_set_current.json?domain_id=$domain_id" -ContentType "application/json" -WebSession $LoggedSession | Out-Null CallAPI -Path "/ADM/domain_set_current.json?domain_id=$domain_id" | Out-Null $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>ADD</Func><Client><Name>'+$Name+'</Name></Client></HNConfig>' $request = Invoke-WebRequest -Uri "$GLOBAL_LINA_SERVER/mng_client.html" -Method "POST" -ContentType "application/xml" -Body $body -WebSession $LoggedSession # Setting back the current tenant to what it was before the New-LinaAgent command $current_tenant | Set-LinaCurrentTenant 6>$null if ([string]$request -like "*[0]*") { Write-Host "Agent $Name successfully created." Return Get-LinaAgent -Name $Name } else { Write-Host "Error occurred trying to create agent $Name : \$request\" Return $null } } function Get-LinaCurrentTenant { Write-Host "Getting current tenant" $timestamp = LinaTimestamp $request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/ADE/check_session.xml?timestamp=$timestamp" -ContentType "application/xml" -WebSession $LoggedSession $current_tenant_id = [int](([xml]$request).session.effective_domain_id) if ($current_tenant_id -eq -1 -OR $current_tenant_id -eq 1) { Write-Host "No current tenant (ID = -1 or 1). Global view on all tenants." Return $current_tenant_id }elseif ($current_tenant_id -gt 1) { Write-Host "Current tenant ID is $current_tenant_id" Return Get-LinaTenant -ID $current_tenant_id } else { Write-Host "Error occurred trying to get current tenant : \$request\" Return $null } } function Set-LinaCurrentTenant { [cmdletbinding(DefaultParameterSetName="ByName")] Param( [Parameter(ValueFromPipeline,ParameterSetName="ByObject")] [pscustomobject]$lina_tenant, [Parameter(Mandatory=$true,ParameterSetName="ByID")] [ValidateNotNullOrEmpty()] [string]$ID, [Parameter(Mandatory=$false,ParameterSetName="All")] [ValidateNotNullOrEmpty()] [switch]$All ) # All tenants is 1 or -1 (no tenant selected, global view) if ($All) { $ID=1 }elseif ($lina_tenant -AND $lina_tenant.TenantID -ge 1) { $ID=$lina_tenant.TenantID }elseif (!$ID) { $ID=1 } Write-Host "Setting current tenant to Tenant ID $ID" $request = Invoke-RestMethod -Uri "$GLOBAL_LINA_SERVER/ADM/domain_set_current.json?domain_id=$ID" -ContentType "application/json" -WebSession $LoggedSession if ($request.status -eq 0) { Write-Host "Current tenant has been set to tenant ID $ID" Return } else { Write-Host "Error occurred trying to set current tenant : \$request\" 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) .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 Remove-LinaAgent -Name "AGENT1*" -Bulk -WhatIf Simulates deletion of a multiple agents by name (WhatIf mode) .EXAMPLE Remove-LinaAgent -Name "AGENT1*" -Bulk Real deletion of a multiple agents by name .EXAMPLE Get-LinaAgent -ID 164 | Remove-LinaAgent -WhatIf Real deletion of a single agent by piping it from a Get .EXAMPLE Get-LinaAgent -Name "TEST1*" | Remove-LinaAgent -Bulk -WhatIf Simulates deletion of multiple agents by piping them from a Get (WhatIf mode) #> [cmdletbinding()] Param( [Parameter(ValueFromPipeline=$True,ParameterSetName="ByPipeline")] [pscustomobject[]]$LinaAgents, [Parameter(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 { if (!$LinaAgents) { $LinaAgents = Get-LinaAgent -Name $Name 6>$null } $names=$LinaAgents.Name Write-Host "Deleting Lina agent(s) : $names"} PROCESS { $nb_of_agents_deleted=0 <# if (!$LinaAgents) { $LinaAgents = Get-LinaAgent -Name $Name 6>$null }#> foreach ($lina_agent in $LinaAgents) { $Name = $lina_agent.Name $delete_id = $lina_agent.ID 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 }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 { Write-Host "Error occurred trying to delete agent $Name : \$request\" } } $nb_of_agents_deleted +=1 if ($nb_of_agents_deleted -ge 1 -AND !$Bulk) { Write-Host "WARNING : Bulk mode has not been enabled, only one agent will be deleted for security." Return } } } END {} } function Set-LinaAgent { [cmdletbinding()] Param( [Parameter(ParameterSetName="UpdateName")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(ParameterSetName="UpdateName")] [ValidateNotNullOrEmpty()] [string]$Newname ) Write-Host "Renaming Lina agent named $Name to new name $Newname" $Items = Get-LinaAgent foreach ($Item in $Items) { if ($Item.Name -eq $Name) { $rename_id=$Item.ID Write-Host "Found agent named $Name with ID $rename_id" } } if (!$rename_id) { Write-Host "WARNING : No agent found with this name." return }else { $body = 'data=<?xml version="1.0" encoding="UTF-8"?><HNConfig><Func>REN</Func><Client><ID>'+$rename_id+'</ID><Name>'+$Newname+'</Name></Client></HNConfig>' $timestamp = LinaTimestamp $request = Invoke-WebRequest -Uri "$GLOBAL_LINA_SERVER/mng_client.html?timestamp=$timestamp" -Method "POST" -ContentType "application/xml" -Body $body -WebSession $LoggedSession if ([string]$request -like "*[0]*") { Write-Host "Agent $Name successfully renamed to $Newname." Return } else { Write-Host "Error occurred trying to delete agent $Name : \$request\" Return } } } |