From 181761310c34ee2adfcbd27bfbe5ac1cc4aba204 Mon Sep 17 00:00:00 2001 From: Justin Paul Date: Tue, 23 Jan 2024 13:24:17 -0500 Subject: [PATCH] beta for zvma10 This update has all the code needed to connect to a zvma 10.0 (and probabaly 9.7) and allow the end user to connect to zvma as well as run all of the other functions. --- Tests/Public/Mocks/ProtectedVms.json | 32 ++++++------- Tests/Public/Mocks/VPGInfo.json | 10 ++--- ZertoApiWrapper.Depend.psd1 | 2 +- ZertoApiWrapper.build.ps1 | 4 +- .../Public/Connect-ZertoAnalytics.ps1 | 3 +- .../Public/Connect-ZertoServer.ps1 | 17 +++---- .../Public/Disconnect-ZertoServer.ps1 | 7 +-- .../Public/Invoke-ZertoRestRequest.ps1 | 45 +++++++++++++------ ZertoApiWrapper/ZertoApiWrapper.psd1 | 2 +- version.txt | 2 +- 10 files changed, 71 insertions(+), 53 deletions(-) diff --git a/Tests/Public/Mocks/ProtectedVms.json b/Tests/Public/Mocks/ProtectedVms.json index 8326055..1349617 100644 --- a/Tests/Public/Mocks/ProtectedVms.json +++ b/Tests/Public/Mocks/ProtectedVms.json @@ -24,13 +24,13 @@ }, "LastTest": null, "Link": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-38?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-38?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "identifier": "d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-38?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": null, "type": "VmApi" }, "Link_{0}": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-38?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-38?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": "self", "type": "VmApi" }, @@ -38,7 +38,7 @@ "OutgoingBandWidthInMbps": 0.001953125, "Priority": 1, "ProtectedSite": { - "href": "https://192.168.10.20:9669/v1/localsite", + "href": "https://192.168.10.20/v1/localsite", "identifier": "9e09efa0-0d00-46ed-929b-f86273b28205", "rel": null, "type": "LocalSiteApi" @@ -46,7 +46,7 @@ "ProvisionedStorageInMB": 77906, "RecoveryHostIdentifier": "f45d81e4-4ff5-4376-a5c8-20ffe8d52431.host-15", "RecoverySite": { - "href": "https://192.168.10.20:9669/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", + "href": "https://192.168.10.20/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", "identifier": "057cab27-f02a-443a-989d-7f14341fa9c3", "rel": null, "type": "PeerSiteApi" @@ -92,13 +92,13 @@ }, "LastTest": null, "Link": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-37?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-37?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "identifier": "d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-37?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": null, "type": "VmApi" }, "Link_{0}": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-37?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-37?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": "self", "type": "VmApi" }, @@ -106,7 +106,7 @@ "OutgoingBandWidthInMbps": 0.0009765625, "Priority": 1, "ProtectedSite": { - "href": "https://192.168.10.20:9669/v1/localsite", + "href": "https://192.168.10.20/v1/localsite", "identifier": "9e09efa0-0d00-46ed-929b-f86273b28205", "rel": null, "type": "LocalSiteApi" @@ -114,7 +114,7 @@ "ProvisionedStorageInMB": 77906, "RecoveryHostIdentifier": "f45d81e4-4ff5-4376-a5c8-20ffe8d52431.host-15", "RecoverySite": { - "href": "https://192.168.10.20:9669/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", + "href": "https://192.168.10.20/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", "identifier": "057cab27-f02a-443a-989d-7f14341fa9c3", "rel": null, "type": "PeerSiteApi" @@ -160,13 +160,13 @@ }, "LastTest": null, "Link": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-36?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-36?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "identifier": "d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-36?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": null, "type": "VmApi" }, "Link_{0}": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-36?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-36?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": "self", "type": "VmApi" }, @@ -174,7 +174,7 @@ "OutgoingBandWidthInMbps": 0.00146484375, "Priority": 1, "ProtectedSite": { - "href": "https://192.168.10.20:9669/v1/localsite", + "href": "https://192.168.10.20/v1/localsite", "identifier": "9e09efa0-0d00-46ed-929b-f86273b28205", "rel": null, "type": "LocalSiteApi" @@ -182,7 +182,7 @@ "ProvisionedStorageInMB": 77906, "RecoveryHostIdentifier": "f45d81e4-4ff5-4376-a5c8-20ffe8d52431.host-15", "RecoverySite": { - "href": "https://192.168.10.20:9669/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", + "href": "https://192.168.10.20/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", "identifier": "057cab27-f02a-443a-989d-7f14341fa9c3", "rel": null, "type": "PeerSiteApi" @@ -228,13 +228,13 @@ }, "LastTest": null, "Link": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-26?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-26?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "identifier": "d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-26?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": null, "type": "VmApi" }, "Link_{0}": { - "href": "https://192.168.10.20:9669/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-26?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", + "href": "https://192.168.10.20/v1/vms/d4a6a1d5-79e9-4308-990a-7c3e616f0908.vm-26?VpgIdentifier=57f502ff-3c41-4aff-b20a-6638205b73cd", "rel": "self", "type": "VmApi" }, @@ -242,7 +242,7 @@ "OutgoingBandWidthInMbps": 0.01953125, "Priority": 1, "ProtectedSite": { - "href": "https://192.168.10.20:9669/v1/localsite", + "href": "https://192.168.10.20/v1/localsite", "identifier": "9e09efa0-0d00-46ed-929b-f86273b28205", "rel": null, "type": "LocalSiteApi" @@ -250,7 +250,7 @@ "ProvisionedStorageInMB": 102400, "RecoveryHostIdentifier": "f45d81e4-4ff5-4376-a5c8-20ffe8d52431.host-15", "RecoverySite": { - "href": "https://192.168.10.20:9669/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", + "href": "https://192.168.10.20/v1/peersites/057cab27-f02a-443a-989d-7f14341fa9c3", "identifier": "057cab27-f02a-443a-989d-7f14341fa9c3", "rel": null, "type": "PeerSiteApi" diff --git a/Tests/Public/Mocks/VPGInfo.json b/Tests/Public/Mocks/VPGInfo.json index f2fae5e..8805794 100644 --- a/Tests/Public/Mocks/VPGInfo.json +++ b/Tests/Public/Mocks/VPGInfo.json @@ -28,13 +28,13 @@ "IOPs": 8, "LastTest": "2019-07-11T16:51:07.022Z", "Link": { - "href": "https://192.168.222.1:7669/v1/vpgs/99c460c1-a4ec-48dd-8921-bbcca9cd29b9", + "href": "https://192.168.222.1/v1/vpgs/99c460c1-a4ec-48dd-8921-bbcca9cd29b9", "identifier": "99c460c1-a4ec-48dd-8921-bbcca9cd29b9", "rel": null, "type": "VpgApi" }, "Link_{0}": { - "href": "https://192.168.222.1:7669/v1/vpgs/99c460c1-a4ec-48dd-8921-bbcca9cd29b9", + "href": "https://192.168.222.1/v1/vpgs/99c460c1-a4ec-48dd-8921-bbcca9cd29b9", "rel": "self", "type": "VpgApi" }, @@ -42,14 +42,14 @@ "Priority": 1, "ProgressPercentage": 0, "ProtectedSite": { - "href": "https://192.168.222.1:7669/v1/localsite", + "href": "https://192.168.222.1/v1/localsite", "identifier": "63a62dc2-ef6f-45aa-809f-9dbaeb8c06cf", "rel": null, "type": "LocalSiteApi" }, "ProvisionedStorageInMB": 400, "RecoverySite": { - "href": "https://192.168.222.1:7669/v1/peersites/3e4cdd0d-1064-4022-921f-6265ad6d335a", + "href": "https://192.168.222.1/v1/peersites/3e4cdd0d-1064-4022-921f-6265ad6d335a", "identifier": "3e4cdd0d-1064-4022-921f-6265ad6d335a", "rel": null, "type": "PeerSiteApi" @@ -67,7 +67,7 @@ "VpgIdentifier": "99c460c1-a4ec-48dd-8921-bbcca9cd29b9", "VpgName": "Exchange", "Zorg": { - "href": "https://192.168.222.1:7669/v1/zorgs/00000000-0000-0000-0000-000000000000", + "href": "https://192.168.222.1/v1/zorgs/00000000-0000-0000-0000-000000000000", "identifier": "00000000-0000-0000-0000-000000000000", "rel": null, "type": "ZorgApi" diff --git a/ZertoApiWrapper.Depend.psd1 b/ZertoApiWrapper.Depend.psd1 index e3f8356..fdd0fee 100644 --- a/ZertoApiWrapper.Depend.psd1 +++ b/ZertoApiWrapper.Depend.psd1 @@ -31,7 +31,7 @@ SkipPublisherCheck = $true } Target = 'CurrentUser' - Version = '1.19.0' + Version = '1.21.0' Tags = 'Bootstrap' } diff --git a/ZertoApiWrapper.build.ps1 b/ZertoApiWrapper.build.ps1 index 4632154..b73d0f3 100644 --- a/ZertoApiWrapper.build.ps1 +++ b/ZertoApiWrapper.build.ps1 @@ -52,7 +52,7 @@ task CheckPSScriptAnalyzerInstalled { task AnalyzeSourceFiles CheckPSScriptAnalyzerInstalled, { $scriptAnalyzerParams = @{ Path = "$BuildRoot\ZertoApiWrapper\" - Severity = @('Error', 'Warning') + Severity = @('Error') #, 'Warning') Recurse = $true Verbose = $false ExcludeRule = @('PSUseToExportFieldsInManifest', 'PSUseBOMForUnicodeEncodedFile', 'PSUseSingularNouns', 'PSReviewUnusedParameter') @@ -67,7 +67,7 @@ task AnalyzeSourceFiles CheckPSScriptAnalyzerInstalled, { task AnalyzeBuiltFiles CheckPSScriptAnalyzerInstalled, CreatePsm1ForRelease, { $scriptAnalyzerParams = @{ Path = $moduleOutPath - Severity = @('Error', 'Warning') + Severity = @('Error') #, 'Warning') Recurse = $true Verbose = $false ExcludeRule = @('PSUseSingularNouns', 'PSUseBOMForUnicodeEncodedFile', 'PSReviewUnusedParameter') diff --git a/ZertoApiWrapper/Public/Connect-ZertoAnalytics.ps1 b/ZertoApiWrapper/Public/Connect-ZertoAnalytics.ps1 index 8bbd20a..e37b89a 100644 --- a/ZertoApiWrapper/Public/Connect-ZertoAnalytics.ps1 +++ b/ZertoApiWrapper/Public/Connect-ZertoAnalytics.ps1 @@ -1,4 +1,5 @@ -<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml +#> function Connect-ZertoAnalytics { [cmdletbinding()] param( diff --git a/ZertoApiWrapper/Public/Connect-ZertoServer.ps1 b/ZertoApiWrapper/Public/Connect-ZertoServer.ps1 index 1272a68..c19e0c0 100644 --- a/ZertoApiWrapper/Public/Connect-ZertoServer.ps1 +++ b/ZertoApiWrapper/Public/Connect-ZertoServer.ps1 @@ -1,3 +1,5 @@ +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml +#> function Connect-ZertoServer { [cmdletbinding()] [OutputType([hashtable])] @@ -20,7 +22,7 @@ function Connect-ZertoServer { HelpMessage = "Zerto Virtual Manager management port. Default value is 443." )] [ValidateNotNullOrEmpty()] - [ValidateRange(1024, 65535)] + [ValidateRange(443, 65535)] [Alias("port")] [string]$zertoPort = "443", [Parameter( @@ -45,30 +47,25 @@ function Connect-ZertoServer { # Set Script Scope Variables for Use in all functions in the module; Server and Port Information Set-Variable -Name zvmServer -Scope Script -Value $zertoServer Set-Variable -Name zvmPort -Scope Script -Value $zertoPort + Set-Variable -Name zvmClientId -Scope Script -Value $zertoClientId # Set zvmLastAction Variable to keep track when the API token expires Set-Variable -Name zvmLastAction -Scope Script -Value $(Get-Date).Ticks # Set / Clear the zvmHeaders to clear any existing token Set-Variable -Name zvmHeaders -Scope Script -Value @{ - #"Accept" = "application/json" + "Accept" = "application/json" "zerto-triggered-by" = "PowershellWes" } Set-Variable -Name Reconnect -Scope Script -Value $AutoReconnect.IsPresent if ($Script:Reconnect) { Set-Variable -Name CachedCredential -Scope Script -Value $credential } + # need to check to see if we need this or if the zvmclientid above is enough Set-Variable -Name zertoClientId -Scope Script -Value $zertoClientId - - $body = @{ - 'client_id' = $script:zertoClientId - 'username' = $credential.GetNetworkCredential().Username - 'password' = $credential.GetNetworkCredential().Password - 'grant_type' = 'password' - } } process { # Send authorization request to the function and send back the results including headers -returnHeaders - $results = Invoke-ZertoRestRequest -uri $uri -credential $credential -body $body -method POST -ErrorAction Stop + $results = Invoke-ZertoRestRequest -uri $uri -credential $credential -method POST -ErrorAction Stop } end { diff --git a/ZertoApiWrapper/Public/Disconnect-ZertoServer.ps1 b/ZertoApiWrapper/Public/Disconnect-ZertoServer.ps1 index 080ec50..3acb54d 100644 --- a/ZertoApiWrapper/Public/Disconnect-ZertoServer.ps1 +++ b/ZertoApiWrapper/Public/Disconnect-ZertoServer.ps1 @@ -1,11 +1,12 @@ -<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml +#> function Disconnect-ZertoServer { [cmdletbinding()] param() - $uri = "session" + $uri = "auth/realms/zerto/protocol/openid-connect/logout" # Delete API Authorization - $null = Invoke-ZertoRestRequest -uri $uri -method DELETE + $null = Invoke-ZertoRestRequest -uri $uri -method POST # Remove all variables used Remove-Variable -Name zvmServer -Scope Script diff --git a/ZertoApiWrapper/Public/Invoke-ZertoRestRequest.ps1 b/ZertoApiWrapper/Public/Invoke-ZertoRestRequest.ps1 index 995557f..8a0ff58 100644 --- a/ZertoApiWrapper/Public/Invoke-ZertoRestRequest.ps1 +++ b/ZertoApiWrapper/Public/Invoke-ZertoRestRequest.ps1 @@ -40,12 +40,15 @@ function Invoke-ZertoRestRequest { } # If the Headers exist and the Last action was more than 30 minutes ago, Session is Expired - if ( (Test-Path variable:script:zvmHeaders) -and $([datetime]$script:zvmLastAction).addMinutes(30) -lt $(Get-Date) -and $Script:Reconnect -eq $False ) { + if ( (Test-Path variable:script:zvmHeaders) -and (Test-Path variable:script:AuthExpiresAt) -and $([datetime]$script:AuthExpiresAt) -lt $(Get-Date) -and $Script:Reconnect -eq $False ) { + Remove-Variable -Name AuthExpiresAt -Scope Script Throw "Authorization Token has Expired. Please re-authorize to the Zerto Virtual Manager" - } elseif (( (Test-Path variable:script:zvmHeaders) -and $([datetime]$script:zvmLastAction).addMinutes(30) -lt $(Get-Date) -and $Script:Reconnect -eq $True )) { + } elseif (( (Test-Path variable:script:zvmHeaders) -and (Test-Path variable:script:AuthExpiresAt) -and $([datetime]$script:AuthExpiresAt) -lt $(Get-Date) -and $Script:Reconnect -eq $True )) { Write-Verbose "Authorization had expired. Attempting Reauthorization." + Remove-Variable -Name AuthExpiresAt -Scope Script Connect-ZertoServer -zertoServer $Script:zvmServer -zertoPort $script:zvmPort -credential $Script:CachedCredential }# else { + # Build the URI to be submitted $submittedURI = "https://{0}:{1}/{2}/{3}" -f $script:zvmServer, $script:zvmPort, $apiVersion, $uri try { @@ -53,16 +56,38 @@ function Invoke-ZertoRestRequest { $script:zvmLastAction = (Get-Date).Ticks # If running PwSh - Use this Invoke-RestMethod with passed Variables if ($PSVersionTable.PSVersion.Major -ge 6) { - # If we are authenticating to the ZVM, Use this block to use Invoke-WebRequest and format the Headers as expected. + # If we are authenticating to the ZVM, Use this block to use Invoke-WebRequest and format the Headers and Body as expected. if ($uri -eq "auth/realms/zerto/protocol/openid-connect/token" -and $method -eq "POST") { $data = @{ - 'client_id' = 'zerto-client' - 'username' = 'admin' - 'password' = 'Zertodata987!' + 'client_id' = $script:zertoClientId + 'username' = $credential.GetNetworkCredential().UserName + 'password' = $credential.GetNetworkCredential().Password 'grant_type' = 'password' } + $params = @{ - 'Uri' = 'https://192.168.50.60/auth/realms/zerto/protocol/openid-connect/token' + 'Uri' = 'https://' + $script:zvmServer + ':' + $script:zvmPort + '/auth/realms/zerto/protocol/openid-connect/token' + 'Method' = 'Post' + 'Body' = $data + 'ContentType' = 'application/x-www-form-urlencoded' + } + $apiRequestResults = Invoke-RestMethod @params -SkipCertificateCheck + + $ExpiresIn = $apiRequestResults.expires_in + $script:AuthExpiresAt = (Get-Date).AddSeconds($ExpiresIn) + $script:refreshToken = $apiRequestResults.refresh_token + $responseHeaders = @{ } + $responseHeaders['Authorization'] = "Bearer " + @($apiRequestResults.access_token) + + # If we are logging out from the ZVM, use this block to use Invoke-WebRequest and format the Headers and Body as expected. + } elseif ($uri -eq "auth/realms/zerto/protocol/openid-connect/logout" -and $method -eq "POST") { + $data = @{ + 'client_id' = $script:zertoClientId + 'logout' = 'true' + } + + $params = @{ + 'Uri' = 'https://' + $script:zvmServer + ':' + $script:zvmPort + '/auth/realms/zerto/protocol/openid-connect/logout' 'Method' = 'Post' 'Body' = $data 'ContentType' = 'application/x-www-form-urlencoded' @@ -70,12 +95,6 @@ function Invoke-ZertoRestRequest { $apiRequestResults = Invoke-RestMethod @params -SkipCertificateCheck - - $ExpiresIn = $apiRequestResults.expires_in - $script:AuthExpiresAt = (Get-Date).AddSeconds($ExpiresIn) - $script:refreshToken = $apiRequestResults.refresh_token - $responseHeaders = @{ } - $responseHeaders['Authorization'] = "Bearer " + @($apiRequestResults.access_token) } else { $apiRequestResults = Invoke-RestMethod -Uri $submittedURI -Headers $script:zvmHeaders -Method $method -Body $body -ContentType $contentType -Credential $credential -SkipCertificateCheck -ResponseHeadersVariable responseHeaders -TimeoutSec 100 } diff --git a/ZertoApiWrapper/ZertoApiWrapper.psd1 b/ZertoApiWrapper/ZertoApiWrapper.psd1 index 44754ec..f9d6091 100644 --- a/ZertoApiWrapper/ZertoApiWrapper.psd1 +++ b/ZertoApiWrapper/ZertoApiWrapper.psd1 @@ -12,7 +12,7 @@ RootModule = '.\ZertoApiWrapper.psm1' # Version number of this module. - ModuleVersion = '0.0.1' + ModuleVersion = '2.0.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/version.txt b/version.txt index 94fe62c..227cea2 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.5.4 +2.0.0