VCPGuru
Contributor
Contributor

Clone VM with powershell

Jump to solution

Hi all, i have two quesitons:

1. how are you cloning today VMs with powershell (command)= ...?

2. are you having an errorhandling integrated, f.e. if a VM cloning fails you get an email alert?

Thank you in advance

Best Regards Simon Ciglia
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership

Have a go with this script to see if it does what you want ?

$fromVMname = <source-vmname>
$newVMName = <target-vmname>
$tgtEsxName = <target-esxname>
$tgtDatastoreName = <target-dsname>

$cloneTask = New-VM -Name $newVMName -VM (Get-VM $fromVMname) -VMHost (Get-VMHost $tgtEsxName) -Datastore (Get-Datastore $tgtDatastoreName) -RunAsync
Wait-Task -Task $cloneTask -ErrorAction SilentlyContinue
Get-Task | where {$_.Id -eq $cloneTask.Id} | %{
	if($_.State -eq "Error"){
		$event = Get-VIEvent -Start $_.FinishTime | where {$_.DestName -eq $newVMName} | select -First 1
		$emailFrom = <from-email-address>
		$emailTo = <to-email-address>
		$subject = "Clone of " + $newVMName + " failed"
		$body = $event.FullFormattedMessage
		$smtpServer = <your-smtp-server>
		$smtp = new-object Net.Mail.SmtpClient($smtpServer)
		$smtp.Send($emailFrom, $emailTo, $subject, $body)
	}
}

____________

Blog: LucD notes

Twitter: lucd22


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

0 Kudos
22 Replies
LucD
Leadership
Leadership

1. I use most of the time the New-VM cmdlet

2. I use a general alarm that sends an SNMP trap when any vSPhere task goes wrong.

But you could easily create an alarm specific for cloning tasks.

____________

Blog: LucD notes

Twitter: lucd22


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
VCPGuru
Contributor
Contributor

could you please describe your clone step more detailed?

f.e.:

--deletes a VM:

Get-VM "VMname" |Remove-VM -DeleteFromDisk -Confirm:$false

BR Simon

Best Regards Simon Ciglia
0 Kudos
LucD
Leadership
Leadership

There are multiple possibilities, depends what exactly you want to do.

For a simple, straightforward clone without any OS customisation I use

New-VM -Name <new-vm-name> -VM (Get-VM <model-vm>) -VMHost (Get-VMHost <esx-name>) -Datastore (Get-Datastore <datastore-name>)

If you want to use OS customisation it depends if the options available in the New-OSCustomizationSpec cmdlet are doing what you want to customise.

Don't forget the New-OSCustomizationNicMapping cmdlet which brings the option to customise the IP address.

If you want to customise other parameters, you will have to fall back on the CloneVM_Task method from the SDK which offers more option through the VirtualMachineCloneSpec object.

Do you have any specific customisation requirements ?

____________

Blog: LucD notes

Twitter: lucd22


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

VCPGuru
Contributor
Contributor

LucD, you really helped me!

I would like to do a simple, straightforward clone...now my last problem: Email alert when my simple powershell clone failed....I want to have everything in powershell, clone vm, alert if clone failed, delete vm.I dont like to create the tasks into vCenter.

Could you help me with this issue too?

Thanks Simon

Best Regards Simon Ciglia
0 Kudos
LucD
Leadership
Leadership

If I understand you correctly, you don't want an Alarm to be defined in vCenter ?

That would mean the script has to wait for the completion of the clone process.

As a consequence you won't be able to clone multiple guest in parallel.

Is that acceptable ?

Also note that is quite easy to monitor the actual clone process but that it is not so straightforward to monitor any customisation.

I understand you're not using any customisation whatsoever. Correct ?

____________

Blog: LucD notes

Twitter: lucd22


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
LucD
Leadership
Leadership

Have a go with this script to see if it does what you want ?

$fromVMname = <source-vmname>
$newVMName = <target-vmname>
$tgtEsxName = <target-esxname>
$tgtDatastoreName = <target-dsname>

$cloneTask = New-VM -Name $newVMName -VM (Get-VM $fromVMname) -VMHost (Get-VMHost $tgtEsxName) -Datastore (Get-Datastore $tgtDatastoreName) -RunAsync
Wait-Task -Task $cloneTask -ErrorAction SilentlyContinue
Get-Task | where {$_.Id -eq $cloneTask.Id} | %{
	if($_.State -eq "Error"){
		$event = Get-VIEvent -Start $_.FinishTime | where {$_.DestName -eq $newVMName} | select -First 1
		$emailFrom = <from-email-address>
		$emailTo = <to-email-address>
		$subject = "Clone of " + $newVMName + " failed"
		$body = $event.FullFormattedMessage
		$smtpServer = <your-smtp-server>
		$smtp = new-object Net.Mail.SmtpClient($smtpServer)
		$smtp.Send($emailFrom, $emailTo, $subject, $body)
	}
}

____________

Blog: LucD notes

Twitter: lucd22


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
VCPGuru
Contributor
Contributor

LucD, you are the man :)!!!

Nice evening

Best Regards Simon Ciglia
0 Kudos
sOltanOBreens
Contributor
Contributor

LucD Hi,

I am trying to automate the process of creating new VMs. I am deploying the VMs from a template I have prepared (has my OS and all software I need). Here are the only customizations I do throughout the wizard (some of which I saved and just reused):

  • Name the VM
  • Choose a datacenter folder
  • Choose a VM host (to spread out the load)
  • Thick provisioning
  • Choose datastorage location (to spread out the load)
  • Assign NetBIOS name same as VM name
  • provide administrator password for auto-login
  • start up the VM after creation
  • VM auto-logs in
  • Computer name is the same as the VM name
  • The new VM gets a new IP

Can you help me with scripting this? my VM names have the same prefix with consecutive numbers appended (A1-A20)

I actually tried to use your GPS tool, but when I click on Execute (with one guest "A1" to experiment) I see an hour glass, and nothing happens for a while. I look at vSphere client and the VM was not created. I look at the powershell console and there are no warning/error messages.

Thanks!

0 Kudos
sOltanOBreens
Contributor
Contributor

I tried to run this manually on vSphere PowerCLI and it is building the VM right now:

New-VM -VMHost (Get-VMHost 10.20.30.40) -Name VM-1 -Location ‘My System’ -Datastore (Get-Datastore BOX-1) -Template MyTemplate -DiskStorageFormat Thick –OSCustomizationSpec (Get-OSCustomizationSpec ‘MyCustoms’) –RunAsync

The result I'm getting so far:
VM-1 was created but was not power-on automatically. I had to turn it on.
its computer name was not VM-1, which is strange since the OS customization spec I used explicitly tells it to use the VM name for the netbios name. I left it alone for 5 minutes, then came back to it out of cirosity and found that it had rebooted itself automatically, weird. And the computer name is now what I expect VM-1

At the end of the day I'd like to achieve this:

foreach(vm host passed in xml or csv file)
{
  loop 8 times: run the above New-VM command appending a number to the VM- prefix. Also, assign Datastore in round-robin fashion I guess, so I will have to read the datastores into an array from some xml or csv file
}
any tips please or code snippets I can piece together to achieve this?
0 Kudos
tdubb123
Expert
Expert

I have a 2008 template that I clone from a script and read hosts from a csv file.

I want to know how to to set the name of the netbios name, ip address whether dhcp or static

since I have only one spec template, how can i use the csv file and specify different ip addresses from the clone?

0 Kudos
LucD
Leadership
Leadership

You can use the Set-OSCustomizationSpec cmdlet to change the hostname.

You can use the Set-OSCustomizationNicMapping cmdlet for the IP address and mode.

To use a CSV, you can do something like this

Import-Csv :C:\vminfo.csv" -UseCulture | %{

   # Your code

}

Inside the script you address the values by their columnnames in the CSV.

If your CSV looks something like this

Name,IPaddr,IPtype

"vm1","192.168.1.1","fixed"

"vm2","192.168.1.2","fixed"

you can use $_.Name, $_.IPaddr and $_.IPtype inside the foreach loop (alias %)


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
LucD
Leadership
Leadership

The GPS script was written for an older vSphere version, so it probably needs to be updated for the newer versions.

Someting for my long list of ToDos Smiley Wink

After a (Windows) machine is cloned it will reboot 1 time automatically and run the sysprep utility.

With the sysprep utility it will make the requested changes inside the guest OS.

These changes come from your CustomizationSpec.

The script you envisage could be something like this

$dsNames = Import-Csv "C:\dsnames.csv" -UseCulture

$count = 0

$vmPrefix = "vmname"

Import-Csv "C:\vms.csv" -UseCulture | %{

   New-VM -Name ($vmPrefix + $count) -VMHost (Get-VMHost $_.hostname) -Datastore $dsName[$count % $dsNames.Count] ....

   $count++

}

The script would read the target datastorenames into an array.

You loop through a CSV that holds all the ESX(i) hostnames in the format

"hostname"

"name1"

"name2"

"name3"

"name4"

For the round-robin datastore selection I propose to use the module of the number of datastores you provide.

Example, if you have 3 datastores and you are in iteration 7, the selected datastore will $dsNames[1].

Does this help ?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
tdubb123
Expert
Expert

hi lucd

why do i have to use set-oscustomizationspec to change hostname if I already have a column called "name" and the customization spec is set to change netbios name to vm name? do i need that?

also how do i set the vm based on which portgroup they should be in?

0 Kudos
sOltanOBreens
Contributor
Contributor

LucD

Thanks, this helps a lot, it gets me started on PowerCLI.

I modified the code a bit. I plan to have 3 csv files. datastores, vmhosts and the actual vm names (instead of playing the game of prefixing, I have another tool that will generate the vm names for me). I will round-robin through the vmhosts and the datastores.

here's the new snippet:

$dstores = Import-Csv "C:\vmstuff\dstores.csv" -UseCulture
$vhosts = Import-Csv "C:\vmstuff\vhosts.csv" -UseCulture
$count = 0
Import-Csv "C:\vmstuff\vms.csv" -UseCulture | %{
    $vhost = $vhosts[$count % $vhosts.Count]
    $dstore = $dstores[$count % $dstores.Count]
    #New-VM -Name $_.vm -VMHost (Get-VMHost $host) -Datastore $store ....
    write-host $count $_.vm $vhost $dstore
   
    $count++
}
vms.csv looks like:
vm
"vm-1"
"vm-2"
...
"vm-8"
vhosts.csv look like:
vhost
1.2.3.4
1.2.3.5
1.2.3.6
1.2.3.7
dstores.csv looks like:
dstore
"lun-1"
"lun-2"
"lun-3"
"lun-4"
Now what I really want to do next is this:

  1. Before starting anything, stop and blow up (delete from disk) all existing vms. Wait until the operations are finished, then the script somehow continues
  2. Check the storage capacity of each data store and save it
  3. When picking a data store, make sure it has enough storage (I shouldn't wait for the previous VM to complete, I'll just decrement the remaining free space on that store after I issue a New-VM on it)
  4. Make sure I don't exceed 10 VMs per vhost (maybe I should include this limit in the vhosts.csv ...)
  5. How do I break from the loop if for example I run out of data storage entirely or if I cap all vhosts?
  6. what is the best way to display informational, debug, log message? i'm using write-host but not sure if this is the best-practice way to do it.

Thanks!

0 Kudos
sOltanOBreens
Contributor
Contributor

Here's the up to date script:

$dstores = Import-Csv "C:\vmstuff\dstores.csv" -UseCulture
$vhosts = Import-Csv "C:\vmstuff\vhosts.csv" -UseCulture
$count = 0
Import-Csv "C:\vmstuff\vms.csv" -UseCulture | %{
    $vhost = $vhosts[$count % $vhosts.Count].name
    $vhostcap = $vhosts[$count % $vhosts.Count].capacity
    $dstore = $dstores[$count % $dstores.Count].name
    $dstorecap = (get-datastore $dstores[$count % $dstores.Count].name).CapacityMB
    $vm = $_.vm
   
    #New-VM -Name $_.vm -VMHost (Get-VMHost $host) -Datastore $store ....
    $vhostused = [int]$vhosts[$count % $vhosts.Count].used
    $vhostused++
    $vhosts[$count % $vhosts.Count].used = $vhostused
   
    $dstoreused = [int]$dstores[$count % $dstores.Count].used
    $dstoreused += 25000
    $dstores[$count % $dstores.Count].used = $dstoreused
   
    write-host "$count: $vm   $vhost ($vhostused/$vhostcap VMs)    $dstore ($dstoreused/$dstorecap MB)"
   
    $count++
}

csv files:

vm

"vm-1"

"vm-2"

"vm-3"

"vm-4"

"vm-5"

"vm-6"

"vm-7"

"vm-8"

name, capacity, used

"1.2.3.10", 10, 0

"1.2.3.11", 10, 0

"1.2.3.12", 10, 0

name, capacity, used

"san_1",0,0

"san_2",0,0

"san_3",0,0

"san_4",0,0

"san_5",0,0

my debug output after I connect to the vicenter on PowerCLI and run the script looks like:

vm-1   1.2.3.10 (1/10 VMs)    san_1 (25000/409344 MB)

vm-2   1.2.3.11 (1/10 VMs)    san_2 (25000/409344 MB)

vm-3   1.2.3.12 (1/10 VMs)    san_3 (25000/409344 MB)

vm-4   1.2.3.10 (2/10 VMs)    san_4 (25000/409344 MB)

vm-5   1.2.3.11 (2/10 VMs)    san_5 (25000/409344 MB)

vm-6   1.2.3.12 (2/10 VMs)    san_1 (50000/409344 MB)

vm-7   1.2.3.10 (3/10 VMs)    san_2 (50000/409344 MB)

vm-8   1.2.3.11 (3/10 VMs)    san_3 (50000/409344 MB)

I am missing:

  1. The blowing up of all existing vm before starting up
  2. check to prevent ocercapping a datastore
  3. check for overcapping the hosts

Any tips and feedback will be appreciated, thanks!!


0 Kudos
sOltanOBreens
Contributor
Contributor

Latest script:

I implemented 2 and 3.

$dstores = Import-Csv "C:\vmstuff\dstores.csv" -UseCulture
$vhosts = Import-Csv "C:\vmstuff\vhosts.csv" -UseCulture
$countvm = 0
$countds = 0
$countvh = 0
Import-Csv "C:\vmstuff\vms.csv" -UseCulture | %{
    $vm = $_.vm
    $vhost = ""
    $vhostcap = 0
    $vhostused = 0
    $dstorecap = 0
    $dstorefree = 0
    $dsfound = $False
    $error = ""
   
  
    # Check for data storage availability:
    for($d=0; $d -lt $dstores.Count; $d++)
    {
        $countds += $d
        $dstore = $dstores[$countds % $dstores.Count].name
        $dstorecap = (get-datastore $dstores[$countds % $dstores.Count].name).CapacityMB
        $dstorefree = (get-datastore $dstores[$countds % $dstores.Count].name).FreeSpaceMB
        $dstoreused = [int]$dstores[$countds % $dstores.Count].used
        if ($dstorefree -gt 25000)
        {
            $dsfound = $True
            break;
        }
    }
  
    if ( $dsfound -eq $True )
    {
        # Check for host capacity:
        $vhfound = $False
        for($h=0; $h -lt $vhosts.Count; $h++)
        {
            $countvh += $h
            $vhost = $vhosts[$countvh % $vhosts.Count].name
            $vhostcap = $vhosts[$countvh % $vhosts.Count].capacity
            $vhostused = [int]$vhosts[$countvh % $vhosts.Count].used
           
            if($vhostused -lt $vhostcap)
            {
                $vhfound = $True
                break;
            }
        }      
       
        if ( $vhfound -eq $True )
        {
            #New-VM -Name $_.vm -VMHost (Get-VMHost $host) -Datastore $store ....
            $vhostused++
            $vhosts[$countvh % $vhosts.Count].used = $vhostused
           
            $dstoreused = [int]$dstores[$countds % $dstores.Count].used
            $dstoreused += 25000
            $dstores[$countds % $dstores.Count].used = $dstoreused
           
            $message = "OK"
        }
        else
        {
            $message = "NOT ENOUGH POWER (!)"
        }
    }
    else
    {  
        $message = "NOT ENOUGH STORAGE (!)"
        #$dstoreused = $dstores[$countds % $dstores.Count].used
    }
   
    write-host "$count: $vm   $vhost ($vhostused/$vhostcap VMs)    $dstore ($dstorefree/$dstoreused/$dstorecap MB)   $message"   
   
    $countvm++
    $countds++
    $countvh++
}

What I will need to do is build up the New-VM cmdlet with my parameters and actually execute this on a real environment and see how it goes.

I am still missing to implement an easy way to blow up all of the existing VMs.

Any tips or feedback will be appreciated.
Thanks

0 Kudos
sOltanOBreens
Contributor
Contributor

LucD Hi,

How do I make the new VM power on automatically? now I have to power it manually. Is there an option I am missing? I'm running this commandlet. vCenter 4.1

New-VM -VMHost (Get-VMHost MyHost) -Name MyVM -Location ‘My System’ -Datastore (Get-Datastore MyBox)-Template MyTemplate -DiskStorageFormat Thick –OSCustomizationSpec (Get-OSCustomizationSpec ‘My Custom’) –RunAsync -Confirm:$False

Thanks!

0 Kudos
LucD
Leadership
Leadership

On the New-VM cmdlet there is no option to start the guest, but you can use the Start-VM cmdlet.

You can use the pipeline, something like this

New-VM ... | Start-VM -Confirm:$false


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
sOltanOBreens
Contributor
Contributor

Fascinating! i'll try it tomorrow when i'm near the tools.
It seems like New-VM executes on its own thread kind of if by pipelining start-vm it'll make this work, given that I have –RunAsync in there.

0 Kudos