Tedious, repetitive processes are the number one factor in deciding whether to write a new script. In my current position, we are asked to deploy new VMs on a pretty regular basis and in some cases it may be upwards of 20 in one batch! The standard way we accomplish this is through the following steps:
1. Create a new VM using the vSphere client, setting the general parameters
2. Attach the vendor created ISO that will do all the customization to the selected OS. (this step takes 30-60 minutes)
3. Set SNMP properties
4. Set IP and DNS settings
5. Disable IPV6
6. Add DNS search suffixes
7. Rename local Admin and Guest accounts
8. Install SCCM 2007 Client
9. Add our AD security group to the Local Administrators group
10. Install Cisco AMP
As you can see, there are a lot of steps in the original build process. For one off VMs, this might be acceptable. However, when you need 24 VMs for a big project, it could easily take several work days of your time. This is when my distaste for repetitive, manual processes comes into play.
The first thing that comes to mind, is how much of this can be streamlined through PowerCLI. As it turns out, most of it! Now, having no official automation tools or even support from within to accomplish this task, I had to improvise. This means there are a few factors other than PowerCLI in play. I have a .CSV file containing all the fields necessary to complete the build. Batch files and small Powershell scripts to do some customization. Custom VM Templates and Customization Specifications. As you can see, quite a few tools being used to achieve our modest goals.
The .csv file contains the following fields:
Name – Name of the VM
Template – Name of the Template we are using
Cluster – Which Cluster are we putting this VM in
Datastore – What Datastore should we use
Customization – This is the Customization Specification
vCPU – Number of vCPUs
Memory – Amount of memory
Network – Network the VM should be on
Diskformat – Thick or Thin
Location – Which folder the VM should be in
ipaddress – The IP address
mask – Subnet mask
gateway – Default Gateway
dns1 – DNS server
dns2 – DNS server
disk1 – Disk one size
disk2 – Disk two size (if no disk two, 0 or leave blank)
The .CSV file has the above names as the first set of values. So the next step in the new process is: create/edit the .CSV, in my case, it is named NEWVMS.CVS
Now we are ready to start the actual script.
First we need to get the data from the .CSV
1 |
$vms = Import-CSV "c:\Scripts\deploy\NewVMs.csv" |
Something I decided to add was a time/date stamp and who built the VM, so we need to get the username of the person running the script (another thing you may consider adding is a project name or sponsor.
1 |
$name = get-aduser -identity $env:username -Properties name | Select-Object -ExpandProperty name |
Now we get to the fun stuff, importing the data and assigning it to variables
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
foreach ($vm in $vms) { #Assign Variables $VMName = $vm.Name $Template = Get-Template -name $vm.Template $Cluster = $vm.Cluster $Datastore = Get-Datastore -Name $vm.Datastore $Custom = Get-OSCustomizationSpec -Name $vm.Customization $vCPU = $vm.vCPU $Memory = $vm.Memory $Network = $vm.Network $Diskformat = $vm.Diskformat $Location = $vm.Location $ipaddress = $vm.ipaddress $mask = $vm.mask $gateway = $vm.gateway $dns1 = $vm.dns1 $dns2 = $vm.dns2 $disk1 = $vm.disk1 $disk2 = $vm.disk2 |
One more step before we build the VM, Set the IP, subnet mask, default gateway and DNS servers in the Customization Specification. If we don’t, we cannot have the VM join the domain using the guest customization routines.
1 2 |
# setup nic maping in the custimzation specification Get-OSCustomizationSpec $Custom | Get-OSCustomizationNicMapping | Set-OSCustomizationNicMapping -IpMode UseStaticIp -IpAddress $ipaddress -SubnetMask $mask -DefaultGateway $gateway -Dns $dns1,$dns2 |
Now we can finally call the New-VM cmdlet and build the VM
1 2 3 |
#Build the VM New-VM -Name $VMName -Template $Template -ResourcePool (Get-Cluster $Cluster | Get-ResourcePool) -location $location -StorageFormat $Diskformat -Datastore $Datastore -OSCustomizationSpec $Custom Start-Sleep -Seconds 10 |
Now we will set the VM name, Amount of Memory, Number of CPUs and the Network the Primary NIC will be on
1 2 3 4 |
#Set the vCPU, memory, and network $NewVM = Get-VM -Name $VMName $NewVM | Set-VM -MemoryGB $Memory -NumCpu $vCPU -Confirm:$false $NewVM | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $Network -Confirm:$false |
the last few steps are add the second disk (if it exists), set vm built date and builder in the notes field and start the VM!
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#Add Disk 2 if it exists if ($disk2 -ne $null) { $NewVM | New-HardDisk -CapacityGB $disk2 -StorageFormat $Diskformat } else { } #Set vm build date $notes = "Built By: " + $name + "`r`nBuild Date: " + (get-date) set-vm -vm $newvm -description $notes -Confirm:$false #Start the VM to continue the customization process $NewVM | Start-VM } |
At this point the VM build process is complete. But wait! What about the other steps? I will cover those in detail in my next post. for now, here is the complete script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
$vms = Import-CSV "c:\Scripts\deploy\NewVMs.csv" $name = get-aduser -identity $env:username -Properties name | Select-Object -ExpandProperty name foreach ($vm in $vms) { #Assign Variables $VMName = $vm.Name $Template = Get-Template -name $vm.Template $Cluster = $vm.Cluster $Datastore = Get-Datastore -Name $vm.Datastore $Custom = Get-OSCustomizationSpec -Name $vm.Customization $vCPU = $vm.vCPU $Memory = $vm.Memory $Network = $vm.Network $Diskformat = $vm.Diskformat $Location = $vm.Location $ipaddress = $vm.ipaddress $mask = $vm.mask $gateway = $vm.gateway $dns1 = $vm.dns1 $dns2 = $vm.dns2 $disk1 = $vm.disk1 $disk2 = $vm.disk2 #setup nic maping in the custimzation specification Get-OSCustomizationSpec $Custom | Get-OSCustomizationNicMapping | Set-OSCustomizationNicMapping -IpMode UseStaticIp -IpAddress $ipaddress -SubnetMask $mask -DefaultGateway $gateway -Dns $dns1,$dns2 #Build the VM New-VM -Name $VMName -Template $Template -ResourcePool (Get-Cluster $Cluster | Get-ResourcePool) -location $location -StorageFormat $Diskformat -Datastore $Datastore -OSCustomizationSpec $Custom Start-Sleep -Seconds 10 #Set the vCPU, memory, and network $NewVM = Get-VM -Name $VMName $NewVM | Set-VM -MemoryGB $Memory -NumCpu $vCPU -Confirm:$false $NewVM | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $Network -Confirm:$false #Add Disk 2 if it exists if ($disk2 -ne $null) { $NewVM | New-HardDisk -CapacityGB $disk2 -StorageFormat $Diskformat } else { } #Set vm build date $notes = "Built By: " + $name + "`r`nBuild Date: " + (get-date) set-vm -vm $newvm -description $notes -Confirm:$false #Start the VM to continue the customization process $NewVM | Start-VM } |