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 file location.
 19
 20	.Parameter CSV_FileName
 21	Supply your own CSV file location.
 22
 23	.Parameter Log_Dir
 24	Supply your own LogFile location.
 25
 26	.Parameter Log_FileName
 27	Supply your own LogFile location. 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_File 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$CSV_Dir = "C:\Scripts\Storage vMotion\Sources"
 66$CSV_FileName = "vmdatastore.csv"
 67$CSV_File = "$CSV_Dir\$CSV_FileName"
 68
 69# Logging
 70$Log_Dir="C:\Scripts\Storage vMotion\Logs"
 71$Log_FileName="svMotion_log"
 72$Log_Breaker="##########################"
 73$global:File=""
 74
 75# Create the necessary directories if necessary
 76If(!(Test-Path -Path $Log_Dir )){
 77	New-Item -ItemType directory -Path $Log_Dir
 78}
 79
 80Function Initialize {
 81	# Logging parameters
 82	$Date = (get-date).tostring("yyyyMMdd_HHmmss")
 83	$global:File = $LogDir + "\" + $Log_FileName + "_" + $Date + ".log"
 84	If ($Debug) {Write-Host "The filename is: $global:File"}
 85
 86	# Initialize log
 87	$Log_Breaker | Out-File "$global:File" -Append
 88	" LogFile:  $global:File" | Out-File "$global:File" -Append
 89	" LogDate:  $Date" | Out-File "$global:File" -Append
 90	" CSV File: $CSV_File" | Out-File "$global:File" -Append
 91	$Log_Breaker | Out-File "$global:File" -Append
 92	Add-Content -Path $global:File -Value "`n"
 93}
 94
 95# Import CSV
 96$CSV = Import-csv "$CSV_File"
 97
 98Initialize
 99
100$Date = (get-date).ToString("yyyy-MM-dd HH:mm:ss.fff")
101
102# Do for each entry in CSV file
103Foreach ($VM in $CSV) {
104	Try {
105		# Does the virtual machine exist
106		Get-VM $VM.Name -ErrorAction Stop | Out-Null
107		
108		# What is the current Datastore of the virtual machine
109		$CurrentDS=(get-vm $VM.Name | Get-Datastore).Name
110		If ($CurrentDS -ne $VM.Datastore) {
111			# Log & Debug
112			$Message = "Starting for VM " + $VM.Name + " to datastore " + $VM.Datastore
113			If ($Debug) {Write-Host "$Message" -Foregroundcolor darkyellow}
114			$Message | Out-File "$global:File" -Append
115
116			If ($DryRun -eq $false) {
117				# Determing amount of vMotions currently executing
118				$num_tasks = (get-task |where {$_.name -eq "RelocateVM_task"} | where {$_.state -eq "running"})
119				Do {
120					$num_tasks = (get-task |where {$_.name -eq "RelocateVM_task"} | where {$_.state -eq "running"})
121				} While ($num_tasks.Count -eq $Num_vMotions)
122				Get-VM $VM.Name | Move-VM -Datastore $VM.DS -RunAsync
123			}
124		}
125		# virtual machine was found on the correct datastore
126		$Message = $VM.Name + " is already on datastore " + $VM.Datastore
127		If ($Debug) {Write-Host "$Message" -Foregroundcolor darkgreen}
128		$Message | Out-File "$global:File" -Append
129	}
130	Catch {
131		$Message = $VM.Name + " was not found."
132		If ($Debug) {Write-Host "$Message" -Foregroundcolor red}
133		$Message | Out-File "$global:File" -Append
134	}
135}