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.
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
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.
Thanks for posting this- much appreciated. We’re currently trying to redo the way we deploy VMs and this will be very helpful. Thumbs up!
This script has been very helpful to me, many thanks for sharing it.
Wonderful post, found exactly what I needed. Does this script work with ConfigMgr 2012?
I’m not sure, I haven’t tested it against 2012. I have a feeling it won’t, since Microsoft basically did a ground-up rewrite of Config Manager. I might be able to look through WMI on a ConfigMgr machine to figure it out and update if needed, I just don’t have access to one anymore :(
Hi Luke,
Fantastic script…this would be really handy if you could re-write it for ConfigMgr 2012? :)
I’d like to do that. My role changed and I don’t do much with SCCM anyone. I’m hoping to fix that, soon, though. Maybe I’ll get a chance to do it some day. :-)
Thanks for the script. It was a great starting point, and made an early proof of concept so that i knew what I was trying to do would be possible. Much of it had to be rewrote for newer vmware and sccm, the functions are still similar.
For anyone else that makes it as far as my comment, you could also call for variables to throw into your OSD task sequence