param( [string]$ParentVMName = “UP1″, [string]$vCenterHostName = “vCenterSS”, [string]$vCenterUserName= “domain\user”, [string]$vCenterUserPassword= “UP2″, [string]$cloneName = “UP3″, [string]$OSType = “Windows”, [string]$TimeZone = “035”, [string]$DHCPPortGroup = “123456”, [string]$Domain = “example.com”, [string]$DomainUserName = “user@example.com”, [string]$DomainPassword = “UP4″, [string]$AdminPassword = “changeme”, [string]$FullName = “CompanyA”, [string]$OrgName = “Example, Inc”, [string]$CustomizeTemplate1 = “$($cloneName)-Temp1″, [string]$CustomizeTemplate2 = “$($cloneName)-Temp2″, [string]$ServerDescription = “UP5″, [string]$PrimaryDNSSuffix = “example.com”, #This is a enum VM, PREFIX CUSTOM [string]$NamingScheme=”VM” ) function LoadSnapin{ param($PSSnapinName) if (!(Get-PSSnapin | where {$_.Name -eq $PSSnapinName})){ Add-pssnapin -name $PSSnapinName } } LoadSnapin -PSSnapinName “VMware.VimAutomation.Core” #Better to pass in an array and randomly select datastore function GetDataStore { $value = (Get-Random) %3 switch ($value) { 0 { return “datastore1″ } 1 { return “datastore2″ } 2 { return “datastore3″ } default {return “datastore1″} } } # Constants for status $STATUS_VM_NOT_STARTED = “VmNotStarted” $STATUS_CUSTOMIZATION_NOT_STARTED = “CustomizationNotStarted” $STATUS_STARTED = “CustomizationStarted” $STATUS_SUCCEEDED = “CustomizationSucceeded” $STATUS_FAILED = “CustomizationFailed” $STATUS_NOT_COMPLETED_LIST = @( $STATUS_CUSTOMIZATION_NOT_STARTED, $STATUS_STARTED ) # constants for event types $EVENT_TYPE_CUSTOMIZATION_STARTED = “VMware.Vim.CustomizationStartedEvent” $EVENT_TYPE_CUSTOMIZATION_SUCCEEDED = “VMware.Vim.CustomizationSucceeded” $EVENT_TYPE_CUSTOMIZATION_FAILED = “VMware.Vim.CustomizationFailed” $EVENT_TYPE_VM_START = “VMware.Vim.VmStartingEvent” # seconds to sleep before next loop iteration $WAIT_INTERVAL_SECONDS = 15 [int] $timeoutSeconds = 1200 function GetOSCustomizationSpecStatus($vm, $timeoutSeconds) { # the moment in which the script has started # the maximum time to wait is measured from this moment $startTime = Get-Date # we will check for “start vm” events 5 minutes before current moment $startTimeEventFilter = $startTime.AddMinutes(-5) # initializing list of helper objects # each object holds VM, customization status and the last VmStarting event $vmDescriptors = New-Object System.Collections.ArrayList Write-Host “Start monitoring customization process for vm ‘$vm'” $obj = “” | select VM,CustomizationStatus,StartVMEvent $obj.VM = $vm # getting all events for the $vm, # filter them by type, # sort them by CreatedTime, # get the last one $obj.StartVMEvent = Get-VIEvent -Entity $vm -Start $startTimeEventFilter | where { $_ -is $EVENT_TYPE_VM_START } | Sort CreatedTime | Select -Last 1 if (-not $obj.StartVMEvent) { $obj.CustomizationStatus = $STATUS_VM_NOT_STARTED } else { $obj.CustomizationStatus = $STATUS_CUSTOMIZATION_NOT_STARTED } [void]($vmDescriptors.Add($obj)) # declaring script block which will evaluate whether # to continue waiting for customization status update $shouldContinue = { # is there more virtual machines to wait for customization status update # we should wait for VMs with status $STATUS_STARTED or $STATUS_CUSTOMIZATION_NOT_STARTED $notCompletedVms = $vmDescriptors | where { $STATUS_NOT_COMPLETED_LIST -contains $_.CustomizationStatus } # evaluating the time that has elapsed since the script is running $currentTime = Get-Date $timeElapsed = $currentTime – $startTime $timoutNotElapsed = ($timeElapsed.TotalSeconds -lt $timeoutSeconds) # returns $true if there are more virtual machines to monitor # and the timeout is not elapsed return ( ($notCompletedVms -ne $null) -and ($timoutNotElapsed) ) } while (& $shouldContinue) { foreach ($vmItem in $vmDescriptors) { $vmName = $vmItem.VM.Name switch ($vmItem.CustomizationStatus) { $STATUS_CUSTOMIZATION_NOT_STARTED { # we should check for customization started event $vmEvents = Get-VIEvent -Entity $vmItem.VM -Start $vmItem.StartVMEvent.CreatedTime $startEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_STARTED } if ($startEvent) { $vmItem.CustomizationStatus = $STATUS_STARTED Write-Host “Customization for VM ‘$($vm)’ has started” } break; } $STATUS_STARTED { # we should check for customization succeeded or failed event $vmEvents = Get-VIEvent -Entity $vmItem.VM -Start $vmItem.StartVMEvent.CreatedTime $succeedEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_SUCCEEDED } $failedEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_FAILED } if ($succeedEvent) { $vmItem.CustomizationStatus = $STATUS_SUCCEEDED Write-Host “Customization for VM ‘$($vm)’ has successfully completed” } if ($failedEvent) { $vmItem.CustomizationStatus = $STATUS_FAILED Write-Host “Customization for VM ‘$($vm)’ has failed” } break; } default { # in all other cases there is nothing to do # $STATUS_VM_NOT_STARTED -> if VM is not started, there’s no point to look for customization events # $STATUS_SUCCEEDED -> customization is already succeeded # $STATUS_FAILED -> customization break; } } # end of switch } # end of the foreach loop Write-Host “Sleeping for $WAIT_INTERVAL_SECONDS seconds” Sleep $WAIT_INTERVAL_SECONDS } # end of while loop # preparing result, without the helper column StartVMEvent $result = $vmDescriptors | select VM,CustomizationStatus Write-Host “I’m here $result[1]” if($result[1] -ne “CustomizationSucceeded”) { write-host “Waiting for VM Tools to Start” <# do { $toolsStatus = (Get-VM $cloneName).extensiondata.Guest.ToolsStatus write-host $toolsStatus start-sleep -s 5 } until ( $toolsStatus -eq ‘toolsOk’ ) #> Write-Host “Tools are running” $InvokeAdminPassword = ConvertTo-SecureString -String $AdminPassword -asplaintext -force $cmdAddToDomain = “netdom join /d:$($Domain) $cloneName /ud:$($DomainUserName) /pd:$($DomainPassword) /reboot:30″ If ($ParentVMName -match ‘DEV1′ ) { $varAddToDomain = Invoke-VMScript -VM $cloneName -ScriptText $cmdAddToDomain -GuestUser “DevAdmin1″ -GuestPassword $InvokeAdminPassword } Else { $varAddToDomain = Invoke-VMScript -VM $cloneName -ScriptText $cmdAddToDomain -GuestUser “Admin1″ -GuestPassword $InvokeAdminPassword } $varAddToDomain = Invoke-VMScript -VM $cloneName -ScriptText $cmdAddToDomain -GuestUser “Admin1″ -GuestPassword $InvokeAdminPassword Write-Host $varAddToDomain } return $result } function AddToDomain { write-host “Waiting for VM Tools to Start” <# do { $toolsStatus = (Get-VM $cloneName).extensiondata.Guest.ToolsStatus write-host $toolsStatus start-sleep -s 5 } until ( $toolsStatus -eq ‘toolsOk’ ) #> Write-Host “Tools are running” Write-Host “Add to Domain using Netdom” $InvokeAdminPassword = ConvertTo-SecureString -String $AdminPassword -asplaintext -force $cmd = “netdom join /d:$($Domain) $cloneName /ud:$($DomainUserName) /pd:$($DomainPassword) /reboot:30″ If ($ParentVMName -match ‘Dev1′ ) { $netDomOutPut = Invoke-VMScript -VM $cloneName -ScriptText $cmd -GuestUser “DevAdmin1″ -GuestPassword $InvokeAdminPassword } Else { $netDomOutPut = Invoke-VMScript -VM $cloneName -ScriptText $cmd -GuestUser “Admin1″ -GuestPassword $InvokeAdminPassword } $netDomOutPut = Invoke-VMScript -VM $cloneName -ScriptText $cmd -GuestUser “Admin1″ -GuestPassword $InvokeAdminPassword $date = Get-Date -Format MM-dd-yy #Adding output of netdom for Scorch to consume Add-Content -path “F:\Logs\NetDomOutput\$($cloneName)-$($date).txt” -value $netDomOutPut -force ipconfig /registerdns } Connect-VIServer -Server $vCenterHostName -User $vCenterUserName -Password $vCenterUserPassword #Creates the VM $sourceVM = Get-VM $ParentVMName | Get-View $cloneFolder = $sourceVM.parent $cloneSpec = new-object Vmware.Vim.VirtualMachineCloneSpec $cloneSpec.Snapshot = $sourceVM.Snapshot.CurrentSnapshot $cloneSpec.Location = new-object Vmware.Vim.VirtualMachineRelocateSpec #Creates Linked clone $cloneSpec.Location.DiskMoveType = [Vmware.Vim.VirtualMachineRelocateDiskMoveOptions]::createNewChildDiskBacking #Defines the Datastore, calls a function to get a specific datastore $Datastore = GetDataStore $cloneSpec.Location.Datastore = (Get-View -ViewType Datastore -Property Name -Filter @{“Name”=$DataStore}).MoRef $sourceVM.CloneVM_Task( $cloneFolder, $cloneName, $cloneSpec ) $Spec1 = New-OSCustomizationSpec -FullName $FullName -OrgName $OrgName -OSType $OSType -ChangeSID -Name $CustomizeTemplate1 -Type NonPersistent -WorkGroup WK -AdminPassword $AdminPassword -TimeZone $TimeZone -Description $ServerDescription -LicenseMode PerSeat -DnsSuffix $PrimaryDNSSuffix Start-Sleep -s 4 $Spec1 = Get-OSCustomizationSpec $CustomizeTemplate1 Start-Sleep -s 4 Get-VM $cloneName | Set-VM -OSCustomizationSpec $Spec1 -Confirm:$false -Description $ServerDescription | Start-VM $NIC = Get-NetworkAdapter -VM $cloneName Set-NetworkAdapter -NetworkAdapter $NIC -Connected $false -StartConnected $false -Confirm:$false Start-Sleep -s 4 GetOSCustomizationSpecStatus -Vm $cloneName -timeoutSeconds 1200 $a = Get-VM -Name $cloneName Stop-VM -VM $a -confirm:0 Start-Sleep -m 250 while ($a.PowerState -eq “PoweredOn”) { Write-Host “Sleeping for 5 seconds” $a = get-vm -Name $cloneName start-sleep -s 5 } Get-VM $cloneName | Start-VM Start-Sleep 30 $NIC = Get-NetworkAdapter -VM $cloneName Set-NetworkAdapter -NetworkAdapter $NIC -NetworkName $DHCPPortGroup -Connected $true -StartConnected $true -Confirm:$false Start-Sleep 30 #GetOSCustomizationSpecStatus -Vm $cloneName -timeoutSeconds 1200 AddToDomain Remove-OSCustomizationSpec -OSCustomizationSpec $Spec1 -Confirm:$false Disconnect-VIServer -Server $vCenterHostName -Confirm:$false | out-null