From 0a2e6974f018524c4fa13202d1a94bdcd757ad4e Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Mon, 8 Jul 2019 11:59:50 -0400 Subject: [PATCH 1/8] Create Function and related files Remove-ZertoVpgSettingsIdentifier --- ...emove-ZertoVpgSettingsIdentifier.Tests.ps1 | 19 ++++ .../Remove-ZertoVpgSettingsIdentifier.ps1 | 26 ++++++ docs/Remove-ZertoVpgSettingsIdentifier.md | 89 +++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 Tests/Public/Remove-ZertoVpgSettingsIdentifier.Tests.ps1 create mode 100644 ZertoApiWrapper/Public/Remove-ZertoVpgSettingsIdentifier.ps1 create mode 100644 docs/Remove-ZertoVpgSettingsIdentifier.md diff --git a/Tests/Public/Remove-ZertoVpgSettingsIdentifier.Tests.ps1 b/Tests/Public/Remove-ZertoVpgSettingsIdentifier.Tests.ps1 new file mode 100644 index 0000000..f5816b6 --- /dev/null +++ b/Tests/Public/Remove-ZertoVpgSettingsIdentifier.Tests.ps1 @@ -0,0 +1,19 @@ +#Requires -Modules Pester +$moduleFileName = "ZertoApiWrapper.psd1" +$here = (Split-Path -Parent $MyInvocation.MyCommand.Path).Replace("Tests", "ZertoApiWrapper") +$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".") +$file = Get-ChildItem "$here\$sut" +$modulePath = $here -replace "Public", "" +$moduleFile = Get-ChildItem "$modulePath\$moduleFileName" +Get-Module -Name ZertoApiWrapper | Remove-Module -Force +Import-Module $moduleFile -Force + +Describe $file.BaseName -Tag 'Unit' { + + It "is valid Powershell (Has no script errors)" { + $contents = Get-Content -Path $file -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors) + $errors | Should -HaveCount 0 + } +} diff --git a/ZertoApiWrapper/Public/Remove-ZertoVpgSettingsIdentifier.ps1 b/ZertoApiWrapper/Public/Remove-ZertoVpgSettingsIdentifier.ps1 new file mode 100644 index 0000000..186e477 --- /dev/null +++ b/ZertoApiWrapper/Public/Remove-ZertoVpgSettingsIdentifier.ps1 @@ -0,0 +1,26 @@ +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> +function Remove-ZertoVpgSettingsIdentifier { + [CmdletBinding(SupportsShouldProcess = $true)] + param ( + # Settings Identifier to Remove + [Parameter(HelpMessage = "VpgSettingsIdentifier to Delete", Mandatory)] + [ValidateNotNullOrEmpty()] + [string[]] + $vpgSettingsIdentifier + ) + + begin { + } + + process { + foreach ($id in $vpgSettingsIdentifier) { + if ($PSCmdlet.ShouldProcess($id)) { + $uri = "vpgSettings/{0}" -f $id + Invoke-ZertoRestRequest -uri $uri -method "DELETE" + } + } + } + + end { + } +} diff --git a/docs/Remove-ZertoVpgSettingsIdentifier.md b/docs/Remove-ZertoVpgSettingsIdentifier.md new file mode 100644 index 0000000..0077fae --- /dev/null +++ b/docs/Remove-ZertoVpgSettingsIdentifier.md @@ -0,0 +1,89 @@ +--- +external help file: ZertoApiWrapper-help.xml +Module Name: ZertoApiWrapper +online version: https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Resume-ZertoVpgSettingsIdentifier.md +schema: 2.0.0 +--- + +# Remove-ZertoVpgSettingsIdentifier + +## SYNOPSIS +When a new VpgSettingsIdentifier is created, all the settings sit in memory for 30 minutes after the last change. This operation will delete the settings from memory and discard any changes. This is akin to clicking cancel on the VPG Edit Wizard screen. + +## SYNTAX + +``` +Remove-ZertoVpgSettingsIdentifier [-vpgSettingsIdentifier] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +When a new VpgSettingsIdentifier is created, all the settings sit in memory for 30 minutes after the last change. This operation will delete the settings from memory and discard any changes. This is akin to clicking cancel on the VPG Edit Wizard screen. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Remove-ZertoVpgSettingsIdentifier -vpgSettingsIdentifier "1234-7890-5678" +``` + +Removes VPG Settings Identifier "1234-7890-5678" from the Zerto Virtual Manager without applying any settings that may have changed. + +## PARAMETERS + +### -vpgSettingsIdentifier +Settings Identifier to Remove + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +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 + +## OUTPUTS + +## NOTES + +## RELATED LINKS +[Zerto Rest API VPG Settings End Point Documentation](http://s3.amazonaws.com/zertodownload_docs/Latest/Zerto%20Virtual%20Replication%20Zerto%20Virtual%20Manager%20%28ZVM%29%20-%20vSphere%20Online%20Help/index.html#page/RestfulAPIs%2FStatusAPIs.5.118.html%23) From 1fd92008ac971116080648e85a75f1cd76d9b2b3 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Mon, 8 Jul 2019 12:55:31 -0400 Subject: [PATCH 2/8] Update Remove-ZertoVpgSettingsIdentifier.md --- docs/Remove-ZertoVpgSettingsIdentifier.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Remove-ZertoVpgSettingsIdentifier.md b/docs/Remove-ZertoVpgSettingsIdentifier.md index 0077fae..0a8faf6 100644 --- a/docs/Remove-ZertoVpgSettingsIdentifier.md +++ b/docs/Remove-ZertoVpgSettingsIdentifier.md @@ -86,4 +86,5 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## NOTES ## RELATED LINKS + [Zerto Rest API VPG Settings End Point Documentation](http://s3.amazonaws.com/zertodownload_docs/Latest/Zerto%20Virtual%20Replication%20Zerto%20Virtual%20Manager%20%28ZVM%29%20-%20vSphere%20Online%20Help/index.html#page/RestfulAPIs%2FStatusAPIs.5.118.html%23) From ef1584032e20bef8b8ed80e3a65bbaf21f9ba0e6 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Mon, 8 Jul 2019 13:22:18 -0400 Subject: [PATCH 3/8] Create function and required associated docs Export-ZertoVmNicSetting --- .../Public/Export-ZertoVmNicSetting.Tests.ps1 | 42 +++++++++ .../Public/Export-ZertoVmNicSetting.ps1 | 85 +++++++++++++++++++ docs/Export-ZertoVmNicSetting.md | 79 +++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 Tests/Public/Export-ZertoVmNicSetting.Tests.ps1 create mode 100644 ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 create mode 100644 docs/Export-ZertoVmNicSetting.md diff --git a/Tests/Public/Export-ZertoVmNicSetting.Tests.ps1 b/Tests/Public/Export-ZertoVmNicSetting.Tests.ps1 new file mode 100644 index 0000000..7e63113 --- /dev/null +++ b/Tests/Public/Export-ZertoVmNicSetting.Tests.ps1 @@ -0,0 +1,42 @@ +#Requires -Modules Pester +#Region - Test Setup +$moduleFileName = "ZertoApiWrapper.psd1" +$here = (Split-Path -Parent $MyInvocation.MyCommand.Path).Replace("Tests", "ZertoApiWrapper") +$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".") +$file = Get-ChildItem "$here\$sut" +$modulePath = $here -replace "Public", "" +$moduleFile = Get-ChildItem "$modulePath\$moduleFileName" +Get-Module -Name ZertoApiWrapper | Remove-Module -Force +Import-Module $moduleFile -Force +#EndRegion + +Describe $file.BaseName -Tag 'Unit' { + + It "is valid Powershell (Has no script errors)" { + $contents = Get-Content -Path $file -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors) + $errors | Should -HaveCount 0 + } + + Context "$($file.BaseName)::Parameter Unit Tests" { + It "has a mantatory string parameter for the output file" { + Get-Command $file.BaseName | Should -HaveParameter OutputFile -Type String -Mandatory + } + + It "has a non-mandatory string array parameter for vpgName(s) to export" { + Get-Command $file.BaseName | Should -HaveParameter vpgName -Type String[] + Get-Command $file.BaseName | Should -HaveParameter vpgName -Not -Mandatory + } + + It "Output File does not take null or empty string" { + { Export-ZertoVpg -outputFile "" } | Should -Throw + { Export-ZertoVpg -outputFile $null } | Should -Throw + } + + It "Vpg Name parameter does not take null or empty string" { + { Export-ZertoVpg -outputFile ".\ExportedInfo.csv" -vpgName = "" } | Should -Throw + { Export-ZertoVpg -outputFile ".\ExportedInfo.csv" -vpgName = $null } | Should -Throw + } + } +} diff --git a/ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 b/ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 new file mode 100644 index 0000000..cace80a --- /dev/null +++ b/ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 @@ -0,0 +1,85 @@ +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> +function Export-ZertoVmNicSetting { + [CmdletBinding()] + param ( + # Vpg(s) to export. If no VPG was named, all data will be exported. + [Parameter( + Helpmessage = "VPG(s) to export" + )] + [ValidateNotNullOrEmpty()] + [string[]] + $VpgName, + # Output file information + [Parameter( + Helpmessage = "File to export the data to. This MUST be a CSV. If a CSV filename is not specified, the file will be forced into a CSV", + Mandatory + )] + [ValidateNotNullOrEmpty()] + [string] + $OutputFile + ) + + begin { + } + + process { + if (($OutputFile.Split('.')[-1]) -ne 'csv') { + $OutputFile += '.csv' + } + if ( "VpgName" -in $PSBoundParameters.Keys ) { + $vpgs = Get-ZertoVpg | Where-Object { $_.VpgName -in $VpgName } + foreach ($group in $VpgName) { + if ($group -notin $vpgs.VpgName) { + Write-Error "$group VPG not found. Skipping." -ErrorAction Continue + } + } + } else { + $vpgs = Get-ZertoVpg + } + $nicSettings = foreach ($group in $vpgs) { + $protectedVms = Get-ZertoProtectedVm -vpgName ($group.vpgname) + $vmMap = @{ } + foreach ($vm in $protectedVms) { + $vmMap["$($vm.vmIdentifier)"] = $vm.vmName + } + $settingsId = New-ZertoVpgSettingsIdentifier -vpgIdentifier $group.vpgIdentifier + $vmSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $settingsId -vms + $networks = Get-ZertoVirtualizationSite -siteIdentifier $group.RecoverySite.identifier -networks + $null = Remove-ZertoVpgSettingsIdentifier -vpgSettingsIdentifier $settingsId + $networkMap = @{ } + foreach ($network in $networks) { + $networkMap[$network.NetworkIdentifier] = $network.VirtualizationNetworkName + } + foreach ($vm in $vmSettings) { + $nicInfo = [PSCustomObject]@{ + VPGName = $group.VPGName + VMName = $vmMap[$($vm.vmIdentifier)] + NicIdentifier = $vm.nics.NicIdentifier + LiveNetwork = $networkMap[$vm.nics.failover.Hypervisor.NetworkIdentifier] + LiveShouldReplaceMac = $vm.nics.failover.Hypervisor.ShouldReplaceMacAddress + LiveIsDHCP = $vm.Nics.failover.Hypervisor.IpConfig.IsDhcp + LiveIpAddress = $vm.nics.failover.Hypervisor.IpConfig.StaticIp + LiveIpSubnetMask = $vm.nics.failover.Hypervisor.IpConfig.SubnetMask + LiveIpDefaultGateway = $vm.nics.failover.Hypervisor.IpConfig.Gateway + LivePrimaryDns = $vm.nics.failover.Hypervisor.IpConfig.PrimaryDns + LiveSecondayDns = $vm.nics.failover.Hypervisor.IpConfig.SecondaryDns + LiveDnsSuffix = $vm.nics.failover.Hypervisor.DnsSuffix + TestNetwork = $networkMap[$vm.nics.failoverTest.Hypervisor.NetworkIdentifier] + TestShouldReplaceMac = $vm.nics.failoverTest.Hypervisor.ShouldReplaceMacAddress + TestIsDHCP = $vm.Nics.failoverTest.Hypervisor.IpConfig.IsDhcp + TestIpAddress = $vm.nics.failoverTest.Hypervisor.IpConfig.StaticIp + TestIpSubnetMask = $vm.nics.failoverTest.Hypervisor.IpConfig.SubnetMask + TestIpDefaultGateway = $vm.nics.failoverTest.Hypervisor.IpConfig.Gateway + TestPrimaryDns = $vm.nics.failoverTest.Hypervisor.IpConfig.PrimaryDns + TestSecondayDns = $vm.nics.failoverTest.Hypervisor.IpConfig.SecondaryDns + TestDnsSuffix = $vm.nics.failoverTest.Hypervisor.DnsSuffix + } + $nicInfo + } + } + $nicSettings | ConvertTo-Csv | Out-File $OutputFile + } + + end { + } +} diff --git a/docs/Export-ZertoVmNicSetting.md b/docs/Export-ZertoVmNicSetting.md new file mode 100644 index 0000000..7e8ce10 --- /dev/null +++ b/docs/Export-ZertoVmNicSetting.md @@ -0,0 +1,79 @@ +--- +external help file: ZertoApiWrapper-help.xml +Module Name: ZertoApiWrapper +online version: https://github.com/ZertoPublic/ZertoApiWrapper/blob/Master/docs/Export-ZertoVmNicSettings.md +schema: 2.0.0 +--- + +# Export-ZertoVmNicSetting + +## SYNOPSIS +Queries VPGs and associated Virtual Machines to export all protected virtual machine NIC settings to a Comma Separated Value (CSV) file. + +## SYNTAX + +``` +Export-ZertoVmNicSetting [[-VpgName] ] [-OutputFile] [] +``` + +## DESCRIPTION +Queries VPGs and associated Virtual Machines to export all protected virtual machine NIC settings to a Comma Separated Value (CSV) file. This file can be used with the Import-ZertoVmNicSetting function to bulk update. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Export-ZertoVmNicSetting -OutputFile "C:\ZertoInfo\VMNicSettings.csv" +``` + +Exports VM Nic Settings for ALL Virtual Protection Groups to a file located at "C:\ZertoInfo\VMNicSettings.csv" + +### Example 2 +```powershell +PS C:\> Export-ZertoVmNicSetting -OutputFile "C:\ZertoInfo\VMNicSettings.csv" -VpgName "Exchange", "Support Forum" +``` + +Exports VM Nic Settings for Exchange and Support Forum Virtual Protection Groups to a file located at "C:\ZertoInfo\VMNicSettings.csv" + +## PARAMETERS + +### -OutputFile +Output file information + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -VpgName +Vpg(s) to export. If no VPG was named, all data will be exported. + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +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 + +## OUTPUTS + +## NOTES + +## RELATED LINKS From 90cf88b5cbd43349204b543bf86d373fc35351f1 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Mon, 8 Jul 2019 13:38:48 -0400 Subject: [PATCH 4/8] Update ZertoApiWrapper.build.ps1 --- ZertoApiWrapper.build.ps1 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ZertoApiWrapper.build.ps1 b/ZertoApiWrapper.build.ps1 index e36079a..1219dfd 100644 --- a/ZertoApiWrapper.build.ps1 +++ b/ZertoApiWrapper.build.ps1 @@ -1,6 +1,6 @@ #Requires -Modules 'InvokeBuild' -$version = "{0}.{1}" -f $(Get-Content .\version.txt), $(get-date -format 'yyyyMMdd') +$version = "{0}.{1}" -f $(Get-Content .\version.txt), $(Get-Date -format 'yyyyMMdd') task . CreateArtifacts @@ -59,9 +59,9 @@ task AnalyzeBuiltFiles CheckPSScriptAnalyzerInstalled, CreatePsm1ForRelease, { task FileTests CheckPesterInstalled, { $testResultsFile = "$BuildRoot\Tests\TestResults.xml" - $script:results = Invoke-Pester -Script "$BuildRoot" -Tag Unit -OutputFile $testResultsFile -PassThru + $script:results = Invoke-Pester -Script "$BuildRoot" -Tag Unit -OutputFile $testResultsFile -PassThru -Show Fails $FailureMessage = '{0} Unit test(s) failed. Aborting build' -f $results.FailedCount - assert ($results.FailedCount -eq 0) $FailureMessage + Assert ($results.FailedCount -eq 0) $FailureMessage } $buildMamlParams = @{ @@ -77,15 +77,15 @@ task BuildMamlHelp CheckPlatyPSInstalled, { } task UpdateMarkdownHelp CheckPlatyPSInstalled, { - remove-module ZertoApiWrapper -force -ErrorAction SilentlyContinue + Remove-Module ZertoApiWrapper -force -ErrorAction SilentlyContinue Import-Module .\ZertoApiWrapper\ZertoApiWrapper.psm1 -Force - Update-MarkDownHelp -Path docs -AlphabeticParamsOrder + Update-MarkdownHelp -Path docs -AlphabeticParamsOrder } task UpdateMarkdownHelpModule CheckPlatyPSInstalled, { - remove-module ZertoApiWrapper -force -ErrorAction SilentlyContinue + Remove-Module ZertoApiWrapper -force -ErrorAction SilentlyContinue Import-Module .\ZertoApiWrapper\ZertoApiWrapper.psm1 -Force - Update-MarkDownHelpModule -Path docs -AlphabeticParamsOrder + Update-MarkdownHelpModule -Path docs -AlphabeticParamsOrder } task CreatePsd1ForRelease CleanTemp, { From 80c82b99e08953f3b8a104ca450fba0960eb73da Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 9 Jul 2019 11:48:21 -0400 Subject: [PATCH 5/8] Update to use Export-CSV --- ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 b/ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 index cace80a..6846145 100644 --- a/ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 +++ b/ZertoApiWrapper/Public/Export-ZertoVmNicSetting.ps1 @@ -77,7 +77,7 @@ function Export-ZertoVmNicSetting { $nicInfo } } - $nicSettings | ConvertTo-Csv | Out-File $OutputFile + $nicSettings | Export-Csv -Path $OutputFile } end { From cb991b3d0c3e7fb6f197eaa8787eac67b6a6f3aa Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 9 Jul 2019 12:38:02 -0400 Subject: [PATCH 6/8] Correct On-line Help Link URI --- docs/Get-ZertoZsspSession.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Get-ZertoZsspSession.md b/docs/Get-ZertoZsspSession.md index 9c698a9..487e2eb 100644 --- a/docs/Get-ZertoZsspSession.md +++ b/docs/Get-ZertoZsspSession.md @@ -1,7 +1,7 @@ --- external help file: ZertoApiWrapper-help.xml Module Name: ZertoApiWrapper -online version: https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZertpZsspSession.md +online version: https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZertoZsspSession.md schema: 2.0.0 --- From 4d7d1391ff07a17329654a61540cf64c60864e07 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 9 Jul 2019 13:12:22 -0400 Subject: [PATCH 7/8] Create function and required supporting docs Import-ZertoVmNicSetting --- .../Public/Import-ZertoVmNicSetting.Tests.ps1 | 39 ++++++ .../Public/Import-ZertoVmNicSetting.ps1 | 125 ++++++++++++++++++ docs/Import-ZertoVmNicSetting.md | 60 +++++++++ 3 files changed, 224 insertions(+) create mode 100644 Tests/Public/Import-ZertoVmNicSetting.Tests.ps1 create mode 100644 ZertoApiWrapper/Public/Import-ZertoVmNicSetting.ps1 create mode 100644 docs/Import-ZertoVmNicSetting.md diff --git a/Tests/Public/Import-ZertoVmNicSetting.Tests.ps1 b/Tests/Public/Import-ZertoVmNicSetting.Tests.ps1 new file mode 100644 index 0000000..5647976 --- /dev/null +++ b/Tests/Public/Import-ZertoVmNicSetting.Tests.ps1 @@ -0,0 +1,39 @@ +#Requires -Modules Pester +$moduleFileName = "ZertoApiWrapper.psd1" +$here = (Split-Path -Parent $MyInvocation.MyCommand.Path).Replace("Tests", "ZertoApiWrapper") +$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".") +$file = Get-ChildItem "$here\$sut" +$modulePath = $here -replace "Public", "" +$moduleFile = Get-ChildItem "$modulePath\$moduleFileName" +Get-Module -Name ZertoApiWrapper | Remove-Module -Force +Import-Module $moduleFile -Force + +Describe $file.BaseName -Tag 'Unit' { + + It "is valid Powershell (Has no script errors)" { + $contents = Get-Content -Path $file -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors) + $errors | Should -HaveCount 0 + } + + Context "$($file.BaseName)::Parameter Unit Tests" { + + It "Has a mandatory string array parameter for the settings file to import" { + Get-Command $file.BaseName | Should -HaveParameter InputFile + Get-Command $file.BaseName | Should -HaveParameter InputFile -Mandatory + Get-Command $file.BaseName | Should -HaveParameter InputFile -Type String + } + + It "Will not accecpt a Null or Empty string for the settings file" { + { Import-ZertoVpg -InputFile $null } | Should -Throw + { Import-ZertoVpg -InputFile "" } | Should -Throw + { Import-ZertoVpg -InputFile @() } | Should -Throw + } + + } + + Context "$($file.BaseName)::Function Unit Tests" { + + } +} diff --git a/ZertoApiWrapper/Public/Import-ZertoVmNicSetting.ps1 b/ZertoApiWrapper/Public/Import-ZertoVmNicSetting.ps1 new file mode 100644 index 0000000..8a24dec --- /dev/null +++ b/ZertoApiWrapper/Public/Import-ZertoVmNicSetting.ps1 @@ -0,0 +1,125 @@ +<# .ExternalHelp ./en-us/ZertoApiWrapper-help.xml #> +function Import-ZertoVmNicSetting { + [CmdletBinding(SupportsShouldProcess)] + param( + # File to process for import + [Parameter(Helpmessage = "CSV file containing the required VM NIC settings", Mandatory)] + [ValidateNotNullOrEmpty()] + [string] + $InputFile + ) + + begin { + } + + process { + if (-not (Test-Path $InputFile)) { + Write-Error "Unable to find $InputFile. Please check the name and path and try again." -ErrorAction Stop + } elseif ((Get-Item $InputFile).Extension -notmatch '.csv') { + Write-Error "$InputFile does not have a 'csv' extension. Please check the name and path and try again." -ErrorAction Stop + } + $ExpectedHeaders = "VPGName", "VMName", "NicIdentifier", "LiveNetwork", "LiveShouldReplaceMac", "LiveIsDHCP", "LiveIpAddress", "LiveIpSubnetMask", "LiveIpDefaultGateway", "LivePrimaryDns", "LiveSecondayDns", "LiveDnsSuffix", "TestNetwork", "TestShouldReplaceMac", "TestIsDHCP", "TestIpAddress", "TestIpSubnetMask", "TestIpDefaultGateway", "TestPrimaryDns", "TestSecondayDns", "TestDnsSuffix" + $HeaderLine = ((Get-Content -Path $InputFile -First 1).Replace('"', '')).Split(',') + foreach ($header in $ExpectedHeaders) { + if ($header -notin $HeaderLine) { + Write-Error "$InputFile is malformed. Please ensure all headers are present." -ErrorAction Stop + } + } + $ImportData = Import-Csv -Path $InputFile + $VpgsToUpdate = $ImportData.VPGName | Select-Object -Unique + foreach ($Vpg in $VpgsToUpdate) { + $VpgInfo = Get-ZertoVpg -vpgName $Vpg + $VpgIdentifier = $VpgInfo.VpgIdentifier + $RecoveryNetworks = Get-ZertoVirtualizationSite -siteIdentifier $VpgInfo.RecoverySite.Identifier -networks + $NetworkMap = @{ } + foreach ($Network in $RecoveryNetworks) { + $NetworkMap[$Network.VirtualizationNetworkName] = $Network.NetworkIdentifier + } + $VpgVms = Get-ZertoProtectedVm -vpgName $Vpg + $VmMap = @{ } + foreach ($Vm in $VpgVms) { + $VmMap[$Vm.vmName] = $Vm.vmIdentifier + } + $VpgSettingsId = New-ZertoVpgSettingsIdentifier -vpgIdentifier $VpgIdentifier + $VmsToUpdate = $ImportData | Where-Object { $_.VPGName -eq $Vpg } + foreach ($vm in $VmsToUpdate) { + if ([string]::IsNullOrWhiteSpace($vm.VpgName) -or + [string]::IsNullOrWhiteSpace($Vm.VMName) -or + [string]::IsNullOrWhiteSpace($Vm.NicIdentifier) -or + [string]::IsNullOrWhiteSpace($Vm.LiveNetwork) -or + [string]::IsNullOrWhiteSpace($Vm.LiveShouldReplaceMac) -or + [string]::IsNullOrWhiteSpace($Vm.TestNetwork) -or + [string]::IsNullOrWhiteSpace($Vm.TestShouldReplaceMac)) { + Write-Error "$($Vm.VMName) does not contain all the required data. Please check the CSV entry for this item and try again." -ErrorAction Continue + } else { + $uri = "vpgSettings/{0}/vms/{1}" -f $vpgSettingsId, $vmMap[$vm.VMName] + $VmNicSettings = Get-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsId -vmIdentifier $vmMap[$vm.VMName] + foreach ($nic in $VmNicSettings.nics) { + if ($nic.NicIdentifier -eq $vm.NicIdentifier) { + $nic.failover.Hypervisor.NetworkIdentifier = $NetworkMap[$vm.LiveNetwork] + $nic.failover.Hypervisor.ShouldReplaceMacAddress = $vm.LiveShouldReplaceMac + if ($null -eq $nic.failover.Hypervisor.IpConfig -and ($null -ne $vm.LiveIsDHCP -or $null -ne $vm.LiveIpAddress)) { + $IpConfig = [PSCustomObject]@{ + IsDhcp = $vm.LiveIsDHCP + StaticIp = $vm.LiveIpAddress + SubnetMask = $vm.LiveIpSubnetMask + Gateway = $vm.LiveIpDefaultGateway + PrimaryDns = $vm.LivePrimaryDns + SecondaryDns = $vm.LiveSecondayDns + } + $nic.failover.Hypervisor.IpConfig = $IpConfig + } elseif ($null -eq $nic.failover.Hypervisor.IpConfig -and $null -eq $vm.LiveIsDHCP -and $null -eq $vm.LiveIpAddress) { + $nic.failover.Hypervisor.IpConfig = $null + } else { + $nic.failover.Hypervisor.IpConfig.IsDhcp = $vm.LiveIsDHCP + $nic.failover.Hypervisor.IpConfig.StaticIp = $vm.LiveIpAddress + $nic.failover.Hypervisor.IpConfig.SubnetMask = $vm.LiveIpSubnetMask + $nic.failover.Hypervisor.IpConfig.Gateway = $vm.LiveIpDefaultGateway + $nic.failover.Hypervisor.IpConfig.PrimaryDns = $vm.LivePrimaryDns + $nic.failover.Hypervisor.IpConfig.SecondaryDns = $vm.LiveSecondayDns + } + $nic.failover.Hypervisor.DnsSuffix = $vm.LiveDnsSuffix + $nic.failoverTest.Hypervisor.NetworkIdentifier = $NetworkMap[$vm.TestNetwork] + $nic.failoverTest.Hypervisor.ShouldReplaceMacAddress = $vm.TestShouldReplaceMac + if ($null -eq $nic.failoverTest.Hypervisor.IpConfig -and ($null -ne $vm.TestIsDHCP -or $null -ne $vm.TestIpAddress)) { + $IpConfig = [PsCustomObject]@{ + IsDhcp = $vm.TestIsDHCP + StaticIp = $vm.TestIpAddress + SubnetMask = $vm.TestIpSubnetMask + Gateway = $vm.TestIpDefaultGateway + PrimaryDns = $vm.TestPrimaryDns + SecondaryDns = $vm.TestSecondayDns + } + $nic.failoverTest.Hypervisor.IpConfig = $IpConfig + } elseif ($null -eq $nic.failoverTest.Hypervisor.IpConfig -and $null -eq $vm.TestIsDHCP -and $null -eq $vm.TestIpAddress) { + $nic.failoverTest.Hypervisor.IpConfig = $null + } else { + $nic.failoverTest.Hypervisor.IpConfig.IsDhcp = $vm.TestIsDHCP + $nic.failoverTest.Hypervisor.IpConfig.StaticIp = $vm.TestIpAddress + $nic.failoverTest.Hypervisor.IpConfig.SubnetMask = $vm.TestIpSubnetMask + $nic.failoverTest.Hypervisor.IpConfig.Gateway = $vm.TestIpDefaultGateway + $nic.failoverTest.Hypervisor.IpConfig.PrimaryDns = $vm.TestPrimaryDns + $nic.failoverTest.Hypervisor.IpConfig.SecondaryDns = $vm.TestSecondayDns + } + $nic.failoverTest.Hypervisor.DnsSuffix = $vm.TestDnsSuffix + } + } + Write-Verbose "Putting Updated Config for VM: $($vm.vmname) in Vpg: $Vpg" + if ($PSCmdlet.ShouldProcess($vm.NicIdentifier, "Updating Nic")) { + Invoke-ZertoRestRequest -uri $uri -Method "PUT" -Body ($VmNicSettings | ConvertTo-Json -Depth 10) > $null + } + } + } + Write-Verbose "Saving updated configuration for VPG: $Vpg" + if ($PSCmdlet.ShouldProcess($Vpg, "Saving Changes")) { + Save-ZertoVpgSetting -vpgSettingsIdentifier $vpgSettingsId + } + Write-Verbose "Waiting 5 Seconds for Next VPG Update" + Start-Sleep 5 + } + } + + + end { + } +} diff --git a/docs/Import-ZertoVmNicSetting.md b/docs/Import-ZertoVmNicSetting.md new file mode 100644 index 0000000..f2a8d62 --- /dev/null +++ b/docs/Import-ZertoVmNicSetting.md @@ -0,0 +1,60 @@ +--- +external help file: ZertoApiWrapper-help.xml +Module Name: ZertoApiWrapper +online version: https://github.com/ZertoPublic/ZertoApiWrapper/blob/master/docs/Get-ZertoZsspSession.md +schema: 2.0.0 +--- + +# Import-ZertoVmNicSetting + +## SYNOPSIS +Using a CSV file, will import updated Live and Test network settings for protected virtual machines. + +## SYNTAX + +``` +Import-ZertoVmNicSetting [-InputFile] [] +``` + +## DESCRIPTION +Using a CSV file, will import updated Live and Test network settings for protected virtual machines. This function will read the provided CSV file and only update VMs defined in the file. + +It should be noted, due to current API limitations once a NIC is defined to update to either a Static IP address or use a DHCP address, it is not possible to remove that configuration via the API. Should you require this functionality, you will need to manually update via the GUI to set the option to "No" + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Import-ZertoVmNicSetting -InputFile 'C:\ZertoScripts\UpdatedNicInformation.csv' +``` + +Imports the data in the CSV located at 'C:\ZertoScripts\UpdatedNicInformation.csv' and updates each VM and each NIC as defined in the CSV file. + +## PARAMETERS + +### -InputFile +File to process for import + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +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 + +## OUTPUTS + +## NOTES + +## RELATED LINKS +[Zerto Virtual Manager REST API VpgSettings end point documentation](http://s3.amazonaws.com/zertodownload_docs/Latest/Zerto%20Virtual%20Replication%20Zerto%20Virtual%20Manager%20%28ZVM%29%20-%20vSphere%20Online%20Help/index.html#page/RestfulAPIs%2FStatusAPIs.5.110.html%23) From ddb639ad6b122b2623114b61baf0a027f0153956 Mon Sep 17 00:00:00 2001 From: Wes Carroll Date: Tue, 9 Jul 2019 13:29:31 -0400 Subject: [PATCH 8/8] Update RELEASENOTES.md --- RELEASENOTES.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4e5cafc..e4df1d1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,8 +2,10 @@ ### Zerto Virtual Manager -* +* Added two functions to aid in bulk updating of NIC information for protected VMs. ([Issue 38](https://github.com/ZertoPublic/ZertoApiWrapper/issues/38)) + * [Export-ZertoVmNicSetting](https://github.com/ZertoPublic/ZertoApiWrapper/blob/Master/docs/Export-ZertoVmNicSettings.md) + * [Import-ZertoVmNicSetting](https://github.com/ZertoPublic/ZertoApiWrapper/blob/Master/docs/Import-ZertoVmNicSettings.md) ### Zerto Analytics -* Fixed an issue where the Zerto Analytics Rest Request function was not checking for the token before attempting a connection. +* Fixed an [issue](https://github.com/ZertoPublic/ZertoApiWrapper/issues/36) where the Zerto Analytics Rest Request function was not checking for the token before attempting a connection.