Public/Move-RubrikMountVMDK.ps1

#Requires -Version 3
function Move-RubrikMountVMDK
{
  <#
      .SYNOPSIS
      Moves the VMDKs from a Live Mount to another VM

      .DESCRIPTION
      The Move-RubrikMountVMDK cmdlet is used to attach VMDKs from a Live Mount to another VM, typically for restore or testing purposes.

      .NOTES
      Written by Chris Wahl for community usage
      Twitter: @ChrisWahl
      GitHub: chriswahl

      .LINK
      https://github.com/rubrikinc/PowerShell-Module

      .EXAMPLE
      Move-RubrikMountVMDK -SourceVM 'SourceVM' -TargetVM 'TargetVM'
      This will create a Live Mount using the latest snapshot of the VM named "SourceVM"
      The Live Mount's VMDKs would then be presented to the VM named "TargetVM"

      .EXAMPLE
      Move-RubrikMountVMDK -SourceVM 'SourceVM' -TargetVM 'TargetVM' -Date '01/30/2016 08:00'
      This will create a Live Mount using the January 30th 08:00AM snapshot of the VM named "SourceVM"
      The Live Mount's VMDKs would then be presented to the VM named "TargetVM"
      Note: The Date parameter will start at the time specified (in this case, 08:00am) and work backwards in time until it finds a snapshot.
      Precise timing is not required.
    
      .EXAMPLE
      Move-RubrikMountVMDK -SourceVM 'SourceVM' -TargetVM 'TargetVM' -ExcludeDisk @(0,1)
      This will create a Live Mount using the latest snapshot of the VM named "SourceVM"
      Disk 0 and 1 (the first and second disks) would be excluded from presentation to the VM named "TargetVM"
      Note: that for the "ExcludeDisk" array, the format is @(#,#,#,...) where each # represents a disk starting with 0.
      Example: To exclude the first and third disks, the value would be @(0,2).
      Example: To exclude just the first disk, use @(0).

      .EXAMPLE
      Move-RubrikMountVMDK -Cleanup 'C:\Users\Person1\Documents\SourceVM_to_TargetVM-1234567890.txt'
      This will remove the disk(s) and live mount, effectively reversing the initial request
      This file is created each time the command is run and stored in the $HOME path as a text file
      The file contains the TargetVM name, MountID value, and a list of all presented disks
  #>


  [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact = 'High')]
  Param(
    # Source virtual machine to use as a Live Mount based on a previous backup
    [Parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ParameterSetName = 'Create')]
    [Alias('Name','VM')]
    [ValidateNotNullorEmpty()]
    [String]$SourceVM,
    # Target virtual machine to attach the Live Mount disk(s)
    [Parameter(Mandatory = $true,Position = 1,ParameterSetName = 'Create')]
    [ValidateNotNullorEmpty()]
    [String]$TargetVM,
    # Backup date to use for the Live Mount
    # Will use the current date and time if no value is specified
    [Parameter(Position = 2,ParameterSetName = 'Create')]
    [String]$Date,
    # An array of disks to exclude from presenting to the target virtual machine
    # By default, all disks will be presented
    [Parameter(Position = 3,ParameterSetName = 'Create')]
    [Array]$ExcludeDisk,
    # The path to a cleanup file to remove the live mount and presented disks
    # The cleanup file is created each time the command is run and stored in the $HOME path as a text file with a random number value
    # The file contains the TargetVM name, MountID value, and a list of all presented disks
    [Parameter(ParameterSetName = 'Destroy')]    
    [String]$Cleanup,
    # Rubrik server IP or FQDN
    [String]$Server = $global:RubrikConnection.server,
    # API version
    [String]$api = $global:RubrikConnection.api
  )

  Begin {

    Test-RubrikConnection
    Test-VMwareConnection
  
  }

  Process {

    if (!$Cleanup)
    {
      if (!$Date) 
      {
        Write-Verbose -Message 'No date entered. Taking current time.'
        $Date = Get-Date
      }

      $HostID = (Get-RubrikVM -VM $TargetVM).hostId

      Write-Verbose -Message "Creating a powered off Live Mount of $SourceVM"
      $mount = Get-RubrikVM $SourceVM | Get-RubrikSnapshot -Date $Date | New-RubrikMount -HostID $HostID
    
      Write-Verbose -Message "Waiting for request $($mount.id) to complete"
      while ((Get-RubrikRequest -ID $mount.id -Type "vmware/vm").status -ne 'SUCCEEDED')
      {
        Start-Sleep -Seconds 1
      }
    
      Write-Verbose -Message 'Live Mount is now available'
      Write-Verbose -Message 'Gathering Live Mount ID value'
      foreach ($link in ((Get-RubrikRequest -ID $mount.id -Type "vmware/vm").links))
      {
        # There are two links - the request (self) and result
        # This will filter the values to just the result
        if ($link.rel -eq 'result')
        {
          # We just want the very last part of the link, which contains the ID value
          $MountID = $link.href.Split('/')[-1]
        }
      }

      Write-Verbose -Message 'Gathering details on the Live Mount'
      $MountVM = Get-RubrikVM -id (Get-RubrikMount -id $MountID).mountedVmId

      Write-Verbose -Message 'Gathering details on the Target VM'
      $TargetHost = Get-VMHost -VM $TargetVM

      Write-Verbose -Message 'Migrating the Mount VMDKs to VM'
      if ($PSCmdlet.ShouldProcess($TargetVM,'Migrating Live Mount VMDK(s)'))
      {
        [array]$MountVMdisk = Get-HardDisk $MountVM.name
        $MountedVMdiskFileNames = @()
        [int]$j = 0
        foreach ($_ in $MountVMdisk)
        {
          if ($ExcludeDisk -contains $j)
          {
            Write-Verbose -Message "Skipping Disk $j" -Verbose
          }
          else 
          {
            try
            {
              $null = Remove-HardDisk -HardDisk $_ -DeletePermanently:$false -Confirm:$false
              $null = New-HardDisk -VM $TargetVM -DiskPath $_.Filename
              $MountedVMdiskFileNames += $_.Filename
              Write-Verbose -Message "Migrated $($_.Filename) to $TargetVM"
            }
            catch
            {
              throw $_
            }
          }
          $j++
        }
      }

      $Diskfile = "$Home\Documents\"+$SourceVM+'_to_'+$TargetVM+'-'+(Get-Date).Ticks+'.txt'
      $TargetVM | Out-File -FilePath $Diskfile -Encoding utf8 -Force
      $MountID | Out-File -FilePath $Diskfile -Encoding utf8 -Append -Force      
      $MountedVMdiskFileNames | Out-File -FilePath $Diskfile -Encoding utf8 -Append -Force

      # Return information needed to cleanup the mounted disks and Live Mount
      $response = @{}
      $response.Add('Status','Success')
      $response.add('CleanupFile',$Diskfile)
      $response.Add('Example',"Move-RubrikMountVMDK -Cleanup `'$Diskfile`'")
      return $response
    }

    elseif ($Cleanup) 
    {
      if ((Test-Path $Cleanup) -ne $true) 
      {
        throw 'File does not exist'
      }
      $TargetVM = (Get-Content -Path $Cleanup -Encoding UTF8)[0]
      $MountID = (Get-Content -Path $Cleanup -Encoding UTF8)[1]      
      $MountedVMdiskFileNames = (Get-Content -Path $Cleanup -Encoding UTF8) | Select-Object -Skip 2
      Write-Verbose -Message 'Removing disks from the VM'
      [array]$SourceVMdisk = Get-HardDisk $TargetVM
      foreach ($_ in $SourceVMdisk)
      {
        if ($MountedVMdiskFileNames -contains $_.Filename)
        {
          Write-Verbose -Message "Removing $_ from $TargetVM"
          Remove-HardDisk -HardDisk $_ -DeletePermanently:$false -Confirm:$false
        }
      }
        
      Write-Verbose -Message "Deleting the Live Mount named $($MountVM.name)"
      Remove-RubrikMount -id $MountID -Confirm:$false
    }

  } # End of process
} # End of function