Storage vMotions based on CSV file

This script reads a CSV file and executes storage vmotions based on the input. The number of storage vmotions can be throttled and is by default set to maximum 6 concurrent vMotions. If the VM is already on the correct location it will not execute a storage vmotion for that vm.

This is still work in progress. The parameters to accept a different CSV file, log file location and number of storage vmotions other than the default have not been added yet.

VERSION INFO

This script has been built and tested on a VMware vSphere 5.5 environment.

**UPDATE : 2016-May-13**

I updated the script to accept all parameters as shown in the description and I also added a Dry Run parameter. The Dry Run parameter will do a dry run escaping the vMotion sequence, so all actions will be logged. Combining the Debug and DryRun parameter will show everything on the console without executing.

The default location for the CSV file is C:\Scripts\Storage vMotion\Sources. The script will log to the C:\Scripts\Storage vMotion\Logs location. If the directory doesn't exist, it will be created.

  1<#
  2  .Synopsis
  3  Moves VMs to the correct datastore based on a csv file.
  4
  5  .Description
  6  Moves VMs to the correct datastore based on a csv file.
  7
  8  .Author
  9  Harold Preyers
 10
 11  .Parameter DryRun
 12  The DryRun parameter will create the log file without vMotion-ing the virtual machines to the correct datastore.
 13
 14  .Parameter Debug
 15  The Debug parameter shows output to the screen. This will not prevent to start the vMotions.
 16
 17  .Parameter CSV_Dir
 18  Supply your own CSV directory location.
 19
 20  .Parameter CSV_FileName
 21  Supply your own CSV filename.
 22
 23  .Parameter Log_Dir
 24  Supply your own LogFile directory location.
 25
 26  .Parameter Log_FileName
 27  Supply your own LogFile filename. Don't add a file extension, the script will construct a filename based on the time of launch and add a .log extension
 28
 29  .Example
 30  # Start storage vmotions without debug output based on the default CSV (C:\Scripts\Storage vMotion\Sources) and log file (C:\Scripts\Storage vMotion\Logs) location
 31  # Name,Datastore
 32  # VM1,DS1
 33  # VM2,DS2
 34  ./svMotion_CSV.ps1
 35
 36  .Example
 37  # Start storage vmotions with debug output based on the default CSV (C:\Scripts\Storage vMotion\Sources) and log file (C:\Scripts\Storage vMotion\Logs) location
 38  # Name,Datastore
 39  # VM1,DS1
 40  # VM2,DS2
 41  ./svMotion_CSV.ps1 -Debug $true
 42
 43  .Example
 44  # Start storage vmotions with debug output based on custom CSV and log file location
 45  # The CSV file should have the following lay-out
 46  # Name,Datastore
 47  # VM1,DS1
 48  # VM2,DS2
 49  ./svMotion_CSV -Debug $true -Log_Dir "C:\Scripts\Storage vMotion\Logs" -Log_FileName svMotion_log -CSV_Dir "C:\Scripts\Storage vMotion\Sources" -CSV_FileName svMotion.csv
 50#>
 51
 52# Accept parameters
 53[cmdletBinding()]
 54  Param (
 55    [Bool]$Debug = $false,      # Write output to console
 56    [Bool]$DryRun = $false,     # Write logfile without vMotion
 57    [string]$CSV_Dir,           # CSV Directory location
 58    [string]$CSV_FileName,      # CSV Filename
 59    [string]$Log_Dir,           # Log Directory location
 60    [string]$Log_FileName       # Log Filename
 61  )
 62
 63# Variables
 64$Num_vMotions = 6
 65
 66# Set defaults if not provided
 67if (!$CSV_Dir) { $CSV_Dir = "C:\Scripts\Storage vMotion\Sources" }
 68if (!$CSV_FileName) { $CSV_FileName = "vmdatastore.csv" }
 69$CSV_File = "$CSV_Dir\$CSV_FileName"
 70if (!$Log_Dir) { $Log_Dir = "C:\Scripts\Storage vMotion\Logs" }
 71if (!$Log_FileName) { $Log_FileName = "svMotion_log" }
 72
 73$Log_Breaker = "##########################"
 74$global:File = ""
 75
 76# Create the necessary directories if necessary
 77If (!(Test-Path -Path $Log_Dir)) {
 78  New-Item -ItemType directory -Path $Log_Dir
 79}
 80
 81Function Initialize {
 82  # Logging parameters
 83  $Date = (get-date).tostring("yyyyMMdd_HHmmss")
 84  $global:File = $Log_Dir + "\" + $Log_FileName + "_" + $Date + ".log"
 85  If ($Debug) { Write-Host "The filename is: $global:File" }
 86
 87  # Initialize log
 88  $Log_Breaker | Out-File "$global:File" -Append
 89  " LogFile:  $global:File" | Out-File "$global:File" -Append
 90  " LogDate:  $Date" | Out-File "$global:File" -Append
 91  " CSV File: $CSV_File" | Out-File "$global:File" -Append
 92  $Log_Breaker | Out-File "$global:File" -Append
 93  Add-Content -Path $global:File -Value "`n"
 94}
 95
 96# Import CSV
 97$CSV = Import-csv "$CSV_File"
 98
 99Initialize
100
101$Date = (get-date).ToString("yyyy-MM-dd HH:mm:ss.fff")
102
103# Do for each entry in CSV file
104Foreach ($VM in $CSV) {
105  Try {
106    # Does the virtual machine exist
107    Get-VM $VM.Name -ErrorAction Stop | Out-Null
108
109    # What is the current Datastore of the virtual machine
110    $CurrentDS = (get-vm $VM.Name | Get-Datastore).Name
111    If ($CurrentDS -ne $VM.Datastore) {
112      # Log & Debug
113      $Message = "Starting for VM " + $VM.Name + " to datastore " + $VM.Datastore
114      If ($Debug) { Write-Host "$Message" -Foregroundcolor darkyellow }
115      $Message | Out-File "$global:File" -Append
116
117      If ($DryRun -eq $false) {
118        # Determining amount of vMotions currently executing
119        $num_tasks = (get-task | where {$_.name -eq "RelocateVM_task"} | where {$_.state -eq "running"})
120        While ($num_tasks.Count -ge $Num_vMotions) {
121          Start-Sleep -Seconds 5
122          $num_tasks = (get-task | where {$_.name -eq "RelocateVM_task"} | where {$_.state -eq "running"})
123        }
124        Get-VM $VM.Name | Move-VM -Datastore $VM.Datastore -RunAsync
125      }
126    }
127    # virtual machine was found on the correct datastore
128    $Message = $VM.Name + " is already on datastore " + $VM.Datastore
129    If ($Debug) { Write-Host "$Message" -Foregroundcolor darkgreen }
130    $Message | Out-File "$global:File" -Append
131  }
132  Catch {
133    $Message = $VM.Name + " was not found."
134    If ($Debug) { Write-Host "$Message" -Foregroundcolor red }
135    $Message | Out-File "$global:File" -Append
136  }
137}