VMware PowerCLI Script to Clone a VM

PowerCLI Clone VM

This is my VMware PowerCLI Clone VM script.   More and more people are building their critical infrastructure servers as virtual machines (VM) these days.  This makes perfect sense and there are many advantages to this approach.  The key is to make sure you are protected and can recover your support servers during an outage.

This script will simply automate the creating of a VM backup by cloning the VM.  I know there are free tools out there to perform this simple task but sometimes just scripting your own solution is best because it will give you full control of the process.

Using a VM for a critical server makes sense.  You have the ability to clone it anytime and can recover it in a matter of minutes.  The problem is remembering to create those clone backups every night or week.  This script will take care of that task for you.  All you need to do is schedule it to run from a host on the network (even from the server being backed up).  The source VM (server to be backed up) can send the job to the VC server or directly to the host and schedule the clone creation.

Script Details

Here is the script I use (Most recent version on my github site).  I am using my standard template and if you would like to use it, here is a link to the Template post.  Before running the script, you will need to add your information to your ESX or VC server with an Id and password.  You will run the command by typing:

“./cloneVM.ps1 SourceVM Datastore ESX”

The PowerCLI Clone VM script will create a clone of the sourceVM appending the name of the VM with “-clone”.  During the second run of the script, it will delete the SourceVM-Clone server and create a new one.  It is also creating a thin copy of the VM to save on space.

CloneVM.ps1

<#
.SYNOPSIS
This script will make a clone backup of an existing VM.

.DESCRIPTION
This script will clone an existing VM to be used as a backup.  The script will 
need to have PowerShell 3 installed as well as PowerCLI version 5x.  This 
should be run from the VC server and run from the Windows Task scheduler
so the backups are automatic using a user account (not system account).  
The Clone will be created using thin provisioning to save space.  The script
will also delete an existing clone before creating a new one.

In order to login to the VC or host server, credentials will need to be 
saved in the script directory.  The first time the script is run manually, 
it will prompt for the UID and PW for the VC or Host server.
Once entered, it will encrypt them into files and use those files for future 
connections.  Once the files are saved, only the same user will be able to 
decrypt them so if the script is run with Task Scheduler, the same user ID
will need to be used in that task.  When changing the UID and PW, delete the
"*-UID.txt" and "*-PW.txt" files and run the script manually to recreate the
credential files.

To create multiple clones of the same VM, we must have different scripts and different tasks to launch those scripts.  
Then in the script, change the extension added to the clone name (currently -clone) to something different
For backing up different servers, create different tasks inputing the name of a different server but there is no need to create a new copy 
of the script.

.PARAMETER <Parameter_Name>
Inputs required at the command line are the Source VM name, Datastore to create 
the clone on, and the ESX server that should host the clone.

.INPUTS
The script will need to know the VM Source Name, the Datastore to place 
the clone on, and the ESX server to host the clone on.  The variables 
associated are $SourceVMName, $Datastore, and $ESXName.

.OUTPUTS
The log file will be stored in the script source directory unless otherwise 
changes in the script and it will be called "VMBackupClone.log".

The script will also add events to the Windows System Log under the User32 
Source (ID 0) and the following information will be logged depending on the 
results.  The event ID and the message below can be searched 
with some monitoring tools.

Deleting old Clone:
    "Failed to delete clone $newVMName"
    "Successful in deleting clone $newVMName"

Creating New Clone:
    "Cloning Failed for server $SourceVMName"
    "Cloning was Successful for server $SourceVMName"

The separate log file can also be used to pull detailed information.

.NOTES
  Version:        1.0
  Author:         TJ Totland, Totland Computer Services, ThriftyAdmin.com
  Creation Date:  5/29/15
  Purpose/Change: Initial script development
  
.EXAMPLE
CloneVM.ps1 <server> <Datastore> <ESX>

.\CloneVM.ps1 233-Windows7-IBM 201-SATA2000 192.168.1.201


#>
#requires -version 3
#Setup Input Parameters (No space or Commands allowed above this Line, Comments OK)
 param(    
    [Parameter(Mandatory=$True,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true, 
            HelpMessage= 'Enter the VM Source, Datastore to clone to, and the Host ESX.')]  

            #Acceptable input parameters
            [String]$sourceVMName,
            [String]$Datastore,
            [String]$esxName
) 

#Setup Variables
#region
#clear 
    #Setting Working Directory
    cd $PSScriptRoot

    #Declarations
    $ScriptVersion = "1.0"
    $ErrorView="CategoryView"
    $ErrorActionPreference = "SilentlyContinue"
    $error.clear()

    #Log File Info
    $LogPath = ".\" #Set Location of Log File
    $LogFile = "VMBackupClone" #Set File Name for Log File
    $date = (Get-Date).ToString('yyyyMMdd')
    $dateGeneral = get-date -format g
    $Datetime = get-date -format `yyyyMMdd_HHmmss`
    $Log = $logPath + $logFile + ".log"

    #Password
    $VCIDFile = $MyInvocation.MyCommand.Name + "-UID.txt"
    $VCPWFile = $MyInvocation.MyCommand.Name + "-PW.txt"
    $VCHost = "192.168.1.219"

    #Creates new VM Clone Name
    $newVMName = "$sourceVMName" + "-clone"

#endregion

#Start Logfile
#region
#Log File Setup
    #Remove-Item $Log #Removed existing Log File and starts fresh
    ""  | Out-File $Log -Append
    "**************************************************************************."  | Out-File $Log -Append
    $Datetime + " Starting Log..." + (Get-Date).ToString('MM/dd/yyyy-HH:mm:ss') | Out-File $Log -Append
    $Datetime + " Cloning:       " + ($SourceVMName) | Out-File $Log -Append
    $Datetime + " Clone Name:    " + ($NewVMName)    | Out-File $Log -Append
    $Datetime + " ESX Host:      " + ($ESXName)      | Out-File $Log -Append
    $Datetime + " ESX Datastore: " + ($Datastore)    | Out-File $Log -Append
    $Datetime + " Working Dir:   " + ($PSScriptRoot) | Out-File $Log -Append
#endregion

#Connections
#region
#Connections
    #Adding VMware Snapins
    $Datetime + " Adding VMware Snapins...." | Out-File $Log -Append 
    Add-PSSnapin VMware.VimAutomation.Core
    if (! $?){
        $Datetime + " Failed to add PowerCLI Snapins"  | Out-File $Log -Append
        $Datetime + " " + $error[0].Exception  | Out-File $Log -Append
        $Datetime + " Ending Log..." + (Get-Date).ToString('MM/dd/yyyy-HH:mm:ss') | Out-File $Log -Append
        "**************************************************************************."  | Out-File $Log -Append
        exit
        } else {
        $Datetime + " Successfully added PowerCLI Snapins"  | Out-File $Log -Append
        }

    #Creating or retreiving credentials
    if (test-path $VCIDFile){
        $Datetime + " Password File Exists at " + $VCIDFile | Out-File $Log -Append
        $VCID = Get-Content $VCIDFile
        $VCPW = Get-Content $VCPWFile | ConvertTo-SecureString
        } else {
        $Datetime + " Creating password file and saving it at " + $passFile | Out-File $Log -Append
        Clear-Variable -Name $Creds
        $creds = Get-Credential
        $creds.UserName | Out-File $VCIDFile
        $creds.Password | ConvertFrom-SecureString | Out-File $VCPWFile
        $VCID = Get-Content $VCIDFile
        $VCPW = Get-Content $VCPWFile | ConvertTo-SecureString
        $Datetime + " Ending Log..." + (Get-Date).ToString('MM/dd/yyyy-HH:mm:ss') | Out-File $Log -Append
        "**************************************************************************."  | Out-File $Log -Append
        exit
        }

    $creds = New-Object -TypeName System.Management.Automation.PSCredential `
    -ArgumentList $VCID, (Get-Content $VCPWFile | ConvertTo-SecureString)

    $Datetime + " Connecting to Server or Host to excute Clone Request....." | Out-File $Log -Append 
    $Datetime + " UID: " + $VCID + " File: " + $VCIDFile | Out-File $Log -Append 
    $Datetime + " PW: " + $VCPW + " PW File " + $VCPWFile | Out-File $Log -Append 
    
    #Connecting to VC or Host server using credentials stored in local files
    Connect-VIServer -server $vchost -Credential $Creds

    if (! $?){
        $Datetime + " Failed to connect to VC Server"  | Out-File $Log -Append
        $Datetime + " " + $error[0].Exception  | Out-File $Log -Append
        $Datetime + " Ending Log..." + (Get-Date).ToString('MM/dd/yyyy-HH:mm:ss') | Out-File $Log -Append
        "**************************************************************************."  | Out-File $Log -Append
        exit
        } else {
        $Datetime + " Connected to " + $VCHost +" Successfully"  | Out-File $Log -Append
        }

#endregion

function DeleteVM {
<#
.Synopsis
This function removes an existing VM Clone
.Description
This function removes an existing VM Clone and logs the information in both a log file and in the 
OS System Log.
.Example
.Parameter
#>
begin {}
process {
    Get-VM -Name $newVMName | Remove-VM -DeletePermanently -Confirm:$false #Deletes old Backup
    if (! $?){
        Write-EventLog –LogName System –Source “User32” –EntryType ERROR –EventID 0 –Message "Failed to delete clone $newVMName"
        $Datetime + " " + "Failed to delete clone " + $newVMName  | Out-File $Log -Append
        $Datetime + " " + $error[0].Exception  | Out-File $Log -Append
        } else {
        Write-EventLog –LogName System –Source “User32” –EntryType Information –EventID 0 –Message "Successful in deleting clone $newVMName"
        $Datetime + " " + "Successful in deleting clone " + $newVMName  | Out-File $Log -Append
        }
    }
end {}
}

function CloneVM {
<#
.Synopsis
This function creates an New VM Clone
.Description
This function creates a new VM Clone and logs the information in both a log file and in the 
OS System Log.
.Example
.Parameter
#>

begin {}
process {
    New-VM -Name $newVMName -VM $sourceVMName -VMHost $esxName -Datastore $Datastore -DiskStorageFormat Thin
        if (! $?){
        Write-EventLog –LogName System –Source “User32” –EntryType ERROR –EventID 0 –Message "Cloning Failed for server $SourceVMName"
        $Datetime + " " + "Cloning Failed for server " + $SourceVMName | Out-File $Log -Append
        $Datetime + " " + $error[0].Exception  | Out-File $Log -Append
        } else {
        Write-EventLog –LogName System –Source “User32” –EntryType Information –EventID 0 –Message "Cloning was Successful for server $SourceVMName"
        $Datetime + " " + "Cloning was Successful for server " + $SourceVMName | Out-File $Log -Append
        Get-VM -name $newVMName | set-vm -notes "backup on $dateGeneral" -confirm:$false
        }
    }
end {}
}

#Excution Section
#region

DeleteVM

CloneVM

#endregion

$Datetime + " Ending Log..." + (Get-Date).ToString('MM/dd/yyyy-HH:mm:ss') | Out-File $Log -Append
"**************************************************************************."  | Out-File $Log -Append

TJ Totland

Todd "TJ" Totland is a computer and network engineer working for IBM. He is certified as a MCSE, MCT, CNE, and CNA with vast experience in many technologies used in businesses today. TJ has designed, built, and managed hundreds of different types of computer and network systems for large and small customers since 1990. He is a subject Matter Expert in Cloud Technologies and has vast knowledge in VMware products.