VSAModule.psm1
<#
Kaseya VSA9 REST API Wrapper Version 0.1.2 Author: Vladislav Semko Description: VSAModule for Kaseya VSA 9 REST API is a PowerShell module that provides cmdlets for interacting with the Kaseya VSA 9 platform via its REST API. This module simplifies the process of automating tasks, retrieving data, and managing resources within the Kaseya VSA 9 environment directly from PowerShell scripts or the command line. Key Features: - Intuitive cmdlets for common operations such as retrieving information about assests and managing entities. - Secure authentication methods, including support for API tokens, ensuring the confidentiality of sensitive information. - Examples to help users get started quickly and effectively integrate Kaseya VSA 9 functionality into their automation workflows. This module is distributed under the MIT License, allowing for free use, modification, and distribution by users. #> #Import additional functions from Private and Public folders $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) Foreach($import in @($Public + $Private)) { Try { . $import.fullname } Catch { Write-Warning -Msg "Failed to import function $($import.fullname): $_" Continue } } #region Class VSAConnection Add-Type -TypeDefinition @' using System; public class VSAConnection { // Properties to store connection details public string URI { get; set; } public string UserName { get; set; } public string Token { get; set; } public bool IgnoreCertificateErrors { get; set; } public DateTime SessionExpiration { get; set; } public static bool IsPersistent { get; set; } // Changed to static // Method to check connection status public string GetStatus() { string status = "Closed"; if (!string.IsNullOrEmpty(Token)) { status = "Open"; } return status; } // Constructor to initialize connection properties public VSAConnection( string uri, string userName, string token, DateTime sessionExpiration, bool ignoreCertificateErrors = false, bool isPersistent = false) { URI = uri; UserName = userName; Token = token; SessionExpiration = sessionExpiration; IgnoreCertificateErrors = ignoreCertificateErrors; IsPersistent = isPersistent; if (isPersistent) { SetPersistent(isPersistent); } } public string GetToken() { return Token; } public void SetPersistent(bool isPersistent = true) { IsPersistent = isPersistent; if (IsPersistent) { string serial = URI + "\t" + Token + "\t" + UserName + "\t" + SessionExpiration + "\t" + IgnoreCertificateErrors + "\t" + IsPersistent; string encoded = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(serial)); Environment.SetEnvironmentVariable("VSAConnection", encoded); } else { Environment.SetEnvironmentVariable("VSAConnection", null); } } public static bool GetPersistent() { return IsPersistent; } public static string GetPersistentURI() { string theURI = string.Empty; if (IsPersistent) { string encoded = Environment.GetEnvironmentVariable("VSAConnection"); if (encoded != null) { string serial = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encoded)); string[] separateValues = serial.Split('\t'); theURI = separateValues[0]; } } return theURI; } public static string GetPersistentToken() { string theToken = string.Empty; if (IsPersistent) { string encoded = Environment.GetEnvironmentVariable("VSAConnection"); if (encoded != null) { string serial = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encoded)); string[] separateValues = serial.Split('\t'); theToken = separateValues[1]; } } return theToken; } public static bool GetIgnoreCertErrors() { bool ignoreCertErrors = false; if (IsPersistent) { string encoded = Environment.GetEnvironmentVariable("VSAConnection"); if (encoded != null) { string serial = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encoded)); string[] separateValues = serial.Split('\t'); bool.TryParse(separateValues[4], out ignoreCertErrors); } } return ignoreCertErrors; } } '@ #endregion Class VSAConnection #region Class TrustAllCertsPolicy # Define a TrustAllCertsPolicy class to handle certificate validation Add-Type -TypeDefinition @' using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } '@ #endregion Class TrustAllCertsPolicy #region function New-VSAConnection function New-VSAConnection { <# .SYNOPSIS Creates a VSAConnection object. .DESCRIPTION Creates a VSAConnection object that encapsulates access token as well as additional connection information. Optionally makes the connection object persistent. .PARAMETER VSAServer Specifies the address of the VSA Server to connect. .PARAMETER Credential Specifies the existing VSA user credentials that are allowed to connect to the VSA through the REST API. .PARAMETER AuthSuffix Specifies the URI suffix for the authorization endpoint, if it differs from the default '/API/v1.0/Auth'. .PARAMETER IgnoreCertificateErrors Indicates whether to allow self-signed certificates. Default is false. .PARAMETER SetPersistent Indicates whether to make the VSAConnection object persistent during the session so that module commandlets will use the connection information implicitly. .EXAMPLE # Example 1: Creating a VSAConnection object with persistent setting # This command creates a VSAConnection object to connect to a VSA server with the provided credentials and makes the connection persistent during the session. New-VSAConnection -VSAServer "https://vsaserver.example.com" -Credential (Get-Credential) -SetPersistent # Example 2: Creating a VSAConnection object with custom authorization URI suffix # This command creates a VSAConnection object and ignores certificate errors. New-VSAConnection -VSAServer "https://vsaserver.example.com" -Credential (Get-Credential) -IgnoreCertificateErrors .INPUTS Accepts response object from the authorization API. .OUTPUTS VSAConnection. New-VSAConnection returns an object of VSAConnection type that encapsulates access token as well as additional connection information. #> [cmdletbinding()] [OutputType([VSAConnection])] param( [parameter( Mandatory = $true, Position = 0)] [ValidateScript( {if ($_ -match '^http(s)?:\/\/([\w.-]+(?:\.[\w\.-]+)+|((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}|((([0-9a-fA-F]){1,4})\:){7}([0-9a-fA-F]){1,4}|localhost)(\/)?$') {$true} else {Throw "$_ is invalid. Enter a valid address that begins with https://"}} )] [String]$VSAServer, [parameter(Mandatory = $false)] [PSCredential] $Credential, [parameter(DontShow, Mandatory=$false)] [ValidateNotNullOrEmpty()] [string] $AuthSuffix = 'API/v1.0/Auth', [parameter(Mandatory=$false)] [switch] $IgnoreCertificateErrors, [parameter(Mandatory=$false)] [Alias('MakePersistent')] [switch] $SetPersistent ) #region Apply Certificate Policy if ($IgnoreCertificateErrors) { Write-Warning -Message "Ignoring certificate errors may expose your connection to potential security risks.`nBy enabling this option, you accept all associated risks, and any consequences are solely your responsibility.`n" } #endregion Apply Certificate Policy if (-not $Credential) { $Credential = Get-Credential -Message "Please provide Username and Personal Authentication Token" } $UserName = $Credential.UserName $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.Password) $Encoded = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("$UserName`:$([System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR))")) $AuthString = "Basic $Encoded" $VSAServerUri = New-Object System.Uri -ArgumentList $VSAServer $AuthEndpoint = [System.Uri]::new($VSAServerUri, $AuthSuffix) | Select-Object -ExpandProperty AbsoluteUri $Msg = "Attempting authentication for user '$UserName' on VSA server '$VSAServer'." if ($PSCmdlet.MyInvocation.BoundParameters['Verbose']) { Write-Verbose $Msg } if ($PSCmdlet.MyInvocation.BoundParameters['Debug']) { Write-Debug $Msg } $AuthParams = @{ URI = $AuthEndpoint AuthString = $AuthString IgnoreCertificateErrors = $IgnoreCertificateErrors } $response = Get-RequestData @AuthParams $result = $response | Select-Object -ExpandProperty Result if ([string]::IsNullOrEmpty($result)) { throw "Failed to retrieve authentication response from '$VSAServer' for user '$username'`n$("Response Code: '$($response.ResponseCode)'`nResponse Error: '$($response.Error)'`n")" } else { $SessionExpiration = [DateTime]::ParseExact($result.SessionExpiration, "yyyy-MM-ddTHH:mm:ssZ", [System.Globalization.CultureInfo]::InvariantCulture) $SessionExpiration = $SessionExpiration.AddMinutes($result.OffSetInMinutes) $VSAConnection = [VSAConnection]::new($VSAServer, $result.UserName, $result.Token, $SessionExpiration, $IgnoreCertificateErrors, $SetPersistent) $Msg = "`tUser '$UserName' authenticated on VSA server '$VSAServer'.`n`tSession token expiration: $SessionExpiration (UTC).`n" Write-Host $Msg if ($PSCmdlet.MyInvocation.BoundParameters['Verbose']) { Write-Verbose $Msg } if ($PSCmdlet.MyInvocation.BoundParameters['Debug']) { Write-Debug $Msg Write-Debug "New-VSAConnection result: '$($result | ConvertTo-Json)'" } if ($SetPersistent) { $Msg = "`tConnection to server '$VSAServer' set Persistent during the current session so the VSAModule's commandlets can use the connection implicitly.`n" Write-Host $Msg if ($PSCmdlet.MyInvocation.BoundParameters['Verbose']) { Write-Verbose $Msg } if ($PSCmdlet.MyInvocation.BoundParameters['Debug']) { Write-Debug $Msg } } } if ($SetPersistent) { $VSAConnection.SetPersistent($true) } return $VSAConnection } #endregion function New-VSAConnection Export-ModuleMember -Function New-VSAConnection |