Deploy a Linux VM using Customization Wizard using Perl SDK

This post will show you how to deploy a VM fully operational in a few seconds in a vSphere environment. You must have a working template to use this script.

You will have to install VMware Tools on this template, or OVT (OpenVMwareTools). They are now fully supported by VMware. I would recommand to use them, just for an ease of use during your weekly vulnerability review 😉 Ideed during your OS upgrade, the OVT package will also be updated. More details on VMware knowledge base. (kb.vmware.com/kb/2073803)

You need to install vSphere SDK for perl on your work station to be able to follow this post.

Overview

During the deployment you will not only clone the VM with all the template configuration, you will also configure IPs, gateway, hostname, DNS, Timezone and you will choose on which cluster, datastore and folder you will deploy your VM. We are going to use the VMware “Configuration Wizard” to deploy your VM.

You understood well, one of the biggest part of the job is not to write the script, you have to configure a working template. You need to have VMware Tools installed (OVT) or VMware Tools themselves. After the cloning operation, the VM will be powered on and the customization process will apply.

I’m not going to show you how to build a template, there are many of ways to do it, from scratch with an ISO, by deploying an existing template and change some settings to fit your needs, by downloading an external appliance, etc. The only thing you need to do is to install OVT.

Now, on your work station, where you installed the vSphere PERL SDK, you can edit a new script, and paste the scripts requirement to connect to your vCenter :

#!/usr/bin/perl
use strict;
use Data::Dumper;

use VMware::VIRuntime;

Opts::set_option('server', 'pcc-178-33-102-10.ovh.com');
Opts::set_option('username', 'apiuser');
Opts::set_option('password', 'XXxxXXxxXX');


print "Connecting \n"; 

Util::connect(); 

## Later, we are going to make actions there

Util::disconnect(); 
print "Disconnected \n";

Now we focus on this post objective : deploy a Linux VM with everything configured.

First, we need to declare our environment :

my $templateName = 'myOwnTemplate';

my $datacenterName = 'pcc-178-33-102-10_datacenter114';

my $vmName = 'myNewVM';
my $clusterName = 'Cluster1';
my $datastoreName = 'pcc-003643';
my $folderName = 'ProductionFolder';

my $custTimezone = 'Europe/Paris';

my $domain = 'ovh.net';
my $dns = [ '213.186.33.99' ];
my $ip = '192.168.0.10';
my $netmask = '255.255.255.0';
my $gateway = '192.168.0.10';

This is exactly what you might see in your inventory.

You need now to get all views :

print "Getting Datacenter view \n";
my $DatacenterView = Vim::find_entity_view(
	'view_type' => 'Datacenter',
	'filter' => {
		'name' => $datacenterName
	}
);
!$DatacenterView and die('Failed to get Datacenter view');

print "Getting Template view\n";
my $VMView = Vim::find_entity_view(
	'view_type' => 'VirtualMachine',
	'filter' => {
		'name' => $templateName
	},
	'begin_entity' => $DatacenterView,
);
!$VMView and die('Failed to get Template view');

print "Getting destination cluster view\n";
my $ClusterView = Vim::find_entity_view(
	'view_type' => 'ClusterComputeResource',
	'begin_entity' => $DatacenterView,
	'filter' => { 
		'name' => $clusterName 
	}
);
!$ClusterView and die('Failed to get cluster view');

print "Getting destination datastore view \n";
my $DatastoreView = Vim::find_entity_view(
	'view_type' => 'Datastore',
	'begin_entity' => $DatacenterView,
	'filter' => {
		'name' => $datastoreName
	}
);
!$DatastoreView and die('Failed to get datastore view');

 print "Getting destination folder view\n";
my $FolderView = Vim::find_entity_view(
	'view_type' => 'Folder',
	'begin_entity' => $DatacenterView,
	'filter' => { 
		'name' => $folderName 
	}
);
!$FolderView and die $FolderView;

Right now, if you passed all the checks, you know that you did not miss any deployment requirements.

Now we need to build some VMware Perl Objects to get the guest OS configuration working. You don’t have to custom this part.

my $CustomizationGlobalIPSettings;
my $custName = CustomizationFixedName->new( name => $vmName );
my $custDomain = $domain;

$CustomizationGlobalIPSettings = CustomizationGlobalIPSettings->new(
	'dnsServerList' => $dns,
	'dnsSuffixList' => [ $domain ],
);

my $custUtcClock = 'false';

my $CustomizationIdentitySettings = CustomizationLinuxPrep->new(
	'hostName' => $custName,
	'domain' => "$custDomain",
	'hwClockUTC' => $custUtcClock,
	'timeZone' => "$custTimezone",
);

my $CustIp = CustomizationFixedIp->new(
	'ipAddress' => $ip
);
my $CustomizationIPSettings = CustomizationIPSettings->new(
	'ip' => $CustIp,
	'subnetMask' => $netmask,
	'dnsServerList' => $dns,
	'gateway' => [ $gateway ],
	'dnsDomain' => $domain,
);
my $CustomizationAdapterMapping = CustomizationAdapterMapping->new(
	'adapter' => $CustomizationIPSettings
);

Then, here are the VMware Objects for the VM itself, you may not change anything there neither :

my $CustomizationSpec = CustomizationSpec->new(
	'globalIPSettings' => $CustomizationGlobalIPSettings,
	'identity' => $CustomizationIdentitySettings,
	'nicSettingMap' => [ $CustomizationAdapterMapping ],
);

my $VirtualMachineRelocateSpec = VirtualMachineRelocateSpec->new(
	'datastore' => $DatastoreView,
	'diskMoveType' => 'moveAllDiskBackingsAndAllowSharing',
	'pool' => $ClusterView->resourcePool,
);

my $VirtualMachineConfigSpec = new VirtualMachineConfigSpec;

my $VirtualMachineCloneSpec = VirtualMachineCloneSpec->new(
	'config' => $VirtualMachineConfigSpec,
	'customization' => $CustomizationSpec,
	'location' => $VirtualMachineRelocateSpec,
	'powerOn' => 'true',
	'template' => 'false'
);

Finally, 3 lines to deploy our VM :

eval{
	$VMView->CloneVM(
		'folder' => $FolderView,
		'name' => $vmName,
		'spec' => $VirtualMachineCloneSpec,
	);
};
if($@)
{
	my @report = ('Error crearting VM : ', $@);
	print Dumper(@report);
	Util::disconnect();
	die;
}

Here is the full script :

#!/usr/bin/perl
use strict;
use Data::Dumper;

use VMware::VIRuntime;

Opts::set_option('server', 'pcc-178-33-102-10.ovh.com');
Opts::set_option('username', 'apiuser');
Opts::set_option('password', 'XXxxXXxxXX');


print "Connecting \n"; 

Util::connect(); 


### Custom properties. To fill with your environment requirements
my $templateName = 'myOwnTemplate';
my $datacenterName = 'pcc-178-33-102-10_datacenter114';
my $vmName = 'myNewVM';
my $clusterName = 'Cluster1';
my $datastoreName = 'pcc-003643';
my $folderName = 'ProductionFolder';
my $custTimezone = 'Europe/Paris';
my $domain = 'ovh.net';
my $dns = [ '213.186.33.99' ];
my $ip = '192.168.0.10';
my $netmask = '255.255.255.0';
my $gateway = '192.168.0.10';


### Checks 
print "Getting Datacenter view \n";
my $DatacenterView = Vim::find_entity_view(
	'view_type' => 'Datacenter',
	'filter' => {
		'name' => $datacenterName
	}
);
!$DatacenterView and die('Failed to get Datacenter view');

print "Getting Template view\n";
my $VMView = Vim::find_entity_view(
	'view_type' => 'VirtualMachine',
	'filter' => {
		'name' => $templateName
	},
	'begin_entity' => $DatacenterView,
);
!$VMView and die('Failed to get Template view');

print "Getting destination cluster view\n";
my $ClusterView = Vim::find_entity_view(
	'view_type' => 'ClusterComputeResource',
	'begin_entity' => $DatacenterView,
	'filter' => { 
		'name' => $clusterName 
	}
);
!$ClusterView and die('Failed to get cluster view');

print "Getting destination datastore view \n";
my $DatastoreView = Vim::find_entity_view(
	'view_type' => 'Datastore',
	'begin_entity' => $DatacenterView,
	'filter' => {
		'name' => $datastoreName
	}
);
!$DatastoreView and die('Failed to get datastore view');

print "Getting destination folder view\n";
my $FolderView = Vim::find_entity_view(
	'view_type' => 'Folder',
	'begin_entity' => $DatacenterView,
	'filter' => { 
		'name' => $folderName 
	}
);
!$FolderView and die $FolderView;

### Guest OS configuration
my $CustomizationGlobalIPSettings;
my $custName = CustomizationFixedName->new( name => $vmName );
my $custDomain = $domain;

$CustomizationGlobalIPSettings = CustomizationGlobalIPSettings->new(
	'dnsServerList' => $dns,
	'dnsSuffixList' => [ $domain ],
);

my $custUtcClock = 'false';

my $CustomizationIdentitySettings = CustomizationLinuxPrep->new(
	'hostName' => $custName,
	'domain' => "$custDomain",
	'hwClockUTC' => $custUtcClock,
	'timeZone' => "$custTimezone",
);

my $CustIp = CustomizationFixedIp->new(
	'ipAddress' => $ip
);
my $CustomizationIPSettings = CustomizationIPSettings->new(
	'ip' => $CustIp,
	'subnetMask' => $netmask,
	'dnsServerList' => $dns,
	'gateway' => [ $gateway ],
	'dnsDomain' => $domain,
);
my $CustomizationAdapterMapping = CustomizationAdapterMapping->new(
	'adapter' => $CustomizationIPSettings
);

### Virtual Hardware configuration
 
my $CustomizationSpec = CustomizationSpec->new(
	'globalIPSettings' => $CustomizationGlobalIPSettings,
	'identity' => $CustomizationIdentitySettings,
	'nicSettingMap' => [ $CustomizationAdapterMapping ],
);

my $VirtualMachineRelocateSpec = VirtualMachineRelocateSpec->new(
	'datastore' => $DatastoreView,
	'diskMoveType' => 'moveAllDiskBackingsAndAllowSharing',
	'pool' => $ClusterView->resourcePool,
);
my $VirtualMachineConfigSpec = new VirtualMachineConfigSpec;

my $VirtualMachineCloneSpec = VirtualMachineCloneSpec->new(
	'config' => $VirtualMachineConfigSpec,
	'customization' => $CustomizationSpec,
	'location' => $VirtualMachineRelocateSpec,
	'powerOn' => 'true',
	'template' => 'false'
);

### Cloning operation
eval{
	$VMView->CloneVM(
		'folder' => $FolderView,
		'name' => $vmName,
		'spec' => $VirtualMachineCloneSpec,
	);
};
if($@)
{
	my @report = ('Error crearting VM : ', $@);
	print Dumper(@report);
	Util::disconnect();
	die;
}

Author: fluatovh

I'm today Senior Cloud Architect at OVH since 2010 and I manage the teams in charge of deployment and improvement of the OVH Dedicated Cloud product since its creation. I'm a VMware Architect and a VMware specialist since more than 8 years. We run today thousands of vCenter in production in a completely automated way and we host hundreds of thousands VMs on top on these VMware infrastructures for OVH Customers. @fluatovh

Leave a Reply

Your email address will not be published. Required fields are marked *