﻿# encoding: utf-8
# api: powershell
# title: ESXiMgmt module
# description: How can you automate your ESXi tasks having only bare ESXi software? May cmdlets in such case don’t work or work with serious limitations. To fulfill though partially the lack of ‘bare ESXi’ management tools, the ESXiMgmt module has been written and tested.
# version: 4.1
# type: module
# author: Alexander Petrovskiy                                                                              
# license: CC0
# x-poshcode-id: 2907
# x-archived: 2011-09-15T09:27:30
# x-published: 2011-08-10T03:01:00
#
# The ESXiMgmt module uses the well-known plink.exe utility to access tht server via SSH and works almost perfectly with exception of the fact that often the cmd.exe window appears for several seconds.
# The module is and will be described in the SoftwareTestingUsingPowerShell.WordPress.Com blog (please use the tag ‘ESXiMgmt module’ during the search).
#
#######################################################################################################################
# File:             ESXiMgmt.psm1                                                                                     #
# Author:           Alexander Petrovskiy                                                                              #
# Publisher:        Alexander Petrovskiy, SoftwareTestingUsingPowerShell.WordPress.Com                                #
# Copyright:        © 2011 Alexander Petrovskiy, SoftwareTestingUsingPowerShell.WordPress.Com. All rights reserved.   #
# Prerequisites:    The module was tested with Vmware ESXi 4.1 U1 on the server side and                              #
#                       Vmware PowerCLI 4.1 U1                                                                        #
#                       plink.exe 0.60.0.0                                                                            #
# Usage:            To load this module run the following instruction:                                                #
#                       Import-Module -Name ESXiMgmt -Force                                                           #
#                   Please provide feedback in the SoftwareTestingUsingPowerShell.WordPress.Com blog.                 #
#######################################################################################################################
Set-StrictMode -Version Latest

#region initialization
# Initailize PowerCLI environment
if ((Get-PSSnapin VMware.VimAutomation.Core) -ne $null -and `
	(Get-PSSnapin VMware.VimAutomation.Core).Name.Length -gt 0)
{
	Remove-PSSnapin VMware.VimAutomation.Core;
}
Add-PSSnapin VMware.VimAutomation.Core;
# x86
if ($Env:PROCESSOR_ARCHITECTURE -eq 'x86'){
	. "$env:ProgramFiles\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1"
}
# x64
if ($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){
	. "${env:ProgramFiles(x86)}\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1"
}

# It is recommended to put plink.exe in the directory this module resides
# plink.exe can be taken from this link
# http://the.earth.li/~sgtatham/putty/latest/x86/plink.exe
if (Test-Path -Path ($PSScriptRoot + '\plink.exe')){
	[string]$script:plinkPath = $PSScriptRoot + '\plink.exe';
}
#endregion initialization

#region private functions
	#region function Get-CurrentTime
function Get-CurrentTime
	<#
		.SYNOPSIS
			The Get-CurrentTime function is used to write in the timestamp in the log file.

		.DESCRIPTION
			The Get-CurrentTime functions is used for getting the current time of operation. 
			As s time source used [System.DateTime]::Now.TimeOfDay property.

		.EXAMPLE
			PS C:\> Get-CurrentTime

		.OUTPUTS
			System.String
	#>
{	$timeOfDay = [System.DateTime]::Now.TimeOfDay;
	$time = "$($timeOfDay.Hours):$($timeOfDay.Minutes):$($timeOfDay.Seconds)`t";
	return $time;}
	#endregion function Get-CurrentTime
#endregion private functions
#region public functions
	#region function Connect-ESXi
function Connect-ESXi
	<#
		.SYNOPSIS
			Connects to a single ESXi you are planning to work with.

		.DESCRIPTION
			The Connect-ESXi function is intended to be the first the user runs 
			while working with this module.

		.PARAMETER  Server
			The name of IP address of the server you work with. 
			It's also used further as an user@server combination.

		.PARAMETER  Port
			Default value is 443. See the Connect-VIServer description.

		.PARAMETER  Protocol
			Default value is HTTPS. See the Connect-VIServer description.
			
		.PARAMETER  User
			Username that is often root. It's also used further as an user@server combination.
			
		.PARAMETER  Password
			Password to connect to a ESXi. It's also used as a parameter for plink.
			
		.PARAMETER  DatastoreName
			The name of the datastore you work with.
			
		.PARAMETER  Drive
			The name that will represents the content of the datastore.
			
		.EXAMPLE
			PS C:\> Connect-ESXi -Server 192.168.1.1 -Port 443
					-Protocol HTTPS -User root -Password 123
					-DatastoreName datastore1 
					-Drive server1

		.INPUTS
			System.String, System.Int32

		.OUTPUTS
			None

	#>
{
	[CmdletBinding()]
	param(
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Server,
		  [Parameter(Mandatory=$false)]
		  [int]$Port = 443,
		  [Parameter(Mandatory=$false)]
		  [string]$Protocol = 'HTTPS',
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$User,
		  [AllowEmptyString()]
		  [string]$Password = '',
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$DatastoreName,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Drive
		  )
	[string]$script:pwd = $Password;
		  
	[VMware.VimAutomation.ViCore.Impl.V1.VIServerImpl]$script:esxiserver = `
		Connect-VIServer -Server $Server `
			-User $User -Password $Password `
			-Protocol $Protocol -Port $Port;
	[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]$script:vmhost = `
		Get-VMHost -Server $script:esxiserver;

	[VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl]$script:datastore = `
		Get-Datastore -Server $script:esxiserver -Name $DatastoreName;
	try{New-DatastoreDrive -Datastore $script:datastore -Name $Drive;
	[string]$script:dsdrive = $Drive;}catch{}
}
	#endregion function Connect-ESXi
	#region function Disconnect-ESXi
function Disconnect-ESXi
	<#
		.SYNOPSIS
			Disconnects the latest connected ESXi server.

		.DESCRIPTION
			The function cleans up the variable that stores VIServer object of the server you connected.

		.EXAMPLE
			PS C:\> Disconnect-ESXi

		.INPUTS
			None

		.OUTPUTS
			None

	#>
{
	Disconnect-VIServer -Server $script:esxiserver -Force:$true;
}
	#endregion function Disconnect-ESXi
	#region function Invoke-ESXiCommand
function Invoke-ESXiCommand
	<#
		.SYNOPSIS
			Runs a command or a semicolon-separated sequence of commands on an ESXi server.

		.DESCRIPTION
			This function runs plink.exe with the command supplied and optionally 
			suppressses the console window.
			
		.PARAMETER  Server
			The name of IP address of the server you work with. 
			It's also used further as an user@server combination.
			
		.PARAMETER  User
			Username that is often root. It's also used further as an user@server combination.
			
		.PARAMETER  Password
			Password to connect to a ESXi. It's also used as a parameter for plink.

		.PARAMETER  Command
			The string that plink.exe runs on a server.

		.PARAMETER  PathToPlink
			Used if for some reason you don't want to put plink.exe in the module folder.

		.PARAMETER  ShowWindow
			Enables or disables appearance of the plink.exe console window.
			
		.PARAMETER  OperationTimeout
			The time that the module sleeps before start next operation.

		.EXAMPLE
			PS C:\> Invoke-ESXiCommand -Server 192.168.1.1 `
					 -User root -Password 123 `
					 -Command 'ls ~; sleep 10s; exit;' `
					 -PathToPlink 'C:\plink.exe' `
					 -ShowWindow $true -OperationTimeout 10;

		.INPUTS
			System.String, System.Int32, System.Boolean

		.OUTPUTS
			None

	#>
{
	[CmdletBinding()]
	param(
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Server,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$User,
		  [AllowEmptyString()]
		  [string]$Password = '',
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Command,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$PathToPlink,
		  [Parameter(Mandatory=$false)]
		  [bool]$ShowWindow = $true,
		  [Parameter(Mandatory=$false)]
		  [int]$OperationTimeout = 2
		 )
	try{
		[string[]]$private:connectArgs = `
			@(
			  "-ssh $($User)@$($Server) -pw $($Password) $($Command) "
			  );
		if ($ShowWindow){
			Start-Process -FilePath $PathToPlink `
				-ArgumentList $private:connectArgs;
		}
		else{
			Start-Process -FilePath $PathToPlink `
				-ArgumentList $private:connectArgs -NoNewWindow;
		}
		sleep -Seconds $OperationTimeout;
	}catch{};
}
	#endregion function Invoke-ESXiCommand
	#region function New-ESXiFSDirectory
function New-ESXiFSDirectory
	<#
		.SYNOPSIS
			Creates a directory on the datastore file system.

		.DESCRIPTION
			This function creates a directory and is a cane.

		.PARAMETER  Server
			The name of IP address of the server you work with. 
			It's also used further as an user@server combination.
			
		.PARAMETER  User
			Username that is often root. It's also used further as an user@server combination.
			
		.PARAMETER  Password
			Password to connect to a ESXi. It's also used as a parameter for plink.
			
		.PARAMETER  Path
			The absolute path to the folder which a new folder will be created within.

		.PARAMETER  Name
			The name for a new folder.

		.PARAMETER  PathToPlink
			Used if for some reason you don't want to put plink.exe in the module folder.

		.PARAMETER  OperationTimeout
			The time that the module sleeps before start next operation.

		.EXAMPLE
			PS C:\> New-ESXiFSDirectory -Server 192.168.1.1 `
					-User root -Password 123 `
					-Path "/vmfs/volumes/datastore3" -Name foldername `
					-PathToPlink C:\plink.exe -OperationTimeout 20;

		.INPUTS
			System.String, System.Int32

		.OUTPUTS
			None

	#>
{
	[CmdletBinding()]
	param(
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Server,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$User,
		  [AllowEmptyString()]
		  [string]$Password = '',
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Path,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Name,
		  [Parameter(Mandatory=$false)]
		  [AllowEmptyString()]
		  [string]$PathToPlink,
		  [int]$OperationTimeout = 2
		 )
	if ($PathToPlink.Length -lt 1){$PathToPlink = $script:plinkPath;}
	[string]$private:commandToRun = "`"cd $($Path);mkdir $($Name);exit;`"";
	Invoke-ESXiCommand -Server $Server -User $User -Password $Password `
		-Command $private:commandToRun -PathToPlink $PathToPlink `
		-ShowWindow $false -OperationTimeout $OperationTimeout;
}
	#endregion function New-ESXiFSDirectory
	#region function Register-ESXiVM
function Register-ESXiVM
	<#
		.SYNOPSIS
			Register an VMX file.

		.DESCRIPTION
			this function register a new virtual machine specified as a full path to its .vmx file.

		.PARAMETER  Server
			The name of IP address of the server you work with. 
			It's also used further as an user@server combination.
			
		.PARAMETER  User
			Username that is often root. It's also used further as an user@server combination.
			
		.PARAMETER  Password
			Password to connect to a ESXi. It's also used as a parameter for plink.

		.PARAMETER  Path
			The full path to a VMX file.

		.PARAMETER  PathToPlink
			Used if for some reason you don't want to put plink.exe in the module folder.
			
		.PARAMETER  OperationTimeout
			The time that the module sleeps before start next operation.

		.EXAMPLE
			PS C:\> Register-ESXiVM -Server 192.168.1.1 `
						-User root -Password 123 `
						-Path "/vmfs/volumes/datastore3/vmname/vmname.vmx" `
						-PathToPlink C:\plink.exe;

		.INPUTS
			System.String, System.Int32

		.OUTPUTS
			System.String

	#>
{
	[CmdletBinding()]
	param(
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Server,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$User,
		  [AllowEmptyString()]
		  [string]$Password = '',
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Path,
		  [Parameter(Mandatory=$false)]
		  [AllowEmptyString()]
		  [string]$PathToPlink,
		  [int]$OperationTimeout = 20
		 )
	if ($PathToPlink.Length -lt 1){$PathToPlink = $script:plinkPath;}
	[string]$private:commandToRun = "`"/bin/vim-cmd solo/registervm $($Path);exit;`"";
	Invoke-ESXiCommand -Server $Server -User $User -Password $Password `
		-Command $private:commandToRun -PathToPlink $PathToPlink `
		-ShowWindow $false -OperationTimeout $OperationTimeout;
}
	#endregion function Register-ESXiVM
	#region function Start-ESXiVM
function Start-ESXiVM
	<#
		.SYNOPSIS
			Starts a powered off orsuspended virtual machine.

		.DESCRIPTION
			This function is intended to start a new virtual machine as well as an existing one.
			In case the machine is generated from an image, it also answer the question whether
			the machine was copied or moved.

		.PARAMETER  Server
			The name of IP address of the server you work with. 
			It's also used further as an user@server combination.
			
		.PARAMETER  User
			Username that is often root. It's also used further as an user@server combination.
			
		.PARAMETER  Password
			Password to connect to a ESXi. It's also used as a parameter for plink.

		.PARAMETER  Id
			The Id of a virtual machine that is generated by the server that hosts it.

		.PARAMETER  PathToPlink
			Used if for some reason you don't want to put plink.exe in the module folder.
			
		.PARAMETER  OperationTimeout
			The time that the module sleeps before start next operation.

		.EXAMPLE
			PS C:\> Start-ESXiVM -Server 192.168.1.1 `
						-User root -Password 123 `
						-Id 504 `
						-PathToPlink C:\plink.exe;

		.INPUTS
			System.String, System.Int32

		.OUTPUTS
			None

	#>
{
	[CmdletBinding()]
	param(
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Server,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$User,
		  [AllowEmptyString()]
		  [string]$Password = '',
		  [Parameter(Mandatory=$true)]
		  [int]$Id,
		  [Parameter(Mandatory=$false)]
		  [AllowEmptyString()]
		  [string]$PathToPlink,
		  [int]$OperationTimeout = 10
		 )
	if ($PathToPlink.Length -lt 1){$PathToPlink = $script:plinkPath;}
	[string]$private:commandToRun = "`"/bin/vim-cmd vmsvc/power.on $($Id.ToString());exit;`"";
	Invoke-ESXiCommand -Server $Server -User $User -Password $Password `
		-Command $private:commandToRun -PathToPlink $PathToPlink `
		-ShowWindow $true -OperationTimeout $OperationTimeout;
	try{Get-VM -Id $Id;
		if ((Get-VM -Id $Id).PowerState -eq `
		[VMware.VimAutomation.ViCore.Types.V1.Inventory.PowerState]::PoweredOff){
		[string]$private:commandToRun = "`"/bin/vim-cmd vmsvc/message $($Id.ToString()) 0 2;exit;`"";
		Invoke-ESXiCommand -Server $Server -User $User -Password $Password `
			-Command $private:commandToRun -PathToPlink $PathToPlink `
			-ShowWindow $true -OperationTimeout 2;}
	}catch{
		[string]$private:commandToRun = "`"/bin/vim-cmd vmsvc/message $($Id.ToString()) 0 2;exit;`"";
		Invoke-ESXiCommand -Server $Server -User $User -Password $Password `
			-Command $private:commandToRun -PathToPlink $PathToPlink `
			-ShowWindow $true -OperationTimeout 2;
	}
}
	#endregion function Start-ESXiVM
	#region function Stop-ESXiVM
function Stop-ESXiVM
	<#
		.SYNOPSIS
			Powers off (or what is set for this option on your server) a virtual machine.

		.DESCRIPTION
			This funciton simply 'presses' the red button.

		.PARAMETER  Server
			The name of IP address of the server you work with. 
			It's also used further as an user@server combination.
			
		.PARAMETER  User
			Username that is often root. It's also used further as an user@server combination.
			
		.PARAMETER  Password
			Password to connect to a ESXi. It's also used as a parameter for plink.

		.PARAMETER  Id
			The Id of a virtual machine that is generated by the server that hosts it.

		.PARAMETER  PathToPlink
			Used if for some reason you don't want to put plink.exe in the module folder.
			
		.PARAMETER  OperationTimeout
			The time that the module sleeps before start next operation.

		.EXAMPLE
			PS C:\> Stop-ESXiVM -Server 192.168.1.1 `
						-User root -Password 123 `
						-Id (Get-ESXiVMId $vm);

		.INPUTS
			System.String, System.Int32

		.OUTPUTS
			None

	#>
{
	[CmdletBinding()]
	param(
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Server,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$User,
		  [AllowEmptyString()]
		  [string]$Password = '',
		  [Parameter(Mandatory=$true)]
		  [int]$Id,
		  [Parameter(Mandatory=$false)]
		  [AllowEmptyString()]
		  [string]$PathToPlink,
		  [int]$OperationTimeout = 60
		 )
	if ($PathToPlink.Length -lt 1){$PathToPlink = $script:plinkPath;}
	[string]$private:commandToRun = "`"/bin/vim-cmd vmsvc/power.off $($Id.ToString());exit;`"";
	Invoke-ESXiCommand -Server $Server -User $User -Password $Password `
		-Command $private:commandToRun -PathToPlink $PathToPlink `
		-ShowWindow $true -OperationTimeout $OperationTimeout;
}
	#endregion function Stop-ESXiVM
	#region function Suspend-ESXiVM
function Suspend-ESXiVM
	<#
		.SYNOPSIS
			Puts a virtual machine into a suspended state.

		.DESCRIPTION
			This function 'presses' the yellow button.

		.PARAMETER  Server
			The name of IP address of the server you work with. 
			It's also used further as an user@server combination.
			
		.PARAMETER  User
			Username that is often root. It's also used further as an user@server combination.
			
		.PARAMETER  Password
			Password to connect to a ESXi. It's also used as a parameter for plink.

		.PARAMETER  Id
			The Id of a virtual machine that is generated by the server that hosts it.

		.PARAMETER  PathToPlink
			Used if for some reason you don't want to put plink.exe in the module folder.
			
		.PARAMETER  OperationTimeout
			The time that the module sleeps before start next operation.

		.EXAMPLE
			PS C:\> Suspend-ESXiVM -Server 192.168.1.1 `
						-User root -Password 123 `
						-Id $private:vmId `
						-PathToPlink C:\plink.exe;

		.INPUTS
			System.String, System.Int32

		.OUTPUTS
			None

	#>
{
	[CmdletBinding()]
	param(
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$Server,
		  [Parameter(Mandatory=$true)]
		  [ValidateNotNullOrEmpty()]
		  [string]$User,
		  [AllowEmptyString()]
		  [string]$Password = '',
		  [Parameter(Mandatory=$true)]
		  [int]$Id,
		  [Parameter(Mandatory=$false)]
		  [AllowEmptyString()]
		  [string]$PathToPlink,
		  [int]$OperationTimeout = 60
		 )
	if ($PathToPlink.Length -lt 1){$PathToPlink = $script:plinkPath;}
	[string]$private:commandToRun = "`"/bin/vim-cmd vmsvc/power.suspend $($Id.ToString());exit;`"";
	Invoke-ESXiCommand -Server $Server -User $User -Password $Password `
		-Command $private:commandToRun -PathToPlink $PathToPlink `
		-ShowWindow $true -OperationTimeout $OperationTimeout;
}
	#endregion function Suspend-ESXiVM
	#region function Get-ESXiVMId
function Get-ESXiVMId
	<#
		.SYNOPSIS
			Returns the Id of a virtual machine.

		.DESCRIPTION
			This function receives a virtual machine object and extract the Id property.

		.PARAMETER  VM
			The VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl object.

		.EXAMPLE
			PS C:\> [int]$private:vmId = `
						Get-ESXiVMId -VM (Get-VM -Name $VMName);

		.INPUTS
			VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl

		.OUTPUTS
			System.Int32

	#>
(
	[CmdletBinding()]
	[OutputType([System.Int32])]
	[Parameter(Mandatory=$true)]
	[ValidateNotNullOrEmpty()]
	[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$VM
)
{
	#[int]$private:vmId = [System.Text.RegularExpressions.Regex]::Match( `
	#		(Get-VM -Name $private:currentVMName).Id.ToString(), "(?<=[-]).*").Value
	[int]$private:vmId = [System.Text.RegularExpressions.Regex]::Match( `
			$VM.Id.ToString(), "(?<=[-]).*").Value;
	return $private:vmId;
}
	#endregion function Get-ESXiVMId
	#region function Get-ESXiVMName
function Get-ESXiVMName
	<#
		.SYNOPSIS
			Returns the name of a virtual machine that contains a guest with name given.

		.DESCRIPTION
			This function enumerates all the virtual machines hosted on a server 
			in order to get the name that has the corresponding virtual machine.

		.PARAMETER  VMHostname
			Ths name(s) of the guest host(s).

		.EXAMPLE
			PS C:\> $vmname = Get-ESXiVMName -VMHostname 'B45E19A64B5E418'

		.INPUTS
			System.String[]

		.OUTPUTS
			System.String

	#>
(
	[CmdletBinding()]
	[OutputType([System.String])]
	[Parameter(Mandatory=$true)]
	[ValidateNotNullOrEmpty()]
	[string[]]$VMHostname
)
{
	[System.Collections.ArrayList]$vmnames = `
		New-Object System.Collections.ArrayList;
	
	[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl[]]$vmss = `
		Get-VM *;

	for ([int]$private:i = 0; $private:i -lt $VMHostname.Length; $private:i++)
	{
		[bool]$addEmptyName = $true;
		foreach($vm in $vmss)
		{
			if ($vm.Name -ne $null -and `
				$vm.Guest -ne $null -and `
				$vm.Guest.HostName -ne $null -and `
				$vm.Guest.HostName.Length -gt 0){
				if ($vm.Guest.HostName.toUpper() -eq `
					$VMHostname[$private:i].toUpper() -or `
					$vm.Guest.HostName.toUpper().Contains($VMHostname[$private:i].toUpper()))
				{
					$null = $vmnames.Add($vm.Name);
					$addEmptyName = $false;
					break;
				}
			}
		}
#		}
		if ($addEmptyName){$vmnames.Add("");}
	}
	return $vmnames;
}
	#endregion function Get-ESXiVMName
	#region function New-ESXiVMs
function New-ESXiVMs
	<#
		.SYNOPSIS
			Generates a bunch of virtual machines.

		.DESCRIPTION
			This function generates new virtual machine by copying the existing virtual machine files,
			replacing the name of the original machine with the name that's provided and
			copying the result to the server the module connected..
			
		.PARAMETER  TemplateVMName
			The name of the virtual machine used as a template.
			
		.PARAMETER  Count
			The number of virtual machines you need.
			
		.PARAMETER  Logname
			The full path to the log file.
			
		.PARAMETER  NewVMName
			The base name of new virtual machines. Virtual machines will be named in the follwing order:
			newvm_1
			newvm_2
			...
			newvm_20.

		.PARAMETER  BasePath
			The path in the local file system where the template resides. the hardcoded values are
			basepath\hdd - the folder where the virtual disk(s) 
			<templatevm>-flat.vmdk,
			<templatevm>-<number>delta.vmdk, 
			<templatevm>-Snapshot<number>.vmsn (snapshot of a machine that was turned on)
			are stored. In short, the files of megabyte of gigabyte size.
			basepath\template - the folder where the rest of files 
			(namely, 
			<templatevm>.vmx, 
			<templatevm>.vmxf, 
			<templatevm>.vmsd, 
			<templatevm>.vmdk (the header),
			<templatevm>-<number>.vmdk,
			<templatevm>-Snapshot<number>.vmsn (snapshot of a machine that was turned off)
			) reside, in other words, the files the size of which
			are measured in kilobytes

		.PARAMETER  PathToPlink
			Used if for some reason you don't want to put plink.exe in the module folder.
			
		.PARAMETER  OperationTimeout
			The time that the module sleeps before start next operation.
			
		.EXAMPLE
			PS C:\> New-ESXiVMs -TemplateVMName 'template XP SP2 Sv' -Count 20 `
						-Logname "C:\VMTests\20VMs.txt" -NewVMName 'XPSP2a' `
						-BasePath 'C:\VMTests\xpsp2';

		.INPUTS
			System.String, System.Int32

		.OUTPUTS
			None

	#>
(
	[CmdletBinding()]
	[Parameter(Mandatory=$true)]
	[ValidateNotNullOrEmpty()]
	[string]$TemplateVMName,
	[Parameter(Mandatory=$true)]
	[int]$Count = 1,
	[Parameter(Mandatory=$true)]
	[ValidateNotNullOrEmpty()]
	[string]$Logname,
	[Parameter(Mandatory=$true)]
	[string]$NewVMName = 'newVM_',
	[Parameter(Mandatory=$true)]
	[ValidateNotNullOrEmpty()]
	[string]$BasePath,
	[Parameter(Mandatory=$false)]
	[AllowEmptyString()]
	[string]$PathToPlink,
	[int]$OperationTimeout = 600
)
{
	if ($PathToPlink.Length -lt 1){$PathToPlink = $script:plinkPath;}
	# the need in storaging a template VM on a host is deprecated
	# if we used traditional PowerCLI cmdlets, it would be easy to read properties from a template
	# or use a template and put the data to a new machine or even create one from template
	#[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$script:templateVM = `
	#	Get-VM -Server $script:esxiserver -Name $TemplateVMName;
	#if ($script:templateVM -eq $null)
	#{
	#	Write-Error "Couldn`'t get the '$($TemplateVMName)' virtual machine on the $($script:esxiserver) host.";
	#	return;
	#}

	for ($private:i = 0; $private:i -lt $Count; $private:i++)
	{
		[string]$private:currentVMName = $NewVMName + ($private:i + 1).ToString();
		"$(Get-CurrentTime)Start creating the virtual machine '$($private:currentVMName)'" >> $Logname; 
	#region	#Unsupported for the ESXi host:
	#	$private:newvm = New-VM -VM $script:templateVM `
	#		-Name $private:currentVMName `
	#		-Datastore $script:datastore `
	#		-VMHost $script:vmhost;
	#endregion	#Unsupported for the ESXi host:
	#region	#Unsupported for the ESXi host:
	#	$private:newvm = New-VM `
	#		-Name $private:currentVMName `
	#		-Datastore $script:datastore `
	#		-VMHost $script:vmhost; # `
	#		#-MemoryMB $script:templateVM.MemoryMB `
	#		#-NumCpu $script:templateVM.NumCpu
	#endregion	#Unsupported for the ESXi host:
		New-Item -Path "$($BasePath)\$($private:currentVMName)" -type directory -Force;
		[string]$templateStorage = $BasePath + "\template";
		[string]$hddStorage = $BasePath + "\hdd";
		Get-ChildItem -LiteralPath $templateStorage | `
		%{[string]$currentFile = $_.FullName; 
			[string]$newFile = `
				$currentFile.ToLower().Replace("$($templateStorage)\$($TemplateVMname)".ToLower(), `
				"$($BasePath)\$($private:currentVMName)\$($private:currentVMName)");
		"Changing $($currentFile) to $($newFile)" >> $Logname;
		Copy-Item -Path $currentFile -Destination $newFile;
			(Get-Content $newFile) | %{ $_ -replace $TemplateVMname, $private:currentVMName; } | `
				Set-Content -Path $newFile;}
		New-ESXiFSDirectory -Server $script:esxiserver.Name -User $script:esxiserver.User -Password $script:pwd `
			-Path "/vmfs/volumes/$($script:datastore.Name)" -Name $private:currentVMName `
			-PathToPlink $PathToPlink -OperationTimeout 20;
		"$(Get-CurrentTime)Copying the config files '$($private:currentVMName)'" >> $Logname;
		Copy-DatastoreItem -Item "$($BasePath)\$($private:currentVMName)\$($private:currentVMName)*.*" `
			-Destination "$($script:dsdrive):\$($private:currentVMName)\";
		"$(Get-CurrentTime)Copying the virtual drive image(s) '$($private:currentVMName)'" >> $Logname;
		Get-ChildItem -Path "$($hddStorage)" | 
			%{[string]$private:newFileName = $_.Name.Replace($TemplateVMname, $private:currentVMName);
			Copy-DatastoreItem -Item $_.FullName `
				-Destination "$($script:dsdrive):\$($private:currentVMName)\$($private:newFileName)";}
	#region	#Unsupported for the ESXi host:
	#	$private:newvm = `
	#		New-VM -VMHost $script:vmhost `
	#		-VMFilePath "$($script:dsdrive):\$($private:currentVMName)\$($private:currentVMName).vmx";
	#endregion	#Unsupported for the ESXi host:
	#region	#Unsupported for the ESXi host:
	#	Copy-HardDisk -HardDisk $vm.HardDisks[0] `
	#		-DestinationPath "[$($DatastoreName)] $($private:currentVMName)/"
	#	Copy-DatastoreItem -Item "$($script:dsdrive):\templXPSP3\templXPSP3.vmdk" -Destination "$($script:dsdrive):\3\1.vmdk"
	#endregion	#Unsupported for the ESXi host:
		
		"$(Get-CurrentTime)Registering the VM '$($private:currentVMName)'" >> $Logname;
		Register-ESXiVM -Server $script:esxiserver.Name -User $script:esxiserver.User -Password $script:pwd `
			-Path "/vmfs/volumes/$($script:datastore.Name)/$($private:currentVMName)/$($private:currentVMName).vmx" `
			-PathToPlink $PathToPlink;

		[int]$private:vmId = Get-ESXiVMId -VM (Get-VM -Name $private:currentVMName);
		
	#region	#Unsupported for the ESXi host:
	#	Start-VM -VM (Get-VM -Name $private:currentVMName) -RunAsync;
	#endregion	#Unsupported for the ESXi host:
		
		"$(Get-CurrentTime)Starting the VM '$($private:currentVMName)'" >> $Logname;
		Start-ESXiVM -Server $script:esxiserver.Name -User $script:esxiserver.User -Password $script:pwd `
			-Id $private:vmId `
			-PathToPlink $PathToPlink;
	#region	#Unsupported for the ESXi host:
	#	Set-VMQuestion -VMQuestion (Get-VM XPSP3_2 | Get-VMQuestion) -Option 'I copied it' -Confirm:$false
	#endregion	#Unsupported for the ESXi host:
		
		sleep -Seconds $OperationTimeout;
	#region	#Unsupported for the ESXi host:
	#	Get-VM $private:currentVMName | Suspend-VMGuest
	#endregion	#Unsupported for the ESXi host:
		Suspend-ESXiVM -Server $script:esxiserver.Name -User $script:esxiserver.User -Password $script:pwd `
			-Id $private:vmId `
			-PathToPlink $PathToPlink;
	}
}
	#endregion function New-ESXiVMs
#endregion public functions

# Tested functions
Export-ModuleMember -Function Connect-ESXi, Invoke-ESXiCommand, New-ESXiFSDirectory; 
Export-ModuleMember -Function Register-ESXiVM, Start-ESXiVM, Suspend-ESXiVM, New-ESXiVMs;
Export-ModuleMember -Function Get-ESXiVMId, Get-ESXiVMName, Disconnect-ESXi, Stop-ESXiVM;
