This is a follow-up to my last post about fully automated deployment

Below is the script. After it pulls the information from you, it creates the VM, adds the second disk, sleeps for 15 seconds, pulls the MAC from the new VM, creates the computer object in SCCM, adds it to the collection, sleeps for 15 seconds, refreshes the collection, sleeps for 15 seconds, then powers on the VM. If you’ve got a mandatory OSD advertised to the collection specified, and the OSD is fully automated, it will lay down the OS and the computername will be the name you provided to the script.

There are some variables you will have to set to make it work for you. Our standard is to have two disks per VM, one for the OS, one for data. I have the OS disk hard coded to 64GB, and the script prompts you for the data disk. The OSID labels for the guest OS were taken from the API reference guide.

The SCCM portion was adapted from Antoine Habert, in combination with the /\/\o\/\/ WMI Browser.

This script also thin provisions both disks. This can be changed in the createvm function. Also, you will need PowerShell v2 and PowerCLI 4.1.1 to run this script.

Add-PSSnapin VMware.* -ErrorAction SilentlyContinue
 
#instantiate script variables
set-variable -name clustername -scope script
set-variable -name vmhostname -scope script
set-variable -name networklabel -scope script
set-variable -name vcenter -scope script
set-variable -name vmname -scope script
set-variable -name vCPU -scope script
set-variable -name vRAM -scope script
set-variable -name vmdisk2 -scope script
set-variable -name store -scope script
set-variable -name desc -scope script
set-variable -name osid -scope script
 
#function to prompt for confirmation
function Confirm($strMessage)
{
write-host -foregroundcolor yellow `n $strMessage
$answer = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
if ($answer.Character -ine "y")
    {
    write ""
    break
    }
 
write ""
}
 
#lists our supplied specs
function listspecs{
write-host -foregroundcolor green `n "These are the specs of the new server:"
write-host -foregroundcolor yellow `n`t "Environment:" $script:vcenter
write-host -foregroundcolor yellow `n`t "Server Name:" $script:vmname
write-host -foregroundcolor yellow `n`t "Number of CPUs:" $script:vCPU
write-host -foregroundcolor yellow `n`t "Amount of RAM:" $script:vRAM "GB"
write-host -foregroundcolor yellow `n`t "OS Disk: 64 GB" # 64GB is our standard
write-host -foregroundcolor yellow `n`t "Data Disk:" $script:vmdisk2 "GB"
write-host -foregroundcolor yellow `n`t "Cluster:" $script:clustername
write-host -foregroundcolor yellow `n`t "Network:" $script:networklabel
write-host -foregroundcolor yellow `n`t "Datastore:" $script:store
if($script:osid -eq "winLonghornGuest"){write-host -foregroundcolor yellow `n`t "Installing Windows 2008 x86"}
if($script:osid -eq "winLonghorn64Guest"){write-host -foregroundcolor yellow `n`t "Installing Windows 2008 x64"}
if($script:osid -eq "windows7Server64Guest"){write-host -foregroundcolor yellow `n`t "Installing Windows 2008 R2"}
if($script:osid -eq "winNetEnterpriseGuest"){write-host -foregroundcolor yellow `n`t "Installing Windows 2003 x86"}
if($script:osid -eq "winNetEnterprise64Guest"){write-host -foregroundcolor yellow `n`t "Installing Windows 2003 x64"}
Confirm("Press 'Y' to continue, or any other key to exit...")
}
 
#prompts for sizing specs of the VM
function getspecs{
    $script:vcenter = (read-host `n "vCenter server").tolower()
    $script:vmname = (read-host `n "Name of VM").toupper() #I like the name being in caps in vC
    $script:desc = (read-host `n "Enter a description for the notes (App, your name, etc)")
    [int]$script:vCPU = (read-host `n "Enter the number of CPUs")
    [int]$script:vram = (read-host `n "Enter the amount of RAM in GB")
    [int]$script:vmdisk2 = (read-host `n "Enter data disk size in GB")
    #need to populate OS ID with proper string to match the label
    write-host `n "Select OS Version"
    write-host `n "0 - Win2k8R2"
    write-host `n "1 - Win2k8x64"
    write-host `n "2 - Win2k8x86"
    write-host `n "3 - Win2k3x86"
    write-host `n "4 - Win2k3x64"
    $answer = read-host `n "Enter the number for the OS you're installing"
    if($answer -eq "0"){$script:osid = "windows7Server64Guest"}
    elseif($answer -eq "1"){$script:osid = "winLonghorn64Guest"}
    elseif($answer -eq "2"){$script:osid = "winLonghornGuest"}
    elseif($answer -eq "3"){$script:osid = "winNetEnterpriseGuest"}
    elseif($answer -eq "4"){$script:osid = "winNetEnterprise64Guest"}
}
 
#gets the desired cluster
#as well as a host in that cluster to gather network & datastore info from
#then builds on that node of the cluster
#notice no sort order, so it won't pull the same host every time
Function ChooseCluster{
    $clslist = Get-cluster |sort name
    $i = 0
    write-host -foregroundcolor green `n "Choose a cluster"
    foreach ($cluster in $clslist){
    write-host -foregroundcolor green "$i - $cluster"
    $i++
    }
    $answer = read-host `n "Enter the number for the cluster you want"
    $script:clustername = $clslist[$answer]
    $vmhosts = get-vmhost -location $script:clustername
    $script:vmhostname = $vmhosts[0]
}
 
#lists datastores for one host of the cluster
#shows provisioned space as well as free space
#yellow if less than 150GB free
#red if less than 100GB free
Function ChooseDatastore{
    $datastores = Get-Datastore -vmhost $script:vmhostname |sort FreeSpaceMB -descending
    $i = 0
    write-host -foregroundcolor green `n "Choose a Datastore"
    foreach ($store in $datastores){
        $color = "green"
        if ($store.FreeSpaceMB -lt 153600){$color = "yellow"}
        if ($store.FreeSpaceMB -lt 102400){$color = "red"}
        $ds = Get-Datastore $store |sort name | Get-View
        $dsout = $ds | Select -ExpandProperty Summary | Select Name, @{N="Capacity GB"; E={[math]::round($_.Capacity/1073741824, 2)}}, @{N="Free GB"; E={[math]::round($_.FreeSpace/1073741824, 2)}}, @{N="Provisioned GB"; E={[math]::round(($_.Capacity - $_.FreeSpace + $_.Uncommitted)/1073741824, 2)}} |sort name
        write-host -foregroundcolor $color "$i - $dsout"
        $i++
    }
    $answer = read-host `n "Enter the number for the datastore you want"
    $script:store = $datastores[$answer]
}
 
#pulls available networks from the host in the cluster
Function ChooseNetwork{
    $networklist = Get-VirtualPortGroup -vmhost $script:vmhostname |sort name
    $i = 0
    write-host -foregroundcolor green `n "Choose a network"
    foreach ($network in $networklist){
    write-host -foregroundcolor green "$i - $network"
    $i++
    }
    $answer = read-host `n "Enter the number for the network you want"
    $script:networklabel = $networklist[$answer]
}
 
#connects to vcenter
function ConnectVCenter($script:vcenter){
    connect-viserver $script:vcenter > $NULL 2>&1
}
 
#change -DiskMB to change disk 0:0 to the desired size
function CreateVM{
    #this creates the VM itself
    new-vm -vmhost $script:vmhostname -name $script:vmname -Datastore $script:store -DiskMB 65536 -diskstorageformat Thin -MemoryMB ($script:vRAM * 1024) -NumCpu $script:vcpu -CD -GuestID $script:osid -NetworkName $script:networklabel -Description $script:desc
 
    #this adds the data disk, since accepted size is kb, and prompt was gb, we have to convert it
    get-vm $script:vmname | New-HardDisk -capacitykb ($script:vmdisk2 * 1048576) -StorageFormat Thin
}
 
#disconnect from vc, null out variables just in case
function garbagecollection{
$script:clustername = $null
$script:vmhostname = $null
$script:networklabel = $null
$script:vcenter = $null
$script:vmname = $null
$script:vCPU = $null
$script:vRAM = $null
$script:vmdisk2 = $null
$script:store = $null
$script:desc = $null
$script:osid = $null
 
disconnect-viserver -confirm:$false
}
 
function AddToSCCM{
 
#sccm variables here
$SCCMServer = "" #enter the SCCM server name, used for WMI
$SCCMSite = "Site_" #example: Site_WSS, always 'Site_' and then 3 letter site code
$Colon = ":" #don't change this, makes life easier for the wmi path
 
#we need the mac to import computer into SCCM
$vnic =  Get-NetworkAdapter (get-vm $script:vmname)
$mac = $vnic.MacAddress
 
#specify collection based on OS ID
if($script:osid -eq "winLonghornGuest"){$targetColl = "2008 x86 Mandatory"}
elseif($script:osid -eq "winLonghorn64Guest"){$targetColl = "2008 x64 Mandatory"}
elseif($script:osid -eq "windows7Server64Guest"){$targetColl = "2008 R2 Mandatory"}
elseif($script:osid -eq "winNetEnterpriseGuest"){$targetColl = "2003 x86 Mandatory"}
elseif($script:osid -eq "winNetEnterprise64Guest"){$targetColl = "2003 x64 Mandatory"}
 
#create computer object
$Method = "ImportMachineEntry"
$Class = "SMS_Site"
$MC = [WmiClass]"\\$SCCMServer\ROOT\SMS\$SCCMSite$Colon$Class"
$InParams = $mc.psbase.GetMethodParameters($Method)
$InParams.MACAddress = $mac
$InParams.NetbiosName = $script:vmname
$InParams.OverwriteExistingRecord = $false
$inparams.PSBase.properties | select name,Value
$objCMComputer = $mc.PSBase.InvokeMethod($Method, $inParams, $Null)
 
#create direct rule for collection for target collection
$Class = "SMS_CollectionRuleDirect"
$objColRuledirect = [WmiClass]"\\$SCCMServer\ROOT\SMS\$SCCMSite$Colon$Class"
$objColRuleDirect.psbase.properties["ResourceClassName"].value = "SMS_R_System"
$objColRuleDirect.psbase.properties["ResourceID"].value = $objCMComputer.resourceID
$Collection = gwmi -computer $SCCMServer -namespace "root\sms\$SCCMSite" -class "SMS_Collection"
$svrCollection = $collection | where{$_.Name -eq "$targetColl"}
 
#add computer to target collection
$Class = "SMS_Collection"
$Method = "AddMembershipRule"
$CollectionID = $svrCollection.CollectionID
$filter="CollectionID = '$CollectionID'"
$MC = Get-WmiObject "SMS_Collection" -computer $SCCMServer -Namespace "ROOT\SMS\$SCCMSite" -filter $filter
$InParams = $mc.psbase.GetMethodParameters($Method)
$InParams.collectionRule = $objColRuledirect
 
$inparams.PSBase.properties | select name,Value
$AddComp = $mc.PSBase.InvokeMethod($Method, $inParams, $Null)
 
#update collection membership
start-sleep -s 15 #sleep for 15 seconds before requesting refresh of collection
$Method = "RequestRefresh"
$InParams = $mc.psbase.GetMethodParameters($Method)
$InParams.includesubcollections = $false
$inparams.PSBase.properties | select name,Value
 
$Refresh = $mc.PSBase.InvokeMethod($Method, $inParams, $Null)
 
}
 
function LaunchConsole{
$conuser = "conuser"
$conpw = "conpw"
$vm = get-vm $script:vmname
$currentvmhost = (get-vmhost -vm $vm).name
$vmxpath = $vm.ExtensionData.Summary.config.vmpathname
D:\"Program Files (x86)"\VMware\Infrastructure\"Virtual Infrastructure Client"\4.0\vmware-vmrc.exe -u $conuser -p $conpw -h $currentvmhost `"$vmxpath`"
}
 
##
#
# and now lets run all the functions
#
##
getspecs
 
ConnectVCenter($script:vcenter)
 
ChooseCluster
 
ChooseNetwork
 
ChooseDatastore
 
listspecs
 
CreateVM
 
start-sleep -s 15
 
AddToSCCM
 
start-sleep -s 15
 
start-vm $script:vmname > $NULL 2>&1
 
LaunchConsole
 
garbagecollection

**UPDATE 06/07/2011**
I added one function to actually launch the remote console (vmware-vmrc.exe). I hard-coded the path (line 222). I have both 4.0 & 4.1 installed, but used 4.0 for no particular reason, and also set the user & pass as variables, or delete the -u & -p from line 222 if you want to manually specify a user/pass each time. This user will need to reside on the host specifically (I have a script that creates the Role, as well as the user, for this). You could comment out the function call itself if you don’t want it (line 252). I know the pw is there in clear text, but if created properly, it can only view the console.