Scripting VM Creation from CSV, with Template and Custimization and more!

Posted: June 18, 2012 in Scripts, VMware

OK, so this one is a bit of a monster. Aside from creating new VMs based on a template, this script also checks some datastore contraints, ensuring it doesn’t get over-provisioned, choses the best host to deploy to based on available memory, if flagged, creates the AD account for the computer, as well as a service account with a random complex password and then adds those details to Password Manager XP. The script as a whole might not fit the environment you are working in, but hopefully there are some good “chunks” in here for you.

Couple of things to note:
For the AD creation, you need the “dsadd” tool on your machine.
For password management, it’s setup for Password Manager XP, you would need to tweak for anything else.
It will display all passwords entered or generated in clear text as well, so be careful who’s watching.
Email notifications will only work if the machine you run this on is allowed the SMTP relay.
The csv file uses the following headers:
vmname,vmrp,vmfolder,vmds,vmdiskmb,vmram,vmcpu,vmnetwork,vmip,vmmask,vmgw,vmdns1,vmdns2,vmtemplate,vmoscs,adenabled,vmnotes,vmowner,vmsupport,vmvendor
Most of those will be fairly self explanitory. The 3 at the end are custom annotations that existed in the environment I built this, you can ignore these, or replace with your own custom attributes.

With all that said, here’s the code. Make sure you adjust all your variables at the top to suite and go through and comment out anything that won’t be valid for your environment.

###################################################################################
# static variables
###################################################################################
$CSVPath = “C:\Scripts\MassVMCreate\new-vms.csv”
$smtpServer = “smtp.yourdomain.com”
$emailFrom = “server@yourdomain.com”
$emailTo = “you@yourdomain.com”

$ProductionVC = “prodvc.yourdomain.com”
$ProdCluster = “Prod – Tier3”
$DRVC = “drvc.yourdomain.com”
$DRCluster = “DR – Tier3”

$VMServerOU = “OU=Virtual Servers,DC=yourdomain,DC=com”
$UPNSuffix = “@yourdomain.com”
$ServiceAccountOU = “OU=Service Accounts,DC=yourdomain,DC=com”

$DataStoreOPMax = 25 # Maximum percentage of total space the datastore can be over provisioned
$DataStoreFSMin = 20 # Minimum ammount of free space the data store should currently have
$DataStoreVMMax = 10 # Maximum number of VMs per datastore
$DataStoreGBMin = 600 # Minimum size of the datastore in GB

###################################################################################
# Internal Functions
###################################################################################

# Generate random passwords. Default length is 8 if no int is passed to function call
Function New-RandomComplexPassword ()
{
param ( [int]$Length = 8 )
#Usage: New-RandomComplexPassword 12
$Assembly = Add-Type -AssemblyName System.Web
$RandomComplexPassword = [System.Web.Security.Membership]::GeneratePassword($Length,2)
Return $RandomComplexPassword
}

# Send out an email
Function SendEmailNotification
{
param ( [string]$Subject, $body )
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)
}

# Select a random host that is powered on, connected and has the most free memory
Function Get-BestHost ()
{
$Hosts = Get-Cluster $vmcluster | Get-VMHost | Where { $_.ConnectionState -eq “Connected” -And $_.PowerState -eq “PoweredOn” } | Select Name, @{N=”MemoryFreeMB”;E={$_.MemoryTotalMB – $_.MemoryUsageMB}} | Sort MemoryFreeMB -Desc
Return $Hosts[0].Name
}

# Check that the datastore exists
Function Check-DatastoreExist ()
{
param ( [string]$DatastoreToCheck )

$temp = Get-DataStore $DataStore.vmds
If ($? -eq $false){
Return $false
}
Else{
Return $true
}

}

# Check if a datastore meets the desired requirements
Function Check-DatastoreRequirements ()
{
param ( [string]$DatastoreToCheck )
$DSCheckResult = $null
$DSCheckResult = Get-Datastore $DataStoreToCheck | Select *, @{N=”NumVM”;E={@($_ | Get-VM).Count}}, @{N=”ProvisionedSpaceMB”;E={@([math]::truncate(((($_.ExtensionData.Summary.Capacity – $_.ExtensionData.Summary.FreeSpace) + $_.ExtensionData.Summary.Uncommitted) / 1024 / 1024)))}} | Where {($_.FreeSpaceMB -gt $_.CapacityMB * ($DataStoreFSMin / 100)) -And ($_.ProvisionedSpaceMB -lt $_.CapacityMB * (1 + ($DataStoreOPMax / 100))) -And ($_.NumVM -lt ($DataStoreVMMax)) -And ($_.CapacityMB -gt $DataStoreGBMin * 1024)}
If ($DSCheckResult -eq $null){
Return $false
}
Else{
Return $true
}

}

###################################################################################

# Check if the CSV file is available
If (!(Test-Path $CSVPath)){
cls
Write-Host “CSV not found at $CSVPath. Terminating Script”
Break
}

# Temporary variables
$temp_oscs = “TEMPOSCS”
$TempPassArray = “”
cls
$LocalAdminPasswd = Read-Host -Prompt “Please enter the password for the local administrator password….”
cls
$PasswordSafePasswd = Read-Host -Prompt “Please enter the password for password safe….”
cls

# Find out which environment we are deploying to
cls
$Prod = New-Object System.Management.Automation.Host.ChoiceDescription “&Prod”,””
$dr = New-Object System.Management.Automation.Host.ChoiceDescription “&DR”,””
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($prod,$dr)
$caption = “Destination Environment”
$message = “Are you deploying VMs to Production or DR?”
$result = $Host.UI.PromptForChoice($caption,$message,$choices,0)

if ( $result -eq 0 ){
$VCServer = $ProductionVC
$vmcluster = $ProdCluster
$vmhost = Get-BestHost
}
if ( $result -eq 1 ){
$VCServer = $DRVC
$vmcluster = $DRCluster
$vmhost = Get-BestHost
}

# Perform checks on connections and variables
If (!(Connect-VIServer $VCServer)){
cls
Write-Host “Failed to connect to vCenter. Terminating script.”
Break
}

#######################################################
# Initial checks passed. Import data and create new VMs
#######################################################

cls
Write-Host “Importing CSV file…”
$new_vms_csv = Import-Csv $CSVPath

foreach ($vm in $new_vms_csv) {

$variablecheck = 0
$FailureList = “”

$vmname = $vm.vmname
$vmdesc = $vm.vmnotes
$vmdiskmb = $vm.vmdiskmb
$vmram = $vm.vmram
$vmcpu = $vm.vmcpu
$vmnetwork = $vm.vmnetwork
$vmip = $vm.vmip
$vmmask = $vm.vmmask
$vmgw = $vm.vmgw
$vmdns = $vm.vmdns1, $vm.vmdns2
$vmtpl = $vm.vmtemplate
$oscs = $vm.vmoscs
$vmnotes = $vm.vmnotes
$vmowner = $vm.vmowner
$vmsupport = $vm.vmsupport
$vmvendor = $vm.vmvendor

$vmrp = $null
$vmrp = Get-Cluster $vmcluster | Get-ResourcePool $vm.vmrp
$vmfolder = $null
$vmfolder = Get-Folder $vm.vmfolder
$vmds = $null
$vmds = Get-Datastore $vm.vmds

$vmhost = Get-BestHost

If ( $vmrp -eq $null ){
$FailureList += “Resource pool not found…`n”
$variablecheck = 1
}
If ( $vmfolder -eq $null ){
$FailureList += “VM folder not found…`n”
$variablecheck = 1
}
If ( $vmds -eq $null ){
$FailureList += “Datastore not found…`n”
$variablecheck = 1
}
If (!(Check-DatastoreRequirements $vmds)){
$FailureList += “Datastore failed to meet resource contraints…`n”
$variablecheck = 1
}
If ( $variablecheck -eq 1 ){
SendEmailNotification “Failed to create $vmname”, “The following errors were found:`n$FailureList”
Continue
}

# temporary clone of customisation spec
Write-Host $vmname”: Creating temporary custimisation…”
$os_spec = New-OSCustomizationSpec -OSCustomizationSpec $oscs -Type NonPersistent -Name $temp_oscs
# set IP address & password
Write-Host $vmname”: Making changes to temporary custimisation spec…”
$nic_mapping = Get-OSCustomizationSpec $os_spec | Get-OSCustomizationNicMapping | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $vmip -SubnetMask $vmmask -Dns $vmdns -DefaultGateway $vmgw
$os_spec = Set-OSCustomizationSpec -OSCustomizationSpec $os_spec -AdminPassword $LocalAdminPasswd

# create VM
Write-Host $vmname”: Deploying from template…”
$newvm = New-VM -VMHost $vmhost -Name $vmname -ResourcePool $vmrp -Location $vmfolder -Datastore $vmds -Template $vmtpl -OSCustomizationSpec $os_spec -Description $vmdesc

# configure VM NIC and print out MAC address
Write-Host $vmname”: Changing virtual network assignment…”
$newvmnic = Get-NetworkAdapter -VM $vmname
$nicinfo = Set-NetworkAdapter -NetworkAdapter $newvmnic -NetworkName $vmnetwork -StartConnected $true -Confirm:$false

# set Annotations
Write-Host $vmname”: Setting annotations…”
$vminfo = Get-VM $vmname | Set-Annotation -CustomAttribute “Business Owner” -Value $vmowner
$vminfo = Get-VM $vmname | Set-Annotation -CustomAttribute “Support Contact” -Value $vmsupport
$vminfo = Get-VM $vmname | Set-Annotation -CustomAttribute “Vendor” -Value $vmvendor

# configure VM resources
Write-Host $vmname”: Changing resource assignments…”
$vmconfig = Set-VM -VM $vmname -MemoryMB $vmram -NumCpu $vmcpu -Confirm:$false

# power on VM
Write-Host $vmname”: Powering on…”
$vmpower = Start-VM -VM $vmname -RunAsync:$true -Confirm:$false

# delete temporary customisation spec
Write-Host $vmname”: Removing temporary customisation spec…”
Remove-OSCustomizationSpec $temp_oscs -Confirm:$false

If ( $vm.adenabled -eq “y” ){
# New Computer Account
# ======================
Write-Host $vmname”: Creating AD account…”
$ADComputerString = “CN=” + $vmname + “,” + $VMServerOU
dsadd computer $ADComputerString -desc $vmdesc

# Service Account
# ======================
Write-Host $vmname”: Creating service account…”
$ServiceAccount = “z” + $vmname
$upn = $ServiceAccount + $UPNSuffix
$ServiceAccountPasswd = New-RandomComplexPassword 12
$ADServiceAccountString = “CN=” + $ServiceAccount + “,” + $ServiceAccountOU

dsadd user $ADServiceAccountString -samid $ServiceAccount -upn $upn -display $ServiceAccount -desc $vmdesc -mustchpwd no -pwdneverexpires yes -pwd $ServiceAccountPasswd

$PassManagerString = “`/add:`”Title=” + $vmdesc + “, User name=” + $serviceAccount + “, Password=” + $ServiceAccountPasswd + “`” `/DbName=`”BACPWD`” `/DbPassword=$PasswordSafePasswd `/DBFolder=`”VMBuild`” `/silent”
$PassManager = “c:\Program Files (x86)\Password Manager XP\PwdManager.exe”

Start-Process $PassManager $PassManagerString

$TempPassArray = $TempPassArray + “$ServiceAccount, $ServiceAccountPasswd`n”
}

Write-Host $vmname”: All tasks completed.”

}

cls
# Display all the random passwords incase import to Password Manager failed.
Write-Host “Confirm that passwords have been imported into Password Manager before closing this window…”
$TempPassArray

# Clean out password variables
$TempPassArray = “”
$ServiceAccountPasswd = “”
$LocalAdminPasswd = “”
$PasswordSafePasswd = “”

Advertisements
Comments
  1. Eddy says:

    Couldn’t have put it better myself.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s