使用PowerShell批量克隆VMware Workstation虚拟机

本文将介绍如何使用PowerShell脚本和YAML配置文件克隆和配置VMware虚拟机。

在开始之前,请确保:

  • 安装VMware Workstation;
  • 有一个配置好的虚拟机,用作克隆源;
  • 安装PowerShell。

YAML配置文件

首先,需要一个YAML配置文件来定义虚拟机克隆的参数。配置文件(vmware-deploy.yaml)包含虚拟机组的名称、存储目录、克隆源信息,以及每个虚拟机的名称、hostname、网络和hosts设置。

以下是YAML配置示例:

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
version: '0.1'

description: Example configuration for clone VMware virtual machines(ubuntu).

#
vmGroup:
name: example
directory: C:\ProgramsData\VMware\Virtual Machines\example
cloneVMSource:
name: Ubuntu22.04.3-dev-000
directory: C:\ProgramsData\VMware\Virtual Machines\basic
instances:
- name: Ubuntu22.04.3-example-01
hostname: example01
ip : 192.168.226.221/24
gateway: 192.168.226.2
dns: 192.168.226.2,1.1.1.1
hostEntries:
- '#The following lines are used for vm_group example'
- 192.168.226.221 example01
- 192.168.226.222 example02
- 192.168.226.223 example03
- name: Ubuntu22.04.3-example-02
hostname: example02
ip : 192.168.226.222/24
gateway: 192.168.226.2
dns: 192.168.226.2,1.1.1.1
hostEntries:
- '#The following lines are used for vm_group example'
- 192.168.226.221 example01
- 192.168.226.222 example02
- 192.168.226.223 example03
- name: Ubuntu22.04.3-example-03
hostname: example03
ip : 192.168.226.223/24
gateway: 192.168.226.2
dns: 192.168.226.2,1.1.1.1
hostEntries:
- '#The following lines are used for vm_group example'
- 192.168.226.221 example01
- 192.168.226.222 example02
- 192.168.226.223 example03

PowerShell脚本

vmware-tools.ps1接收YAML文件作为输入,执行以下功能:

  • 克隆虚拟机;
  • 设置hostname、网络和hosts文件;
  • 虚拟机启动、停止和暂停功能。
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
param (
[string]$command="deploy",
[string]$file="./vmware-deploy.yaml",
[string]$vmrunPath= "C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe",
[string]$username = "root",
[string]$password = "Abc@12345"
)

$module = Get-Module -ListAvailable -Name powershell-yaml
if (-not $module) {
try {
Install-Module -Name powershell-yaml -Scope CurrentUser -Force -ErrorAction Stop
} catch {
Write-Error "Failed to install the powershell-yaml module. Please check."
return
}
}

function GetVmxPath {
param (
[ValidateNotNullOrEmpty()]
[string]$directory,
[ValidateNotNullOrEmpty()]
[string]$name
)
return Join-Path $directory $name ($name + ".vmx")
}

function CheckAllVMsForDeploy {
param (
[array]$instances,
[string]$sourceLocation,
[string]$targetLocation
)
$sourceVmx = GetVmxPath -directory $sourceLocation -name $config.vmGroup.cloneVMSource.name
if (-not (Test-Path $sourceVmx)) {
Write-Error "The source virtual machine does not exist: $sourceVmx"
return $false
}
foreach ($instance in $instances) {
$destVmx = GetVmxPath -directory $targetLocation -name $instance.name
if (Test-Path $destVmx) {
Write-Error "The target virtual machine already exists: $destVmx"
return $false
}
}
return $true
}

function CheckAllVMs {
param (
[array]$instances,
[string]$targetLocation
)
foreach ($instance in $instances) {
$destVmx = GetVmxPath -directory $targetLocation -name $instance.name
if (-not (Test-Path $destVmx)) {
Write-Error "The target virtual machine does not exists.: $destVmx"
return $false
}
}
return $true
}

function CheckVMStartedStatus{
param (
[string]$vmxPath ,
[int]$maxChecks = 5
)
$checkCount = 0
do {
$runningVMs = & $vmrunPath list
$vmStarted = $runningVMs -match [regex]::Escape($vmxPath)
$checkCount++
if (-not $vmStarted) {
Write-Host "Waiting for the virtual machine to start... ($checkCount/$maxChecks)"
Start-Sleep -Seconds 5
}
} while (-not $vmStarted -and $checkCount -lt $maxChecks)
if(-not $vmStarted){
Write-Error "Virtual machine failed to start:$vmxPath"
}
}

function DeployVMs{
param(
[Parameter(Mandatory=$true)]
[object]$config
)
$sourceLocation = $config.vmGroup.cloneVMSource.directory
$targetLocation = $config.vmGroup.directory
if (CheckAllVMsForDeploy -instances $config.vmGroup.instances -sourceLocation $sourceLocation -targetLocation $targetLocation) {
foreach ($instance in $config.vmGroup.instances) {
$cloneFolderPath = Join-Path $targetLocation $instance.name
$sourceVmx = GetVmxPath -directory $sourceLocation -name $config.vmGroup.cloneVMSource.name
$destVmx = GetVmxPath -directory $targetLocation -name $instance.name
if (-not (Test-Path $cloneFolderPath)) {
New-Item -Path $cloneFolderPath -ItemType Directory
}
Write-Host "Clone virtual machine: $($instance.name)"
& $vmrunPath clone $sourceVmx $destVmx full -cloneName="$($instance.name)"
Write-Host "Start virtual machine: $($instance.name)"
& $vmrunPath start $destVmx
CheckVMStartedStatus -vmxPath $destVmx
Write-Host "Modify the virtual machine's hostname: $($instance.name)"
& $vmrunPath -T ws -gu $username -gp $password runScriptInGuest $destVmx "" "echo '$($instance.hostname)' | tee /etc/hostname && hostname -F /etc/hostname"
$netplanConfig = @"
network:
version: 2
ethernets:
ens33:
dhcp4: no
addresses: [$($instance.ip)]
routes:
- to: default
via: $($instance.gateway)
nameservers:
addresses: [$($instance.dns)]
"@
Write-Host "Modify the virtual machine's network settings: $($instance.name)"
& $vmrunPath -T ws -gu $username -gp $password runScriptInGuest $destVmx "" "echo '$netplanConfig' | tee /etc/netplan/00-installer-config.yaml"
& $vmrunPath -T ws -gu $username -gp $password runScriptInGuest $destVmx "" "netplan apply"
Write-Host "Modify the virtual machine's hosts: $($instance.name)"
foreach ($hostEntry in $instance.hostEntries) {
& $vmrunPath -T ws -gu $username -gp $password runScriptInGuest $destVmx "" "echo '$hostEntry' | tee -a /etc/hosts"
}
# Write-Host "重启虚拟机: $($instance.name)"
# & $vmrunPath -T ws -gu $username -gp $password runScriptInGuest $destVmx "" "systemctl reboot"
}
Write-Host "The virtual machine group clone is complete."
} else {
Write-Error "The Deploy process has stopped due to a failed existence check."
}
}

function StartVMs {
param(
[Parameter(Mandatory=$true)]
[object]$config
)
$targetLocation = $config.vmGroup.directory
if (CheckAllVMs -instances $config.vmGroup.instances -targetLocation $targetLocation) {
foreach ($instance in $config.vmGroup.instances) {
$destVmx = GetVmxPath -directory $targetLocation -name $instance.name
Write-Host "Start the virtual machine: $($instance.name)"
& $vmrunPath start $destVmx
}
Write-Host "The virtual machine group has been successfully started"
} else {
Write-Error "The Start process has stopped due to a failed existence check."
}
}

function StopVMs {
param(
[Parameter(Mandatory=$true)]
[object]$config
)
$targetLocation = $config.vmGroup.directory
if (CheckAllVMs -instances $config.vmGroup.instances -targetLocation $targetLocation) {
foreach ($instance in $config.vmGroup.instances) {
$destVmx = GetVmxPath -directory $targetLocation -name $instance.name
Write-Host "Stop the virtual machine: $($instance.name)"
& $vmrunPath stop $destVmx
}
Write-Host "The virtual machine group has been successfully stopped"
} else {
Write-Error "The Stop process has stopped due to a failed existence check."
}
}

function SuspendVMs{
param(
[Parameter(Mandatory=$true)]
[object]$config
)
$targetLocation = $config.vmGroup.directory
if (CheckAllVMs -instances $config.vmGroup.instances -targetLocation $targetLocation) {
foreach ($instance in $config.vmGroup.instances) {
$destVmx = GetVmxPath -directory $targetLocation -name $instance.name
Write-Host "Suspend the virtual machine: $($instance.name)"
& $vmrunPath suspend $destVmx
}
Write-Host "The virtual machine group has been successfully suspende."
} else {
Write-Error "The Suspend process has stopped due to a failed existence check."
}
}

try {
# Read YAML configuration file
$configData = Get-Content $file -Raw | ConvertFrom-Yaml
switch ($command) {
"deploy" { DeployVMs -config $configData }
"start" { StartVMs -config $configData }
"stop" { StopVMs -config $configData }
"suspend" { SuspendVMs -config $configData }
default { Write-Host "Unknown command: $command" }
}
} catch {
Write-Error "An error occurred: $_"
}

如何使用

1
2
3
4
5
6
7
8
9
10
11
12
13
# depoly
.\vmware-tools.ps1
or
.\vmware-tools.ps1 -file "./vmware-deploy-another.yaml"

#start
.\vmware-tools.ps1 start

#stop
.\vmware-tools.ps1 stop

#suspend
.\vmware-tools.ps1 suspend

在使用之前,请根据你自己的需求调整。

参考资料


使用PowerShell批量克隆VMware Workstation虚拟机
https://withesse.co/post/how-to-clone-virtual-machine/
Author
zt
Posted on
May 23, 2020
Licensed under