From df82bc26caac86d0e8baf21daa9990c96d07573f Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Fri, 31 Jul 2020 17:42:28 -0400 Subject: [PATCH 01/21] Add function Add-ZertoVpgVm --- ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 | 77 +++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 diff --git a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 new file mode 100644 index 0000000..c65160b --- /dev/null +++ b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 @@ -0,0 +1,77 @@ +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> +function Add-ZertoVpgVm { + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter( + Mandatory, + HelpMessage = "Vpg Settings Identifier", + ValueFromPipeline, + ValueFromPipelineByPropertyName, + ValueFromRemainingArguments + )] + [ValidateNotNullOrEmpty()] + [String]$vpgSettingsIdentifier, + [Parameter( + Mandatory, + HelpMessage = "Name of VM(s) to add to the VPG" + )] + [ValidateNotNullOrEmpty()] + [String[]]$Vm + ) + + begin { + + } + + process { + $baseUrl = "vpgsettings/{0}" -f $vpgSettingsIdentifier + $baseSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier + $unprotectedVms = Get-ZertoUnprotectedVm + $protectedVms = Get-ZertoProtectedVm + $vmMap = Get-Map -inputObject $unprotectedVms -key VmName -value VmIdentifier + $vmMap = $vmMap + (Get-Map -inputObject $protectedVms -key VmName -value VmIdentifier) + # Create array of VM identifiers + $vmIdentifiers = foreach ($machine in $Vm) { + if ($vmMap[$machine] -notin $baseSettings.Vms.vmIdentifier ) { + # If the VM is unprotected, get the identifier + $vmIdentifier = $unprotectedVms | Where-Object { $_.vmName -like $machine } | Select-Object -ExpandProperty vmIdentifier + # If the VM is not unprotected, check the protected VMs + if ( -not $vmIdentifier) { + # Get all identifiers to test if the VM is eligible to be a member of an additional VPG + $results = $protectedVms | Where-Object { $_.VmName -like $machine } | Select-Object -ExpandProperty vmIdentifier + $recoverySiteIdentifiers = $protectedVms | Where-Object { $_.VmName -like $machine } | Select-Object -ExpandProperty RecoverySite | Select-Object -ExpandProperty identifier + # If VM is currently a member of 3 VPGs, skip it. If it cannot be found, skip it. Otherwise, set the identifier + if ($baseSettings.basic.RecoverySiteIdentifier -in $recoverySiteIdentifiers) { + Write-Warning "$machine is already replicating to target site. It cannot be added to an additional VPG replicating to that site. Please check your configurations and try again. Skipping $machine" + continue + } elseif ($results.count -eq 3) { + Write-Warning "$machine is already a part of 3 VPGs and cannot be part of an additional VPG. Skipping $machine" + continue + } elseif ($results.count -eq 0) { + Write-Warning "$machine not found. Skipping $machine" + continue + } else { + $vmIdentifier = $results | Select-Object -First 1 + } + } + # Create a custom object to store the information to easily convert to JSON. Return to vmIdentifiers array. + $returnObject = New-Object PSObject + $returnObject | Add-Member -MemberType NoteProperty -Name "VmIdentifier" -Value $vmIdentifier + $returnObject + } else { + Write-Warning "$machine is already a member of this VPG Settings object. It will not be added again. Skipping $machine" + continue + } + } + if ($vmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess($Vm, "Adding VM to Vpg")) { + $baseSettings.Vms += $vmIdentifiers + Invoke-ZertoRestRequest -uri $baseUrl -method PUT -body $($baseSettings | ConvertTo-Json -Depth 10) + } else { + Write-Warning "No VMs found to add. Please check your parameters and try again." + } + } + + end { + + } +} From d9e6c74f27b82849983d4a99c21d6435252680c4 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Fri, 31 Jul 2020 18:02:00 -0400 Subject: [PATCH 02/21] Add parameter aliases --- ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 index c65160b..7add179 100644 --- a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 @@ -10,6 +10,7 @@ function Add-ZertoVpgVm { ValueFromRemainingArguments )] [ValidateNotNullOrEmpty()] + [Alias("sid", "settingsIdentifier", "vpgSettingsId")] [String]$vpgSettingsIdentifier, [Parameter( Mandatory, From d9e1063654794ade3e56b69ee51b2ac6f893dccb Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Mon, 3 Aug 2020 21:13:38 -0400 Subject: [PATCH 03/21] update GitIgnore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ee2dd02..614cba9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ SourceTestResults.xml publish/* CodeCoverage.xml scratch +.DS_Store From a59feb2d5fba1b308055f0b7b509d9f45b65afd0 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Mon, 3 Aug 2020 21:21:05 -0400 Subject: [PATCH 04/21] Add switch for vpgName --- ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 | 38 +++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 index 7add179..9b7c98e 100644 --- a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 @@ -1,17 +1,25 @@ <# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> function Add-ZertoVpgVm { - [CmdletBinding(SupportsShouldProcess)] + [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "VpgName")] param ( [Parameter( Mandatory, HelpMessage = "Vpg Settings Identifier", ValueFromPipeline, ValueFromPipelineByPropertyName, - ValueFromRemainingArguments + ValueFromRemainingArguments, + ParameterSetName = "VpgSettingsIdentifier" )] [ValidateNotNullOrEmpty()] [Alias("sid", "settingsIdentifier", "vpgSettingsId")] [String]$vpgSettingsIdentifier, + [Parameter( + Mandatory, + HelpMessage = "Target VPG Name to Add the VM", + ParameterSetName = "VpgName" + )] + [ValidateNotNullOrEmpty()] + [String]$VpgName, [Parameter( Mandatory, HelpMessage = "Name of VM(s) to add to the VPG" @@ -25,7 +33,11 @@ function Add-ZertoVpgVm { } process { - $baseUrl = "vpgsettings/{0}" -f $vpgSettingsIdentifier + if ($PSCmdlet.ParameterSetName -eq "VpgName"){ + $VpgIdentifier = Get-ZertoVpg -name $VpgName | Select-Object -ExpandProperty VpgIdentifier + $vpgSettingsIdentifier = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgIdentifier + } + $baseUrl = "vpgsettings/{0}/vms" -f $vpgSettingsIdentifier $baseSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier $unprotectedVms = Get-ZertoUnprotectedVm $protectedVms = Get-ZertoProtectedVm @@ -56,19 +68,27 @@ function Add-ZertoVpgVm { } } # Create a custom object to store the information to easily convert to JSON. Return to vmIdentifiers array. - $returnObject = New-Object PSObject - $returnObject | Add-Member -MemberType NoteProperty -Name "VmIdentifier" -Value $vmIdentifier - $returnObject + $vmIdentifier } else { Write-Warning "$machine is already a member of this VPG Settings object. It will not be added again. Skipping $machine" continue } } - if ($vmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess($Vm, "Adding VM to Vpg")) { - $baseSettings.Vms += $vmIdentifiers - Invoke-ZertoRestRequest -uri $baseUrl -method PUT -body $($baseSettings | ConvertTo-Json -Depth 10) + if ($vmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess($VmIdentifiers, "Adding VM to Vpg")) { + foreach ($id in $VmIdentifiers) { + # Build the Body + $Body = @{VmIdentifier = $id } + # Submit the request. Out to Null to prevent line returns while running. + $null = Invoke-ZertoRestRequest -uri $baseUrl -method POST -body ($Body | ConvertTo-Json -Depth 10) + } + if ($PSCmdlet.ParameterSetName -eq "VpgName") { + $vpgSettingsIdentifier + } } else { Write-Warning "No VMs found to add. Please check your parameters and try again." + if ($PSCmdlet.ParameterSetName -eq "VpgName") { + Remove-ZertoVpgSettingsIdentifier -vpgSettingsIdentifier $vpgSettingsIdentifier + } } } From c64c82f8bcd937d7d2b10be9b7e5f6c812541a89 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 07:53:15 -0400 Subject: [PATCH 05/21] Add parameter alias --- ZertoApiWrapper/Public/Save-ZertoVpgSetting.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZertoApiWrapper/Public/Save-ZertoVpgSetting.ps1 b/ZertoApiWrapper/Public/Save-ZertoVpgSetting.ps1 index 055febc..3f7fed8 100644 --- a/ZertoApiWrapper/Public/Save-ZertoVpgSetting.ps1 +++ b/ZertoApiWrapper/Public/Save-ZertoVpgSetting.ps1 @@ -11,7 +11,7 @@ function Save-ZertoVpgSetting { ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] - [Alias("vpgSettingsId")] + [Alias("sid", "settingsIdentifier", "vpgSettingsId")] [string]$vpgSettingsIdentifier ) From d7e46bd2634ab63736f05b6d359323c950c656d6 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 10:48:58 -0400 Subject: [PATCH 06/21] Check for existence of Vpg --- ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 index 9b7c98e..c4b1ef8 100644 --- a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 @@ -33,9 +33,13 @@ function Add-ZertoVpgVm { } process { - if ($PSCmdlet.ParameterSetName -eq "VpgName"){ + if ($PSCmdlet.ParameterSetName -eq "VpgName") { $VpgIdentifier = Get-ZertoVpg -name $VpgName | Select-Object -ExpandProperty VpgIdentifier - $vpgSettingsIdentifier = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgIdentifier + if (-not $VpgIdentifier) { + Write-Error "Unable to find Vpg with name $VpgName. Please check your parameters and try again." -ErrorAction Stop + } else { + $vpgSettingsIdentifier = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgIdentifier + } } $baseUrl = "vpgsettings/{0}/vms" -f $vpgSettingsIdentifier $baseSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier From 14ef5048e475dcb70db7ff33505014a632f33e8a Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 11:22:45 -0400 Subject: [PATCH 07/21] Only process unique VM names --- ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 index c4b1ef8..ec5fc1c 100644 --- a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 @@ -48,7 +48,7 @@ function Add-ZertoVpgVm { $vmMap = Get-Map -inputObject $unprotectedVms -key VmName -value VmIdentifier $vmMap = $vmMap + (Get-Map -inputObject $protectedVms -key VmName -value VmIdentifier) # Create array of VM identifiers - $vmIdentifiers = foreach ($machine in $Vm) { + $vmIdentifiers = foreach ($machine in ($Vm | Select-Object -Unique)) { if ($vmMap[$machine] -notin $baseSettings.Vms.vmIdentifier ) { # If the VM is unprotected, get the identifier $vmIdentifier = $unprotectedVms | Where-Object { $_.vmName -like $machine } | Select-Object -ExpandProperty vmIdentifier From 5f786775dcadb0e2c849a5a5939bc24901753719 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 11:23:20 -0400 Subject: [PATCH 08/21] Verbose Output - VM and VPG name --- ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 index ec5fc1c..7301083 100644 --- a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 @@ -43,6 +43,9 @@ function Add-ZertoVpgVm { } $baseUrl = "vpgsettings/{0}/vms" -f $vpgSettingsIdentifier $baseSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier + if ($PSCmdlet.ParameterSetName -eq "VpgSettingsIdentifier") { + $VpgName = $baseSettings.Basic.Name + } $unprotectedVms = Get-ZertoUnprotectedVm $protectedVms = Get-ZertoProtectedVm $vmMap = Get-Map -inputObject $unprotectedVms -key VmName -value VmIdentifier @@ -78,7 +81,7 @@ function Add-ZertoVpgVm { continue } } - if ($vmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess($VmIdentifiers, "Adding VM to Vpg")) { + if ($vmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess($VmIdentifiers, "Adding VM(s): $Vm to Vpg $VpgName")) { foreach ($id in $VmIdentifiers) { # Build the Body $Body = @{VmIdentifier = $id } From 56bbe59ea77ce766fed6bb8f7baed6217f92d22a Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 11:23:37 -0400 Subject: [PATCH 09/21] Always return the Vpg Settings Identifier --- ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 index 7301083..d504acf 100644 --- a/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Add-ZertoVpgVm.ps1 @@ -88,9 +88,8 @@ function Add-ZertoVpgVm { # Submit the request. Out to Null to prevent line returns while running. $null = Invoke-ZertoRestRequest -uri $baseUrl -method POST -body ($Body | ConvertTo-Json -Depth 10) } - if ($PSCmdlet.ParameterSetName -eq "VpgName") { - $vpgSettingsIdentifier - } + + $vpgSettingsIdentifier } else { Write-Warning "No VMs found to add. Please check your parameters and try again." if ($PSCmdlet.ParameterSetName -eq "VpgName") { From aa73534d7ffd55aaf6362b0666a420e4446ea3bd Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 11:42:41 -0400 Subject: [PATCH 10/21] Create Add-ZertoVpgVm Help File --- docs/Add-ZertoVpgVm.md | 138 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/Add-ZertoVpgVm.md diff --git a/docs/Add-ZertoVpgVm.md b/docs/Add-ZertoVpgVm.md new file mode 100644 index 0000000..2aaf484 --- /dev/null +++ b/docs/Add-ZertoVpgVm.md @@ -0,0 +1,138 @@ +--- +external help file: ZertoApiWrapper-help.xml +Module Name: ZertoApiWrapper +online version: +schema: 2.0.0 +--- + +# Add-ZertoVpgVm + +## SYNOPSIS +Adds one or more virtual machines to an existing VPG. A VPG Settings Identifier will be returned for use in either further customization of the VPG or passed to the `Save-ZertoVpgSetting` command to commit the changes. + +## SYNTAX + +### VpgName (Default) +``` +Add-ZertoVpgVm -VpgName -Vm [-WhatIf] [-Confirm] [] +``` + +### VpgSettingsIdentifier +``` +Add-ZertoVpgVm -vpgSettingsIdentifier -Vm [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +Adds one or more virtual machines to an existing VPG. A VPG Settings Identifier will be returned for use in either further customization of the VPG or passed to the `Save-ZertoVpgSetting` command to commit the changes. + +Internal logic to the function will only process unique items and ensure that VMs meet requirements to replicate to the target site. These requirements include not currently a member of the specified VPG, not currently replicating to the target site, and not a member of 3 or more VPGs. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Add-ZertoVpgVm -VpgSettingsIdentifier $vpgSettingsIdentifier -Vm "VM 1", "Vm 2" +``` + +Adds "VM 1" and "VM 2" to the Vpg with VpgSettingsIdentifer specified in `$VpgSettingsIdentifier`. This variable was obtained via other functions that create and return a Vpg Settings Identifier. The Vpg Settings Identifier passed into the function will be returned. + +### Example 2 +```powershell +PS C:\> Add-ZertoVpgVm -VpgName "My Vpg" -Vm "VM 1", "Vm 2" +``` + +Adds "VM 1" and "VM 2" to the Vpg named "My Vpg". In this case, a new Vpg Settings Identifier will be created and returned. + +## PARAMETERS + +### -Vm +Name of VM(s) to add to the VPG + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -VpgName +Target VPG Name to Add the VM + +```yaml +Type: String +Parameter Sets: VpgName +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -vpgSettingsIdentifier +Vpg Settings Identifier + +```yaml +Type: String +Parameter Sets: VpgSettingsIdentifier +Aliases: sid, settingsIdentifier, vpgSettingsId + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### System.String + +## OUTPUTS + +### System.Object + +## NOTES + +## RELATED LINKS +[Adding VMs to a VPG Settings Object](https://s3.amazonaws.com/zertodownload_docs/Latest/Zerto%20Virtual%20Replication%20Zerto%20Virtual%20Manager%20(ZVM)%20-%20vSphere%20Online%20Help/content/zvr_apis/vpg_management_api.htm?tocpath=ZVR%20RESTful%20APIs%7CZerto%20APIs%7C_____20#statusapis_4057192544_1358357) From 961eafb945d963cfdb40fd31cb45228662bddbca Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 11:44:36 -0400 Subject: [PATCH 11/21] Update Event Documentation --- docs/Get-ZertoEvent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Get-ZertoEvent.md b/docs/Get-ZertoEvent.md index 25532cb..f3b8c67 100644 --- a/docs/Get-ZertoEvent.md +++ b/docs/Get-ZertoEvent.md @@ -19,7 +19,7 @@ Get-ZertoEvent [] ### filter ``` -Get-ZertoEvent [-startDate ] [-endDate ] [-vpgName ] [-vpgIdentifier ] +Get-ZertoEvent [-startDate ] [-endDate ] [-vpg ] [-vpgIdentifier ] [-eventType ] [-siteName ] [-siteIdentifier ] [-zorgIdentifier ] [-entityType ] [-userName ] [-category ] [-eventCategory ] [-alertIdentifier ] [] From 9561936be7ebd02e24111028477b2732da44b910 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 11:46:12 -0400 Subject: [PATCH 12/21] Update Help Files --- docs/Get-ZertoEvent.md | 12 ++++++------ docs/Invoke-ZertoMove.md | 26 +++++++++++++------------- docs/New-ZertoVpg.md | 24 ++++++++++++------------ docs/Remove-ZertoVpg.md | 4 ++-- docs/Save-ZertoVpgSetting.md | 4 ++-- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/Get-ZertoEvent.md b/docs/Get-ZertoEvent.md index f3b8c67..e42daa2 100644 --- a/docs/Get-ZertoEvent.md +++ b/docs/Get-ZertoEvent.md @@ -309,13 +309,13 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -vpgIdentifier -The identifier of the VPG for which you want to return events. +### -vpg +The name of the VPG for which you want to return events. ```yaml Type: String Parameter Sets: filter -Aliases: vpgId +Aliases: vpgName Required: False Position: Named @@ -324,13 +324,13 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -vpgName -The name of the VPG for which you want to return events. +### -vpgIdentifier +The identifier of the VPG for which you want to return events. ```yaml Type: String Parameter Sets: filter -Aliases: vpg +Aliases: vpgId Required: False Position: Named diff --git a/docs/Invoke-ZertoMove.md b/docs/Invoke-ZertoMove.md index 420088e..eb1c661 100644 --- a/docs/Invoke-ZertoMove.md +++ b/docs/Invoke-ZertoMove.md @@ -14,21 +14,20 @@ Start a move of a VPG. ### main (Default) ``` -Invoke-ZertoMove [-vpgName] [[-commitPolicy] ] [[-commitPolicyTimeout] ] - [-forceShutdown] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] +Invoke-ZertoMove -vpgName [-commitPolicy ] [-commitPolicyTimeout ] [-forceShutdown] + [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] ``` ### disableReverseProtection ``` -Invoke-ZertoMove [-vpgName] [[-commitPolicy] ] [[-commitPolicyTimeout] ] - [-forceShutdown] [-disableReverseProtection] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] - [] +Invoke-ZertoMove -vpgName [-commitPolicy ] [-commitPolicyTimeout ] [-forceShutdown] + [-disableReverseProtection] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] ``` ### keepSourceVms ``` -Invoke-ZertoMove [-vpgName] [[-commitPolicy] ] [[-commitPolicyTimeout] ] - [-forceShutdown] [-keepSourceVms] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] +Invoke-ZertoMove -vpgName [-commitPolicy ] [-commitPolicyTimeout ] [-forceShutdown] + [-keepSourceVms] [-ContinueOnPreScriptFailure] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -58,9 +57,10 @@ Default is the Site Settings setting. Type: String Parameter Sets: (All) Aliases: +Accepted values: Rollback, Commit, None Required: False -Position: 1 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -76,7 +76,7 @@ Parameter Sets: (All) Aliases: Required: False -Position: 2 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -91,7 +91,7 @@ Parameter Sets: (All) Aliases: Required: False -Position: 6 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -121,7 +121,7 @@ Parameter Sets: (All) Aliases: Required: False -Position: 3 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -136,7 +136,7 @@ Parameter Sets: keepSourceVms Aliases: Required: True -Position: 5 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -151,7 +151,7 @@ Parameter Sets: (All) Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False diff --git a/docs/New-ZertoVpg.md b/docs/New-ZertoVpg.md index c288dbd..e8a69d4 100644 --- a/docs/New-ZertoVpg.md +++ b/docs/New-ZertoVpg.md @@ -12,6 +12,16 @@ Creates a New VPG with default settings only. Customization of VM settings can b ## SYNTAX +### recoveryHostDatastore (Default) +``` +New-ZertoVpg -vpgName [-vpgPriority ] [-journalHistoryInHours ] -protectedVm + -recoverySite -recoveryHost -datastore -recoveryFolder + [-rpoInSeconds ] [-testIntervalInMinutes ] [-serviceProfile ] + [-useWanCompression ] [-zorg ] -recoveryNetwork -testNetwork + [-journalDatastore ] [-journalHardLimitInMb ] [-journalWarningThresholdInMb ] + [-WhatIf] [-Confirm] [] +``` + ### recoveryClusterDatastoreCluster ``` New-ZertoVpg -vpgName [-vpgPriority ] [-journalHistoryInHours ] -protectedVm @@ -42,16 +52,6 @@ New-ZertoVpg -vpgName [-vpgPriority ] [-journalHistoryInHours < [-WhatIf] [-Confirm] [] ``` -### recoveryHostDatastore -``` -New-ZertoVpg -vpgName [-vpgPriority ] [-journalHistoryInHours ] -protectedVm - -recoverySite -recoveryHost -datastore -recoveryFolder - [-rpoInSeconds ] [-testIntervalInMinutes ] [-serviceProfile ] - [-useWanCompression ] [-zorg ] -recoveryNetwork -testNetwork - [-journalDatastore ] [-journalHardLimitInMb ] [-journalWarningThresholdInMb ] - [-WhatIf] [-Confirm] [] -``` - ### recoveryResourcePoolDatastoreCluster ``` New-ZertoVpg -vpgName [-vpgPriority ] [-journalHistoryInHours ] -protectedVm @@ -176,7 +176,7 @@ Name of the datastore where the VM(s), Volume(s), and Journal(s) will reside. ```yaml Type: String -Parameter Sets: recoveryClusterDatastore, recoveryHostDatastore, recoveryResourcePoolDatastore +Parameter Sets: recoveryHostDatastore, recoveryClusterDatastore, recoveryResourcePoolDatastore Aliases: Required: True @@ -312,7 +312,7 @@ Name of the host where the VM(s) will be recovered. ```yaml Type: String -Parameter Sets: recoveryHostDatastoreCluster, recoveryHostDatastore +Parameter Sets: recoveryHostDatastore, recoveryHostDatastoreCluster Aliases: Required: True diff --git a/docs/Remove-ZertoVpg.md b/docs/Remove-ZertoVpg.md index c365cd7..877e44e 100644 --- a/docs/Remove-ZertoVpg.md +++ b/docs/Remove-ZertoVpg.md @@ -21,7 +21,7 @@ Remove-ZertoVpg -vpgidentifier [-keepRecoveryVolumes] [-force] [-What ### vpgName ``` -Remove-ZertoVpg [-vpgName] [-keepRecoveryVolumes] [-force] [-WhatIf] [-Confirm] [] +Remove-ZertoVpg -vpgName [-keepRecoveryVolumes] [-force] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -122,7 +122,7 @@ Parameter Sets: vpgName Aliases: Required: True -Position: 0 +Position: Named Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False diff --git a/docs/Save-ZertoVpgSetting.md b/docs/Save-ZertoVpgSetting.md index cd2cc59..4604408 100644 --- a/docs/Save-ZertoVpgSetting.md +++ b/docs/Save-ZertoVpgSetting.md @@ -36,10 +36,10 @@ VpgSettings Identifier to save ```yaml Type: String Parameter Sets: (All) -Aliases: vpgSettingsId +Aliases: sid, settingsIdentifier, vpgSettingsId Required: True -Position: 1 +Position: 0 Default value: None Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False From 510f0b9ed780240b8c8c01c2234db57d4b1dc7b9 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 12:31:37 -0400 Subject: [PATCH 13/21] Add-ZertoVpgVm Tests --- Tests/Public/Add-ZertoVpgVm.Tests.ps1 | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Tests/Public/Add-ZertoVpgVm.Tests.ps1 diff --git a/Tests/Public/Add-ZertoVpgVm.Tests.ps1 b/Tests/Public/Add-ZertoVpgVm.Tests.ps1 new file mode 100644 index 0000000..27af791 --- /dev/null +++ b/Tests/Public/Add-ZertoVpgVm.Tests.ps1 @@ -0,0 +1,53 @@ +#Requires -Modules Pester +$global:here = (Split-Path -Parent $PSCommandPath) +$global:function = ((Split-Path -Leaf $PSCommandPath).Split('.'))[0] + +Describe $global:function -Tag 'Unit', 'Source', 'Built' { + BeforeAll { + $script:ScriptBlock = (Get-Command $global:function).ScriptBlock + } + + Context "$global:function::Parameter Unit Tests" { + + It "$global:function should have exactly 16 parameters defined" { + (Get-Command $global:function).Parameters.Count | Should -Be 16 + } + + $ParameterTestCases = @( + @{ParameterName = 'vpgSettingsIdentifier'; Type = 'String'; Mandatory = $true; Validation = 'NotNullOrEmpty' } + @{ParameterName = 'Vm'; Type = 'String[]'; Mandatory = $true; Validation = 'NotNullOrEmpty' } + @{ParameterName = 'VpgName'; Type = 'String'; Mandatory = $true; Validation = 'NotNullOrEmpty' } + ) + + It " parameter is of type" -TestCases $ParameterTestCases { + param($ParameterName, $Type, $Mandatory, $Validation) + Get-Command $global:function | Should -HaveParameter $ParameterName -Mandatory:$Mandatory -Type $Type + } + + It " parameter has correct validation setting" -TestCases $ParameterTestCases { + param($ParameterName, $Validation) + Switch ($Validation) { + 'NotNullOrEmpty' { + $attrs = (Get-Command $global:function).Parameters[$ParameterName].Attributes + $attrs.Where{ $_ -is [ValidateNotNullOrEmpty] }.Count | Should -Be 1 + } + + default { + $true | Should -Be $false -Because "No Validation Selected. Review test cases" + } + } + } + + It "Supports 'SupportsShouldProcess'" { + Get-Command $global:function | Should -HaveParameter WhatIf + Get-Command $global:function | Should -HaveParameter Confirm + $script:ScriptBlock | Should -Match 'SupportsShouldProcess' + $script:ScriptBlock | Should -Match '\$PSCmdlet\.ShouldProcess\(.+\)' + } + } + + Context "Add-ZertoPeerSite::Functional Unit Tests" { + } +} +Remove-Variable -Name here -Scope Global +Remove-Variable -Name function -Scope Global From 9d4b4d353384a49e171a631ecb2a62b2027d67b4 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 17:42:37 -0400 Subject: [PATCH 14/21] Starting Remove-VpgVm --- ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 | 113 +++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 diff --git a/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 new file mode 100644 index 0000000..b34737a --- /dev/null +++ b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 @@ -0,0 +1,113 @@ +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> +function Remove-ZertoVpgVm { + [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "VpgName")] + param ( + [Parameter( + Mandatory, + HelpMessage = "Vpg Settings Identifier", + ValueFromPipeline, + ValueFromPipelineByPropertyName, + ValueFromRemainingArguments, + ParameterSetName = "VpgSettingsIdentifier" + )] + [ValidateNotNullOrEmpty()] + [Alias("sid", "settingsIdentifier", "vpgSettingsId")] + [String]$vpgSettingsIdentifier, + [Parameter( + Mandatory, + HelpMessage = "Name of the VPG that contains the VM you wish to remove", + ParameterSetName = "VpgName" + )] + [ValidateNotNullOrEmpty()] + [String]$VpgName, + [Parameter( + Mandatory, + HelpMessage = "Name of VM(s) to remove from the VPG" + )] + [ValidateNotNullOrEmpty()] + [String[]]$Vm + ) + + begin { + + } + + process { + switch ($PSCmdlet.ParameterSetName) { + "VpgName" { + $VpgData = Get-ZertoVpg -vpgName $VpgName + if (-not $VpgData) { + Write-Error "Unable to find Vpg with name $VpgName. Please check your parameters and try again." -ErrorAction Stop + } else { + $protectedVms = Get-ZertoProtectedVm -vpgName $VpgData.VpgName + $vpgSettingsIdentifier = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgIdentifier + } + } + Default {} + } + + if ($PSCmdlet.ParameterSetName -eq "VpgName") { + + + } + $baseUrl = "vpgsettings/{0}/vms" -f $vpgSettingsIdentifier + $baseSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier + if ($PSCmdlet.ParameterSetName -eq "VpgSettingsIdentifier") { + $VpgName = $baseSettings.Basic.Name + } + $unprotectedVms = Get-ZertoUnprotectedVm + $protectedVms = Get-ZertoProtectedVm + $vmMap = Get-Map -inputObject $unprotectedVms -key VmName -value VmIdentifier + $vmMap = $vmMap + (Get-Map -inputObject $protectedVms -key VmName -value VmIdentifier) + # Create array of VM identifiers + $vmIdentifiers = foreach ($machine in ($Vm | Select-Object -Unique)) { + if ($vmMap[$machine] -notin $baseSettings.Vms.vmIdentifier ) { + # If the VM is unprotected, get the identifier + $vmIdentifier = $unprotectedVms | Where-Object { $_.vmName -like $machine } | Select-Object -ExpandProperty vmIdentifier + # If the VM is not unprotected, check the protected VMs + if ( -not $vmIdentifier) { + # Get all identifiers to test if the VM is eligible to be a member of an additional VPG + $results = $protectedVms | Where-Object { $_.VmName -like $machine } | Select-Object -ExpandProperty vmIdentifier + $recoverySiteIdentifiers = $protectedVms | Where-Object { $_.VmName -like $machine } | Select-Object -ExpandProperty RecoverySite | Select-Object -ExpandProperty identifier + # If VM is currently a member of 3 VPGs, skip it. If it cannot be found, skip it. Otherwise, set the identifier + if ($baseSettings.basic.RecoverySiteIdentifier -in $recoverySiteIdentifiers) { + Write-Warning "$machine is already replicating to target site. It cannot be added to an additional VPG replicating to that site. Please check your configurations and try again. Skipping $machine" + continue + } elseif ($results.count -eq 3) { + Write-Warning "$machine is already a part of 3 VPGs and cannot be part of an additional VPG. Skipping $machine" + continue + } elseif ($results.count -eq 0) { + Write-Warning "$machine not found. Skipping $machine" + continue + } else { + $vmIdentifier = $results | Select-Object -First 1 + } + } + # Create a custom object to store the information to easily convert to JSON. Return to vmIdentifiers array. + $vmIdentifier + } else { + Write-Warning "$machine is already a member of this VPG Settings object. It will not be added again. Skipping $machine" + continue + } + } + if ($vmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess($VmIdentifiers, "Adding VM(s): $Vm to Vpg $VpgName")) { + foreach ($id in $VmIdentifiers) { + # Build the Body + $Body = @{VmIdentifier = $id } + # Submit the request. Out to Null to prevent line returns while running. + $null = Invoke-ZertoRestRequest -uri $baseUrl -method POST -body ($Body | ConvertTo-Json -Depth 10) + } + + $vpgSettingsIdentifier + } else { + Write-Warning "No VMs found to add. Please check your parameters and try again." + if ($PSCmdlet.ParameterSetName -eq "VpgName") { + Remove-ZertoVpgSettingsIdentifier -vpgSettingsIdentifier $vpgSettingsIdentifier + } + } + } + + end { + + } +} From b6d8083b69c31954d876490a446410114aa6b159 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 4 Aug 2020 22:05:35 -0400 Subject: [PATCH 15/21] Remove-ZertoVpgVm update --- ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 | 102 ++++--------------- 1 file changed, 21 insertions(+), 81 deletions(-) diff --git a/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 index b34737a..f0e0b33 100644 --- a/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 @@ -1,18 +1,7 @@ <# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> function Remove-ZertoVpgVm { - [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "VpgName")] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] param ( - [Parameter( - Mandatory, - HelpMessage = "Vpg Settings Identifier", - ValueFromPipeline, - ValueFromPipelineByPropertyName, - ValueFromRemainingArguments, - ParameterSetName = "VpgSettingsIdentifier" - )] - [ValidateNotNullOrEmpty()] - [Alias("sid", "settingsIdentifier", "vpgSettingsId")] - [String]$vpgSettingsIdentifier, [Parameter( Mandatory, HelpMessage = "Name of the VPG that contains the VM you wish to remove", @@ -33,78 +22,29 @@ function Remove-ZertoVpgVm { } process { - switch ($PSCmdlet.ParameterSetName) { - "VpgName" { - $VpgData = Get-ZertoVpg -vpgName $VpgName - if (-not $VpgData) { - Write-Error "Unable to find Vpg with name $VpgName. Please check your parameters and try again." -ErrorAction Stop - } else { - $protectedVms = Get-ZertoProtectedVm -vpgName $VpgData.VpgName - $vpgSettingsIdentifier = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgIdentifier - } - } - Default {} - } - - if ($PSCmdlet.ParameterSetName -eq "VpgName") { - - - } - $baseUrl = "vpgsettings/{0}/vms" -f $vpgSettingsIdentifier - $baseSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier - if ($PSCmdlet.ParameterSetName -eq "VpgSettingsIdentifier") { - $VpgName = $baseSettings.Basic.Name - } - $unprotectedVms = Get-ZertoUnprotectedVm - $protectedVms = Get-ZertoProtectedVm - $vmMap = Get-Map -inputObject $unprotectedVms -key VmName -value VmIdentifier - $vmMap = $vmMap + (Get-Map -inputObject $protectedVms -key VmName -value VmIdentifier) - # Create array of VM identifiers - $vmIdentifiers = foreach ($machine in ($Vm | Select-Object -Unique)) { - if ($vmMap[$machine] -notin $baseSettings.Vms.vmIdentifier ) { - # If the VM is unprotected, get the identifier - $vmIdentifier = $unprotectedVms | Where-Object { $_.vmName -like $machine } | Select-Object -ExpandProperty vmIdentifier - # If the VM is not unprotected, check the protected VMs - if ( -not $vmIdentifier) { - # Get all identifiers to test if the VM is eligible to be a member of an additional VPG - $results = $protectedVms | Where-Object { $_.VmName -like $machine } | Select-Object -ExpandProperty vmIdentifier - $recoverySiteIdentifiers = $protectedVms | Where-Object { $_.VmName -like $machine } | Select-Object -ExpandProperty RecoverySite | Select-Object -ExpandProperty identifier - # If VM is currently a member of 3 VPGs, skip it. If it cannot be found, skip it. Otherwise, set the identifier - if ($baseSettings.basic.RecoverySiteIdentifier -in $recoverySiteIdentifiers) { - Write-Warning "$machine is already replicating to target site. It cannot be added to an additional VPG replicating to that site. Please check your configurations and try again. Skipping $machine" - continue - } elseif ($results.count -eq 3) { - Write-Warning "$machine is already a part of 3 VPGs and cannot be part of an additional VPG. Skipping $machine" - continue - } elseif ($results.count -eq 0) { - Write-Warning "$machine not found. Skipping $machine" - continue - } else { - $vmIdentifier = $results | Select-Object -First 1 - } - } - # Create a custom object to store the information to easily convert to JSON. Return to vmIdentifiers array. - $vmIdentifier - } else { - Write-Warning "$machine is already a member of this VPG Settings object. It will not be added again. Skipping $machine" - continue - } - } - if ($vmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess($VmIdentifiers, "Adding VM(s): $Vm to Vpg $VpgName")) { - foreach ($id in $VmIdentifiers) { - # Build the Body - $Body = @{VmIdentifier = $id } - # Submit the request. Out to Null to prevent line returns while running. - $null = Invoke-ZertoRestRequest -uri $baseUrl -method POST -body ($Body | ConvertTo-Json -Depth 10) - } - - $vpgSettingsIdentifier + $VpgData = Get-ZertoVpg -vpgName $VpgName + if (-not $VpgData) { + Write-Error "Unable to find Vpg with name $VpgName. Please check your parameters and try again." -ErrorAction Stop } else { - Write-Warning "No VMs found to add. Please check your parameters and try again." - if ($PSCmdlet.ParameterSetName -eq "VpgName") { - Remove-ZertoVpgSettingsIdentifier -vpgSettingsIdentifier $vpgSettingsIdentifier + $protectedVms = Get-ZertoProtectedVm -vpgName $VpgData.VpgName + } + $VmIdentifiers = foreach ($machine in ($vm | Select-Object -Unique)) { + if ($machine -in $protectedVms.VmName) { + $protectedVms.VmName.Where( { $_.VmName -like $machine }) | Select-Object -ExpandProperty VmIdentifier + } else { + Write-Warning "$machine is not found in $VpgName. Check your parameters. Skipping $machine" } } + if ($VmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess(($Vm | Select-Object -Unique), "Removing VM(s): $($Vm | Select-Object -Unique) from Vpg $VpgName")) { + $vpgSettingsIdentifier = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgData.VpgIdentifier + foreach ($identifier in $VmIdentifiers) { + $url = "vpgSettings/{0}/vms/{1}" -f $vpgSettingsIdentifier, $identifier + Invoke-ZertoRestRequest -uri $uri -method DELETE + } + Save-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier + } else { + Write-Warning "No VMs found to remove. Please check your parameters and try again." + } } end { From af6e81267bcff49e586521e08fc397d44f083936 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Wed, 5 Aug 2020 07:45:55 -0400 Subject: [PATCH 16/21] Add Online Help URI --- docs/Add-ZertoVpgVm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Add-ZertoVpgVm.md b/docs/Add-ZertoVpgVm.md index 2aaf484..5997286 100644 --- a/docs/Add-ZertoVpgVm.md +++ b/docs/Add-ZertoVpgVm.md @@ -1,7 +1,7 @@ --- external help file: ZertoApiWrapper-help.xml Module Name: ZertoApiWrapper -online version: +online version: https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Add-ZertoVpgVm.md schema: 2.0.0 --- From 649e7413a7ec7d7b7d49212f9187fc8eeeeb12d3 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Wed, 5 Aug 2020 07:46:11 -0400 Subject: [PATCH 17/21] Fix URL typo --- ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 index f0e0b33..d1c0df6 100644 --- a/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 @@ -39,7 +39,7 @@ function Remove-ZertoVpgVm { $vpgSettingsIdentifier = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgData.VpgIdentifier foreach ($identifier in $VmIdentifiers) { $url = "vpgSettings/{0}/vms/{1}" -f $vpgSettingsIdentifier, $identifier - Invoke-ZertoRestRequest -uri $uri -method DELETE + Invoke-ZertoRestRequest -uri $url -method DELETE } Save-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsIdentifier } else { From 6f798fd96af45fd706ee39e20f9980b887a284c0 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Wed, 5 Aug 2020 08:22:19 -0400 Subject: [PATCH 18/21] Update VM Filter Method --- ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 index d1c0df6..2af5029 100644 --- a/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 +++ b/ZertoApiWrapper/Public/Remove-ZertoVpgVm.ps1 @@ -30,9 +30,9 @@ function Remove-ZertoVpgVm { } $VmIdentifiers = foreach ($machine in ($vm | Select-Object -Unique)) { if ($machine -in $protectedVms.VmName) { - $protectedVms.VmName.Where( { $_.VmName -like $machine }) | Select-Object -ExpandProperty VmIdentifier + $protectedVms.Where( { $_.VmName -like $machine }) | Select-Object -ExpandProperty VmIdentifier } else { - Write-Warning "$machine is not found in $VpgName. Check your parameters. Skipping $machine" + Write-Warning "Virtual Machine: '$machine' is not found in Vpg: '$VpgName'. Check your parameters. Skipping $machine" } } if ($VmIdentifiers.Count -gt 0 -and $PSCmdlet.ShouldProcess(($Vm | Select-Object -Unique), "Removing VM(s): $($Vm | Select-Object -Unique) from Vpg $VpgName")) { From 2dad34754474c80152632a4e2203eee51b57ca9a Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Wed, 5 Aug 2020 08:41:55 -0400 Subject: [PATCH 19/21] Remove-ZertoVpgVm Help --- docs/Remove-ZertoVpgVm.md | 121 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 docs/Remove-ZertoVpgVm.md diff --git a/docs/Remove-ZertoVpgVm.md b/docs/Remove-ZertoVpgVm.md new file mode 100644 index 0000000..624c3b4 --- /dev/null +++ b/docs/Remove-ZertoVpgVm.md @@ -0,0 +1,121 @@ +--- +external help file: ZertoApiWrapper-help.xml +Module Name: ZertoApiWrapper +online version: https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Resume-ZertoVpgVm.md +schema: 2.0.0 +--- + +# Remove-ZertoVpgVm + +## SYNOPSIS +Removes one or more VMs from a specified VPG. A Task Identifier is returned to track progress. + +## SYNTAX + +``` +Remove-ZertoVpgVm -VpgName -Vm [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +Removes one or more VMs from a specified VPG. A Task Identifier is returned to track progress. Internal logic will remove duplicate VM names from the list of VMs provided as well as ensure membership in the VPG specified prior to attempting to remove the VM from the VPG. Finally the VPG is saved to commit the changes. ChangeImpact is set to 'High' to get the confirmation prompt. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Remove-ZertoVpgVm -VpgName 'My Vpg' -Vm 'Vm 1' +``` + +Removes 'Vm 1' from Vpg named 'My Vpg' + +### Example 2 +```powershell +PS C:\> Remove-ZertoVpgVm -VpgName 'My Vpg' -Vm 'Vm 1', 'Vm 2' +``` + +Removes 'Vm 1' and 'Vm 2' from Vpg named 'My Vpg'. + +### Example 3 +```powershell +PS C:\> Remove-ZertoVpgVm -VpgName 'My Vpg' -Vm 'Vm 1', 'Vm 2' -Confirm:$False +``` + +Removes 'Vm 1' and 'Vm 2' from Vpg named 'My Vpg' and bypasses the confirmation prompt + +## PARAMETERS + +### -Vm +Name of VM(s) to remove from the VPG + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -VpgName +Name of the VPG that contains the VM you wish to remove + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS +[Remove Vms from a VPG Settings Object](https://s3.amazonaws.com/zertodownload_docs/Latest/Zerto%20Virtual%20Replication%20Zerto%20Virtual%20Manager%20(ZVM)%20-%20vSphere%20Online%20Help/content/zvr_apis/vpg_management_api.htm?tocpath=ZVR%20RESTful%20APIs%7CZerto%20APIs%7C_____20#statusapis_4057192544_1361409) From 8d8cb58558819f19c0110d0f4df3a43c5d1c662d Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Wed, 5 Aug 2020 08:47:17 -0400 Subject: [PATCH 20/21] Remove-ZertoVpgVm Tests --- Tests/Public/Remove-ZertoVpgVm.Tests.ps1 | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Tests/Public/Remove-ZertoVpgVm.Tests.ps1 diff --git a/Tests/Public/Remove-ZertoVpgVm.Tests.ps1 b/Tests/Public/Remove-ZertoVpgVm.Tests.ps1 new file mode 100644 index 0000000..12d2860 --- /dev/null +++ b/Tests/Public/Remove-ZertoVpgVm.Tests.ps1 @@ -0,0 +1,52 @@ +#Requires -Modules Pester +$global:here = (Split-Path -Parent $PSCommandPath) +$global:function = ((Split-Path -Leaf $PSCommandPath).Split('.'))[0] + +Describe $global:function -Tag 'Unit', 'Source', 'Built' { + BeforeAll { + $script:ScriptBlock = (Get-Command $global:function).ScriptBlock + } + + Context "$global:function::Parameter Unit Tests" { + + It "$global:function should have exactly 15 parameters defined" { + (Get-Command $global:function).Parameters.Count | Should -Be 15 + } + + $ParameterTestCases = @( + @{ParameterName = 'Vm'; Type = 'String[]'; Mandatory = $true; Validation = 'NotNullOrEmpty' } + @{ParameterName = 'VpgName'; Type = 'String'; Mandatory = $true; Validation = 'NotNullOrEmpty' } + ) + + It " parameter is of type" -TestCases $ParameterTestCases { + param($ParameterName, $Type, $Mandatory, $Validation) + Get-Command $global:function | Should -HaveParameter $ParameterName -Mandatory:$Mandatory -Type $Type + } + + It " parameter has correct validation setting" -TestCases $ParameterTestCases { + param($ParameterName, $Validation) + Switch ($Validation) { + 'NotNullOrEmpty' { + $attrs = (Get-Command $global:function).Parameters[$ParameterName].Attributes + $attrs.Where{ $_ -is [ValidateNotNullOrEmpty] }.Count | Should -Be 1 + } + + default { + $true | Should -Be $false -Because "No Validation Selected. Review test cases" + } + } + } + + It "Supports 'SupportsShouldProcess'" { + Get-Command $global:function | Should -HaveParameter WhatIf + Get-Command $global:function | Should -HaveParameter Confirm + $script:ScriptBlock | Should -Match 'SupportsShouldProcess' + $script:ScriptBlock | Should -Match '\$PSCmdlet\.ShouldProcess\(.+\)' + } + } + + Context "Add-ZertoPeerSite::Functional Unit Tests" { + } +} +Remove-Variable -Name here -Scope Global +Remove-Variable -Name function -Scope Global From c91d53ecad0e0967bb4a9529f89c947c7bf85b05 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Wed, 5 Aug 2020 09:34:35 -0400 Subject: [PATCH 21/21] Update with changes --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341c6f1..bbc9387 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). +## Unreleased + +### Zerto Virtual Manager + +#### New + +* Added `Add-ZertoVpgVm` function to the module. Read the [help file](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Add-ZertoVpgVm.md) for more information. +* Added `Remove-ZertoVpgVm` function to the module. Read the [help file](https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Remove-ZertoVpgVm.md) for more information. + ## [1.4.3] ### Zerto Virtual Manager