diff --git a/CHANGELOG.md b/CHANGELOG.md index beb3345..ffc6a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project is transitioning to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.5.1] + +### Zerto Virtual Manager + +#### Fixed + +* Fixed an [issue](https://github.com/ZertoPublic/ZertoApiWrapper/issues/108) with `New-ZertoVpg` where when specifying a single host as a recovery target, the host identifier was not properly assigned. - Thanks @jonsouzerto! +* Fixed an [issue](https://github.com/ZertoPublic/ZertoApiWrapper/issues/86) with `Invoke-ZertoMoveVpg` where parameters should have been mandatory in certain workflows. - Thanks @gdbarron! + ## [1.5.0] ### Zerto Virtual Manager diff --git a/Tests/Public/Invoke-ZertoMove.Tests.ps1 b/Tests/Public/Invoke-ZertoMove.Tests.ps1 index aaca87b..efb1260 100644 --- a/Tests/Public/Invoke-ZertoMove.Tests.ps1 +++ b/Tests/Public/Invoke-ZertoMove.Tests.ps1 @@ -5,17 +5,18 @@ $global:function = ((Split-Path -leaf $PSCommandPath).Split('.'))[0] Describe $global:function -Tag 'Unit', 'Source', 'Built' { Context "$global:function::Parameter Unit Tests" { - It "$global:function should have exactly 20 parameters defined" { - (Get-Command $global:function).Parameters.Count | Should -Be 20 + It "$global:function should have exactly 21 parameters defined" { + (Get-Command $global:function).Parameters.Count | Should -Be 21 } $ParameterTestCases = @( + @{ParameterName = 'vpgIdentifier'; Type = 'Guid[]'; Mandatory = $true; Validation = 'NotNullOrEmpty' } @{ParameterName = 'vpgName'; Type = 'String[]'; Mandatory = $true; Validation = 'NotNullOrEmpty' } @{ParameterName = 'commitPolicy'; Type = 'String'; Mandatory = $false; Validation = 'Set' } @{ParameterName = 'commitPolicyTimeout'; Type = 'Int'; Mandatory = $false; Validation = 'Range' } @{ParameterName = 'forceShutdown'; Type = 'Switch'; Mandatory = $false; Validation = $null } - @{ParameterName = 'disableReverseProtection'; Type = 'Switch'; Mandatory = $true; Validation = $null } - @{ParameterName = 'keepSourceVms'; Type = 'Switch'; Mandatory = $true; Validation = $null } + @{ParameterName = 'disableReverseProtection'; Type = 'Switch'; Mandatory = $false; Validation = $null } + @{ParameterName = 'keepSourceVms'; Type = 'Switch'; Mandatory = $false; Validation = $null } @{ParameterName = 'ContinueOnPreScriptFailure'; Type = 'Switch'; Mandatory = $false; Validation = $null } @{ParameterName = 'whatIf'; Type = 'Switch'; Mandatory = $false; Validation = 'ShouldProcess' } ) diff --git a/ZertoApiWrapper/Public/Invoke-ZertoMove.ps1 b/ZertoApiWrapper/Public/Invoke-ZertoMove.ps1 index 476f7d3..a63bb98 100644 --- a/ZertoApiWrapper/Public/Invoke-ZertoMove.ps1 +++ b/ZertoApiWrapper/Public/Invoke-ZertoMove.ps1 @@ -1,22 +1,57 @@ <# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> function Invoke-ZertoMove { - [CmdletBinding( DefaultParameterSetName = "main", SupportsShouldProcess = $true )] + [CmdletBinding( DefaultParameterSetName = "id", SupportsShouldProcess = $true )] param( [Parameter( + ParameterSetName = 'name', + HelpMessage = "Name(s) of the VPG(s) you want to move.", + Mandatory + )] + [Parameter( + ParameterSetName = 'commitName', HelpMessage = "Name(s) of the VPG(s) you want to move.", Mandatory )] [ValidateNotNullOrEmpty()] [string[]]$vpgName, [Parameter( + ParameterSetName = 'id', + HelpMessage = "ID(s) of the VPG(s) you want to move.", + Mandatory, + ValueFromPipelineByPropertyName + )] + [Parameter( + ParameterSetName = 'commitId', + HelpMessage = "ID(s) of the VPG(s) you want to move.", + Mandatory, + ValueFromPipelineByPropertyName + )] + [ValidateNotNullOrEmpty()] + [guid[]]$vpgIdentifier, + [Parameter( + ParameterSetName = 'commitName', HelpMessage = "'Rollback': After the seconds specified in the commitValue setting have elapsed, the failover is rolled back. 'Commit': After the seconds specified in the commitValue setting have elapsed, the failover continues, committing the virtual machines in the recovery site. 'None': The virtual machines in the VPG being failed over remain in the Before Commit state until either they are committed with Commit a failover, or rolled back with Roll back a failover. - Default is the Site Settings setting." + Default is the Site Settings setting.", + Mandatory + )] + [Parameter( + ParameterSetName = 'commitId', + HelpMessage = "'Rollback': After the seconds specified in the commitValue setting have elapsed, the failover is rolled back. + 'Commit': After the seconds specified in the commitValue setting have elapsed, the failover continues, committing the virtual machines in the recovery site. + 'None': The virtual machines in the VPG being failed over remain in the Before Commit state until either they are committed with Commit a failover, or rolled back with Roll back a failover. + Default is the Site Settings setting.", + Mandatory )] [ValidateSet("Rollback", "Commit", "None")] [string]$commitPolicy, [Parameter( + ParameterSetName = 'commitName', + HelpMessage = "The amount of time, in seconds, the Move is in a 'Before Commit' state, before performing the commitPolicy setting. If omitted, the site settings default will be applied." + )] + [Parameter( + ParameterSetName = 'commitId', HelpMessage = "The amount of time, in seconds, the Move is in a 'Before Commit' state, before performing the commitPolicy setting. If omitted, the site settings default will be applied." )] # Min 5 Minutes, Max 24 Hours, Default Site Settigns. @@ -27,15 +62,11 @@ function Invoke-ZertoMove { )] [switch]$forceShutdown, [Parameter( - ParameterSetName = "disableReverseProtection", - HelpMessage = "Do not enable reverse protection. The VPG definition is kept with the status Needs Configuration and the reverse settings in the VPG definition are not set.", - Mandatory + HelpMessage = "Do not enable reverse protection. The VPG definition is kept with the status Needs Configuration and the reverse settings in the VPG definition are not set." )] [switch]$disableReverseProtection, [Parameter( - ParameterSetName = "keepSourceVms", - HelpMessage = "Prevent the protected virtual machines from being deleted in the protected site. Using this setting disables reverse protection.", - Mandatory + HelpMessage = "Prevent the protected virtual machines from being deleted in the protected site. Using this setting disables reverse protection." )] [switch]$keepSourceVms, [Parameter( @@ -46,52 +77,50 @@ function Invoke-ZertoMove { begin { $baseUri = "vpgs" + $body = @{ + forceShutdown = $forceShutdown.IsPresent + ContinueOnPreScriptFailure = $ContinueOnPreScriptFailure.IsPresent + keepSourceVms = $keepSourceVms.IsPresent + reverseProtection = -not $disableReverseProtection.IsPresent + } + + if ( $keepSourceVms.IsPresent -and -not $disableReverseProtection.IsPresent ) { + Write-Verbose 'Disabling reverse protection as keepSourceVms requires it' + $body['reverseProtection'] = $false + } + + if ($PSBoundParameters.ContainsKey('commitPolicy')) { + $body['commitPolicy'] = $commitPolicy + if ($PSBoundParameters.ContainsKey('commitPolicyTimeout')) { + $body['commitPolicyTimeout'] = $commitPolicyTimeout + } + } } process { - $body = @{ } - #TODO - use a foreach loop to populate the body without all the if statments - if ($PSBoundParameters.ContainsKey('commitPolicy')) { - $body['commitPolicy'] = $commitPolicy - } - if ($PSBoundParameters.ContainsKey('commitPolicyTimeout')) { - $body['commitPolicyTimeout'] = $commitPolicyTimeout - } - if ($PSBoundParameters.ContainsKey('forceShutdown')) { - $body['forceShutdown'] = $true - } else { - $body['forceShutdown'] = $false - } - if ($PSBoundParameters.ContainsKey('ContinueOnPreScriptFailure')) { - $body['ContinueOnPreScriptFailure'] = $true - } else { - $body['ContinueOnPreScriptFailure'] = $false - } + switch ($PSCmdlet.ParameterSetName) { - "disableReverseProtection" { - $body['reverseProtection'] = $false - $body['keepSourceVms'] = $false + { $_ -in 'name', 'commitName' } { + $vpgIds = foreach ($name in $vpgName) { + $vpgId = $(Get-ZertoVpg -name $name).vpgIdentifier + if ( -not $vpgId ) { + Write-Error "VPG: '$name' not found. Please check the name and try again. Skipping" + } else { + Write-Verbose "VPG: $name, ID: $vpgId" + $vpgId + } + } } - "keepSourceVms" { - $body['reverseProtection'] = $false - $body['keepSourceVms'] = $true - } - - "main" { - $body['reverseProtection'] = $true - $body['keepSourceVms'] = $false + { $_ -in 'id', 'commitId' } { + $vpgIds = $vpgIdentifier } } - foreach ($name in $vpgName) { - $vpgId = $(Get-ZertoVpg -name $name).vpgIdentifier - if ( -not $vpgId ) { - Write-Error "VPG: $name not found. Please check the name and try again. Skipping" - } else { - $uri = "{0}/{1}/move" -f $baseUri, $vpgId - if ($PSCmdlet.ShouldProcess("Moving VPG: $name with settings: $($body | convertto-json)")) { - Invoke-ZertoRestRequest -uri $uri -method "POST" -body $($body | ConvertTo-Json) - } + + foreach ($thisId in $vpgIds) { + $uri = "{0}/{1}/move" -f $baseUri, $thisId + if ($PSCmdlet.ShouldProcess("Moving VPG: $thisId with settings: $($body | ConvertTo-Json)")) { + Invoke-ZertoRestRequest -uri $uri -method "POST" -body $($body | ConvertTo-Json) } } } diff --git a/ZertoApiWrapper/Public/New-ZertoVpg.ps1 b/ZertoApiWrapper/Public/New-ZertoVpg.ps1 index 710f51d..582e0cc 100644 --- a/ZertoApiWrapper/Public/New-ZertoVpg.ps1 +++ b/ZertoApiWrapper/Public/New-ZertoVpg.ps1 @@ -295,11 +295,11 @@ function New-ZertoVpg { "recoveryHostDatastoreCluster" { $baseSettings.Recovery.DefaultDatastoreClusterIdentifier = $identifiersTable['datastoreClusterIdentifier'] - $baseSettings.Recovery.DefaultHostIdentifier = $identifiersTable['hostIdentifier'] + $baseSettings.Recovery.DefaultHostIdentifier = $identifiersTable['recoveryHostIdentifier'] } "recoveryHostDatastore" { - $baseSettings.Recovery.DefaultHostIdentifier = $identifiersTable['hostIdentifier'] + $baseSettings.Recovery.DefaultHostIdentifier = $identifiersTable['recoveryHostIdentifier'] $baseSettings.Recovery.DefaultDatastoreIdentifier = $identifiersTable['datastoreIdentifier'] } diff --git a/docs/Invoke-ZertoMove.md b/docs/Invoke-ZertoMove.md index eb1c661..030ba21 100644 --- a/docs/Invoke-ZertoMove.md +++ b/docs/Invoke-ZertoMove.md @@ -12,22 +12,30 @@ Start a move of a VPG. ## SYNTAX -### main (Default) +### id (Default) ``` -Invoke-ZertoMove -vpgName [-commitPolicy ] [-commitPolicyTimeout ] [-forceShutdown] +Invoke-ZertoMove -vpgIdentifier [-forceShutdown] [-disableReverseProtection] [-keepSourceVms] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] ``` -### disableReverseProtection +### commitName ``` -Invoke-ZertoMove -vpgName [-commitPolicy ] [-commitPolicyTimeout ] [-forceShutdown] - [-disableReverseProtection] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] +Invoke-ZertoMove -vpgName -commitPolicy [-commitPolicyTimeout ] [-forceShutdown] + [-disableReverseProtection] [-keepSourceVms] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] + [] ``` -### keepSourceVms +### name ``` -Invoke-ZertoMove -vpgName [-commitPolicy ] [-commitPolicyTimeout ] [-forceShutdown] - [-keepSourceVms] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] +Invoke-ZertoMove -vpgName [-forceShutdown] [-disableReverseProtection] [-keepSourceVms] + [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] +``` + +### commitId +``` +Invoke-ZertoMove -vpgIdentifier -commitPolicy [-commitPolicyTimeout ] [-forceShutdown] + [-disableReverseProtection] [-keepSourceVms] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] + [] ``` ## DESCRIPTION @@ -37,10 +45,24 @@ Start a move of a VPG. ### Example 1 ```powershell -PS C:\> Invoke-ZertoMove -vpgName "MyVpg" +PS C:\> Invoke-ZertoMove -vpgName 'MyVpg' ``` -Starts a move operation of VPG "MyVpg" +Specify the name of a vpg to move + +### Example 2 +```powershell +PS C:\> Invoke-ZertoMove -vpgIdentifier '2fbbf6b5-cddc-4653-b1fe-564f069eeb64' +``` + +Specify the identifier of a vpg to move + +### Example 3 +```powershell +PS C:\> Get-ZertoVpg | Invoke-ZertoMove +``` + +Utilize the pipeline to move multiple vpgs ## PARAMETERS @@ -55,11 +77,11 @@ Default is the Site Settings setting. ```yaml Type: String -Parameter Sets: (All) +Parameter Sets: commitName, commitId Aliases: Accepted values: Rollback, Commit, None -Required: False +Required: True Position: Named Default value: None Accept pipeline input: False @@ -72,7 +94,7 @@ If omitted, the site settings default will be applied. ```yaml Type: Int32 -Parameter Sets: (All) +Parameter Sets: commitName, commitId Aliases: Required: False @@ -102,10 +124,10 @@ Do not enable reverse protection. The VPG definition is kept with the status Nee ```yaml Type: SwitchParameter -Parameter Sets: disableReverseProtection +Parameter Sets: (All) Aliases: -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -132,13 +154,28 @@ Use this switch to Prevent the protected virtual machines from being deleted in ```yaml Type: SwitchParameter -Parameter Sets: keepSourceVms +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -vpgIdentifier +ID(s) of the VPG(s) you want to move. + +```yaml +Type: Guid[] +Parameter Sets: id, commitId Aliases: Required: True Position: Named Default value: None -Accept pipeline input: False +Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` @@ -147,7 +184,7 @@ Name(s) of the VPG(s) you want to move. ```yaml Type: String[] -Parameter Sets: (All) +Parameter Sets: commitName, name Aliases: Required: True diff --git a/version.txt b/version.txt index bc80560..26ca594 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.5.0 +1.5.1