Functions/Common.ps1
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, [Parameter(Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$Method ) 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 (!$Method) { $Method ="POST" } 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 $Method -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 $Method -Body $Body -WebSession $LoggedSession -DisableKeepAlive } } if ($LINA_DEBUG_MODE -AND $Path -notlike "*locale/*.js*") { if ($Path -like "*.json*") { $req_return = $request | ConvertTo-Json }else { $req_return = ([xml]$request).OuterXml } Write-Host2 "Call API $Path : $request $req_return/ Body : $Body" "ERROR" } if ($LINA_DOC_MODE -AND $Path -notlike "*locale/*.js*") { if ($Path -like "*.json*") { $req_return = $request | ConvertTo-Json }else { $type = $request.GetType().FullName if ($type -eq "System.Xml.XmlDocument") { $req_return = ([xml]$request).OuterXml }else { $req_return = $request } } #$clean_path=$path -replace "?timestamp=","xyz" # Get Parent function Name $callStack = Get-PSCallStack if ($callStack.Count -gt 1) { $ParentFunctionName = $callStack[1].FunctionName } $function_description = (Get-Command $ParentFunctionName | get-Help -full | Select-Object -Property "Synopsis").Synopsis $CUR_DOC_OUPUT = "=================================================`n" $CUR_DOC_OUPUT += "POST $Path $ParentFunctionName $function_description`n" $CUR_DOC_OUPUT += "Response :`n" $CUR_DOC_OUPUT += $req_return Write-Host $CUR_DOC_OUPUT $LINA_DOC_OUPUT += $CUR_DOC_OUPUT } 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-Host2 "ERROR : an error occurred trying to $FailedAction : $found_error / Error $LinaError" } function InitTranslations() { Param( [Parameter(Mandatory = $True, Position = 0)] [ValidateNotNullOrEmpty()] [string]$Lang ) # Do not use CallApi to avoid issues with POST method not authorized $Path = "/Admin/locale/$lang.js" $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -Method "GET" -SessionVariable Currentsession -DisableKeepAlive $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", "USER_PROFILE_NAME_","USER_GROUP_NAME_","AGENT_VIEW_BY_AGENT_GROUP_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 } # Translating an array of items => returns array of item translated if ($ToTranslate.Count -gt 1 ) { $words_translated=@() foreach ( $word in $ToTranslate ) { $words_translated+=(Translate $word $FixEncoding) } return $words_translated } $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 -ge 0) { $global:INT_LINA_API_VERSION = 50+$LINA_VERSION.Minor } else { $global:INT_LINA_API_VERSION = 50 } InitTranslations($Locale) Write-Host2 "Successfully connected (Lina version $LINA_VERSION)" Write-Verbose "API version used $INT_LINA_API_VERSION" } else { Write-Host2 "ERROR : cannot connect to server with Lina version below 5.0" Disconnect-LinaServer } } else { Write-Error "ERROR : an 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-Host2 "Successfully disconnected." } else { TranslateErrorCode -LinaError $request -FailedAction "Disconnecting from server" } } # Write to console intelligently with colors Function Write-Host2 ( $message , $type) { Switch -Wildcard ($message) { "*SUCCESS*" { $color = "Green" } "WARNING*" { $color = "Yellow" } "ERROR*" { $color = "Red" } Default { $color = "White" } } # If type is forced , using type Switch -Wildcard ($type) { "*SUCCES*" { $color = "Green" } "*WARN*" { $color = "Yellow" } "*ERR*" { $color = "Red" } "*INFO*" { $color = "White" } } Write-Host $message -ForegroundColor $color } |