lib/Authentication.ps1
## Define a ZertoSessions Variable to store connection details in New-Variable -Scope global -Name 'ZertoSessions' -Value @{ } -Force function New-ZertoSession { <# .SYNOPSIS Creates a connection to a Zerto VM instance .DESCRIPTION This function creates a connection to the specified Zerto instance using the provided credentials. This session can be used when calling other functions within the Zerto module .PARAMETER SessionName The name that will be used when referring to the created ZertoSession .PARAMETER Server The URI of the Zerto instance to connect to .PARAMETER Credential The credentials to be used twhen connecting to Zerto .PARAMETER AllowInsecureSSL Boolean indicating whether or not an insecure SSL connection is allowed .PARAMETER ProfileName The name of the stored ZertoProfile which contains the connection settings .EXAMPLE $Session = @{ SessionName = 'TMDDEV' Server = 'tmddev.Zerto.net' Credential = (Get-StoredCredential -Name 'ME') } New-ZertoSession @Session .EXAMPLE Get-ZertoProfile -Name 'Zerto.example.com' | New-ZertoSession .EXAMPLE New-ZertoSession -ProfileName 'Zerto.example.com' .OUTPUTS None #> [CmdletBinding()] [Alias('Connect-ZertoServer')] param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] [String]$SessionName = 'Default', [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [String]$Server, [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [Int]$Port = 9669, [Parameter(Mandatory = $true, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [PSCredential]$Credential, [Parameter(Mandatory = $false, ParameterSetName = 'ByProperty', ValueFromPipelineByPropertyName = $true)] [Bool]$AllowInsecureSSL = $false, [Parameter(Mandatory = $false)] [Switch]$Passthru, # [Parameter(Mandatory = $true, # ParameterSetName = 'ByProfileObject')] # [ZertoProfile]$ZertoProfile, [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByProfile')] [ArgumentCompleter( { Get-ZertoProfile -List })] [Alias('Profile')] [String]$ProfileName ) begin { ## Create the TMServers Array that will be reachable at $global:ZertoSessions if (-not $global:ZertoSessions) { New-Variable -Name ZertoSessions -Scope Global -Value @{} } } process { if ($ProfileName) { $ZertoProfile = Get-ZertoProfile -Name $ProfileName if (!$ZertoProfile) { Write-Error "Could not load a Zerto profile named '$ProfileName'" return } } if ($ZertoProfile) { $SessionName = (($global:ZertoSessions.Keys -contains 'Default') -and ($SessionName -eq 'Default')) ? $ZertoProfile.Name : 'Default' $Server = $ZertoProfile.Server $Credential = $ZertoProfile.Credential $AllowInsecureSSL = $ZertoProfile.AllowInsecureSSL } else { if ($SessionName -eq 'Default') { $SessionName = (($global:ZertoSessions.Keys -contains 'Default') -and ($SessionName -eq 'Default')) ? $Server : 'Default' } } ## Check for Existing Session to this server if ($global:ZertoSessions.Keys -contains $SessionName) { $NewZertoSession = $global:ZertoSessions[$SessionName] } else { ## Create a session object for this new connection $NewZertoSession = [ZertoSession]::new($SessionName, $Server, $AllowInsecureSSL) } ## Honor SSL Settings from the user $CertSettings = @{ SkipCertificateCheck = $AllowInsecureSSL } ## Trim the server name $Instance = $Server.Replace('https://', '').Replace('http://', '') ## Save the Instance and Port used $NewZertoSession.ZertoServer = $Instance $NewZertoSession.ZertoPort = $Port ## Prepare Request Headers for use in the Session Header Cache $ContentType = 'application/json;charset=UTF-8' # Format the uri $uri = "https://$($Instance):$($Port)/v1/session/add" # Authenticating with Zerto APIs - Basic AUTH over SSL if ($Credential.GetNetworkCredential().domain) { $authInfo = ("{0}\{1}:{2}" -f $Credential.GetNetworkCredential().domain , $Credential.GetNetworkCredential().UserName, $Credential.GetNetworkCredential().Password ) } else { $authInfo = ("{0}:{1}" -f $Credential.GetNetworkCredential().UserName, $Credential.GetNetworkCredential().Password ) } $authInfo = [System.Text.Encoding]::UTF8.GetBytes($authInfo) $authInfo = [System.Convert]::ToBase64String($authInfo) $RequestHeaders = @{ "Authorization" = ("Basic {0}" -f $authInfo) "Accept-Version" = "1.0"; "Content-Type" = $ContentType; "Accept" = "application/json"; "Cache-Control" = "no-cache"; } $WebRequestSplat = @{ Method = 'POST' Uri = $uri Headers = $RequestHeaders SessionVariable = 'ZertoWebSession' PreserveAuthorizationOnRedirect = $true ContentType = $ContentType } ## Attempt Login if ($VerbosePreference -eq 'Continue') { Write-Host "Logging into Zerto instance [ " -NoNewline Write-Host $Instance -ForegroundColor Cyan -NoNewline Write-Host " ]" } # Make the request try { Write-Verbose "Web Request Parameters:" Write-Verbose ($WebRequestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $Response = Invoke-WebRequest @WebRequestSplat @CertSettings -ProgressAction 'SilentlyContinue' Write-Verbose "Response status code: $($Response.StatusCode)" Write-Verbose "Response Content: $($Response.Content)" ## Check the Response code for 200 if ($Response.StatusCode -eq 200) { $ZertoSessionToken = $Response.headers.get_item("x-zerto-session") | Select-Object -First 1 $ZertoWebSession.Headers["x-zerto-session"] = $ZertoSessionToken $ZertoWebSession.Headers.Remove('Authorization') $NewZertoSession.ZertoVersion = 9 } } catch { ## Check for a 404, which would possibly indicate that the server is a v10 if ($_.Exception.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) { Write-Verbose "Session endpoint is missing, this might be a v10 system" } else { throw $_ } } ## Check for API Version change 9 -> 10 if (-Not $Response) { # Make the request try { $uri = "https://$($Instance):$($Port)/auth/realms/zerto/protocol/openid-connect/token" $WebRequestSplat = @{ Method = 'POST' Uri = $uri SessionVariable = 'ZertoWebSession' PreserveAuthorizationOnRedirect = $true Body = @{ grant_type = 'password' client_id = 'zerto-client' username = $Credential.UserName password = $Credential.GetNetworkCredential().Password } } Write-Verbose "Web Request Parameters:" Write-Verbose ($WebRequestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" ## Honor SSL Settings from the user $CertSettings = @{ SkipCertificateCheck = $AllowInsecureSSL } $Response = Invoke-WebRequest @WebRequestSplat @CertSettings -ProgressAction 'SilentlyContinue' Write-Verbose "Response status code: $($Response.StatusCode)" Write-Verbose "Response Content: $($Response.Content)" ## Check the Response code for 200 if ($Response.StatusCode -eq 200) { $ResponseContent = $Response.Content | ConvertFrom-Json $ZertoWebSession.Headers['Authorization'] = "Bearer $($ResponseContent.access_token)" $NewZertoSession.ZertoVersion = 10 Write-Verbose "Now: $(Get-Date)" $NewZertoSession.AccessToken = $ResponseContent.access_token $NewZertoSession.TokenExpires = (Get-Date).AddSeconds($ResponseContent.expires_in) Write-Verbose "Token Expires: $($NewZertoSession.TokenExpires)" $NewZertoSession.RefreshToken = $ResponseContent.refresh_token $NewZertoSession.RefreshTokenExpires = (Get-Date).AddSeconds($ResponseContent.refresh_expires_in) Write-Verbose "Refresh Token Expires: $($NewZertoSession.RefreshTokenExpires)" } } catch { Write-Host $_.Exception.Message Write-Host $_.Exception.InnerException.Message throw $_ } } $NewZertoSession.ZertoWebSession = $ZertoWebSession ## Add this Session to the ZertoSessions list $global:ZertoSessions[$SessionName] = $NewZertoSession ## Return the session if requested if ($Passthru) { $NewZertoSession } } } Function Get-ZertoSession { [CmdletBinding(DefaultParameterSetName = 'ListAll')] param( [Parameter(HelpMessage = 'Zerto Session Name', ParameterSetName='ByName')][String]$SessionName = 'Default', [Parameter(HelpMessage = 'List All Zerto Sessions', ParameterSetName='ListAll')][switch]$List ) if($List.IsPresent){ return $global:ZertoSessions.Keys } ## Check for Existing Session to this server if ($global:ZertoSessions -and $global:ZertoSessions.Keys -contains $SessionName) { $ZertoSessionConfig = $global:ZertoSessions[$SessionName] } else { Write-Host 'ZertoSession: [' -NoNewline Write-Host $SessionName -ForegroundColor Cyan Write-Host '] was not Found. Please use the New-ZertoSession command.' Throw "Zerto Session [$($SessionName)] Not Found. Use New-ZertoSession command before using features." } if ($ZertoSessionConfig.ZertoVersion -eq "10") { ## Set time of Now with a bit of an offset to force refresh before expiration $Now = (Get-Date).AddSeconds(-10) if($ZertoSessionConfig.TokenExpires -lt $Now){ Write-Verbose "Token Is Expired" if($ZertoSessionConfig.RefreshTokenExpires -gt $Now){ Write-Verbose "Token Refresh is still valid. Refreshing Token." # Make the request $uri = "https://$($ZertoSessionConfig.ZertoServer):$($ZertoSessionConfig.ZertoPort)/auth/realms/zerto/protocol/openid-connect/token" $WebRequestSplat = @{ Method = 'POST' Uri = $uri SessionVariable = 'ZertoWebSession' PreserveAuthorizationOnRedirect = $true Body = @{ grant_type = 'refresh_token' client_id = 'zerto-client' refresh_token = $ZertoSessionConfig.RefreshToken } } Write-Verbose "Web Request Parameters:" Write-Verbose ($WebRequestSplat | ConvertTo-Json -Depth 10) Write-Verbose "Invoking web request" $CertSettings = @{ SkipCertificateCheck = $ZertoSessionConfig.AllowInsecureSSL } $Response = Invoke-WebRequest @WebRequestSplat @CertSettings -ProgressAction 'SilentlyContinue' Write-Verbose "Response status code: $($Response.StatusCode)" Write-Verbose "Response Content: $($Response.Content)" ## Check the Response code for 200 if ($Response.StatusCode -eq 200) { $ResponseContent = $Response.Content | ConvertFrom-Json $ZertoSessionConfig.ZertoWebSession.Headers['Authorization'] = "Bearer $($ResponseContent.access_token)" $ZertoSessionConfig.ZertoVersion = 10 $ZertoSessionConfig.AccessToken = $ResponseContent.access_token $ZertoSessionConfig.TokenExpires = (Get-Date).AddSeconds($ResponseContent.expires_in) $ZertoSessionConfig.RefreshToken = $ResponseContent.refresh_token $ZertoSessionConfig.RefreshTokenExpires = (Get-Date).AddSeconds($ResponseContent.refresh_expires_in) } } } } return $ZertoSessionConfig } function Remove-ZertoSession { [CmdletBinding()] param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] [String]$SessionName = 'Default' ) ## Check for Existing Session to this server if ($global:ZertoSessions.Keys -contains $SessionName) { $TheSession = $global:ZertoSessions[$SessionName] } # $ThisSession.Remove() } |