我承担了在VMware中克隆大量虚拟机的任务。我不想通过克隆向导点击数百次,而是希望尽可能自动化。
我已经配置了模板机并且已经“密封”了。一旦它被克隆并打开电源,新克隆的实例将启动,在系统中进行一段时间,然后进行系统预测等。这需要大约20分钟左右。
我找到了一个非常好的script over on MSFT TechNet,可以完成我需要做的一切。我已经对它进行了一些修改,因此我不必更改值并为该过程的每个步骤重新保存脚本。在整个脚本中我没有使用$ Step计数器,而是将其替换为一些启动 - 睡眠延迟。此时,它工作正常,并成功克隆从CSV文件中读取的所有计算机。每台机器在准备好之前大约需要35分钟(机器被移动到不同的AD OU)。
唯一的问题是它正在以串行方式运行,等待整个进程(克隆,更改VLAN,启动计算机并等待域加入,并将最终机器对象移动到另一个AD OU),然后再开始另一个克隆。< / p>
我真正喜欢的是多线程,以使整个操作更快。我在测试中发现,一旦大约五个单独的克隆作业正在运行,vSphere中的克隆开始变慢,所以我想修改此脚本以一次运行四个克隆(完成整个工作流程)。
有什么想法吗?如果需要,我可以粘贴代码。
答案 0 :(得分:2)
您可以从一个模板最多并行克隆8个vms。如果使用-RunAsync运行new-vm。使用-RunAsync该命令立即返回,输出包含一个或多个Task对象。
如果要克隆多个VM以下应该有所帮助。只是循环它。
Write-host "Deploying VM " -ForegroundColor Green -NoNewline; Write-Host $vmname -ForegroundColor Yellow
get-OScustomizationspec $cs | get-OScustomizationNicMapping | set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $vm.IP -SubnetMask $vm.subnet -DefaultGateway $vm.gateway -Dns $vm.Dns1, $vm.Dns2
$vms = New-VM -Name $vm.Name -Location $vm.cluster -VMhost $vm.vmhost -Template $vm.template -Datastore $vm.datastore -OSCustomizationSpec $cs -confirm:$false **-RunAsync**
if ($vm1.error) {
Write-Host "Error in deploying $vmname" -ForegroundColor Red
}
答案 1 :(得分:0)
以下是我过去几天编写的脚本,该脚本将VM模板部署到大约650台服务器中。它从全国各地不同数据中心中的3个不同VM主机部署它。它同时从Irving位置部署了5个,从Plano部署了15个,从Atlanta部署了15个。它的运行时间为周一至周五的晚上7点至凌晨6点,以及周六和周日的全天。如果再运行其他时间,则退出。
在尝试使用Start-Job调用该函数时如何找出多个参数传递给函数时,我用光了时间,所以我只是为这三个位置创建了一个单独的函数。
使用此脚本,我们能够在大约一周的晚上和周末将新的RDC映像部署到650多个位置。
param( [Parameter(ValueFromPipelineByPropertyName = $ true)] [string] $ InputFile = $ null )
Get-Module -ListAvailable VMware* | Import-Module | Out-Null
Import-Module ActiveDirectory
$Global:CompletedHosts = @()
$MAXVMHostCount = 50
$IrvingMaxJobs = 6
$PlanoMaxJobs = 15
$AtlantaMaxJobs = 15
Function Add-VMHosts() {
param(
[Parameter(Mandatory=$true)][int]$MAXVMHostCount,
[Parameter(Mandatory=$false)][string]$InputFile
)
$AllVMHosts = @()
If ($InputFile) {
$AllVMHosts = Get-Content $InputFile
}
Else {
$Jobs = (Get-Job).Name
If ($Jobs -ne $null) {
$Jobs = $Jobs.Trim("-TXINTATL")
}
ForEach ($Server in (Get-ADComputer -Server *************** -SearchBase "OU=************************" -Filter { Name -like "**********" })) {
If ($Server.Name.Substring(10,1) -eq "0") {
$IP = "10." + $Server.Name.Substring(11,1) + "."
}
Else {
$IP = "10." + $Server.Name.Substring(10,2) + "."
}
If ($Server.Name.Substring(12,2) -eq "00") {
$IP += "100"
}
ElseIf ($Server.Name.Substring(12,1) -eq "0") {
$IP += $Server.Name.Substring(13,1)
}
Else {
$IP += $Server.Name.Substring(12,2)
}
$IP += ".252"
If ($IP -notin $Global:CompletedHosts -and $IP -notin $Jobs) {
$AllVMHosts = $AllVMHosts + $IP
}
}
}
Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null
$CurrentVMHostCount = (Get-VMHost -Location (Get-Datacenter "Profit Centers")).Count
$HostCount = 0
ForEach ($VMHost in $AllVMHosts) {
If ($HostCount -ge ($MaxVMHostCount - $CurrentVMHostCount)) {
Break
}
Else {
$AddFailed = $false
$ConnectFailed = $false
$Test = $null
$Test = Get-VMHost $VMHost -ErrorAction SilentlyContinue
If (!$Test -and (Test-Connection $VMHost -Quiet)) {
Try {
Connect-VIServer $VMHost -User "********************" -Password "********************" -WarningAction SilentlyContinue | Out-Null
}
Catch {
$ConnectFailed = $true
}
If (!$ConnectFailed -and (Get-VMHost $VMHost -ErrorAction SilentlyContinue | Get-VM -ErrorAction SilentlyContinue | Where { $_.Name -like "*********************" }) -eq $null -and (Test-Connection $VMHost -Quiet)) {
Set-VMHost -VMHost $VMHost -LicenseKey "********************" | Out-Null
Disconnect-VIServer -Server $VMHost -Confirm:$false | Out-Null
Add-VMHost $VMHost -Location (Get-DataCenter -Name "Profit Centers") -User "********************" -Password "********************" -Force:$true -ErrorAction SilentlyContinue | Out-Null
Start-Sleep -Seconds 5
Write-Host "$VMHost added to vCenter successfully"
$myVMHost = Get-VMHost $VMHost
$myVMHost | Get-VirtualPortGroup | Get-NicTeamingPolicy | Set-NicTeamingPolicy -InheritLoadBalancingPolicy $true -InheritNetworkFailoverDetectionPolicy $true -InheritNotifySwitches $true -InheritFailback $true -InheritFailoverOrder $true -WarningAction SilentlyContinue | Out-Null
$myVMHost | Get-VirtualPortGroup | Get-SecurityPolicy | Set-SecurityPolicy -AllowPromiscuousInherited $true -ForgedTransmitsInherited $true -MacChangesInherited $true | Out-Null
ForEach ($PortGroup in (Get-VirtualPortGroup -VMHost $VMHost)) {
$netSys = Get-View (Get-VMHost -Name $VMHost).ExtensionData.ConfigManager.NetworkSystem
$spec = (Get-VirtualPortGroup -Name $PortGroup.Name -VMHost $VMHost).ExtensionData.Spec
$spec.Policy.ShapingPolicy.Enabled = $null
$netSys.UpdatePortgroup($PortGroup.Name,$spec)
}
$DisconnectedNICs = Get-VMHostNetworkAdapter -VMHost $VMHost -Physical | Where-Object { $_.BitRatePerSec -eq "0" }
If (($DisconnectedNICs.Count -gt 0)) {
If (($DisconnectedNICs.DeviceName).Contains("vmnic0")) {
$myVMHost | Get-VirtualSwitch -Name vSwitch0 | Get-NicTeamingPolicy | Set-NicTeamingPolicy -MakeNicActive vmnic1,vmnic0 | Out-Null
}
}
$HostCount++
}
ElseIf ($ConnectFailed) {
Write-Host "Failed to connect to $VMHost" -ForegroundColor Yellow
}
Else {
Write-Host "$VMHost already has RDC Image" -ForegroundColor Yellow
If ($VMHost.Name -notin $Global:CompletedHosts) {
$Global:CompletedHosts = $Global:CompletedHosts + $VMHost.Name
}
}
}
Else {
Write-Host "$VMHost already exists in vCenter" -ForegroundColor Yellow
}
}
}
Get-AlarmDefinition "Network uplink redundancy lost" -ErrorAction SilentlyContinue | Set-AlarmDefinition -Enabled $false | Out-Null
Get-AlarmDefinition "Network uplink redundancy lost" -ErrorAction SilentlyContinue | Set-AlarmDefinition -Enabled $true | Out-Null
Disconnect-VIServer -Server * -Confirm:$false
}
Function CopyVMToHostIrving([string]$VMHost) {
Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null
If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null) {
$NewVMName = "CED-RDC-PC" + (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" }).Name.Substring(9,4)
New-VM -VMHost $VMHost -Name $NewVMName -Datastore (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" } | Get-Datastore).Name -Template "W2k19Std-Template-TX" -OSCustomizationSpec "Windows Server 2019 Customizations" | Out-Null
Start-Sleep -Seconds 10
Start-VM $NewVMName | Out-Null
Start-Sleep -Seconds 5
If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null -or (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }).PowerState -eq “PoweredOff”) {
Exit
}
Write-Verbose -Message "Verifying that Customization for VM $NewVMName has started ..." -Verbose
While($True) {
$DCvmEvents = Get-VIEvent -Entity $NewVMName
$DCstartedEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }
If ($DCstartedEvent) {
Break
}
Else {
Start-Sleep -Seconds 5
}
}
Write-Verbose -Message "Customization of VM $NewVMName has started. Checking for Completed Status......." -Verbose
While($True) {
$DCvmEvents = Get-VIEvent -Entity $NewVMName
$DCSucceededEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }
$DCFailureEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }
If ($DCFailureEvent) {
Write-Warning -Message "Customization of VM $NewVMName failed" -Verbose
Break
}
If ($DCSucceededEvent) {
Break
}
Start-Sleep -Seconds 5
}
Write-Verbose -Message "Customization of VM $NewVMName Completed Successfully!" -Verbose
Start-Sleep -Seconds 30
Write-Verbose -Message "Waiting for VM $NewVMName to complete post-customization reboot." -Verbose
Wait-Tools -VM $NewVMName -TimeoutSeconds 500 | Out-Null
Start-Sleep -Seconds 30
Shutdown-VMGuest $NewVMName -Confirm:$false | Out-Null
}
Disconnect-VIServer -Server * -Confirm:$false
}
Function CopyVMToHostPlano([string]$VMHost) {
Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null
If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null) {
$NewVMName = "CED-RDC-PC" + (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" }).Name.Substring(9,4)
New-VM -VMHost $VMHost -Name $NewVMName -Datastore (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" } | Get-Datastore).Name -Template "W2k19Std-Template-INT" -OSCustomizationSpec "Windows Server 2019 Customizations" | Out-Null
Start-Sleep -Seconds 10
Start-VM $NewVMName | Out-Null
Start-Sleep -Seconds 5
If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null -or (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }).PowerState -eq “PoweredOff”) {
Exit
}
Write-Verbose -Message "Verifying that Customization for VM $NewVMName has started ..." -Verbose
While($True) {
$DCvmEvents = Get-VIEvent -Entity $NewVMName
$DCstartedEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }
If ($DCstartedEvent) {
Break
}
Else {
Start-Sleep -Seconds 5
}
}
Write-Verbose -Message "Customization of VM $NewVMName has started. Checking for Completed Status......." -Verbose
While($True) {
$DCvmEvents = Get-VIEvent -Entity $NewVMName
$DCSucceededEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }
$DCFailureEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }
If ($DCFailureEvent) {
Write-Warning -Message "Customization of VM $NewVMName failed" -Verbose
Break
}
If ($DCSucceededEvent) {
Break
}
Start-Sleep -Seconds 5
}
Write-Verbose -Message "Customization of VM $NewVMName Completed Successfully!" -Verbose
Start-Sleep -Seconds 30
Write-Verbose -Message "Waiting for VM $NewVMName to complete post-customization reboot." -Verbose
Wait-Tools -VM $NewVMName -TimeoutSeconds 500 | Out-Null
Start-Sleep -Seconds 30
Shutdown-VMGuest $NewVMName -Confirm:$false | Out-Null
}
Disconnect-VIServer -Server * -Confirm:$false
}
Function CopyVMToHostAtlanta([string]$VMHost) {
Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null
If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null) {
$NewVMName = "CED-RDC-PC" + (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" }).Name.Substring(9,4)
New-VM -VMHost $VMHost -Name $NewVMName -Datastore (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-DC-PC*" } | Get-Datastore).Name -Template "W2k19Std-Template-ATL" -OSCustomizationSpec "Windows Server 2019 Customizations" | Out-Null
Start-Sleep -Seconds 10
Start-VM $NewVMName | Out-Null
Start-Sleep -Seconds 5
If ((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -eq $null -or (Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }).PowerState -eq “PoweredOff”) {
Exit
}
Write-Verbose -Message "Verifying that Customization for VM $NewVMName has started ..." -Verbose
While($True) {
$DCvmEvents = Get-VIEvent -Entity $NewVMName
$DCstartedEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }
If ($DCstartedEvent) {
Break
}
Else {
Start-Sleep -Seconds 5
}
}
Write-Verbose -Message "Customization of VM $NewVMName has started. Checking for Completed Status......." -Verbose
While($True) {
$DCvmEvents = Get-VIEvent -Entity $NewVMName
$DCSucceededEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }
$DCFailureEvent = $DCvmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }
If ($DCFailureEvent) {
Write-Warning -Message "Customization of VM $NewVMName failed" -Verbose
Break
}
If ($DCSucceededEvent) {
Break
}
Start-Sleep -Seconds 5
}
Write-Verbose -Message "Customization of VM $NewVMName Completed Successfully!" -Verbose
Start-Sleep -Seconds 30
Write-Verbose -Message "Waiting for VM $NewVMName to complete post-customization reboot." -Verbose
Wait-Tools -VM $NewVMName -TimeoutSeconds 500 | Out-Null
Start-Sleep -Seconds 30
Shutdown-VMGuest $NewVMName -Confirm:$false | Out-Null
}
Disconnect-VIServer -Server * -Confirm:$false
}
$Functions = [scriptblock]::Create(@"
Function CopyVMToHostIrving { $Function:CopyVMToHostIrving([string]$myVMHost) }
Function CopyVMToHostPlano { $Function:CopyVMToHostPlano([string]$myVMHost) }
Function CopyVMToHostAtlanta { $Function:CopyVMToHostAtlanta([string]$myVMHost) }
"@)
$TotalHostNum = (Get-ADComputer -Server ******************** -SearchBase "OU=********************" -Filter { Name -like "*CED-SQL-PC*" }).Count
While ($Global:CompletedHosts.Count -lt $TotalHostNum) {
Connect-VIServer ******************** -User "********************" -Password "********************" | Out-Null
Write-Host "Removing completed hosts from vCenter..."
ForEach ($VMHost in (Get-VMHost -Location (Get-Datacenter "Profit Centers") | Where-Object { $_.Parent -ne "Upgrade Failures" })) {
If (((Get-VMHost $VMHost | Get-VM | Where { $_.Name -like "CED-RDC-PC*" }) -ne $null -and $VMHost.Name -notin (Get-Job -State Running).Name.Trim("-TXINTATL")) -or (Get-VMHost $VMHost).ConnectionState -ne "Connected") {
Remove-VMHost $VMHost -Confirm:$false | Out-Null
If ($VMHost.Name -notin $Global:CompletedHosts) {
$Global:CompletedHosts = $Global:CompletedHosts + $VMHost.Name
}
}
}
Write-Host "Adding additional hosts to vCenter..."
If ($InputFile) {
Add-VMHosts -MAXVMHostCount $MAXVMHostCount -InputFile $InputFile
}
Else {
Add-VMHosts -MAXVMHostCount $MAXVMHostCount
}
$VMHosts = (Get-VMHost -Location (Get-Datacenter "Profit Centers") | Where-Object { $_.Parent -ne "Upgrade Failures" })
Disconnect-VIServer -Server * -Confirm:$false
ForEach ($VMHost in $VMHosts) {
Write-Host "Checking if max job count has been reached..."
While ((Get-Job -State Running).Count -ge ($IrvingMaxJobs + $PlanoMaxJobs + $AtlantaMaxJobs)) {
If (((Get-Date).hour -ge 6 -and (Get-Date).hour -lt 18) -and (Get-Date).DayOfWeek -ne "Saturday" -and (Get-Date).DayOfWeek -ne "Sunday") {
Write-Host "Total count of hosts that new VM was copied to: $Global:CompletedHosts.Count"
Exit
}
Else {
Start-Sleep -Seconds 60
}
}
Write-Host "Removing completed jobs..."
ForEach ($Job in (Get-Job -State Completed)) {
If ($Job.Name.Trim("-TXINTATL") -notin $GlobalCompletedHosts) {
$Global:CompletedHosts = $Global:CompletedHosts + $Job.Name.Trim("-TXINTATL")
}
Remove-Job -Id $Job.Id
}
Write-Host "Starting jobs..."
If ((Get-Job | Where { $_.Name.Trim("-TXINTATL") -eq $VMHost.Name }) -eq $null -and $VMHost.Name -notin $Global:CompletedHosts) {
If ((Get-Job | Where { $_.Name -like "*-TX" }).Count -lt $IrvingMaxJobs) {
Write-Host "Starting job for $VMHost using Irving Source" -ForegroundColor Yellow
Start-Job -InitializationScript $Functions -Script { CopyVMToHostIrving($Args[0]) } -ArgumentList $VMHost.Name -Name ($VMHost.Name + "-TX") | Out-Null
}
ElseIf ((Get-Job | Where { $_.Name -like "*-INT" }).Count -lt $PlanoMaxJobs) {
Write-Host "Starting job for $VMHost using Plano Source" -ForegroundColor Yellow
Start-Job -InitializationScript $Functions -Script { CopyVMToHostPlano($Args[0]) } -ArgumentList $VMHost.Name -Name ($VMHost.Name + "-INT") | Out-Null
}
ElseIf ((Get-Job | Where { $_.Name -like "*-ATL" }).Count -lt $AtlantaMaxJobs) {
Write-Host "Starting job for $VMHost using Atlanta Source" -ForegroundColor Yellow
Start-Job -InitializationScript $Functions -Script { CopyVMToHostAtlanta($Args[0]) } -ArgumentList $VMHost.Name -Name ($VMHost.Name + "-ATL") | Out-Null
}
}
}
}