param
(
[
Parameter
(
Mandatory
=
$true
,
Position
= 0)]
[String]
$ESXiHost
)
Write-Host
Loading PowerShell modules...
Import-Module
PoSH-SSH
Import-Module
VMware.PowerCLI
Push-Location
-Path
(
[System.IO.Path]
::GetDirectoryName(
$PSCommandPath
))
If
(
-not
(
Test-Path
-Path
'.\creds
' -PathType Container)) {
New-Item -Path '
.\creds
' -ItemType Directory | Out-Null
}
# Looks for credentials file for the VMware host. Passwords are stored encrypted
# and will only work for the user and machine on which they'
re stored.
$credsFile
= (
'.\creds\'
+
$ESXiHost
+
'.creds'
)
If
(
-not
(
Test-Path
-Path
$credsFile
)) {
$creds
=
Get-Credential
-Message
"Enter root password for VMware host $ESXiHost"
-User
root
$creds
.Password |
ConvertFrom-SecureString
|
Set-Content
$credsFile
}
$ESXICredential
=
New-Object
System.Management.Automation.PSCredential( `
"root"
, `
(
Get-Content
$credsFile
|
ConvertTo-SecureString
)
)
Set-PowerCLIConfiguration
-InvalidCertificateAction
Ignore
-Confirm
:
$false
|
Out-Null
Connect-VIServer
-Server
$ESXiHost
-Protocol
https
-Credential
$ESXICredential
-Force
|
Out-Null
If
(
-not
$?) {
Throw
"Connection to ESXi failed. If password issue, delete $credsFile and try again."
}
$guests
= (
Get-VM
-Server
$ESXiHost
|
Sort-Object
)
$padWidth
= (
[string]
(
$guests
.Count - 1)).Length
Write-Host
(
"Existing VMs ("
+
$guests
.Count +
"), sorted by name:"
)
for
(
$i
= 0;
$i
-lt
$guests
.count;
$i
++)
{
If
(
$guests
[
$i
].PowerState
-eq
"PoweredOn"
) {
Write-Host
-ForegroundColor
Red (
"["
+
"$i"
.PadLeft(
$padWidth
,
' '
) +
"](ON) : "
+
$guests
[
$i
].Name)
}
Else
{
Write-Host
(
"["
+
"$i"
.PadLeft(
$padWidth
,
' '
) +
"](off): "
+
$guests
[
$i
].Name)
}
}
Write-Host
$chosenVM
= 0
do
{
$inputValid
=
[int]
::TryParse((
Read-Host
'Enter the [number] of the VM to clone (the donor)'
),
[ref]
$chosenVM
)
if
(
$chosenVM
-lt
0
-or
$chosenVM
-ge
$guests
.Count) {
$inputValid
=
$false
}
if
(
-not
$inputValid
) {
Write-Host
(
"Must be a number in the range 0 to "
+ (
$guests
.Count - 1).ToString() +
". Try again."
)
}
}
while
(
-not
$inputValid
)
if
(
$guests
[
$chosenVM
].PowerState
-ne
"PoweredOff"
) {
Throw
"ERROR: VM must be powered off before cloning"
}
If
(
-not
(
$guests
[
$chosenVM
].ExtensionData.Config.Files.VmPathName
-match
'\[(.*)\] ([^\/]*)\/(.*)
')) {
Throw "ERROR: Could not calculate the datastore"
}
$VMdatastore = $Matches[1]
$VMdirectory = $Matches[2]
$VMXlocation = ("/vmfs/volumes/" + $VMdatastore + "/" + $VMdirectory + "/" + $Matches[3])
$VMdisks = $guests[$chosenVM] | Get-HardDisk
###############################
## File test (PoSH-SSH SFTP) ##
###############################
# Clear any open SFTP sessions
Get-SFTPSession | Remove-SFTPSession | Out-Null
# Start a new SFTP session
(New-SFTPSession -Computername $ESXiHost -Credential $ESXICredential -Acceptkey -Force -WarningAction SilentlyContinue) | Out-Null
# Test that we can locate the VMX file
If(-not (Test-SFTPPath -SessionId 0 -Path $VMXlocation)) {
Throw "ERROR: Cannot find donor VM'
s VMX file"
}
$validInput
=
$false
While
(
-not
$validInput
) {
$newVMname
=
Read-Host
"Enter the name of the new VM"
$newVMdirectory
= (
"/vmfs/volumes/"
+
$VMdatastore
+
"/"
+
$newVMname
)
If
(
Test-SFTPPath
-SessionId
0
-Path
$newVMdirectory
) {
$ynTest
=
$false
While
(
-not
$ynTest
) {
$yn
= (
Read-Host
"A directory already exists with that name. Continue? [Y/N]"
).ToUpper()
if
((
$yn
-ne
'Y'
)
-and
(
$yn
-ne
'N'
)) {
Write-Host
"ERROR: enter Y or N"
}
else
{
$ynTest
=
$true
}
}
if
(
$yn
-eq
'Y'
) {
$validInput
=
$true
}
else
{
Write-Host
"You will need to choose a different VM name."
}
}
else
{
If
(
$newVMdirectory
.Length
-lt
1) {
Write-Host
"ERROR: enter a name"
}
else
{
$validInput
=
$true
New-SFTPItem
-SessionId
0
-Path
$newVMdirectory
-ItemType
Directory |
Out-Null
}
}
}
Get-SSHSession
|
Remove-SSHSession
|
Out-Null
(
New-SSHSession
-Computername
$ESXiHost
-Credential
$ESXICredential
-Acceptkey
-Force
-WarningAction
SilentlyContinue) |
Out-Null
Write-Host
"Cloning the VMX file..."
$newVMXlocation
=
$newVMdirectory
+
'/'
+
$newVMname
+
'.vmx'
$command
= (
'sed -e "s/'
+
$VMdirectory
+
'/'
+
$newVMname
+
'/g" "'
+
$VMXlocation
+
'" > "'
+
$newVMXlocation
+
'"'
)
(
$commandResult
=
Invoke-SSHCommand
-Index
0
-Command
$command
) |
Out-Null
$find
= 'displayName \=
".*"
'
$replace = '
displayName =
"' + $newVMname + '"
'
$command = ("sed -i '
s/
$find
/
$replace
/
' '
$newVMXlocation
'")
($commandResult = Invoke-SSHCommand -Index 0 -Command $command) | Out-Null
# Blank the MAC address for adapter 1
$find = '
ethernet0.generatedAddress \=
".*"
'
$replace = '
ethernet0.generatedAddress = "
"'
$command = ("
sed
-i
's/
$find
/
$replace
/
' '
$newVMXlocation
'")
($commandResult = Invoke-SSHCommand -Index 0 -Command $command) | Out-Null
#####################
## Clone the VMDKs ##
#####################
Write-Host "Please be patient while cloning disks. This can take some time!"
foreach($VMdisk in $VMdisks) {
# Extract the filename
$VMdisk.Filename -match "([^/]*\.vmdk)" | Out-Null
$oldDisk = ("/vmfs/volumes/" + $VMdatastore + "/" + $VMdirectory + "/" + $Matches[1])
$newDisk = ($newVMdirectory + "/" + ($Matches[1] -replace $VMdirectory, $newVMname))
# Clone the disk
$command = ('
/bin/vmkfstools
-i
"
' + $oldDisk + '
" -d thin "
' + $newDisk + '
"')
Write-Host "
Cloning disk
$oldDisk
to
$newDisk
with command:
"
Write-Host $command
# Set a timeout of 10 minutes/600 seconds for the disk to clone
($commandResult = Invoke-SSHCommand -Index 0 -Command $command -TimeOut 600) | Out-Null
#Write-Host $commandResult.Output
}
########################
## Register the clone ##
########################
Write-Host "
Registering the clone...
"
$command = ('vim-cmd solo/register "
' + $newVMXlocation + '
"')
(
$commandResult
=
Invoke-SSHCommand
-Index
0
-Command
$command
) |
Out-Null
Disconnect-VIServer
-Server
$ESXiHost
-Force
-Confirm
:
$false
Get-SSHSession
|
Remove-SSHSession
|
Out-Null
Get-SFTPSession
|
Remove-SFTPSession
|
Out-Null
Pop-Location