MKguy
Virtuoso
Virtuoso

I do that in a custom deployment script too, it's really not difficult.

I would post the whole script if it was a bit less accommodated to our environment and less horribly coded by my hunble self, also there's probably a few better examples out there. But here are a few snippets which you should be able to recycle in your script.

First, I'm setting all the custom VM parameters like how many vCPUs etc, you could also read that from a CSV-file or something:

 $displayname = "My_Test_VM" #vCenter Name
$vcpu = 2 $memMB = 2048
$Disk1GB
= 30
$Disk2GB = 20  # 0 = no 2nd/3rd disk
$Disk3GB
= 0    # 0 = no 2nd/3rd disk $haprio = "ClusterRestartPriority" #ClusterRestartPriority, Disabled, Low, Medium, High
$prodtest
= "PROD" # PROD or TEST

$description
= "testvm scripted"
$sourcetemplate = "W2k8R2-Template" #vCenter name of the template
$responsible
= "Mr. Nameless"
$network
=  "VLAN_311" #Name of the vSSwitch/dvSwitch Portgroup
$NetIP = "10.1.1.72"
$NetMask
= "255.255.255.0"
$NetGW = "10.1.1.1"
$NetDNS
= ("10.10.1.20", "10.10.1.21", "10.10.1.22") $NetWins = "10.10.1.25" $computername = "MyNETBIOSName"
$domainfqdn
= "mydomain.local"
$localadmingroup
= "MyADAdmins"

$RunOnce = (     "net config server /SRVCOMMENT:`"$description`"",     "netsh interface set interface name=`"Local Area Connection`" newname=`"NIC1 - $NetIP`"",     "c:\install\someagent.exe /install"

)

Then I have a couple of functions or code blocks doing the actual work, here are a few (UpdateVMSettings is probably what you want):

if($prodtest -eq "PROD") {
    $cluster = (Get-Cluster ProductiveCluster)
    $datastore = (Get-DatastoreCluster MyDRSCluster)
}
elseif($prodtest -eq "TEST") {
    $cluster = (Get-Cluster TestCluster)
    $datastore = ($cluster | Get-VMHost | Get-Datastore TEST_LUN_* | Sort -Property FreeSpaceMB | Select -last 1)
}

function Get-LocalAdminCreds {
    Write-Host "Enter Password for local admin of the VM:`n"    return (Get-Credential administrator)
}

function CreateTemplateSpec {
    New-OSCustomizationSpec -Type NonPersistent -Name "Tempspec" -OSType Windows -Description "Temp scripted Spec $description" -FullName "My Name" -OrgName "My Company" -NamingScheme Fixed -NamingPrefix $computername -AdminPassword $localadmincreds.GetNetworkCredential().password -TimeZone 110 -ChangeSid -Domain $domainfqdn -DomainCredentials $adcredentials -GuiRunOnce $RunOnce -AutoLogonCount 1    Get-OSCustomizationSpec Tempspec | Get-OSCustomizationNicMapping | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IPAddress $NetIP -SubnetMask $NetMask -DefaultGateway $NetGW -Dns $NetDNS -Wins $NetWins}

function UpdateVMSettings {
    Write-Host "Changing Portgroup and virtual Hardware..."
    $PortGroup = ($vm | Get-VMHost | Get-VirtualPortGroup -Name $network)     Get-NetworkAdapter -VM $vm | Set-NetworkAdapter -NetworkName $PortGroup -StartConnected $true -Confirm:$false
   
Set-VM $vm -MemoryMB $memMB -NumCpu $vcpu -Confirm:$false

   
Get-HardDisk -VM $vm | Set-HardDisk -CapacityKB ($Disk1GB * 1MB) -Confirm:$false   
    if
($Disk2GB -gt 0) { New-HardDisk -VM $vm -CapacityKB ($Disk2GB * 1MB) -Storageformat Thin -Confirm:$false }     if($Disk3GB -gt 0) { New-HardDisk -VM $vm -CapacityKB ($Disk3GB * 1MB) -Storageformat Thin -Confirm:$false }     Set-Annotation $vm -CustomAttribute "Created on" -Value (Get-Date -uformat %d.%m.%Y) -Confirm:$false
   
Set-Annotation $vm -CustomAttribute "Created by" -Value $env:username -Confirm:$false
    Set-Annotation $vm -CustomAttribute "System owner" -Value $responsible -Confirm:$false
}
New-VM -Name $displayname -Location $vclocation -ResourcePool $cluster -Datastore $datastore -Description $description -Template $sourcetemplate -OSCustomizationspec Tempspec -HARestartPriority $haprio -DiskStorageFormat Thin

The script is also taking care checking for existing AD computers, used IPs or vCenter names, updates the AD computeraccount description afterwards and adds DRS VM group membership, stuff which I've not included here for reasons mentioned above.

Here's also snippet that will run scripts inside the guest via Invoke-VMScript to add a local admins, extend C:\ and partition other disks:

function GuestScript {
    param(
        [string]$admingroup, 
        [string]$domain,
        [bool]$extend,
        [bool]$format    )

    if(($admingroup -ne "") -and ($domain -ne "")) {
        Write-Host "Adding $admingroup to local admin group..." 
        Invoke-VMScript -ScriptText "([adsi]`"WinNT://./Administrators,group`").Add(`"WinNT://$domain/$admingroup,group`")" -VM $vm -GuestCredential $localadmincreds    }

    if($extend -eq 1) {
        Write-Host "Extending partition C:\"        Invoke-VMScript -ScriptText 'out-file c:\install\diskpartscript.txt -Encoding ASCII -InputObject "select volume 2`r`n extend`r`n"' -VM $vm -GuestCredential $localadmincreds        Invoke-VMScript -ScriptText "diskpart /s c:\install\diskpartscript.txt" -ScriptType bat -VM $vm -GuestCredential $localadmincreds    }

    if($format -eq 1) {
        Write-Host "Partitioning other disks..."
       
$script = '        $NumDisks = (Get-Wmiobject -class "Win32_DiskDrive").count         for($CurrentDisk = 1; $NumDisks -gt $CurrentDisk; $CurrentDisk++) {             for($j=67; gdr($NextFreeLetter = [char]++$j) 2>$null) {}             out-file c:\install\diskpartscript.txt -Encoding ASCII -InputObject "select disk $CurrentDisk`r`n online disk noerr`r`n attributes disk clear readonly`r`n clean`r`n create partition primary`r`n format fs=ntfs quick`r`n assign letter=$NextFreeLetter"             diskpart /s c:\install\diskpartscript.txt         } '   
    
Invoke-VMScript -ScriptText $script -VM $vm -GuestCredential $localadmincreds     } }
-- http://alpacapowered.wordpress.com