diff --git a/CHANGELOG.md b/CHANGELOG.md index e85c64ff0..3fb665cef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] + +## 2019-09-26 + +### Added initial support for VMware vCloud Director (vCD) API endpoints in CDM + +* Added cmdlets `Update-RubrikVCD`, `Set-RubrikVCD`, `Restore-RubrikVApp`, `Protect-RubrikVApp`, `Get-RubrikVcdTemplateExportOptions`, `Get-RubrikVCD`, `Get-RubrikVappSnapshot`, `RubrikVAppRecoverOptions`, `Get-RubrikVAppExportOptions`, `Get-RubrikVApp`, `Export-RubrikVCDTemplate`, `Export-RubrikVApp` and related tests as requested in [Issue 273](https://github.com/rubrikinc/rubrik-sdk-for-powershell/issues/273) +* Updated `Get-RubrikSnapshot` to support vCD vApps +* Misc bug fixes and typos corrected + ## 2019-09-24 ### Added [Private Function to apply TypeName information to objects] diff --git a/Rubrik/Private/Get-RubrikAPIData.ps1 b/Rubrik/Private/Get-RubrikAPIData.ps1 index 49cb7aca6..045385696 100644 --- a/Rubrik/Private/Get-RubrikAPIData.ps1 +++ b/Rubrik/Private/Get-RubrikAPIData.ps1 @@ -111,6 +111,56 @@ function Get-RubrikAPIData($endpoint) { Success = '200' } } + 'Export-RubrikVApp' = @{ + '1.0' = @{ + Description = 'Exports a given snapshot for a vCD vApp' + URI = '/api/internal/vcd/vapp/snapshot/{id}/export' + Method = 'Post' + Body = @{ + exportMode = 'exportMode' + networksToRestore = [System.Collections.ArrayList]@() + vmsToExport = @( + @{ + name = 'name' + vcdMoid = 'vcdMoid' + networkConnections = @{ + nicIndex = 'nicIndex' + addressingMode = 'addressingMode' + ipAddress = 'ipAddress' + isConnected = 'isConnected' + vappNetworkName = 'vappNetworkName' + } + } + ) + shouldPowerOnVmsAfterRecovery = 'shouldPowerOnVmsAfterRecovery' + newVappParams = @{ + name = 'name' + orgVdcId = 'orgVdcId' + } + } + Query = '' + Result = '' + Filter = '' + Success = '202' + } + } + 'Export-RubrikVcdTemplate' = @{ + '1.0' = @{ + Description = 'Exports a given vCD template' + URI = '/api/v1/vcd/vapp/template/snapshot/{id}/export' + Method = 'Post' + Body = @{ + name = 'name' + catalogId = 'catalogId' + orgVdcId = 'orgVdcId' + storagePolicyId = 'storagePolicyId' + } + Query = '' + Result = '' + Filter = '' + Success = '202' + } + } 'Get-RubrikAPIToken' = @{ '5.0' = @{ Description = 'Retrieves list of generated API tokens from the Rubrik cluster' @@ -662,6 +712,7 @@ function Get-RubrikAPIData($endpoint) { Nutanix = '/api/internal/nutanix/vm/{id}/snapshot' VolumeGroup = '/api/internal/volume_group/{id}/snapshot' Oracle = '/api/internal/oracle/db/{id}/snapshot' + VcdVapp = '/api/internal/vcd/vapp/{id}/snapshot' } Method = 'Get' Body = '' @@ -735,6 +786,106 @@ function Get-RubrikAPIData($endpoint) { ObjectTName = 'Rubrik.UnmanagedObject' } } + 'Get-RubrikVApp' = @{ + '1.0' = @{ + Description = 'Get summary of all the vCD vApps' + URI = '/api/internal/vcd/vapp' + Method = 'Get' + Body = '' + Query = @{ + is_relic = 'is_relic' + name = 'name' + effective_sla_domain_id = 'effective_sla_domain_id' + sla_assignment = 'sla_assignment' + primary_cluster_id = 'primary_cluster_id' + } + Result = 'data' + Filter = @{ + 'Name' = 'name' + 'SLA' = 'effectiveSlaDomainName' + 'SourceObjectId' = 'effectiveSlaSourceObjectId' + 'SourceObjectName' = 'effectiveSlaSourceObjectName' + 'vcdClusterId' = 'vcdClusterId' + 'vcdClusterName' = 'vcdClusterName' + } + Success = '200' + } + } + 'Get-RubrikVAppExportOptions' = @{ + '1.0' = @{ + Description = 'Retrieves export options for a vCD vApp' + URI = '/api/internal/vcd/vapp/snapshot/{id}/export/options' + Method = 'Get' + Body = '' + Query = @{ + export_mode = 'export_mode' + target_vapp_id = 'target_vapp_id' + target_org_vdc_id = 'target_org_vdc_id' + } + Result = '' + Filter = '' + Success = '200' + } + } + 'Get-RubrikVAppRecoverOptions' = @{ + '1.0' = @{ + Description = 'Retrieves instant recovery options for a vCD vApp' + URI = '/api/internal/vcd/vapp/snapshot/{id}/instant_recover/options' + Method = 'Get' + Body = '' + Query = @{} + Result = '' + Filter = '' + Success = '200' + } + } + 'Get-RubrikVcdTemplateExportOptions' = @{ + '1.0' = @{ + Description = 'Retrieves export options for a vCD Template' + URI = '/api/v1/vcd/vapp/template/snapshot/{id}/export/options' + Method = 'Get' + Body = '' + Query = @{ + catalog_id = 'catalog_id' + name = 'name' + org_vdc_id = 'org_vdc_id' + } + Result = '' + Filter = '' + Success = '200' + } + } + 'Get-RubrikVAppSnapshot' = @{ + '1.0' = @{ + Description = 'Retrieve information of a vCD vApp snapshot' + URI = '/api/internal/vcd/vapp/snapshot/{id}' + Method = 'Get' + Body = '' + Query = @{ + id = 'id' + } + Result = 'data' + Filter = '' + Success = '200' + } + } + 'Get-RubrikVCD' = @{ + '1.0' = @{ + Description = 'Retrieve summary information for all vCD cluster objects' + URI = '/api/internal/vcd/cluster' + Method = 'Get' + Body = '' + Query = @{ + name = 'name' + status = 'status' + } + Result = 'data' + Filter = @{ + 'Hostname' = 'hostname' + } + Success = '200' + } + } 'Get-RubrikVCenter' = @{ '1.0' = @{ Description = 'Retrieves all vCenter settings of the Rubrik cluster' @@ -1259,6 +1410,7 @@ function Get-RubrikAPIData($endpoint) { VMware = '/api/v1/vmware/vm/{id}/snapshot' VolumeGroup = '/api/internal/volume_group/{id}/snapshot' Oracle = '/api/internal/oracle/db/{id}/snapshot' + VcdVapp = '/api/internal/vcd/vapp/{id}/snapshot' } Method = 'Post' Body = @{ @@ -1343,6 +1495,20 @@ function Get-RubrikAPIData($endpoint) { Success = '204' } } + 'Protect-RubrikVApp' = @{ + '1.0' = @{ + Description = 'Update a vCD vApp with the specified SLA Domain.' + URI = '/api/internal/vcd/vapp/{id}' + Method = 'Patch' + Body = @{ + configuredSlaDomainId = 'configuredSlaDomainId' + } + Query = '' + Result = '' + Filter = '' + Success = '200' + } + } 'Protect-RubrikVM' = @{ '1.0' = @{ Description = 'Update a VM with the specified SLA Domain.' @@ -1585,6 +1751,31 @@ function Get-RubrikAPIData($endpoint) { Success = '202' } } + 'Restore-RubrikVApp' = @{ + '1.0' = @{ + Description = 'Restores a given snapshot for a vCD vApp' + URI = '/api/internal/vcd/vapp/snapshot/{id}/instant_recover' + Method = 'Post' + Body = @{ + vmsToRestore = @{ + name = 'name' + vcdMoid = 'vcdMoid' + networkConnections = @{ + nicIndex = 'nicIndex' + addressingMode = 'addressingMode' + ipAddress = 'ipAddress' + isConnected = 'isConnected' + vappNetworkName = 'vappNetworkName' + } + } + shouldPowerOnVmsAfterRecovery = 'shouldPowerOnVmsAfterRecovery' + } + Query = '' + Result = '' + Filter = '' + Success = '202' + } + } 'Set-RubrikAvailabilityGroup' = @{ '1.0' = @{ Description = 'Update a Microsoft SQL availability group.' @@ -1926,6 +2117,23 @@ function Get-RubrikAPIData($endpoint) { Success = '200' } } + 'Set-RubrikVCD' = @{ + '1.0' = @{ + Description = 'Updates settings of a vCD connection' + URI = '/api/internal/vcd/cluster/{id}' + Method = 'Patch' + Body = @{ + hostname = "hostname" + username = "username" + password = "password" + configuredSlaDomainId = "configuredSlaDomainId" + } + Query = '' + Result = '' + Filter = '' + Success = '200' + } + } 'Set-RubrikUser' = @{ '1.0' = @{ Description = 'Updates a Rubrik user.' @@ -2169,6 +2377,18 @@ function Get-RubrikAPIData($endpoint) { Success = '202' } } + 'Update-RubrikVCD' = @{ + '1.0' = @{ + Description = 'Refresh the metadata for the specified vCD Server' + URI = '/api/internal/vcd/cluster/{id}/refresh' + Method = 'Post' + Body = '' + Query = '' + Result = '' + Filter = '' + Success = '202' + } + } 'Update-RubrikVMwareVM' = @{ '1.0' = @{ Description = 'Refresh the metadata for the specified VMware VM' diff --git a/Rubrik/Private/Test-QueryParam.ps1 b/Rubrik/Private/Test-QueryParam.ps1 index 4af802d70..2bf748b2b 100644 --- a/Rubrik/Private/Test-QueryParam.ps1 +++ b/Rubrik/Private/Test-QueryParam.ps1 @@ -52,6 +52,11 @@ Write-Verbose -Message 'Loading OracleDatabase API data' $uri = ('https://'+$Server+$resources.URI.Oracle) -replace '{id}', $id } + 'VcdVapp::*' + { + Write-Verbose -Message 'Loading vCD API data' + $uri = ('https://'+$Server+$resources.URI.VcdVapp) -replace '{id}', $id + } default { throw 'The supplied id value has no matching endpoint' diff --git a/Rubrik/Public/Export-RubrikVApp.ps1 b/Rubrik/Public/Export-RubrikVApp.ps1 new file mode 100644 index 000000000..4923fb2c4 --- /dev/null +++ b/Rubrik/Public/Export-RubrikVApp.ps1 @@ -0,0 +1,323 @@ +#Requires -Version 3 +function Export-RubrikVApp +{ + <# + .SYNOPSIS + Exports a given snapshot for a vCD vApp + + .DESCRIPTION + The Export-RubrikVApp cmdlet exports a snapshot from a protected vCD vApp. + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + Export-RubrikVApp -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -snapshotid '7acdf6cd-2c9f-4661-bd29-b67d86ace70b' -ExportMode 'ExportToNewVapp' -PowerOn:$true + This exports the vApp snapshot with an id of 7acdf6cd-2c9f-4661-bd29-b67d86ace70b to a new vApp in the same Org VDC + + .EXAMPLE + Export-RubrikVApp -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -snapshotid '7acdf6cd-2c9f-4661-bd29-b67d86ace70b' -ExportMode 'ExportToNewVapp' -NoMapping -PowerOn:$true + This exports the vApp snapshot with an id of 7acdf6cd-2c9f-4661-bd29-b67d86ace70b to a new vApp in the same Org VDC and remove existing network mappings from VM NICs + + .EXAMPLE + Export-RubrikVApp -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -snapshotid '7acdf6cd-2c9f-4661-bd29-b67d86ace70b' -ExportMode 'ExportToNewVapp' -TargetOrgVDCID 'VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d567' -PowerOn:$true + This exports the vApp snapshot with an id of 7acdf6cd-2c9f-4661-bd29-b67d86ace70b to a new vApp in an alternate Org VDC + + .EXAMPLE + $vapp = Get-RubrikVApp -Name 'vApp01' -PrimaryClusterID local + $snapshot = Get-RubrikSnapshot -id $vapp.id -Latest + $restorableVms = $vapp.vms + $restorableVms[0].PSObject.Properties.Remove('vcenterVm') + $vm = @() + $vm += $restorableVms[0] + Export-RubrikVApp -id $vapp.id -snapshotid $snapshot.id -Partial $vm -ExportMode ExportToTargetVapp -PowerOn:$false + + This retreives the latest snapshot from the given vApp 'vApp01' and perform a partial export on the first VM in the vApp. + The VM is exported into the existing parent vApp. Set the ExportMode parameter to 'ExportToNewVapp' parameter to create a new vApp for the partial export. + This is an advanced use case and the user is responsible for parsing the output from Get-RubrikVApp, or gathering data directly from the API. + Syntax of the object passed with the -Partial Parameter is available in the API documentation. +#> + + [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact = 'High')] + Param( + # Rubrik id of the vApp to export + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName='Full')] + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName='Partial')] + [ValidateNotNullOrEmpty()] + [String]$id, + # Rubrik snapshot id of the vApp to export + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName='Full')] + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName='Partial')] + [ValidateNotNullOrEmpty()] + [Alias('snapshot_id')] + [String]$snapshotid, + # Perform a Partial vApp restore. Default operation is a Full vApp restore, unless this parameter is specified. + [Parameter(Mandatory = $true,ParameterSetName='Partial')] + [ValidateNotNullOrEmpty()] + [ValidateScript({ + if ('PSCustomObject' -ne $_.GetType().Name) { + Throw "Partial parameter should be a PSCustomObject" + } + $requiredProperties = @("name","vcdMoid","networkConnections") + ForEach($item in $requiredProperties) { + if(!$_.PSObject.Properties.Name.Contains($item)) { + Throw "Object passed via Partial parameter missing property $($item)" + } + } + return $true + })] + [PSCustomObject]$Partial, + # Specifies whether export should use the existing vApp or create a new vApp. Valid values are ExportToNewVapp or ExportToTargetVapp + [Parameter(Mandatory = $true)] + [ValidateSet('ExportToNewVapp','ExportToTargetVapp')] + [Alias('export_mode')] + [String]$ExportMode, + # ID of target vApp for Partial vApp Export. By default the VM(s) will be exported to their existing vApp. + [Parameter(ParameterSetName='Partial')] + [String]$TargetVAppID, + # ID of target Org VDC for Full vApp Export. By default the VM(s) will be exported to their existing Org VDC. /vcd/hierarchy API calls can be used to determine Org VDC IDs. + [Parameter(ParameterSetName='Full')] + [String]$TargetOrgVDCID, + # Disable NICs upon restoration. The NIC(s) will be disabled, but remain mapped to their existing network. + [Parameter(ParameterSetName='Full')] + [Switch]$DisableNetwork, + # Remove network mapping upon restoration. The NIC(s) will not be connected to any existing networks. + [Parameter(ParameterSetName='Full')] + [Switch]$NoMapping, + # Remove network interfaces from the restored vApp virtual machines. + [Parameter(ParameterSetName='Full')] + [Switch]$RemoveNetworkDevices, + # Map all vApp virtual machine NICs to specified network. + [Parameter(ParameterSetName='Full')] + [ValidateNotNullOrEmpty()] + [String]$NetworkMapping, + # Power on vApp after restoration. + [Parameter(ParameterSetName='Full',Mandatory = $true)] + [Parameter(ParameterSetName='Partial',Mandatory = $true)] + [Bool]$PowerOn, + # Rubrik server IP or FQDN + [Parameter(ParameterSetName='Full')] + [Parameter(ParameterSetName='Partial')] + [String]$Server = $global:RubrikConnection.server, + # API version + [Parameter(ParameterSetName='Full')] + [Parameter(ParameterSetName='Partial')] + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + #region oneoff + $resources.Body.exportMode = $ExportMode + $resources.Body.shouldPowerOnVmsAfterRecovery = $PowerOn + + if($Partial) { + Write-Verbose -Message "Performing Partial vApp Recovery" + $resources.Body.vmsToExport = @() + $resources.Body.vmsToExport += $Partial + + # Rename vApp VMs and remove unneeded data + foreach($vm in $resources.Body.vmsToExport) { + $vm.name = $vm.name + "-" + [string](Get-Date -Format "ddd MMM yyyy HH:mm:ss 'GMT'K") + Write-Verbose -Message "vApp VM renamed to $($vm.name)" + $vm.PSObject.Properties.Remove('storagePolicyId') + # If exporting to a different vApp, unmap network connections + if($TargetVAppID) { + foreach($network in $vm.networkConnections) { + Write-Verbose -Message "Unmapping $($network.vappNetworkName) from $($vm.Name)" + $network.PSObject.Properties.Remove('vappNetworkName') + } + } + } + + if($ExportMode -eq 'ExportToTargetVapp') { + Write-Verbose -Message "Performing Partial vApp Export to Existing Target vApp" + if($TargetVAppID) { + $resources.Body.targetVappId = $TargetVAppID + } + else + { + $resources.Body.targetVappId = $id + } + $resources.Body.networksToRestore = @() + $resources.Body.Remove('newVappParams') + + $body = ConvertTo-Json -InputObject $resources.Body -Depth 4 + Write-Verbose -Message "vApp Export REST Request Body `n$($body)" + } + else { + Write-Verbose -Message "Performing Partial vApp Export to New vApp" + $vapp = Get-RubrikVapp -id $id + + # Collect networks to restore + $networks = [System.Collections.ArrayList]@() + foreach($vm in $resources.Body.vmsToExport) { + foreach($network in $vm.networkConnections) { + if($false -eq $networks.Contains($network.vappNetworkName)) { + $networks.Add($network.vappNetworkName) | Out-Null + Write-Verbose -Message "Flagged network $($network.vappNetworkName) for restore" + } + } + } + + if($TargetOrgVDCID) { + $orgvdc = $TargetOrgVDCID + } + else { + # Find orgVdcId from existing vApp + foreach($item in $vapp.infraPath) { + if($item.id.StartsWith('VcdOrgVdc:::')) { + $orgvdc = $item.id + Write-Verbose -Message "Using Org VDC $($orgvdc) for export" + } + } + } + + # Build networksToRestore based on networks collected from vApp + $resources.Body.networksToRestore = [System.Collections.ArrayList]@() + foreach($availablenet in $vapp.networks) { + if($networks.Contains($availablenet.name)) { + $resources.Body.networksToRestore.Add($availablenet) | Out-Null + Write-Verbose -Message "Found network $($availablenet.name) information for restore" + } + } + + # Set new vApp name and orgVdcId + $resources.Body.newVappParams.name = $vapp.name + "-" + [string](Get-Date -Format "ddd MMM yyyy HH:mm:ss 'GMT'K") + Write-Verbose -Message "Exported vApp will be named $($resources.Body.newVappParams.name)" + $resources.Body.newVappParams.orgVdcId = $orgvdc + + $body = ConvertTo-Json -InputObject $resources.Body -Depth 4 + Write-Verbose -Message "vApp Export REST Request Body `n$($body)" + } + } + else { + # Full vApp export is always to a New vApp + Write-Verbose -Message "Performing Full vApp Export to New vApp" + $vapp = Get-RubrikVapp -id $id + + # Collect networks to restore + $networks = [System.Collections.ArrayList]@() + foreach($vm in $vapp.vms) { + foreach($network in $vm.networkConnections) { + if($false -eq $networks.Contains($network.vappNetworkName)) { + $networks.Add($network.vappNetworkName) | Out-Null + Write-Verbose -Message "Flagged network $($network.vappNetworkName) for restore" + } + } + } + + if($TargetOrgVDCID) { + $orgvdc = $TargetOrgVDCID + } + else { + # Find orgVdcId from existing vApp + foreach($item in $vapp.infraPath) { + if($item.id.StartsWith('VcdOrgVdc:::')) { + $orgvdc = $item.id + Write-Verbose -Message "Using Org VDC $($orgvdc) for export" + } + } + } + + # Set new vApp name and orgVdcId + $resources.Body.newVappParams.name = $vapp.name + "-" + [string](Get-Date -Format "ddd MMM yyyy HH:mm:ss 'GMT'K") + Write-Verbose -Message "Exported vApp will be named $($resources.Body.newVappParams.name)" + $resources.Body.newVappParams.orgVdcId = $orgvdc + $resources.Body.vmsToExport = [System.Collections.ArrayList]@() + foreach($vm in $vapp.vms) { + $resources.Body.vmsToExport.Add($vm) + Write-Verbose -Message "Added $($vm.name) to request" + } + + # Build networksToRestore based on networks collected from vApp + $resources.Body.networksToRestore = [System.Collections.ArrayList]@() + foreach($availablenet in $recoveropts.availableVappNetworks) { + if($networks.Contains($availablenet.name)) { + $resources.Body.networksToRestore.Add($availablenet) | Out-Null + Write-Verbose -Message "Found network $($availablenet.name) information for restore" + } + } + + # Rename vApp VMs and remove unneeded data + foreach($vm in $resources.Body.vmsToExport) { + $vm.name = $vm.name + "-" + [string](Get-Date -Format "ddd MMM yyyy HH:mm:ss 'GMT'K") + Write-Verbose -Message "vApp VM renamed to $($vm.name)" + $vm.PSObject.Properties.Remove('vcenterVm') + $vm.PSObject.Properties.Remove('storagePolicyId') + } + + if($DisableNetwork) { + foreach($vm in $resources.Body.vmsToExport) { + foreach($network in $vm.networkConnections) { + $network.isConnected = $false + Write-Verbose -Message "Disabled NIC $($network.nicIndex) on $($vm.Name)" + } + } + } + + if($NoMapping) { + foreach($vm in $resources.Body.vmsToExport) { + foreach($network in $vm.networkConnections) { + Write-Verbose -Message "Unmapping $($network.vappNetworkName) from $($vm.Name)" + $network.PSObject.Properties.Remove('vappNetworkName') + } + } + } + + if($RemoveNetworkDevices) { + foreach($vm in $resources.Body.vmsToExport) { + $vm.networkConnections = @() + } + Write-Verbose -Message "Removed all NICs from vApp" + } + + if($NetworkMapping) { + foreach($vm in $resources.Body.vmsToExport) { + foreach($network in $vm.networkConnections) { + Write-Verbose -Message "Mapping NIC $($network.nicIndex) to $($NetworkMapping) network" + $network.vappNetworkName = $NetworkMapping + } + } + } + + $body = ConvertTo-Json -InputObject $resources.Body -Depth 4 + Write-Verbose -Message "vApp Export REST Request Body `n$($body)" + } + #endregion + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $snapshotid + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + # $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function \ No newline at end of file diff --git a/Rubrik/Public/Export-RubrikVCDTemplate.ps1 b/Rubrik/Public/Export-RubrikVCDTemplate.ps1 new file mode 100644 index 000000000..97a42d5b4 --- /dev/null +++ b/Rubrik/Public/Export-RubrikVCDTemplate.ps1 @@ -0,0 +1,124 @@ +#requires -Version 3 +function Export-RubrikVCDTemplate +{ + <# + .SYNOPSIS + Exports a given vCD template + + .DESCRIPTION + The Export-RubrikVCDTemplate cmdlet exports the specified vCD template + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + Export-RubrikVCDTemplate -id '01234567-8910-1abc-d435-0abc1234d567' -Name 'Template-Export' -catalogid '01234567-8910-1abc-d435-0abc1234d567' -orgvdcid '01234567-8910-1abc-d435-0abc1234d567' + This exports the vCD Template snapshot with ID 01234567-8910-1abc-d435-0abc1234d567 to the vCD catalog with ID 01234567-8910-1abc-d435-0abc1234d567. + The template will be exported to Org vDC ID with 01234567-8910-1abc-d435-0abc1234d567 temporarily, before being imported to the vCD catalog. This should be an Org vDC in the same vCD Org where the target catalog exists. + Finding needed IDs can be done directly via API, or via a command similar to (Invoke-RubrikRESTCall -Endpoint 'vcd/hierarchy/VcdOrg:::01234567-8910-1abc-d435-0abc1234d567/children' -api 'internal' -Method GET).data + #> + + [CmdletBinding()] + Param( + # Rubrik snapshot id of the vApp to export + [Parameter(ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [String]$id, + # Name of the newly exported vCD Template. Defaults to [TemplateName]-Export + [String]$name, + # ID of target catalog. Defaults to the existing catalog. + [Alias('catalog_id')] + [String]$catalogid, + # Org vDC ID to export the vCD Template to. This should be an Org vDC in the same vCD Org where the target catalog exists. + [Alias('org_vdc_id')] + [String]$orgvdcid, + # ID of the storage policy used to create the template. Defaults to Org VDC settings. + [String]$storagePolicyId, + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + #region oneoff + + if((!$name -or !$orgvdcid) -or !$catalogid) { + $snapshot = Get-RubrikVAppSnapshot -id $id + $vapp = Get-RubrikVApp -Name $snapshot.vappName -PrimaryClusterID 'local' -DetailedObject + + if(!$catalogid) { + $catalogid = $vapp.catalogid + Write-Verbose -Message "Using Catalog $($catalogid) for export" + } + if(!$name) { + $name = $vapp.name + "-Export" + Write-Verbose -Message "Using $($name) for export" + } + if(!$orgvdcid) { + $options = Get-RubrikVcdTemplateExportOptions -id $id -catalogid $catalogid -name $name + $orgvdcid = $options.originalVdcExportOptions.orgVdcId + Write-Verbose -Message "Using Org VDC $($orgvdc) for export" + } + } + + # Remove beginning of catalog ID to adjust for API + if($catalogid.StartsWith('VcdCatalog:::')) { + $catalogid = $catalogid -replace 'VcdCatalog:::','' + } + + # Remove beginning of Org VDC ID to adjust for API + if($orgvdcid.StartsWith('VcdOrgVdc:::')) { + $orgvdcid = $orgvdcid -replace 'VcdOrgVdc:::','' + } + + $resources.Body.name = $name + $resources.Body.catalogId = $catalogid + $resources.Body.orgVdcId = $orgvdcid + if($storagePolicyId) { + $resources.Body.storagePolicyId = $storagePolicyId + } + else { + $resources.Body.Remove('storagePolicyId') + } + + $body = ConvertTo-Json -InputObject $resources.Body -Depth 4 + Write-Verbose -Message "vApp Export REST Request Body `n$($body)" + #endregion + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + #$body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function diff --git a/Rubrik/Public/Get-RubrikSnapshot.ps1 b/Rubrik/Public/Get-RubrikSnapshot.ps1 index 5d0d5b84b..05c7f52ea 100644 --- a/Rubrik/Public/Get-RubrikSnapshot.ps1 +++ b/Rubrik/Public/Get-RubrikSnapshot.ps1 @@ -27,23 +27,23 @@ function Get-RubrikSnapshot This will return all snapshot (backup) data for the fileset with id of "Fileset:::01234567-8910-1abc-d435-0abc1234d567" .EXAMPLE - Get-Rubrikvm 'Server1' | Get-RubrikSnapshot -Date '03/21/2017' + Get-RubrikVM 'Server1' | Get-RubrikSnapshot -Date '03/21/2017' This will return the closest matching snapshot, within 1 day, to March 21st, 2017 for any virtual machine named "Server1" .EXAMPLE - Get-Rubrikvm 'Server1' | Get-RubrikSnapshot -Date '03/21/2017' -Range 3 + Get-RubrikVM 'Server1' | Get-RubrikSnapshot -Date '03/21/2017' -Range 3 This will return the closest matching snapshot, within 3 days, to March 21st, 2017 for any virtual machine named "Server1" .EXAMPLE - Get-Rubrikvm 'Server1' | Get-RubrikSnapshot -Date '03/21/2017' -Range 3 -ExactMatch + Get-RubrikVM 'Server1' | Get-RubrikSnapshot -Date '03/21/2017' -Range 3 -ExactMatch This will return the closest matching snapshot, within 3 days, to March 21st, 2017 for any virtual machine named "Server1". -ExactMatch specifies that no results are returned if a match is not found, otherwise all snapshots are returned. .EXAMPLE - Get-Rubrikvm 'Server1' | Get-RubrikSnapshot -Date (Get-Date) + Get-RubrikVM 'Server1' | Get-RubrikSnapshot -Date (Get-Date) This will return the closest matching snapshot to the current date and time for any virtual machine named "Server1" .EXAMPLE - Get-Rubrikvm 'Server1' | Get-RubrikSnapshot -Latest + Get-RubrikVM 'Server1' | Get-RubrikSnapshot -Latest This will return the latest snapshot for the virtual machine named "Server1" .EXAMPLE diff --git a/Rubrik/Public/Get-RubrikVApp.ps1 b/Rubrik/Public/Get-RubrikVApp.ps1 new file mode 100644 index 000000000..39efcbbfa --- /dev/null +++ b/Rubrik/Public/Get-RubrikVApp.ps1 @@ -0,0 +1,175 @@ +#Requires -Version 3 +function Get-RubrikVApp +{ + <# + .SYNOPSIS + Connects to Rubrik and retrieves the current Rubrik vCD vApp settings + + .DESCRIPTION + The Get-RubrikVApp cmdlet retrieves all the vCD vApp settings actively running on the system. This requires authentication with your Rubrik cluster. + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + https://github.com/rubrikinc/PowerShell-Module + + .EXAMPLE + Get-RubrikVApp + This returns details on all vCD vApps that are currently or formerly protected by Rubrik. + + .EXAMPLE + Get-RubrikVApp -Name 'vApp01' + This returns details on all vCD vApps named "vApp01". + + .EXAMPLE + Get-RubrikVApp -Name 'Server1' -SLA 'Gold' + This returns details on all vCD vApps named "Server1" that are protected by the Gold SLA Domain. + + .EXAMPLE + Get-RubrikVApp -Relic + This returns all removed vCD vApps that were formerly protected by Rubrik. + + .EXAMPLE + Get-RubrikVApp -Relic:false + This returns all vCD vApps that are currently protected by Rubrik. + + .EXAMPLE + Get-RubrikVApp -SourceObjectId 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' + This returns details on all vCD vApps with the Source Object ID "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567" + + .EXAMPLE + Get-RubrikVApp -vcdClusterId 'Vcd:::01234567-8910-1abc-d435-0abc1234d567' + This returns details on all vCD vApps on the vCD Cluster with id "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + #> + + [CmdletBinding(DefaultParameterSetName = 'Query')] + Param( + # Name of the vCD vApp + [Parameter( + ParameterSetName='Query', + Position = 0, + ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [Alias('VM')] + [String]$Name, + # vCD vApp id + [Parameter( + ParameterSetName='ID', + Position = 0, + Mandatory = $true, + ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [String]$id, + # Filter results to include only relic (removed) vCD vApps + [Parameter(ParameterSetName='Query')] + [Alias('is_relic')] + [Switch]$Relic, + # DetailedObject will retrieved the detailed vApp object, the default behavior of the API is to only retrieve a subset of the full vApp object unless we query directly by ID. Using this parameter does affect performance as more data will be retrieved and more API-queries will be performed. + [Parameter(ParameterSetName='Query')] + [Switch]$DetailedObject, + # SLA Domain policy assigned to the vCD vApp + [Parameter(ParameterSetName='Query')] + [ValidateNotNullOrEmpty()] + [String]$SLA, + # Filter by SLA Domain assignment type + [Parameter(ParameterSetName='Query')] + [ValidateNotNullOrEmpty()] + [ValidateSet('Derived', 'Direct','Unassigned')] + [Alias('sla_assignment')] + [String]$SLAAssignment, + # Filter the summary information based on the primarycluster_id of the primary Rubrik cluster. Use **local** as the primary_cluster_id of the Rubrik cluster that is hosting the current REST API session. + [Parameter(ParameterSetName='Query')] + [ValidateNotNullOrEmpty()] + [Alias('primary_cluster_id')] + [String]$PrimaryClusterID, + # SLA id value + [Parameter(ParameterSetName='Query')] + [ValidateNotNullOrEmpty()] + [Alias('effective_sla_domain_id')] + [String]$SLAID, + # ID of Source Object in terms of vCD Hierarchy + [Parameter(ParameterSetName='Query')] + [Parameter(ParameterSetName='ID')] + [ValidateNotNullOrEmpty()] + [String]$SourceObjectId, + # Name of Source Object in terms of vCD Hierarchy + [Parameter(ParameterSetName='Query')] + [Parameter(ParameterSetName='ID')] + [ValidateNotNullOrEmpty()] + [String]$SourceObjectName, + # ID of vCD Cluster + [Parameter(ParameterSetName='Query')] + [Parameter(ParameterSetName='ID')] + [ValidateNotNullOrEmpty()] + [String]$vcdClusterId, + # Name of vCD Cluster + [Parameter(ParameterSetName='Query')] + [Parameter(ParameterSetName='ID')] + [ValidateNotNullOrEmpty()] + [String]$vcdClusterName, + # Rubrik server IP or FQDN + [Parameter(ParameterSetName='Query')] + [Parameter(ParameterSetName='ID')] + [String]$Server = $global:RubrikConnection.server, + # API version + [Parameter(ParameterSetName='Query')] + [Parameter(ParameterSetName='ID')] + [String]$api = $global:RubrikConnection.api +) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + + #region One-off + if ($SLAID.Length -eq 0 -and $SLA.Length -gt 0) { + $SLAID = Test-RubrikSLA -SLA $SLA -Inherit $Inherit -DoNotProtect $DoNotProtect + } + #endregion + + # If the switch parameter was not explicitly specified remove from query params + if(-not $PSBoundParameters.ContainsKey('Relic')) { + $Resources.Query.Remove('is_relic') + } + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + # If the Get-RubrikVApp function has been called with the -DetailedObject parameter a separate API query will be performed if the initial query was not based on ID + if (($DetailedObject) -and (-not $PSBoundParameters.containskey('id'))) { + for ($i = 0; $i -lt @($result).Count; $i++) { + $Percentage = [int]($i/@($result).count*100) + Write-Progress -Activity "DetailedObject queries in Progress, $($i+1) out of $(@($result).count)" -Status "$Percentage% Complete:" -PercentComplete $Percentage + Get-RubrikVApp -id $result[$i].id + } + } else { + return $result + } + + } # End of process +} # End of function diff --git a/Rubrik/Public/Get-RubrikVAppExportOptions.ps1 b/Rubrik/Public/Get-RubrikVAppExportOptions.ps1 new file mode 100644 index 000000000..f393f22cf --- /dev/null +++ b/Rubrik/Public/Get-RubrikVAppExportOptions.ps1 @@ -0,0 +1,82 @@ +#requires -Version 3 +function Get-RubrikVAppExportOptions +{ + <# + .SYNOPSIS + Retrieves export for a vCD vApp known to a Rubrik cluster + + .DESCRIPTION + The Get-RubrikVAppExportOptions cmdlet retrieves export options for a vCD vApp known to a Rubrik cluster + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + $SnapshotID = (Get-RubrikVApp -Name 'vApp01' | Get-RubrikSnapshot -Latest).id + Get-RubrikVAppExportOptions -id $SnapshotID -ExportMode 'ExportToNewVapp' + This returns available export options for the specific snapshot. + #> + + [CmdletBinding()] + Param( + # Snapshot ID of the vApp to retrieve options for + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [Alias('snapshot_id')] + [String]$id, + # Specifies whether export should use the existing vApp or create a new vApp. Valid values are ExportToNewVapp or ExportToTargetVapp + [Parameter(Mandatory = $true)] + [ValidateSet('ExportToNewVapp','ExportToTargetVapp')] + [Alias('export_mode')] + [String]$ExportMode, + # ID of target vApp + [Alias('target_vapp_id')] + [Parameter(ParameterSetName='Existing')] + [String]$TargetVAppID, + # ID of target vApp + [Alias('target_org_vdc_id')] + [Parameter(ParameterSetName='Existing')] + [String]$TargetOrgVDCID, + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function diff --git a/Rubrik/Public/Get-RubrikVAppRecoverOptions.ps1 b/Rubrik/Public/Get-RubrikVAppRecoverOptions.ps1 new file mode 100644 index 000000000..337d99cde --- /dev/null +++ b/Rubrik/Public/Get-RubrikVAppRecoverOptions.ps1 @@ -0,0 +1,69 @@ +#requires -Version 3 +function Get-RubrikVAppRecoverOptions +{ + <# + .SYNOPSIS + Retrieves instant recovery options a vCD vApp known to a Rubrik cluster + + .DESCRIPTION + The Get-RubrikVAppRecoverOptions cmdlet is used to retrieve instant recovery options for a vCD vApp known to a Rubrik cluster + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + Github: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + $SnapshotID = (Get-RubrikVApp -Name 'vApp01' | Get-RubrikSnapshot -Latest).id + Get-RubrikVAppRecoverOptions -id $SnapshotID + This will return recovery options on the specific snapshot. + #> + + [CmdletBinding()] + Param( + # Snapshot ID of the vApp to retrieve options for + [Parameter(ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [Alias('snapshot_id')] + [String]$id, + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function diff --git a/Rubrik/Public/Get-RubrikVAppSnapshot.ps1 b/Rubrik/Public/Get-RubrikVAppSnapshot.ps1 new file mode 100644 index 000000000..61eaa67f8 --- /dev/null +++ b/Rubrik/Public/Get-RubrikVAppSnapshot.ps1 @@ -0,0 +1,67 @@ +#requires -Version 3 +function Get-RubrikVAppSnapshot +{ + <# + .SYNOPSIS + Retrieves details on one or more virtual machines known to the Rubrik cluster as a vCD-managed vApp + + .DESCRIPTION + The Get-RubrikVAppSnapshot cmdlet pulls detailed information from a vCD vApp snapshot + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + Get-RubrikVAppSnapshot -id '01234567-8910-1abc-d435-0abc1234d567' + This returns details on the specific snapshot. + #> + + [CmdletBinding()] + Param( + # ID of the snapshot + [Parameter(ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [String]$id, + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function diff --git a/Rubrik/Public/Get-RubrikVCD.ps1 b/Rubrik/Public/Get-RubrikVCD.ps1 new file mode 100644 index 000000000..610208b53 --- /dev/null +++ b/Rubrik/Public/Get-RubrikVCD.ps1 @@ -0,0 +1,86 @@ +#Requires -Version 3 +function Get-RubrikVCD +{ + <# + .SYNOPSIS + Connect to Rubrik and retrieve the current Rubrik vCD settings + + .DESCRIPTION + The Get-RubrikVCD cmdlet retrieves all vCD settings actively running on the system. This requires authentication. + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + https://github.com/rubrikinc/PowerShell-Module + + .EXAMPLE + Get-RubrikVCD + This returns the vCD settings on the currently connected Rubrik cluster + + .EXAMPLE + Get-RubrikVCD -Name 'My vCD Cluster' + This returns the vCD settings on the currently connected Rubrik cluster matching the name 'My vCD Cluster' + + .EXAMPLE + Get-RubrikVCD -Hostname 'vcd.example.com' + This returns the vCD settings on the currently connected Rubrik cluster matching the hostname 'vcd.example.com' + + .EXAMPLE + Get-RubrikVCD -Status 'Connected' + This returns the vCD settings on the currently connected Rubrik cluster with the status of 'Connected' + #> + + [CmdletBinding()] + Param( + # vCD Cluster Name + [ValidateNotNullOrEmpty()] + [String]$Name, + # vCD Cluster Hostname + [ValidateNotNullOrEmpty()] + [String]$Hostname, + # vCD Cluster Status + [ValidateSet('Disconnected', 'Refreshing','Connected','BadlyConfigured','Deleting','Remote')] + [String]$Status, + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [ValidateNotNullorEmpty()] + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function \ No newline at end of file diff --git a/Rubrik/Public/Get-RubrikVCDTemplateExportOptions.ps1 b/Rubrik/Public/Get-RubrikVCDTemplateExportOptions.ps1 new file mode 100644 index 000000000..364076333 --- /dev/null +++ b/Rubrik/Public/Get-RubrikVCDTemplateExportOptions.ps1 @@ -0,0 +1,96 @@ +#requires -Version 3 +function Get-RubrikVCDTemplateExportOptions +{ + <# + .SYNOPSIS + Retrieves export options for a vCD Template known to a Rubrik cluster + + .DESCRIPTION + The Get-RubrikVCDTemplateExportOptions cmdlet retrieves export options for a vCD Template known to a Rubrik cluster + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + $SnapshotID = (Get-RubrikVApp -Name 'vAppTemplate01' | Get-RubrikSnapshot -Latest).id + Get-RubrikVCDTemplateExportOptions -id $SnapshotID -catalogid 'VcdCatalog:::01234567-8910-1abc-d435-0abc1234d567' -Name 'vAppTemplate01-export' + This will return export options details on the specific snapshot. + #> + + [CmdletBinding()] + Param( + # Snapshot ID of the vCD Template to retrieve options for + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [Alias('snapshot_id')] + [String]$id, + # ID of target catalog. Defaults to the existing catalog. + [Alias('catalog_id')] + [String]$catalogid, + # Name of the newly exported vCD Template. Defaults to [TemplateName]-Export + [String]$name, + # Org vDC ID to export the vCD Template to. This should be an Org vDC in the same vCD Org where the target catalog exists. + [Alias('org_vdc_id')] + [String]$orgvdcid, + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [String]$api = $global:RubrikConnection.api + ) + + Begin { + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + #region oneoff + + # Remove beginning of catalog ID to adjust for API + if($catalogid.StartsWith('VcdCatalog:::')) { + $catalogid = $catalogid -replace 'VcdCatalog:::','' + } + + # Remove beginning of Org VDC ID to adjust for API + if($orgvdcid.StartsWith('VcdOrgVdc:::')) { + $orgvdcid = $orgvdcid -replace 'VcdOrgVdc:::','' + } + + if(!$name) { + $snapshot = Get-RubrikVAppSnapshot -id $id + $vapp = Get-RubrikVApp -Name $snapshot.vappName -PrimaryClusterID 'local' -DetailedObject + $name = $vapp.name + "-Export" + Write-Verbose -Message "Using $($name) for export" + } + #endregion + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function diff --git a/Rubrik/Public/Protect-RubrikVApp.ps1 b/Rubrik/Public/Protect-RubrikVApp.ps1 new file mode 100644 index 000000000..2b84aac45 --- /dev/null +++ b/Rubrik/Public/Protect-RubrikVApp.ps1 @@ -0,0 +1,89 @@ +#Requires -Version 3 +function Protect-RubrikVApp +{ + <# + .SYNOPSIS + Connects to Rubrik and assigns an SLA to a vCD vApp + + .DESCRIPTION + The Protect-RubrikVApp cmdlet will update a vCD vApp's SLA Domain assignment within the Rubrik cluster. + The SLA Domain contains all policy-driven values needed to protect workloads. + Note that this function requires the vApp ID value and not the vApp name. This is because vApp names may not be unique across clusters. + It is suggested that you first use Get-RubrikVApp to narrow down the one or more vApps to protect, and then pipe the results to Protect-RubrikVApp. + You will be asked to confirm each vApp you wish to protect, or you can use -Confirm:$False to skip confirmation checks. + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/reference/Protect-RubrikVApp.html + + .EXAMPLE + Get-RubrikVApp "vApp1" | Protect-RubrikVApp -SLA 'Gold' + This will assign the Gold SLA Domain to any vApp named "vApp1" + + .EXAMPLE + Get-RubrikVApp "vApp1" -SLA 'Silver' | Protect-RubrikVApp -SLA 'Gold' -Confirm:$False + This will assign the Gold SLA Domain to any vApp named "vApp1" that is currently assigned to the Silver SLA Domain + without asking for confirmation + #> + + [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact = 'High',DefaultParameterSetName="None")] + Param( + # vApp ID + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [String]$id, + # The SLA Domain in Rubrik + [Parameter(ParameterSetName = 'SLA_Explicit')] + [String]$SLA, + # Removes the SLA Domain assignment + [Parameter(ParameterSetName = 'SLA_Unprotected')] + [Switch]$DoNotProtect, + # Inherits the SLA Domain assignment from a parent object + [Parameter(ParameterSetName = 'SLA_Inherit')] + [Switch]$Inherit, + # SLA id value + [Alias('configuredSlaDomainId')] + [String]$SLAID = (Test-RubrikSLA -SLA $SLA -Inherit $Inherit -DoNotProtect $DoNotProtect -Mandatory:$true), + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function \ No newline at end of file diff --git a/Rubrik/Public/Restore-RubrikVApp.ps1 b/Rubrik/Public/Restore-RubrikVApp.ps1 new file mode 100644 index 000000000..523c316cb --- /dev/null +++ b/Rubrik/Public/Restore-RubrikVApp.ps1 @@ -0,0 +1,188 @@ +#Requires -Version 3 +function Restore-RubrikVApp +{ + <# + .SYNOPSIS + Restores a given snapshot for a vCD vApp + + .DESCRIPTION + The Restore-RubrikVApp cmdlet is used to restore a snapshot from a protected vCD vApp. The existing vApp will be marked as 'deprecated' if it exists at the time of restore. + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + Restore-RubrikVApp -id '7acdf6cd-2c9f-4661-bd29-b67d86ace70b' -PowerOn:$true + This restores the vApp snapshot with an id of 7acdf6cd-2c9f-4661-bd29-b67d86ace70b + + .EXAMPLE + (Get-RubrikVApp 'vApp01' -PrimaryClusterID local | Get-RubrikSnapshot -Latest).id | Restore-RubrikVApp -PowerOn:$true + This retreives the latest snapshot from the given vApp 'vApp01' and restores it + + .EXAMPLE + $id = (Get-RubrikVApp -Name "vApp01" -PrimaryClusterID local | Get-RubrikSnapshot -Latest ).id + $recoveropts = Get-RubrikVAppRecoverOptions -Id $id + $restorableVms = $recoveropts.restorableVms + $vm = @() + $vm += $restorableVms[0] + Restore-RubrikVApp -id $id -Partial $vm -PowerOn:$false + This retreives the latest snapshot from the given vApp 'vApp01' and performs a partial restore on the first VM in the vApp. + This is an advanced use case and the user is responsible for parsing the output from Get-RubrikVAppRecoverOptions. + Syntax of the object passed with the -Partial Parameter must match the format of the object returned from (Get-RubrikVAppRecoverOptions).restorableVms +#> + + [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact = 'High')] + Param( + # Rubrik id of the vApp snapshot to restore + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName='Full')] + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName='Partial')] + [ValidateNotNullOrEmpty()] + [Alias('snapshot_id')] + [String]$id, + # Perform a Partial vApp restore. Default operation is a Full vApp restore, unless this parameter is specified. + [Parameter(Mandatory = $true,ParameterSetName='Partial')] + [ValidateNotNullOrEmpty()] + [ValidateScript({ + if ('PSCustomObject' -ne $_.GetType().Name) { + Throw "Partial parameter should be a PSCustomObject" + } + $requiredProperties = @("name","vcdMoid","networkConnections") + ForEach($item in $requiredProperties) { + if(!$_.PSObject.Properties.Name.Contains($item)) { + Throw "Object passed via Partial parameter missing property $($item)" + } + } + return $true + })] + [PSCustomObject]$Partial, + # Disable NICs upon restoration. The NIC(s) will be disabled, but remain mapped to their existing network. + [Parameter(ParameterSetName='Full')] + [Switch]$DisableNetwork, + # Remove network mapping upon restoration. The NIC(s) will not be connected to any existing networks. + [Parameter(ParameterSetName='Full')] + [Switch]$NoMapping, + # Remove network interfaces from the restored vApp virtual machines. + [Parameter(ParameterSetName='Full')] + [Switch]$RemoveNetworkDevices, + # Map all vApp virtual machine NICs to specified network. + [Parameter(ParameterSetName='Full')] + [ValidateNotNullOrEmpty()] + [String]$NetworkMapping, + # Power on vApp after restoration. + [Parameter(ParameterSetName='Full',Mandatory = $true)] + [Parameter(ParameterSetName='Partial',Mandatory = $true)] + [Bool]$PowerOn, + # Rubrik server IP or FQDN + [Parameter(ParameterSetName='Full')] + [Parameter(ParameterSetName='Partial')] + [String]$Server = $global:RubrikConnection.server, + # API version + [Parameter(ParameterSetName='Full')] + [Parameter(ParameterSetName='Partial')] + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + #region oneoff + if($Partial) { + Write-Verbose -Message "Performing Partial vApp Recovery" + $resources.Body.vmsToRestore = @() + $resources.Body.vmsToRestore += $Partial + $resources.Body.shouldPowerOnVmsAfterRecovery = $PowerOn + + $body = ConvertTo-Json -InputObject $resources.Body -Depth 4 + Write-Verbose -Message "REST Body $($body)" + } + else { + Write-Verbose -Message "Performing Full vApp Recovery" + $recoveropts = Get-RubrikVAppRecoverOptions -id $id + $resources.Body.vmsToRestore = $recoveropts.restorableVms + $resources.Body.shouldPowerOnVmsAfterRecovery = $PowerOn + + if($DisableNetwork) { + foreach($vm in $resources.Body.vmsToRestore) { + foreach($network in $vm.networkConnections) { + $network.isConnected = $false + Write-Verbose -Message "Disabled NIC $($network.nicIndex) on $($vm.Name)" + } + } + } + + if($NoMapping) { + foreach($vm in $resources.Body.vmsToRestore) { + foreach($network in $vm.networkConnections) { + Write-Verbose -Message "Unmapping $($network.vappNetworkName) from $($vm.Name)" + $network.PSObject.Properties.Remove('vappNetworkName') + } + } + } + + if($RemoveNetworkDevices) { + foreach($vm in $resources.Body.vmsToRestore) { + $vm.networkConnections = @() + } + Write-Verbose -Message "Removed all NICs from vApp" + } + + if($NetworkMapping) { + # Check RubrikVAppRecoverOptions to verify + $found = $false + Write-Verbose -Message "Validating $($NetworkMapping) network" + foreach($network in $recoveropts.availableVappNetworks) { + if($network.name -eq $NetworkMapping) { + $found = $true + Write-Verbose -Message "Validated $($NetworkMapping) network" + } + } + if($false -eq $found) { + throw "$($NetworkMapping) is not a valid network for this vApp. Please supply a valid network." + } + foreach($vm in $resources.Body.vmsToRestore) { + foreach($network in $vm.networkConnections) { + Write-Verbose -Message "Mapping NIC $($network.nicIndex) to $($NetworkMapping) network" + $network.vappNetworkName = $NetworkMapping + } + } + } + + $body = ConvertTo-Json -InputObject $resources.Body -Depth 4 + Write-Verbose -Message "REST Body $($body)" + } + #endregion + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + # $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function diff --git a/Rubrik/Public/Set-RubrikVCD.ps1 b/Rubrik/Public/Set-RubrikVCD.ps1 new file mode 100644 index 000000000..834ba310a --- /dev/null +++ b/Rubrik/Public/Set-RubrikVCD.ps1 @@ -0,0 +1,128 @@ +#Requires -Version 3 +function Set-RubrikVCD +{ + <# + .SYNOPSIS + Connects to Rubrik and modifies an existing vCD connection + + .DESCRIPTION + The Set-RubrikVCD cmdlet modifies an existing vCloud Director connection on the system. This requires authentication. + + .NOTES + Written by Matt Elliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + https://github.com/rubrikinc/PowerShell-Module + + .EXAMPLE + Set-RubrikVCD -ID Vcd:::01234567-8910-1abc-d435-0abc1234d567 -SLA "Bronze" + This will update the vCD cluster settings on the Rubrik cluster and assign the 'Bronze' SLA on the vCD cluster with ID "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + + .EXAMPLE + Set-RubrikVCD -ID Vcd:::01234567-8910-1abc-d435-0abc1234d567 -DoNotProtect + This will update the vCD cluster settings on the Rubrik cluster and clear the SLA assignment on the vCD cluster with ID "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + + .EXAMPLE + Set-RubrikVCD -ID Vcd:::01234567-8910-1abc-d435-0abc1234d567 -Hostname newserver.company.com + This will update the vCD cluster settings on the Rubrik cluster to assign a new hostname to the vCD cluster with ID "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + + .EXAMPLE + Set-RubrikVCD -ID Vcd:::01234567-8910-1abc-d435-0abc1234d567 + This will update the vCD cluster settings on the Rubrik cluster to update the credentials used to connect to the vCD cluster with ID "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + #> + + [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact = 'High',DefaultParameterSetName="None")] + Param( + # vCD Instance ID + [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullorEmpty()] + [String]$id, + # vCD Hostname (FQDN) + [string]$Hostname, + # Updates vCD Credentials + [Switch]$UpdateCreds, + # The SLA Domain in Rubrik + [Parameter(ParameterSetName = 'SLA_Explicit')] + [String]$SLA, + # Removes the SLA Domain assignment + [Parameter(ParameterSetName = 'SLA_Unprotected')] + [Switch]$DoNotProtect, + # Inherits the SLA Domain assignment from a parent object + [Parameter(ParameterSetName = 'SLA_Inherit')] + [Switch]$Inherit, + # SLA id value + [Alias('configuredSlaDomainId')] + [String]$SLAID = (Test-RubrikSLA -SLA $SLA -Inherit $Inherit -DoNotProtect $DoNotProtect), + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [ValidateNotNullorEmpty()] + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + $id = [System.Web.HttpUtility]::UrlEncode($id) + if($true -eq $UpdateCreds) { + $Credentials = $(Get-Credential -Message "Type vCD Credentials.") + $username = $Credentials.UserName + $password = $Credentials.GetNetworkCredential().Password + } + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + + #region One-off + Write-Verbose -Message "Build the body" + + $body = @{} + + # Update Credentials + if($true -eq $UpdateCreds) { + $body.Add($resources.Body.username, $username) + $body.Add($resources.Body.password, $password) + } + + # Update Hostame + if($true -eq $Hostname) { + $body.Add($resources.Body.hostname, $hostname) + } + + # Update SLA + if($true -eq $SLAID) { + $body.Add($resources.Body.configuredSlaDomainId, $SLAID) + } + + $body = ConvertTo-Json $body + Write-Verbose -Message "Body = $body" + #endregion + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + #$uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + #$body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function \ No newline at end of file diff --git a/Rubrik/Public/Update-RubrikVCD.ps1 b/Rubrik/Public/Update-RubrikVCD.ps1 new file mode 100644 index 000000000..85a4070cd --- /dev/null +++ b/Rubrik/Public/Update-RubrikVCD.ps1 @@ -0,0 +1,75 @@ +#Requires -Version 3 +function Update-RubrikVCD +{ + <# + .SYNOPSIS + Connects to Rubrik and refreshes the metadata for the specified vCD Server + + .DESCRIPTION + The Update-RubrikVCD cmdlet refreshes all vCD metadata known to the connected Rubrik cluster. + + .NOTES + Written by Matt Eliott for community usage + Twitter: @NetworkBrouhaha + GitHub: shamsway + + .LINK + http://rubrikinc.github.io/rubrik-sdk-for-powershell/ + + .EXAMPLE + Get-RubrikVCD -Name 'vcd.domain.local' | Update-RubrikVCD + This will refresh the vCD metadata on the currently connected Rubrik cluster + + .EXAMPLE + Get-RubrikVCD | Update-RubrikVCD + This will refresh the vCD metadata for all connected vCD instances on the currently connected Rubrik cluster + #> + + [CmdletBinding()] + Param( + # vCD OD value from the Rubrik Cluster + [Parameter( + ValueFromPipelineByPropertyName = $true, + Mandatory = $true )] + [ValidateNotNullOrEmpty()] + [String]$id, + # Rubrik server IP or FQDN + [String]$Server = $global:RubrikConnection.server, + # API version + [ValidateNotNullorEmpty()] + [String]$api = $global:RubrikConnection.api + ) + + Begin { + + # The Begin section is used to perform one-time loads of data necessary to carry out the function's purpose + # If a command needs to be run with each iteration or pipeline input, place it in the Process section + + # Check to ensure that a session to the Rubrik cluster exists and load the needed header data for authentication + Test-RubrikConnection + + # API data references the name of the function + # For convenience, that name is saved here to $function + $function = $MyInvocation.MyCommand.Name + + # Retrieve all of the URI, method, body, query, result, filter, and success details for the API endpoint + Write-Verbose -Message "Gather API Data for $function" + $resources = Get-RubrikAPIData -endpoint $function + Write-Verbose -Message "Load API data for $($resources.Function)" + Write-Verbose -Message "Description: $($resources.Description)" + + } + + Process { + + $uri = New-URIString -server $Server -endpoint ($resources.URI) -id $id + $uri = Test-QueryParam -querykeys ($resources.Query.Keys) -parameters ((Get-Command $function).Parameters.Values) -uri $uri + $body = New-BodyString -bodykeys ($resources.Body.Keys) -parameters ((Get-Command $function).Parameters.Values) + $result = Submit-Request -uri $uri -header $Header -method $($resources.Method) -body $body + $result = Test-ReturnFormat -api $api -result $result -location $resources.Result + $result = Test-FilterObject -filter ($resources.Filter) -result $result + + return $result + + } # End of process +} # End of function \ No newline at end of file diff --git a/Rubrik/Rubrik.psd1 b/Rubrik/Rubrik.psd1 index 9a624654b..e6dfe08a6 100755 --- a/Rubrik/Rubrik.psd1 +++ b/Rubrik/Rubrik.psd1 @@ -83,9 +83,9 @@ FormatsToProcess = @('ObjectDefinitions/Rubrik.SLADomain.ps1xml','ObjectDefiniti # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @('Connect-Rubrik', 'Disconnect-Rubrik', 'Export-RubrikDatabase', - 'Export-RubrikReport', 'Export-RubrikVM', 'Get-RubrikAPIToken', - 'Get-RubrikAPIVersion', 'Get-RubrikAvailabilityGroup', - 'Get-RubrikBootStrap', 'Get-RubrikDatabase', + 'Export-RubrikReport', 'Export-RubrikVM', 'Export-RubrikVApp', + 'Export-RubrikVCDTemplate', 'Get-RubrikAPIToken', 'Get-RubrikAPIVersion', + 'Get-RubrikAvailabilityGroup', 'Get-RubrikBootStrap', 'Get-RubrikDatabase', 'Get-RubrikDatabaseFiles', 'Get-RubrikDatabaseMount', 'Get-RubrikDatabaseRecoverableRange', 'Get-RubrikEvent', 'Get-RubrikFileset', 'Get-RubrikFilesetTemplate', 'Get-RubrikHost', @@ -95,41 +95,41 @@ FunctionsToExport = @('Connect-Rubrik', 'Disconnect-Rubrik', 'Export-RubrikDatab 'Get-RubrikOracleDB', 'Get-RubrikOrganization', 'Get-RubrikReport', 'Get-RubrikReportData', 'Get-RubrikRequest', 'Get-RubrikSetting', 'Get-RubrikSLA', 'Get-RubrikSnapshot', 'Get-RubrikSoftwareVersion', - 'Get-RubrikSQLInstance', 'Get-RubrikSupportTunnel', - 'Get-RubrikUnmanagedObject', 'Get-RubrikUser', 'Get-RubrikVCenter', - 'Get-RubrikVersion', 'Get-RubrikVM', 'Get-RubrikVMSnapshot', - 'Get-RubrikVMwareDatastore', 'Get-RubrikVMwareHost', - 'Get-RubrikVolumeGroup', 'Get-RubrikVolumeGroupMount', - 'Invoke-RubrikRESTCall', 'Move-RubrikMountVMDK', 'New-RubrikAPIToken', - 'New-RubrikBootStrap', 'New-RubrikDatabaseMount', 'New-RubrikFileset', + 'Get-RubrikSQLInstance', 'Get-RubrikSupportTunnel', 'Get-RubrikVAppExportOptions', + 'Get-RubrikVAppRecoverOptions', 'Get-RubrikVCDTemplateExportOptions', 'Get-RubrikUnmanagedObject', + 'Get-RubrikUser', 'Get-RubrikVApp', 'Get-RubrikVAppSnapshot', 'Get-RubrikVCD', + 'Get-RubrikVCenter', 'Get-RubrikVersion', 'Get-RubrikVM', 'Get-RubrikVMSnapshot', + 'Get-RubrikVMwareDatastore', 'Get-RubrikVMwareHost', 'Get-RubrikVolumeGroup', + 'Get-RubrikVolumeGroupMount', 'Invoke-RubrikRESTCall', + 'Move-RubrikMountVMDK', 'New-RubrikAPIToken', 'New-RubrikBootStrap', + 'New-RubrikDatabaseMount', 'New-RubrikFileset', 'New-RubrikFilesetTemplate', 'New-RubrikHost', 'New-RubrikLDAP', 'New-RubrikLogBackup', 'New-RubrikLogShipping', 'New-RubrikManagedVolume', 'New-RubrikManagedVolumeExport', 'New-RubrikMount', 'New-RubrikNASShare', 'New-RubrikReport', - 'New-RubrikSLA', 'New-RubrikSnapshot', 'New-RubrikUser', - 'New-RubrikVCenter', 'New-RubrikVMDKMount', - 'New-RubrikVolumeGroupMount', 'Protect-RubrikDatabase', - 'Protect-RubrikFileset', 'Protect-RubrikHyperVVM', - 'Protect-RubrikNutanixVM', 'Protect-RubrikTag', 'Protect-RubrikVM', + 'New-RubrikSLA', 'New-RubrikSnapshot', 'New-RubrikUser', + 'New-RubrikVCenter', 'New-RubrikVMDKMount', 'New-RubrikVolumeGroupMount', + 'Protect-RubrikDatabase', 'Protect-RubrikFileset', + 'Protect-RubrikHyperVVM', 'Protect-RubrikNutanixVM', + 'Protect-RubrikTag', 'Protect-RubrikVM', 'Protect-RubrikVApp', 'Register-RubrikBackupService', 'Remove-RubrikAPIToken', 'Remove-RubrikDatabaseMount', 'Remove-RubrikFileset', 'Remove-RubrikHost', 'Remove-RubrikLogShipping', 'Remove-RubrikManagedVolume', 'Remove-RubrikManagedVolumeExport', 'Remove-RubrikMount', 'Remove-RubrikNASShare', 'Remove-RubrikReport', 'Remove-RubrikSLA', 'Remove-RubrikUnmanagedObject', - 'Remove-RubrikUser', 'Remove-RubrikVCenter', - 'Remove-RubrikVMSnapshot', 'Remove-RubrikVolumeGroupMount', - 'Reset-RubrikLogShipping', 'Restore-RubrikDatabase', - 'Set-RubrikAvailabilityGroup', 'Set-RubrikBlackout', - 'Set-RubrikDatabase', 'Set-RubrikHyperVVM', 'Set-RubrikLogShipping', - 'Set-RubrikManagedVolume', 'Set-RubrikMount', 'Set-RubrikNASShare', - 'Set-RubrikNutanixVM', 'Set-RubrikSetting', 'Set-RubrikSLA', - 'Set-RubrikSQLInstance', 'Set-RubrikSupportTunnel', 'Set-RubrikUser', - 'Set-RubrikVCenter', 'Set-RubrikVM', 'Set-RubrikVolumeFilterDriver', + 'Remove-RubrikUser', 'Remove-RubrikVCenter', 'Remove-RubrikVMSnapshot', + 'Remove-RubrikVolumeGroupMount', 'Reset-RubrikLogShipping', + 'Restore-RubrikDatabase', 'Restore-RubrikVApp', 'Set-RubrikAvailabilityGroup', + 'Set-RubrikBlackout', 'Set-RubrikDatabase', 'Set-RubrikHyperVVM', + 'Set-RubrikLogShipping', 'Set-RubrikManagedVolume', 'Set-RubrikMount', + 'Set-RubrikNASShare', 'Set-RubrikNutanixVM', 'Set-RubrikSetting', + 'Set-RubrikSLA', 'Set-RubrikSQLInstance', 'Set-RubrikSupportTunnel', + 'Set-RubrikVCD', 'Set-RubrikVCenter', 'Set-RubrikVM', 'Set-RubrikVolumeFilterDriver', 'Start-RubrikManagedVolumeSnapshot', 'Stop-RubrikManagedVolumeSnapshot', 'Sync-RubrikAnnotation', - 'Sync-RubrikTag', 'Update-RubrikHost', 'Update-RubrikVCenter', - 'Update-RubrikVMwareVM') + 'Sync-RubrikTag', 'Update-RubrikHost', 'Update-RubrikVCD', + 'Update-RubrikVCenter', 'Update-RubrikVMwareVM') # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @() diff --git a/Tests/Export-RubrikVApp.Tests.ps1 b/Tests/Export-RubrikVApp.Tests.ps1 new file mode 100644 index 000000000..f86375624 --- /dev/null +++ b/Tests/Export-RubrikVApp.Tests.ps1 @@ -0,0 +1,268 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force +Describe -Name 'Public/Export-RubrikVApp' -Tag 'Public', 'Export-RubrikVApp' -Fixture { + InModuleScope -ModuleName Rubrik -ScriptBlock { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + + $vappjson = '{ + "isPaused": false, + "vcdVmMoidsToExcludeFromSnapshot": [], + "configuredSlaDomainName": "Silver", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1a vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d567" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaSourceObjectName": "vApp01", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaDomainName": "Silver", + "networks": [ + { + "name": "network01", + "isDeployed": true + } + ], + "isRelic": false, + "name": "vApp01", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567", + "isBestEffortSynchronizationEnabled": true, + "vms": [ + { + "name": "vm01", + "vcdMoid": "vm-0a09772b-fc0f-466c-88d3-84d370012737", + "storagePolicyId": "7a9d26f0-bb4f-40e6-a76e-66c18d4b4917", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:24", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": false + } + ] + }, + { + "name": "vm02", + "vcdMoid": "vm-a5549267-3873-4c2d-a0b2-eafd4b01a682", + "storagePolicyId": "7a9d26f0-bb4f-40e6-a76e-66c18d4b4917", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:64", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": true + } + ] + } + ] + }' + $vapp = ConvertFrom-Json -InputObject $vappjson + + $vmjson = '{ + "name": "vApp01", + "vcdMoid": "vm-0609c47f-1c9f-4928-a18a-36815a55885f", + "networkConnections": [ + { + "nicIndex": 0, + "addressingMode": "DHCP", + "ipAddress": "192.168.1.103", + "vappNetworkName": "network01", + "isConnected": true + } + ] + }' + + $vm = ConvertFrom-Json -InputObject $vmjson + + $result = @{ + 'id' = 'VCD_VAPP_EXPORT_01234567-8910-1abc-d435-0abc1234d567_01234567-8910-1abc-d435-0abc1234d567:::0' + 'status' = 'QUEUED' + 'progress' = '0' + } + #endregion + + Context -Name 'Test script logic' { + Mock -CommandName Get-RubrikVApp -Verifiable -ModuleName 'Rubrik' -MockWith { + return $vapp + } + + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + return $result + } + + It -Name 'Should run without error - Partial Export to Existing Target vApp' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $vm -ExportMode 'ExportToTargetVapp' -PowerOn:$false ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Partial Export to Alternate Target vApp' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $vm -ExportMode 'ExportToTargetVapp' -TargetVAppID 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -PowerOn:$false ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Partial Export to New vApp' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $vm -ExportMode 'ExportToNewVapp' -PowerOn:$false ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Full Restore to New vApp in Existing Org VDC' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -ExportMode 'ExportToNewVapp' -PowerOn:$true ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Full Restore to New vApp in Alternate Org VDC' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -ExportMode 'ExportToNewVapp' -TargetOrgVDCID 'VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d567' -PowerOn:$true ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Full Restore with RemoveNetworkDevices parameter' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -ExportMode 'ExportToNewVapp' -PowerOn:$true -RemoveNetworkDevices).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Full Restore with DisableNetwork parameter' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -ExportMode 'ExportToNewVapp' -PowerOn:$true -DisableNetwork).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Full Restore with NoMapping parameter' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -ExportMode 'ExportToNewVapp' -PowerOn:$true -NoMapping).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Full Restore with NetworkMapping parameter' -Test { + ( Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -ExportMode 'ExportToNewVapp' -PowerOn:$true -NetworkMapping 'network01').status | + Should -BeExactly 'QUEUED' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Get-RubrikVApp -ModuleName 'Rubrik' -Exactly 7 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Exactly 9 + + } + + Context -Name 'ValidationScript of Partial Parameter' { + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $true + } + + It -Name 'Empty Parital object should throw error' -Test { + $empty = '' + {(Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $empty -ExportMode 'ExportToNewVapp' -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Partial parameter should be a PSCustomObject" + } + + It -Name 'Missing name should throw error' -Test { + $badrequest = $vm.PSObject.Copy() + $badrequest.PSObject.Properties.Remove('name') + {(Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -ExportMode 'ExportToNewVapp' -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Object passed via Partial parameter missing property name" + } + + It -Name 'Missing vcdMoid should throw error' -Test { + $badrequest = $vm.PSObject.Copy() + $badrequest.PSObject.Properties.Remove('vcdMoid') + {(Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -ExportMode 'ExportToNewVapp' -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Object passed via Partial parameter missing property vcdMoid" + } + + It -Name 'Missing networkConnections should throw error' -Test { + $badrequest = $vm.PSObject.Copy() + $badrequest.PSObject.Properties.Remove('networkConnections') + {(Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -ExportMode 'ExportToNewVapp' -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Object passed via Partial parameter missing property networkConnections" + } + + It -Name 'Junk data should throw error' -Test { + $badrequest = 'Junk data' + {(Export-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -snapshotid '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -ExportMode 'ExportToNewVapp' -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Partial parameter should be a PSCustomObject" + } + } + + Context -Name 'Parameter Validation' { + $vmjson = '{ + "name": "vApp01", + "vcdMoid": "vm-0609c47f-1c9f-4928-a18a-36815a55885f", + "networkConnections": [ + { + "nicIndex": 0, + "addressingMode": "DHCP", + "ipAddress": "192.168.1.103", + "vappNetworkName": "network01", + "isConnected": true + } + ] + }' + + $vm = ConvertFrom-Json -InputObject $vmjson + + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $true + } + + It -Name 'Parameter ID cannot be $null' -Test { + { Export-RubrikVApp -Id $null } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter ID cannot be empty' -Test { + { Export-RubrikVApp -Id '' } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter Partial cannot be $null' -Test { + { Export-RubrikVApp -Partial $null } | + Should -Throw "Cannot validate argument on parameter 'Partial'" + } + It -Name 'Parameter Partial cannot be empty' -Test { + { Export-RubrikVApp -Partial '' } | + Should -Throw "Cannot validate argument on parameter 'Partial'" + } + It -Name 'Parameters TargetOrgVDCID and TargetVAppID cannot be simultaneously used' -Test { + { Export-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -Partial $vm -PowerOn:$false -TargetOrgVDCID 'VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d567' -TargetVAppID 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters Partial and DisableNetwork cannot be simultaneously used' -Test { + { Export-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -DisableNetwork } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters Partial and NoMapping cannot be simultaneously used' -Test { + { Export-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -NoMapping } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters Partial and RemoveNetworkDevices cannot be simultaneously used' -Test { + { Export-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -RemoveNetworkDevices } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters Partial and NetworkMapping cannot be simultaneously used' -Test { + { Export-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -NetworkMapping 'Foo' } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + } + } +} \ No newline at end of file diff --git a/Tests/Export-RubrikVCDTemplate.Tests.ps1 b/Tests/Export-RubrikVCDTemplate.Tests.ps1 new file mode 100644 index 000000000..e163e689d --- /dev/null +++ b/Tests/Export-RubrikVCDTemplate.Tests.ps1 @@ -0,0 +1,133 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force +Describe -Name 'Public/Export-RubrikVcdTemplate' -Tag 'Public', 'Export-RubrikVcdTemplate' -Fixture { + InModuleScope -ModuleName Rubrik -ScriptBlock { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + + $vappjson = '{ + "isPaused": false, + "vcdVmMoidsToExcludeFromSnapshot": [], + "configuredSlaDomainName": "Silver", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Templates", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Templates", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Templates", + "id": "VcdCatalog:::01234567-8910-1abc-d435-0abc1234d567" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaSourceObjectName": "vAppTemplate", + "catalogId": "01234567-8910-1abc-d435-0abc1234d567", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaDomainName": "Silver", + "networks": [ + { + "name": "network01", + "isDeployed": true + } + ], + "isTemplate": true, + "isRelic": false, + "name": "Template", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567", + "isBestEffortSynchronizationEnabled": true, + "vms": [ + { + "name": "vm01", + "vcdMoid": "vm-0a09772b-fc0f-466c-88d3-84d370012737", + "storagePolicyId": "7a9d26f0-bb4f-40e6-a76e-66c18d4b4917", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:24", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": false + } + ] + } + ] + }' + $vapp = ConvertFrom-Json -InputObject $vappjson + + $result = @{ + 'id' = 'VCD_VAPP_EXPORT_01234567-8910-1abc-d435-0abc1234d567_01234567-8910-1abc-d435-0abc1234d567:::0' + 'status' = 'QUEUED' + 'progress' = '0' + } + #endregion + + Context -Name 'Test script logic' { + Mock -CommandName Get-RubrikVAppSnapshot -Verifiable -ModuleName 'Rubrik' -MockWith { + return @{vappName = 'Template'} + } + Mock -CommandName Get-RubrikVApp -Verifiable -ModuleName 'Rubrik' -MockWith { + return $vapp + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + return $result + } + + It -Name 'Should run without error - Export to Template to Existing Catalog' -Test { + ( Export-RubrikVcdTemplate -id '01234567-8910-1abc-d435-0abc1234d567' ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Export to Template to Alternate Catalog' -Test { + ( Export-RubrikVcdTemplate -id '01234567-8910-1abc-d435-0abc1234d567' -catalogid 'VcdOrgVdc:::03b4c110-2d14-437f-a2b3-9458741ab20d' ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Export to Template to Alternate Catalog and set Name' -Test { + ( Export-RubrikVcdTemplate -id '01234567-8910-1abc-d435-0abc1234d567' -catalogid 'VcdOrgVdc:::03b4c110-2d14-437f-a2b3-9458741ab20d' -Name 'Template02' ).status | + Should -BeExactly 'QUEUED' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Get-RubrikVApp -ModuleName 'Rubrik' -Exactly 3 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Exactly 6 + + } + + Context -Name 'Parameter Validation' { + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $true + } + + It -Name 'Parameter ID cannot be $null' -Test { + { Export-RubrikVcdTemplate -Id $null } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter ID cannot be empty' -Test { + { Export-RubrikVcdTemplate -Id '' } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + } + } +} diff --git a/Tests/Get-RubrikVApp.Tests.ps1 b/Tests/Get-RubrikVApp.Tests.ps1 new file mode 100644 index 000000000..7a0996b16 --- /dev/null +++ b/Tests/Get-RubrikVApp.Tests.ps1 @@ -0,0 +1,373 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Get-RubrikVApp' -Tag 'Public', 'Get-RubrikVApp' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Parameters' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hasMore": false, + "data": [ + { + "configuredSlaDomainName": "Silver", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1a vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d567" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaSourceObjectName": "vApp01", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaDomainName": "Silver", + "isRelic": false, + "name": "vApp01", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "configuredSlaDomainName": "Inherit", + "effectiveSlaDomainId": "UNPROTECTED", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant2", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d568" + }, + { + "name": "Tenant2 vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d568" + } + ], + "slaAssignment": "Unassigned", + "effectiveSlaSourceObjectId": "Global:::All", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "INHERIT", + "effectiveSlaDomainName": "Unprotected", + "isRelic": false, + "name": "vApp02-Tue Aug 27 2019 14:55:37 GMT-0700 (Pacific Daylight Time)", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d568" + }, + { + "configuredSlaDomainName": "Bronze", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1b vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d569" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d569", + "effectiveSlaSourceObjectName": "vApp03", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "effectiveSlaDomainName": "Bronze", + "isRelic": false, + "name": "vApp03", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d569" + }, + { + "configuredSlaDomainName": "Bronze", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant2", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d568" + }, + { + "name": "Tenant2 vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d568" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d570", + "effectiveSlaSourceObjectName": "vApp 04", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "effectiveSlaDomainName": "Bronze", + "isRelic": false, + "name": "vApp 04", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d570" + } + ], + "total": 4 + }' + return ConvertFrom-Json $response + } + + It -Name 'filtering on Silver SLA should return one result' -Test { + Mock -CommandName Get-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ 'id' = '01234567-8910-1abc-d435-0abc1234d567' } + } + + ( Get-RubrikVApp -SLA 'Silver' ).Name | + Should -BeExactly "vApp01" + } + + It -Name 'filtering on Bronze SLA should return two results' -Test { + Mock -CommandName Get-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ 'id' = '01234567-8910-1abc-d435-0abc1234d568' } + } + + ( Get-RubrikVApp -SLA 'Bronze' ).Length | + Should -BeExactly 2 + } + + It -Name 'filtering on Name should return one result' -Test { + ( Get-RubrikVApp -Name 'vApp01' ).id | + Should -BeExactly "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567" + } + + It -Name 'filtering on SourceObjectId should return one result' -Test { + ( Get-RubrikVApp -SourceObjectId 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d570' ).name | + Should -BeExactly "vApp 04" + } + + It -Name 'filtering on SourceObjectName should return one result' -Test { + ( Get-RubrikVApp -SourceObjectName 'vApp03' ).id | + Should -BeExactly "VcdVapp:::01234567-8910-1abc-d435-0abc1234d569" + } + + It -Name 'filtering on Relics only should return one result' -Test { + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hasMore": false, + "data": [ + { + "configuredSlaDomainName": "Inherit", + "effectiveSlaDomainId": "UNPROTECTED", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant2", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d568" + }, + { + "name": "Tenant2 vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d568" + } + ], + "slaAssignment": "Unassigned", + "effectiveSlaSourceObjectId": "Global:::All", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "INHERIT", + "effectiveSlaDomainName": "Unprotected", + "isRelic": false, + "name": "vApp02-Tue Aug 27 2019 14:55:37 GMT-0700 (Pacific Daylight Time)", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d568" + } + ], + "total": 1 + }' + return ConvertFrom-Json $response + } + + ( Get-RubrikVApp -Relic ).id | + Should -BeExactly "VcdVapp:::01234567-8910-1abc-d435-0abc1234d568" + } + + It -Name 'filtering on Silver SLA ID should return one result' -Test { + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hasMore": false, + "data": [ + { + "configuredSlaDomainName": "Silver", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1a vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d567" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaSourceObjectName": "vApp01", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "effectiveSlaDomainName": "Silver", + "isRelic": false, + "name": "vApp01", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d567" + } + ], + "total": 1 + }' + return ConvertFrom-Json $response + } + ( Get-RubrikVApp -SLAID '01234567-8910-1abc-d435-0abc1234d567' ).Name | + Should -BeExactly "vApp01" + } + + It -Name 'filtering on Bronze SLA ID should return two results' -Test { + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hasMore": false, + "data": [ + { + "configuredSlaDomainName": "Bronze", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant1b vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d569" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d569", + "effectiveSlaSourceObjectName": "vApp02", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "effectiveSlaDomainName": "Bronze", + "isRelic": false, + "name": "vApp03", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d569" + }, + { + "configuredSlaDomainName": "Bronze", + "effectiveSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "infraPath": [ + { + "name": "VMware vCloud Director", + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567" + }, + { + "name": "Tenant2", + "id": "VcdOrg:::01234567-8910-1abc-d435-0abc1234d568" + }, + { + "name": "Tenant2 vDC", + "id": "VcdOrgVdc:::01234567-8910-1abc-d435-0abc1234d568" + } + ], + "slaAssignment": "Direct", + "effectiveSlaSourceObjectId": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d570", + "effectiveSlaSourceObjectName": "vApp04", + "vcdClusterName": "VMware vCloud Director", + "vcdClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d568", + "effectiveSlaDomainName": "Bronze", + "isRelic": false, + "name": "vApp 04", + "id": "VcdVapp:::01234567-8910-1abc-d435-0abc1234d570" + } + ], + "total": 2 + }' + return ConvertFrom-Json $response + } + + ( Get-RubrikVApp -SLAID '01234567-8910-1abc-d435-0abc1234d568' ).Length | + Should -BeExactly 2 + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 4 + Assert-MockCalled -CommandName Get-RubrikSLA -ModuleName 'Rubrik' -Times 2 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 4 + } + + Context -Name 'Parameter Validation' { + It -Name 'Parameter Name cannot be $null' -Test { + { Get-RubrikVApp -Name $null } | + Should -Throw "Cannot validate argument on parameter 'Name'" + } + It -Name 'Parameter Name cannot be empty' -Test { + { Get-RubrikVApp -Name '' } | + Should -Throw "Cannot validate argument on parameter 'Name'" + } + It -Name 'Parameter ID cannot be $null' -Test { + { Get-RubrikVApp -Id $null } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter ID cannot be empty' -Test { + { Get-RubrikVApp -Id '' } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameters Id and Name cannot be simultaneously used' -Test { + { Get-RubrikVApp -Id VirtualMachine:::1226ff04-6100-454f-905b-5df817b6981a-vm-1025 -Name 'foo' } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameter SLAAssignment must be valid' -Test { + { Get-RubrikVApp -SLAAssignment 'foo' } | + Should -Throw "Cannot validate argument on parameter 'SLAAssignment'" + } + } +} \ No newline at end of file diff --git a/Tests/Get-RubrikVAppExportOptions.Tests.ps1 b/Tests/Get-RubrikVAppExportOptions.Tests.ps1 new file mode 100644 index 000000000..08e546547 --- /dev/null +++ b/Tests/Get-RubrikVAppExportOptions.Tests.ps1 @@ -0,0 +1,96 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Get-RubrikVAppExportOptions' -Tag 'Public', 'Get-RubrikVAppExportOptions' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Request Succeeds' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $exportoptsjson = '{ + "allChildVmsWithDefaultNetworkConnections": [ + { + "name": "vApp01", + "vcdMoid": "vm-0609c47f-1c9f-4928-a18a-36815a55885f", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:24", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": true + } + ] + }, + { + "name": "vApp02", + "vcdMoid": "vm-a5549267-3873-4c2d-a0b2-eafd4b01a682", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:64", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": true + } + ] + } + ], + "restorableNetworks": [], + "targetVappNetworks": [ + { + "name": "network01", + "isDeployed": true + }, + { + "name": "network02", + "parentNetworkId": "01234567-8910-1abc-d435-0abc1234d567", + "isDeployed": true + } + ], + "availableStoragePolicies": [ + { + "name": "Default Storage Policy", + "id": "01234567-8910-1abc-d435-0abc1234d567" + } + ] + }' + return ConvertFrom-Json -InputObject $exportoptsjson + } + + It -Name 'Expected results returned' -Test { + $result = Get-RubrikVAppExportOptions -id '01234567-8910-1abc-d435-0abc1234d567' -ExportMode 'ExportToNewVapp' + $result.allChildVmsWithDefaultNetworkConnections[0].name | Should -BeExactly 'vApp01' + $result.targetVappNetworks[0].name | Should -BeExactly 'network01' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be null' -Test { + { Get-RubrikVAppExportOptions -id $null } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + It -Name 'Parameter ID cannot be empty' -Test { + { Get-RubrikVAppExportOptions -id '' } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + } +} \ No newline at end of file diff --git a/Tests/Get-RubrikVAppRecoverOptions.Tests.ps1 b/Tests/Get-RubrikVAppRecoverOptions.Tests.ps1 new file mode 100644 index 000000000..ad4004ebd --- /dev/null +++ b/Tests/Get-RubrikVAppRecoverOptions.Tests.ps1 @@ -0,0 +1,89 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Get-RubrikVAppRecoverOptions' -Tag 'Public', 'Get-RubrikVAppRecoverOptions' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Request Succeeds' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $recoveroptsjson = '{ + "restorableVms": [ + { + "name": "vApp01", + "vcdMoid": "vm-0609c47f-1c9f-4928-a18a-36815a55885f", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:24", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": true + } + ] + }, + { + "name": "vApp02", + "vcdMoid": "vm-a5549267-3873-4c2d-a0b2-eafd4b01a682", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:64", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": true + } + ] + } + ], + "availableVappNetworks": [ + { + "name": "network01", + "isDeployed": true + }, + { + "name": "network02", + "parentNetworkId": "01234567-8910-1abc-d435-0abc1234d567", + "isDeployed": true + } + ] + }' + return ConvertFrom-Json -InputObject $recoveroptsjson + } + + It -Name 'Expected results returned' -Test { + $result = Get-RubrikVAppRecoverOptions -id '01234567-8910-1abc-d435-0abc1234d567' + $result.restorableVms[0].name | Should -BeExactly 'vApp01' + $result.availableVappNetworks[0].name | Should -BeExactly 'network01' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be null' -Test { + { Get-RubrikVAppRecoverOptions -id $null } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + It -Name 'Parameter ID cannot be empty' -Test { + { Get-RubrikVAppRecoverOptions -id '' } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + } +} \ No newline at end of file diff --git a/Tests/Get-RubrikVAppSnapshot.Tests.ps1 b/Tests/Get-RubrikVAppSnapshot.Tests.ps1 new file mode 100644 index 000000000..2bd5bd2c2 --- /dev/null +++ b/Tests/Get-RubrikVAppSnapshot.Tests.ps1 @@ -0,0 +1,93 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Get-RubrikVAppSnapshot' -Tag 'Public', 'Get-RubrikVAppSnapshot' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Parameters' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "date": "2019-07-19T16:38:22.826Z", + "indexState": 1, + "slaName": "Silver", + "vappName": "vApp01", + "slaId": "01234567-8910-1abc-d435-0abc1234d567", + "replicationLocationIds": [], + "archivalLocationIds": [ + "01234567-8910-1abc-d435-0abc1234d567" + ], + "networks": [ + { + "name": "Production", + "isDeployed": true + }, + { + "name": "Production", + "parentNetworkId": "01234567-8910-1abc-d435-0abc1234d567", + "isDeployed": true + } + ], + "isOnDemandSnapshot": true, + "cloudState": 2, + "excludedVcdVmMoids": [], + "vmSnapshots": [ + { + "vcenterVmId": "01234567-8910-1abc-d435-0abc1234d567-vm-12345", + "indexState": 2, + "vmName": "vm01", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:11:22:33:44:55", + "addressingMode": "DHCP", + "ipAddress": "192.168.1.101", + "vappNetworkName": "Production", + "isConnected": true + } + ], + "vmSnapshotId": "01234567-8910-1abc-d435-0abc1234d567", + "vcdVmMoid": "vm-01234567-8910-1abc-d435-0abc1234d567" + } + ], + "id": "01234567-8910-1abc-d435-0abc1234d567" + }' + return ConvertFrom-Json $response + } + + It -Name 'Expected results returned' -Test { + ( Get-RubrikVAppSnapshot -ID '4a7c0e3a-e48b-4f3b-8a51-efc6bd769863' ).vappName | + Should -BeExactly "vApp01" + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be $null' -Test { + { Get-RubrikVAppSnapshot -Id $null } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter ID cannot be empty' -Test { + { Get-RubrikVAppSnapshot -Id '' } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + } +} \ No newline at end of file diff --git a/Tests/Get-RubrikVCD.Tests.ps1 b/Tests/Get-RubrikVCD.Tests.ps1 new file mode 100644 index 000000000..b05339e84 --- /dev/null +++ b/Tests/Get-RubrikVCD.Tests.ps1 @@ -0,0 +1,98 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Get-RubrikVCD' -Tag 'Public', 'Get-RubrikVCD' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Parameters' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hasMore": false, + "data": [ + { + "hostname": "vcd.example.com", + "configuredSlaDomainName": "Inherit", + "primaryClusterId": "12345678-1234-abcd-8910-1234567890ab", + "name": "VMware vCloud Director", + "connectionStatus": { + "status": "Connected" + }, + "id": "Vcd:::f96a2b1b-a06d-4f17-b6be-52141795532e", + "configuredSlaDomainId": "INHERIT", + "username": "administrator" + } + ], + "total": 1 + }' + return ConvertFrom-Json $response + } + + It -Name 'should return one result with no parameters' -Test { + ( Get-RubrikVCD -Name 'VMware vCloud Director' | Measure-Object ).Count | + Should -BeExactly 1 + } + + It -Name 'should filter on vCD Cluster Name' -Test { + ( Get-RubrikVCD -Name 'VMware vCloud Director' ).Name | + Should -BeExactly 'VMware vCloud Director' + } + + It -Name 'should filter on vCD Cluster Hostname' -Test { + ( Get-RubrikVCD -Hostname 'vcd.example.com' ).Hostname | + Should -BeExactly 'vcd.example.com' + } + + It -Name 'should filter on vCD Cluster Status' -Test { + ( Get-RubrikVCD -Status 'Connected' ).Hostname | + Should -BeExactly 'vcd.example.com' + } + + It -Name 'should return one result with all parameters' -Test { + ( Get-RubrikVCD -Name 'VMware vCloud Director' -Hostname 'vcd.example.com' -Status 'Connected' | Measure-Object ).Count | + Should -BeExactly 1 + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 5 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 5 + } + + Context -Name 'Parameter Validation' { + It -Name 'Parameter Name cannot be $null' -Test { + { Get-RubrikVCD -Name $null } | + Should -Throw "Cannot validate argument on parameter 'Name'" + } + It -Name 'Parameter Name cannot be empty' -Test { + { Get-RubrikVCD -Name '' } | + Should -Throw "Cannot validate argument on parameter 'Name'" + } + It -Name 'Parameter Hostname cannot be $null' -Test { + { Get-RubrikVCD -Hostname $null } | + Should -Throw "Cannot validate argument on parameter 'Hostname'" + } + It -Name 'Parameter Hostname cannot be empty' -Test { + { Get-RubrikVCD -Hostname '' } | + Should -Throw "Cannot validate argument on parameter 'Hostname'" + } + It -Name 'Parameter Status must be valid' -Test { + { Get-RubrikVCD -Status 'foo' } | + Should -Throw "Cannot validate argument on parameter 'Status'" + } + } +} \ No newline at end of file diff --git a/Tests/Get-RubrikVcdTemplateExportOptions.Tests.ps1 b/Tests/Get-RubrikVcdTemplateExportOptions.Tests.ps1 new file mode 100644 index 000000000..1ff4df77b --- /dev/null +++ b/Tests/Get-RubrikVcdTemplateExportOptions.Tests.ps1 @@ -0,0 +1,138 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Get-RubrikVcdTemplateExportOptions' -Tag 'Public', 'Get-RubrikVcdTemplateExportOptions' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Test Export Options' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $exportoptsjson = '{ + "originalVdcExportOptions": { + "orgVdcId": "01234567-8910-1abc-d435-0abc1234d567", + "availableStoragePolicies": [ + { + "name": "Storage Policy", + "id": "01234567-8910-1abc-d435-0abc1234d567" + } + ] + }, + "defaultCatalogExportOptions": { + "orgVdcId": "01234567-8910-1abc-d435-0abc1234d567", + "availableStoragePolicies": [ + { + "name": "Storage Policy", + "id": "01234567-8910-1abc-d435-0abc1234d567" + } + ] + } + }' + return ConvertFrom-Json -InputObject $exportoptsjson + } + + It -Name 'Expected results returned' -Test { + $result = Get-RubrikVcdTemplateExportOptions -id '01234567-8910-1abc-d435-0abc1234d567' -catalogid 'VcdCatalog:::01234567-8910-1abc-d435-0abc1234d567' -name 'Template-Export' + $result.originalVdcExportOptions.orgVdcId | Should -BeExactly '01234567-8910-1abc-d435-0abc1234d567' + $result.defaultCatalogExportOptions.orgVdcId | Should -BeExactly '01234567-8910-1abc-d435-0abc1234d567' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + Context -Name 'Test Advanced Export Options' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $exportoptsjson = '{ + "advancedExportOptions": { + "orgVdcId": "01234567-8910-1abc-d435-0abc1234d567", + "availableStoragePolicies": [ + { + "name": "Storage Policy", + "id": "01234567-8910-1abc-d435-0abc1234d567" + } + ] + } + }' + return ConvertFrom-Json -InputObject $exportoptsjson + } + + It -Name 'Expected results returned' -Test { + $result = Get-RubrikVcdTemplateExportOptions -id '01234567-8910-1abc-d435-0abc1234d567' -catalogid 'VcdCatalog:::01234567-8910-1abc-d435-0abc1234d567' -orgvdcid 'VcdOrgVdc:::665f23c6-1fc3-4d49-bb58-5841b0c20818' -name 'Template-Export' + $result.advancedExportOptions.orgVdcId | Should -BeExactly '01234567-8910-1abc-d435-0abc1234d567' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + Context -Name 'Test Export Options with Name parameter missing' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Get-RubrikVAppSnapshot -Verifiable -ModuleName 'Rubrik' -MockWith { + return @{vappName = 'Template'} + } + Mock -CommandName Get-RubrikVApp -Verifiable -ModuleName 'Rubrik' -MockWith { + return @{name = 'Template'} + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $exportoptsjson = '{ + "originalVdcExportOptions": { + "orgVdcId": "01234567-8910-1abc-d435-0abc1234d567", + "availableStoragePolicies": [ + { + "name": "Storage Policy", + "id": "01234567-8910-1abc-d435-0abc1234d567" + } + ] + }, + "defaultCatalogExportOptions": { + "orgVdcId": "01234567-8910-1abc-d435-0abc1234d567", + "availableStoragePolicies": [ + { + "name": "Storage Policy", + "id": "01234567-8910-1abc-d435-0abc1234d567" + } + ] + } + }' + return ConvertFrom-Json -InputObject $exportoptsjson + } + + It -Name 'Expected results returned' -Test { + $result = Get-RubrikVcdTemplateExportOptions -id '01234567-8910-1abc-d435-0abc1234d567' -catalogid 'VcdCatalog:::01234567-8910-1abc-d435-0abc1234d567' + $result.originalVdcExportOptions.orgVdcId | Should -BeExactly '01234567-8910-1abc-d435-0abc1234d567' + $result.defaultCatalogExportOptions.orgVdcId | Should -BeExactly '01234567-8910-1abc-d435-0abc1234d567' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Get-RubrikVAppSnapshot -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Get-RubrikVApp -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be null' -Test { + { Get-RubrikVcdTemplateExportOptions -id $null } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + It -Name 'Parameter ID cannot be empty' -Test { + { Get-RubrikVcdTemplateExportOptions -id '' } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + } +} \ No newline at end of file diff --git a/Tests/Protect-RubrikVApp.Tests.ps1 b/Tests/Protect-RubrikVApp.Tests.ps1 new file mode 100644 index 000000000..0500b9608 --- /dev/null +++ b/Tests/Protect-RubrikVApp.Tests.ps1 @@ -0,0 +1,107 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Protect-RubrikVApp' -Tag 'Public', 'Protect-RubrikVApp' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Parameter/SLA' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Test-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ 'slaid' = '12345678-1234-abcd-8910-1234567890ab' } + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ + 'id' = 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' + 'effectiveSlaDomainName' = 'test-valid_sla_name' + } + } + It -Name 'Should assign SLA' -Test { + ( Protect-RubrikVApp -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -SLA 'test-valid_sla_name' ).effectiveSlaDomainName | + Should -BeExactly 'test-valid_sla_name' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Test-RubrikSLA -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + Context -Name 'Parameter/DoNotProtect' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Test-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ 'id' = 'UNPROTECTED' } + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ + 'id' = 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' + 'effectiveSlaDomainId' = 'UNPROTECTED' + } + } + It -Name 'Should be Unprotected' -Test { + ( Protect-RubrikVApp -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -DoNotProtect).effectiveSlaDomainId | + Should -BeExactly 'UNPROTECTED' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Test-RubrikSLA -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + Context -Name 'Parameter/Inherit' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Test-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ 'id' = 'INHERIT' } + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ + 'id' = 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' + 'effectiveSlaDomainId' = 'INHERIT' + } + } + It -Name 'Should be Inherited' -Test { + ( Protect-RubrikVApp -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -Inherit).effectiveSlaDomainId | + Should -BeExactly 'INHERIT' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Test-RubrikSLA -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be $null' -Test { + { Protect-RubrikVApp -Id $null } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter ID cannot be empty' -Test { + { Protect-RubrikVApp -Id '' } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameters SLA and DoNotProtect cannot be simultaneously used' -Test { + { Protect-RubrikVApp -Id VcdVapp:::01234567-8910-1abc-d435-0abc1234d567 -SLA Gold -DoNotProtect} | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters SLA and Inherit cannot be simultaneously used' -Test { + { Protect-RubrikVApp -Id VcdVapp:::01234567-8910-1abc-d435-0abc1234d567 -SLA Gold -Inherit} | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters DoNotProtect and Inherit cannot be simultaneously used' -Test { + { Protect-RubrikVApp -Id VcdVapp:::01234567-8910-1abc-d435-0abc1234d567 -DoNotProtect -Inherit} | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + } +} \ No newline at end of file diff --git a/Tests/Restore-RubrikVApp.Tests.ps1 b/Tests/Restore-RubrikVApp.Tests.ps1 new file mode 100644 index 000000000..030ffb4a6 --- /dev/null +++ b/Tests/Restore-RubrikVApp.Tests.ps1 @@ -0,0 +1,196 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +$result = @{ + 'id' = 'VCD_VAPP_RESTORE_01234567-8910-1abc-d435-0abc1234d567_01234567-8910-1abc-d435-0abc1234d567:::0' + 'status' = 'QUEUED' + 'progress' = '0' +} + +Describe -Name 'Public/Restore-RubrikVApp' -Tag 'Public', 'Restore-RubrikVApp' -Fixture { + InModuleScope -ModuleName Rubrik -ScriptBlock { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + + $recoveroptsjson = '{ + "restorableVms": [ + { + "name": "vApp01", + "vcdMoid": "vm-0609c47f-1c9f-4928-a18a-36815a55885f", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:24", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": true + } + ] + }, + { + "name": "vApp02", + "vcdMoid": "vm-a5549267-3873-4c2d-a0b2-eafd4b01a682", + "networkConnections": [ + { + "nicIndex": 0, + "macAddress": "00:50:56:01:00:64", + "addressingMode": "DHCP", + "vappNetworkName": "network01", + "isConnected": true + } + ] + } + ], + "availableVappNetworks": [ + { + "name": "network01", + "isDeployed": true + }, + { + "name": "network02", + "parentNetworkId": "01234567-8910-1abc-d435-0abc1234d567", + "isDeployed": true + } + ] + }' + $recoveropts = ConvertFrom-Json -InputObject $recoveroptsjson + + $vmjson = '{ + "name": "vApp01", + "vcdMoid": "vm-0609c47f-1c9f-4928-a18a-36815a55885f", + "networkConnections": [ + { + "nicIndex": 0, + "addressingMode": "DHCP", + "ipAddress": "192.168.1.103", + "vappNetworkName": "network01", + "isConnected": true + } + ] + }' + + $vm = ConvertFrom-Json -InputObject $vmjson + + $result = @{ + 'id' = 'VCD_REFRESH_01234567-8910-1abc-d435-0abc1234d567_01234567-8910-1abc-d435-0abc1234d567:::0' + 'status' = 'QUEUED' + 'progress' = '0' + } + #endregion + + Context -Name 'Test script logic' { + Mock -CommandName Get-RubrikVAppRecoverOptions -Verifiable -ModuleName 'Rubrik' -MockWith { + return $recoveropts + } + + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + return $result + } + + It -Name 'Should run without error - Partial Restore' -Test { + ( Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -Partial $vm -PowerOn:$false ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - Full Restore' -Test { + ( Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -PowerOn:$true ).status | + Should -BeExactly 'QUEUED' + } + + It -Name 'Should run without error - NetworkMapping' -Test { + ( Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -PowerOn:$true -NetworkMapping 'network01').status | + Should -BeExactly 'QUEUED' + } + + Assert-VerifiableMock + Assert-MockCalled -CommandName Get-RubrikVAppRecoverOptions -ModuleName 'Rubrik' -Exactly 2 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Exactly 3 + + } + + Context -Name 'ValidationScript of Partial Parameter' { + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $true + } + + It -Name 'Empty Parital object should throw error' -Test { + $empty = '' + {(Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -Partial $empty -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Partial parameter should be a PSCustomObject" + } + + It -Name 'Missing name should throw error' -Test { + $badrequest = $vm.PSObject.Copy() + $badrequest.PSObject.Properties.Remove('name') + {(Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Object passed via Partial parameter missing property name" + } + + It -Name 'Missing vcdMoid should throw error' -Test { + $badrequest = $vm.PSObject.Copy() + $badrequest.PSObject.Properties.Remove('vcdMoid') + {(Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Object passed via Partial parameter missing property vcdMoid" + } + + It -Name 'Missing networkConnections should throw error' -Test { + $badrequest = $vm.PSObject.Copy() + $badrequest.PSObject.Properties.Remove('networkConnections') + {(Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Object passed via Partial parameter missing property networkConnections" + } + + It -Name 'Junk data should throw error' -Test { + $badrequest = 'Junk data' + {(Restore-RubrikVApp -id '01234567-8910-1abc-d435-0abc1234d567' -Partial $badrequest -PowerOn:$false)} | + Should -Throw "Cannot validate argument on parameter 'Partial'. Partial parameter should be a PSCustomObject" + } + } + + + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be $null' -Test { + { Restore-RubrikVApp -Id $null } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter ID cannot be empty' -Test { + { Restore-RubrikVApp -Id '' } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter Partial cannot be $null' -Test { + { Restore-RubrikVApp -Partial $null } | + Should -Throw "Cannot validate argument on parameter 'Partial'" + } + It -Name 'Parameter Partial cannot be empty' -Test { + { Restore-RubrikVApp -Partial '' } | + Should -Throw "Cannot validate argument on parameter 'Partial'" + } + It -Name 'Parameters Partial and DisableNetwork cannot be simultaneously used' -Test { + { Restore-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -DisableNetwork } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters Partial and NoMapping cannot be simultaneously used' -Test { + { Restore-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -NoMapping } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters Partial and RemoveNetworkDevices cannot be simultaneously used' -Test { + { Restore-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -RemoveNetworkDevices } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters Partial and NetworkMapping cannot be simultaneously used' -Test { + { Restore-RubrikVApp -Id 01234567-8910-1abc-d435-0abc1234d567 -PowerOn:$false -Partial $vm -NetworkMapping 'Foo' } | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + } + + } +} \ No newline at end of file diff --git a/Tests/Set-RubrikVCD.Tests.ps1 b/Tests/Set-RubrikVCD.Tests.ps1 new file mode 100644 index 000000000..491b520da --- /dev/null +++ b/Tests/Set-RubrikVCD.Tests.ps1 @@ -0,0 +1,194 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Set-RubrikVCD' -Tag 'Public', 'Set-RubrikVCD' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Parameter/SLA' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Test-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ 'slaid' = '12345678-1234-abcd-8910-1234567890ab' } + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hostname": "vcd.example.com", + "configuredSlaDomainName": "Bronze", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "name": "VMware vCloud Director", + "connectionStatus": { + "status": "Connected" + }, + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "01234567-8910-1abc-d435-0abc1234d567", + "username": "administrator" + }' + return ConvertFrom-Json $response + } + It -Name 'Should assign SLA' -Test { + ( Set-RubrikVCD -id 'Vcd:::01234567-8910-1abc-d435-0abc1234d567' -SLA 'Bronze' ).configuredSlaDomainId | + Should -BeExactly '01234567-8910-1abc-d435-0abc1234d567' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Test-RubrikSLA -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + Context -Name 'Parameter/DoNotProtect' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Test-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + 'UNPROTECTED' + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hostname": "vcd.example.com", + "configuredSlaDomainName": "Unprotected", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "name": "VMware vCloud Director", + "connectionStatus": { + "status": "Connected" + }, + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "UNPROTECTED", + "username": "administrator" + }' + return ConvertFrom-Json $response + } + It -Name 'Should be Unprotected' -Test { + ( Set-RubrikVCD -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -DoNotProtect).configuredSlaDomainId | + Should -BeExactly 'UNPROTECTED' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Test-RubrikSLA -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + Context -Name 'Parameter/Inherit' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Test-RubrikSLA -Verifiable -ModuleName 'Rubrik' -MockWith { + 'UNPROTECTED' + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hostname": "vcd.example.com", + "configuredSlaDomainName": "Inherit", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "name": "VMware vCloud Director", + "connectionStatus": { + "status": "Connected" + }, + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "INHERIT", + "username": "administrator" + }' + return ConvertFrom-Json $response + } + It -Name 'Should be Unprotected' -Test { + ( Set-RubrikVCD -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -Inherit).configuredSlaDomainId | + Should -BeExactly 'INHERIT' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Test-RubrikSLA -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + Context -Name 'Parameter/Hostname' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hostname": "newvcd.example.com", + "configuredSlaDomainName": "Bronze", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "name": "VMware vCloud Director", + "connectionStatus": { + "status": "Connected" + }, + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "Vcd:::01234567-8910-1abc-d435-0abc1234d567", + "username": "administrator" + }' + return ConvertFrom-Json $response + } + It -Name 'Hostname Should be Updated' -Test { + ( Set-RubrikVCD -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -Hostname 'newvcd.example.com').hostname | + Should -BeExactly 'newvcd.example.com' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + + <# + Context -Name 'Parameter/UpdateCreds' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + function Get-Credential { + $password = ConvertTo-SecureString 'ANewPassword' -AsPlainText -Force + $credential = New-Object System.Management.Automation.PSCredential ('newadmin', $password) + $credential + } + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + $response = '{ + "hostname": "vcd.example.com", + "configuredSlaDomainName": "Bronze", + "primaryClusterId": "01234567-8910-1abc-d435-0abc1234d567", + "name": "VMware vCloud Director", + "connectionStatus": { + "status": "Connected" + }, + "id": "Vcd:::01234567-8910-1abc-d435-0abc1234d567", + "configuredSlaDomainId": "Vcd:::01234567-8910-1abc-d435-0abc1234d567", + "username": "newadmin" + }' + return ConvertFrom-Json $response + } + It -Name 'Hostname Should be Updated' -Test { + ( Set-RubrikVCD -id 'VcdVapp:::01234567-8910-1abc-d435-0abc1234d567' -UpdateCreds).username | + Should -BeExactly 'newadmin' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Get-Credential -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + #> + + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be $null' -Test { + { Set-RubrikVCD -Id $null } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameter ID cannot be empty' -Test { + { Set-RubrikVCD -Id '' } | + Should -Throw "Cannot validate argument on parameter 'ID'" + } + It -Name 'Parameters SLA and DoNotProtect cannot be simultaneously used' -Test { + { Set-RubrikVCD -Id VcdVapp:::01234567-8910-1abc-d435-0abc1234d567 -SLA Gold -DoNotProtect} | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters SLA and Inherit cannot be simultaneously used' -Test { + { Set-RubrikVCD -Id VcdVapp:::01234567-8910-1abc-d435-0abc1234d567 -SLA Gold -Inherit} | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + It -Name 'Parameters DoNotProtect and Inherit cannot be simultaneously used' -Test { + { Set-RubrikVCD -Id VcdVapp:::01234567-8910-1abc-d435-0abc1234d567 -DoNotProtect -Inherit} | + Should -Throw "Parameter set cannot be resolved using the specified named parameters." + } + } +} \ No newline at end of file diff --git a/Tests/Update-RubrikVCD.Tests.ps1 b/Tests/Update-RubrikVCD.Tests.ps1 new file mode 100644 index 000000000..32ccde964 --- /dev/null +++ b/Tests/Update-RubrikVCD.Tests.ps1 @@ -0,0 +1,49 @@ +Remove-Module -Name 'Rubrik' -ErrorAction 'SilentlyContinue' +Import-Module -Name './Rubrik/Rubrik.psd1' -Force + +foreach ( $privateFunctionFilePath in ( Get-ChildItem -Path './Rubrik/Private' | Where-Object extension -eq '.ps1').FullName ) { + . $privateFunctionFilePath +} + +Describe -Name 'Public/Update-RubrikVCD' -Tag 'Public', 'Update-RubrikVCD' -Fixture { + #region init + $global:rubrikConnection = @{ + id = 'test-id' + userId = 'test-userId' + token = 'test-token' + server = 'test-server' + header = @{ 'Authorization' = 'Bearer test-authorization' } + time = (Get-Date) + api = 'v1' + version = '4.0.5' + } + #endregion + + Context -Name 'Request Succeeds' { + Mock -CommandName Test-RubrikConnection -Verifiable -ModuleName 'Rubrik' -MockWith {} + Mock -CommandName Submit-Request -Verifiable -ModuleName 'Rubrik' -MockWith { + @{ + 'id' = 'VCD_REFRESH_01234567-8910-1abc-d435-0abc1234d567_01234567-8910-1abc-d435-0abc1234d567:::0' + 'status' = 'QUEUED' + 'progress' = '0' + } + } + It -Name 'Should return QUEUED status' -Test { + ( Update-RubrikVCD -id 'VCD_REFRESH_01234567-8910-1abc-d435-0abc1234d567_01234567-8910-1abc-d435-0abc1234d567:::0').status | + Should -BeExactly 'QUEUED' + } + Assert-VerifiableMock + Assert-MockCalled -CommandName Test-RubrikConnection -ModuleName 'Rubrik' -Times 1 + Assert-MockCalled -CommandName Submit-Request -ModuleName 'Rubrik' -Times 1 + } + Context -Name 'Parameter Validation' { + It -Name 'Parameter ID cannot be null' -Test { + { Update-RubrikVCD -id $null } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + It -Name 'Parameter ID cannot be empty' -Test { + { Update-RubrikVCD -id '' } | + Should -Throw "Cannot validate argument on parameter 'id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + } +} \ No newline at end of file