KVM虚拟化日志

  • 首页
  • HPC
  • K8S
  • Python
  • 数据库
  • 登录
专注HPC、虚拟化、DevOps、云原生技术
  1. 首页
  2. IaC
  3. 正文

Terraform批量创建vCenter虚拟机(同时设置IP地址)

2025/09/30 72点热度 1人点赞 0条评论

经过验证,这种方法可以通过terraform批量创建虚拟机并设置IP地址,Ubuntu和RHEL都可用。

环境准备

  •  vCenter服务器(设置好数据中心、集群、主机)
  •  CentOS 7 安装镜像(上传到datastore)
  •  packer和terraform

1. 编写kickstart文件

本实验采用CentOS 7.9镜像,不同的发行版可能有细微区别,下面是一份示例

kickstart
Shell
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# Base CentOS 7.x install
firewall --disabled
selinux --disabled
#--------------------------------------------------------------------------
# set password for root
#--------------------------------------------------------------------------
rootpw --plaintext VMware1!
#--------------------------------------------------------------------------
# set bootloader and use eth0
#--------------------------------------------------------------------------
## bootloader
# elevator=noop - Use a simple FIFO queue for I/O algorithm since hypervisor will also manage this
# pci=bfsort - Breadth-first pci order for NIC enumeration
# net.ifnames=0 - Disable predictable network interface naming
# biosdevname=0 - Disables consistent network interface naming
bootloader --location=mbr --append="elevator=noop pci=bfsort net.ifnames=0 biosdevname=0 ipv6.disable=1"
 
#--------------------------------------------------------------------------
# Config network use dhcp
#--------------------------------------------------------------------------
# network --bootproto=static --ip=192.168.10.100 --netmask=255.255.255.0 --gateway=192.168.10.1 --nameserver 192.168.10.1,192.168.10.2
network --bootproto=dhcp --device=eth0 --noipv6 --onboot=yes --device=eth0 --hostname=CentOS7Template --activate
 
authconfig --enableshadow --passalgo=sha512
 
keyboard --vckeymap=us --xlayouts='us'
 
# Set language to use during installation and the default language to use on the installed system (required)
lang en_US.UTF-8 --addsupport=zh_CN.UTF-8
skipx
install
#--------------------------------------------------------------------------
# set timezone
#--------------------------------------------------------------------------
timezone Asia/Shanghai --ntpservers=3.centos.pool.ntp.org,0.centos.pool.ntp.org
eula --agreed
services --enabled=NetworkManager,sshd
 
#--------------------------------------------------------------------------
# Setup disk and LVM
#--------------------------------------------------------------------------
zerombr
 
# Only partition sda, leave other disks unpartitioned
ignoredisk --only-use=sda
clearpart --all --drives=sda
 
part /boot --fstype=xfs --size=512
part pv.01 --grow --size=1
 
volgroup sys_vg pv.01
logvol / --fstype=xfs --name=root --vgname=sys_vg --size=10240
logvol swap --name=swap --vgname=sys_vg --size=4096
logvol /tmp --fstype=xfs --name=tmp --vgname=sys_vg --size=4096
logvol /usr --fstype=xfs --name=usr --vgname=sys_vg --size=3072
logvol /var --fstype=xfs --name=var --vgname=sys_vg --size=2048
logvol /var/log --fstype=xfs --name=var_log --vgname=sys_vg --size=4096
logvol /app-data --fstype=xfs --name=app-data --vgname=sys_vg --size=1 --grow
 
#--------------------------------------------------------------------------
# Select packages for installation
#--------------------------------------------------------------------------
%packages --ignoremissing
 
Require @Base
@Base
@core
biosdevname
sed
perl
less
dmidecode
bzip2
iproute
iputils
sysfsutils
rsync
nano
mdadm
setserial
man-pages.noarch
findutils
tar
net-tools
tmpwatch
lsof
python
screen
lvm2
curl
ypbind
yp-tools
smartmontools
openssh-clients
acpid
irqbalance
which
bind-utils
ntsysv
ntp
man
open-vm-tools
vim
lrzsz
wget
tree
screen
tcpdump
#mysql
#postfix
chkconfig
gzip
%end
 
# End of %packages section
 
#--------------------------------------------------------------------------
# Run post installation script
#--------------------------------------------------------------------------
%post --log=/root/ks-post.log
 
#!/bin/sh
(
set -x
 
#--------------------------------------------------------------------------
# Disable the tiered-progress bar during boot
#--------------------------------------------------------------------------
/bin/sed -i -e 's/ rhgb//' -e 's/ quiet//' /boot/grub2/grub.cfg
/bin/sed -i -e 's/ rhgb//' -e 's/ quiet//' /etc/grub2.cfg
/bin/sed -i -e 's/ rhgb//' -e 's/ quiet//' /etc/default/grub
 
plymouth-set-default-theme text
/usr/libexec/plymouth/plymouth-update-initrd
 
#--------------------------------------------------------------------------
# Remove default user/group accounts that are not needed
#--------------------------------------------------------------------------
/usr/sbin/userdel operator
/usr/sbin/userdel games
/usr/sbin/userdel lp
/usr/sbin/userdel sync
/usr/sbin/userdel shutdown
/usr/sbin/userdel halt
/usr/sbin/groupdel games
 
#--------------------------------------------------------------------------
# Create local ops user with password "VMware1!"
#--------------------------------------------------------------------------
 
/usr/sbin/useradd ops; echo 'VMware1!' | passwd --stdin ops
/usr/sbin/usermod -a -G wheel ops
/usr/bin/chage -M -1 -E -1 ops
 
#--------------------------------------------------------------------------
# Add local ops user to sudoers
#--------------------------------------------------------------------------
/bin/cat <<'EOF'>> /etc/sudoers
 
Defaults:ops !requiretty
ops ALL=(ALL) NOPASSWD: ALL
EOF
 
#--------------------------------------------------------------------------
# sync hardware clock
#--------------------------------------------------------------------------
/usr/sbin/ntpdate ntp.ntsc.ac.cn
/sbin/hwclock --systohc --utc
 
#--------------------------------------------------------------------------
# configure NTP
#--------------------------------------------------------------------------
/bin/cat <<'EOF'> /etc/ntp.conf
restrict default ignore
restrict 127.0.0.1
driftfile /var/lib/ntp/drift
logfile /var/log/ntpd
broadcastdelay 0.008
server ntp.ntsc.ac.cn
restrict ntp.ntsc.ac.cn mask 255.255.255.255 nomodify notrap noquery
EOF
 
/bin/cat <<'EOF'> /etc/ntp/step-tickers
ntp.ntsc.ac.cn
EOF
 
/bin/touch /var/log/ntpd
 
#--------------------------------------------------------------------------
# SSHD setup
#--------------------------------------------------------------------------
/bin/sed -i /etc/ssh/sshd_config \
-e 's/^#UseDNS yes$/UseDNS no/' \
-e 's/^GSSAPIAuthentication yes$/GSSAPIAuthentication no/' \
# -e 's/^#PermitRootLogin yes/PermitRootLogin no/'
 
#--------------------------------------------------------------------------
# Increase open file limmits
#--------------------------------------------------------------------------
/bin/cat <<'EOF'>> /etc/sysctl.conf
 
# Increases maximum open file limmit
fs.file-max = 65536
 
EOF
 
/bin/cat <<'EOF'>> /etc/security/limits.conf
# Custom configuration files in /etc/security/limits.d
EOF
 
/bin/cat <<'EOF'> /etc/security/limits.d/10-nofile.conf
* soft nofile 65535
* hard nofile 65535
EOF
 
/bin/cat <<'EOF'> /etc/security/limits.d/11-stack.conf
* soft stack 65535
* hard stack 65535
EOF
 
#--------------------------------------------------------------------------
# Remove hard coded UUID + MAC from network device configs and DNS/Gateway information
#--------------------------------------------------------------------------
/bin/sed -i '/^DNS1*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e*
/bin/sed -i '/^DNS2*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e*
/bin/sed -i '/^GATEWAY*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e*
/bin/sed -i '/^HOSTNAME*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e*
/bin/sed -i '/^HWADDR*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e*
/bin/sed -i '/^NM_CONTROLLED*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e*
/bin/sed -i '/^UUID*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e*
 
/bin/mv /etc/sysconfig/network-scripts/ifcfg-e* /etc/sysconfig/network-scripts/ifcfg-eth0
/bin/sed -i 's/ens192/eth0/g' /etc/sysconfig/network-scripts/ifcfg-eth0
/bin/sed -i "s\ONBOOT=no\ONBOOT=yes\g" /etc/sysconfig/network-scripts/ifcfg-eth0
/bin/sed -i "s\IPV6INIT=yes\IPV6INIT=no\g" /etc/sysconfig/network-scripts/ifcfg-eth0
 
#--------------------------------------------------------------------------
# Update ifcfg-eth0 to use static ip address
#--------------------------------------------------------------------------
#/bin/rm -rf /etc/sysconfig/network-scripts/ifcfg-eth*
#/bin/cat <<'EOF'>> /etc/sysconfig/network-scripts/ifcfg-eth0
#TYPE=Ethernet
#PROXY_METHOD=none
#BROWSER_ONLY=no
#BOOTPROTO=static
#DEFROUTE=yes
#IPV4_FAILURE_FATAL=no
#IPV6INIT=no
#IPV6_AUTOCONF=no
#IPV6_DEFROUTE=no
#IPV6_FAILURE_FATAL=no
#IPV6_ADDR_GEN_MODE=stable-privacy
#NAME=eth0
#DEVICE=eth0
#ONBOOT=yes
#IPADDR=192.168.10.100
#NETMASK=255.255.255.0
#GATEWAY=192.168.10.1
#DNS1=192.168.10.1
#DOMAIN=corp.local
#EOF
 
#--------------------------------------------------------------------------
# Configure NetworkManager
#--------------------------------------------------------------------------
#/bin/cat <<'EOF'> /etc/NetworkManager/conf.d/11-corp.conf
#[main]
#no-auto-default=*
#dns=none
#EOF
 
#--------------------------------------------------------------------------
# Configure DNS
#--------------------------------------------------------------------------
#/bin/cat <<'EOF'> /etc/resolv.conf
#nameserver 10.208.10.1
#EOF
 
#--------------------------------------------------------------------------
# For root, disable color "ls", and use old style sorting order.
#--------------------------------------------------------------------------
touch /root/.dir_colors
 
/bin/cat <<'EOF'>> /root/.i18n
LC_COLLATE=C
EOF
 
#--------------------------------------------------------------------------
# Setup logrotate configuration
#--------------------------------------------------------------------------
/bin/cat <<'EOF'> /etc/logrotate.conf
# see "man logrotate" for details
# rotate log files monthly
monthly
 
# keep 12 months worth of backlogs
rotate 12
 
# create new (empty) log files after rotating old ones
create
 
# uncomment this if you want your log files compressed
compress
 
# RPM packages drop log rotation information into this directory
include /etc/logrotate.d
 
# no packages own wtmp -- we'll rotate them here
/var/log/wtmp {
create 0664 root utmp
}
EOF
 
#--------------------------------------------------------------------------
# Setup default yum repos
#--------------------------------------------------------------------------
 
/bin/rm -f /etc/yum.repos.d/CentOS-*
 
/bin/cat <<'EOF'> /etc/yum.repos.d/CentOS-Base.repo
[base]
name=CentOS-$releasever - Base - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#released updates
[updates]
name=CentOS-$releasever - Updates - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
 
#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
EOF
 
/bin/cat <<'EOF'> /etc/yum.repos.d/epel-7.repo
[epel]
name=Extra Packages for Enterprise Linux 7 - $basearch
baseurl=http://mirrors.aliyun.com/epel/7/$basearch
failovermethod=priority
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
 
[epel-debuginfo]
name=Extra Packages for Enterprise Linux 7 - $basearch - Debug
baseurl=http://mirrors.aliyun.com/epel/7/$basearch/debug
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck=0
 
[epel-source]
name=Extra Packages for Enterprise Linux 7 - $basearch - Source
baseurl=http://mirrors.aliyun.com/epel/7/SRPMS
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck=0
EOF
 
#--------------------------------------------------------------------------
# Remove UUID for /boot in fstab
#--------------------------------------------------------------------------
/bin/sed -i "s/UUID*..*\/boot/\/dev\/sda1\t\t\/boot/" /etc/fstab
 
#--------------------------------------------------------------------------
# Symlink /var/tmp to /tmp
#--------------------------------------------------------------------------
/bin/rm -rf /var/tmp
/bin/ln -s /tmp /var/tmp
 
#--------------------------------------------------------------------------
# Enable or Disable Specific OS Services/Daemons
#--------------------------------------------------------------------------
/usr/bin/systemctl enable autofs
/usr/bin/systemctl enable ntpd
/usr/bin/systemctl disable firewalld.service
/usr/bin/systemctl disable auditd
/usr/bin/systemctl disable mdmonitor
/usr/bin/systemctl disable postfix
/usr/bin/systemctl disable abrt-ccpp.service
/usr/bin/systemctl disable abrt-oops.service
/usr/bin/systemctl disable abrt-vmcore.service
/usr/bin/systemctl disable abrt-xorg.service
/usr/bin/systemctl disable abrtd.service
/usr/bin/systemctl disable iscsi.service
/usr/bin/systemctl disable iscsid.socket
/usr/bin/systemctl disable iscsiuio.socket
/usr/bin/systemctl disable libstoragemgmt.service
/usr/bin/systemctl disable multipathd.service
/usr/bin/systemctl disable wpa_supplicant.service
 
#--------------------------------------------------------------------------
# End of post
#--------------------------------------------------------------------------
) 2>&1
%end
 
 
# Reboot after the installation is complete (optional)
# --eject attempt to eject CD or DVD media before rebooting
reboot --eject

 

### 2. 编写packer配置文件centos-vsphere.json
如下是packer的配置文件,在其中指定
1. vCenter的连接信息(包括地址、用户名、密码)
2. 镜像信息(datastore中的操作系统安装ISO路径)
3. 虚拟机模板基本信息(名称、vm-version版本、所在数据中心、集群等)
4. 虚拟机配置信息(CPU、内存、硬盘、网卡、显存等)

需要根据需要修改和自定义,vm-version和esxi对应关系可以参考官方文档[ESXi hosts and compatible virtual machine hardware versions](https://knowledge.broadcom.com/external/article?legacyId=2007240)

vsphere.json
JavaScript
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
{
"builders": [
{
"CPUs": "{{user `vm-cpu-num`}}",
"RAM": "{{user `vm-mem-size`}}",
"RAM_reserve_all": false,
"boot_command": [
"",
"linux ks=hd:fd0:/ks.cfg"
],
"boot_order": "disk,cdrom,floppy",
"boot_wait": "10s",
"cluster": "{{user `vsphere-cluster`}}",
"convert_to_template": true,
"datacenter": "{{user `vsphere-datacenter`}}",
"datastore": "{{user `vsphere-datastore`}}",
"disk_controller_type": "pvscsi",
"floppy_files": [
"ks.cfg"
],
"folder": "{{user `vm-folder`}}",
"guest_os_type": "centos7_64Guest",
"insecure_connection": "true",
"iso_paths": [
"{{user `iso_url`}}"
],
"network_adapters": [
{
"network": "{{user `vsphere-network`}}",
"network_card": "vmxnet3"
}
],
"notes": "Build via Packer in {{ (isotime ) }}",
"password": "{{user `vsphere-password`}}",
"ssh_password": "VMware1!",
"ssh_username": "root",
"storage": [
{
"disk_size": "{{user `vm-disk-size`}}",
"disk_thin_provisioned": true
}
],
"type": "vsphere-iso",
"username": "{{user `vsphere-user`}}",
"vcenter_server": "{{user `vsphere-server`}}",
"video_ram": "{{user `vm-video-ram`}}",
"vm_name": "{{user `vm-name`}}-{{isotime \"2006-01-02\"}}",
"vm_version": "{{user `vm-version`}}"
}
],
"provisioners": [
{
"inline": [
"rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7",
"yum install deltarpm -y",
"yum update -y",
"yum clean all"
],
"type": "shell"
}
],
"variables": {
"iso_url": "[datastore1] images/CentOS-7-x86_64-Everything-2009.iso",
"vm-cpu-num": "1",
"vm-disk-size": "81920",
"vm-folder": "Templates",
"vm-mem-size": "1024",
"vm-name": "CentOS7-T",
"vm-version": "19",
"vm-video-ram": "16384",
"vsphere-cluster": "Cluster1",
"vsphere-datacenter": "Datacenter",
"vsphere-datastore": "datastore1",
"vsphere-network": "VM Network",
"vsphere-password": "VMware1!",
"vsphere-server": "192.168.111.11",
"vsphere-user": "administrator@vsphere.local"
}
}

 

## 开始制作镜像
首先验证一下文件是否编写正确

Shell
1
packer validate centos-vsphere.json

 

正确无误则开始构建虚拟机模板

Shell
1
packer build centos-vsphere.json

 

过程中需要网络正常不要断开,一般会在20分钟内完成构建,过程中你可以在vCenter中看到一个正在运行的虚拟机,实际上就是在执行kickstart文件中定义的内容,完成过后启动虚拟机并通过ssh连接然后执行一些后置的自定义命令(因此需要保证DHCP服务是正常的,并且packer命令的执行机可以连接到这个网段的IP)
## 使用terraform批量创建虚拟机
在工作中,我遇到过需要大量新建虚拟机的场景,但那时我们没有利用iac工具和其他更高级的技术手段,只是对每台虚拟机进行克隆,然后手工的进入console配置IP等信息,当几百台虚拟机需要创建时,大家都想丢给别人做这种dirty work。经过实验,可以通过纯脚本方式实现这个繁琐的步骤,简化工作,提高效率。
### 1. 准备需要的文件
#### 1.1 变量定义variables.tf
定义用来后续操作的变量类型和描述信息

Haskell
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
#===========================#
# VMware vCenter connection #
#===========================#
 
variable "vsphere-user" {
type = string
description = "VMware vSphere user name"
}
 
variable "vsphere-password" {
type = string
description = "VMware vSphere password"
}
 
variable "vsphere-vcenter" {
type = string
description = "VMWare vCenter server FQDN / IP"
}
 
variable "vsphere-unverified-ssl" {
type = string
description = "Is the VMware vCenter using a self signed certificate (true/false)"
}
 
variable "vsphere-datacenter" {
type = string
description = "VMWare vSphere datacenter"
}
 
variable "vsphere-cluster" {
type = string
description = "VMWare vSphere cluster"
default = ""
}
 
variable "vsphere-template-folder" {
type = string
description = "Template folder"
default = "Templates"
}
 
#================================#
# VMware vSphere virtual machine #
#================================#
 
variable "vm-count" {
type = string
description = "Number of VM"
default = 1
}
 
variable "vm-name-prefix" {
type = string
description = "Name of VM prefix"
default = "tftest"
}
 
variable "vm-datastore" {
type = string
description = "Datastore used for the vSphere virtual machines"
}
 
variable "vm-network" {
type = string
description = "Network used for the vSphere virtual machines"
}
 
variable "vm-network-cidr" {
description = "Network CIDR like 192.168.111.0/24"
type = string
}
 
variable "vm-network-mask" {
description = "Network Mask"
type = number
}
 
variable "vm-network-gateway" {
description = "Default Gateway"
type = string
}
 
variable "vm-dns-servers" {
description = "DNS"
type = list(string)
}
 
variable "vm-linked-clone" {
type = string
description = "Use linked clone to create the vSphere virtual machine from the template (true/false). If you would like to use the linked clone feature, your template need to have one and only one snapshot"
default = "false"
}
 
variable "vm-cpu" {
type = string
description = "Number of vCPU for the vSphere virtual machines"
default = "2"
}
 
variable "vm-ram" {
type = string
description = "Amount of RAM for the vSphere virtual machines (example: 2048)"
}
 
variable "vm-disk-size" {
type = string
description = "Amount of Disk for the vSphere virtual machines (example: 80)"
default = "80"
}
 
variable "vm-name" {
type = string
description = "The name of the vSphere virtual machines and the hostname of the machine"
}
 
variable "vm-guest-id" {
type = string
description = "The ID of virtual machines operating system"
}
 
variable "vm-template-name" {
type = string
description = "The template to clone to create the VM"
}
 
variable "vm-domain" {
type = string
description = "Linux virtual machine domain name for the machine. This, along with host_name, make up the FQDN of the virtual machine"
default = ""
}
 
variable "vm-folder" {
type = string
description = "The VM folder"
}
 
variable "vm-resource-pool" {
type = string
description = "The VM resource pool"
}
 
variable "vm-tag-category" {
type = string
description = "The category for tags"
}
 
variable "vm-tag-list" {
type = list(string)
description = "The VM tags"
}
 
variable "vm-annotation" {
type = string
description = "The VM notes"
}
 
variable "vm-application" {
type = string
description = "The VM Custom Attributes"
}
 
variable "vm-owner" {
type = string
description = "The VM Custom Attributes"
}

 

#### 1.2 准备terraform.tfvars变量内容文件
在这个文件中保存变量的值(虚拟机数量;名称;使用的模板信息;CPU/内存/磁盘容量;资源池/数据中心/文件夹;网络信息如IP起始地址、网关、DNS等;当然还有vCenter连接信息)

你可能需要先定义好vm-tag的信息,这个部分其实可以不需要,按照你自己的需求去调整,可能可以用来控制NSX网络策略等信息,自行摸索。

Shell
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
# ======================== #
# VMware VMs configuration #
# ======================== #
 
vm-count = "1"
vm-name = "devops-svc"
vm-template-name = "CentOS7-T-2025-09-26"
vsphere-template-folder = "Templates"
vm-cpu = 8
vm-ram = 16384
vm-disk-size = 80
vm-guest-id = "centos7_64Guest"
vm-resource-pool = "Resources"
vm-folder = "Terraform"
vsphere-datacenter = "Datacenter"
vsphere-cluster = "Cluster1"
vm-datastore = "datastore1"
vm-network = "VM Network"
vm-network-cidr = "192.168.111.0/24"
vm-network-mask = 24
vm-network-gateway = "192.168.111.1"
vm-dns-servers = ["192.168.111.1"]
vm-domain = "cd.ikvmcn.com"
 
vm-annotation = "Create by Terraform"
 
# ============================ #
# vm tags and category #
# ============================ #
vm-tag-category = "terraform-test-category"
vm-tag-list = ["web-443", "web-80"]
 
# ============================ #
# VM Custom Notes #
# ============================ #
vm-application = "WebServer"
vm-owner = "Terraform-user"
 
# ============================ #
# VMware vSphere configuration #
# ============================ #
 
# VMware vCenter IP/FQDN
vsphere-vcenter = "192.168.111.11"
 
# VMware vSphere username used to deploy the infrastructure
vsphere-user = "administrator@vsphere.local"
 
# VMware vSphere password used to deploy the infrastructure
vsphere-password = "VMware1!"
 
# Skip the verification of the vCenter SSL certificate (true/false)
vsphere-unverified-ssl = "true"

 

#### 1.3 main.tf编排文件
这个文件描述terraform如何读取、处理的流程

Shell
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
# ==================== #
# Deploying vSphere VM #
# ==================== #
 
# Connect to VMware vSphere vCenter
provider "vsphere" {
user = var.vsphere-user
password = var.vsphere-password
vsphere_server = var.vsphere-vcenter
 
# If you have a self-signed cert
allow_unverified_ssl = var.vsphere-unverified-ssl
}
 
# Define VMware vSphere
data "vsphere_datacenter" "dc" {
name = var.vsphere-datacenter
}
 
data "vsphere_datastore" "datastore" {
name = var.vm-datastore
datacenter_id = data.vsphere_datacenter.dc.id
}
 
data "vsphere_compute_cluster" "cluster" {
name = var.vsphere-cluster
datacenter_id = data.vsphere_datacenter.dc.id
}
 
data "vsphere_network" "network" {
name = var.vm-network
datacenter_id = data.vsphere_datacenter.dc.id
}
 
data "vsphere_virtual_machine" "template" {
name = "/${var.vsphere-datacenter}/vm/${var.vsphere-template-folder}/${var.vm-template-name}"
datacenter_id = data.vsphere_datacenter.dc.id
}
 
data "vsphere_resource_pool" "resource_pool" {
name = var.vm-resource-pool
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
 
data "vsphere_tag_category" "category" {
name = var.vm-tag-category
}
 
# ==================== #
# Get tag id from vars #
# ==================== #
data "vsphere_tag" "tag" {
for_each = toset(var.vm-tag-list)
name = each.value
category_id = "${data.vsphere_tag_category.category.id}"
}
 
locals {
tags = [
for tag-id in data.vsphere_tag.tag :
tag-id.id
]
}
 
# =================== #
# get CST Time. #
# =================== #
 
locals {
time = "${formatdate("YYYY-MM-DD hh:mm",timeadd(timestamp(),"8h"))}"
}
 
# Create VMs
resource "vsphere_virtual_machine" "vm" {
count = var.vm-count
name = "${var.vm-name}-${count.index + 1}"
resource_pool_id = data.vsphere_resource_pool.resource_pool.id
datastore_id = data.vsphere_datastore.datastore.id
folder = var.vm-folder
tags = local.tags
annotation = "${var.vm-annotation}\nVM-Application:${var.vm-application}\nVM-Owner:${var.vm-owner}\nVM-CreateDate:${local.time}"
num_cpus = var.vm-cpu
memory = var.vm-ram
guest_id = var.vm-guest-id
network_interface {
network_id = data.vsphere_network.network.id
}
disk {
label = "${var.vm-name}-${count.index + 1}-disk"
size = var.vm-disk-size
}
clone {
template_uuid = data.vsphere_virtual_machine.template.id
customize {
timeout = 0
linux_options {
host_name = "${var.vm-name}-${count.index + 1}"
domain = var.vm-domain
}
network_interface {
ipv4_address = cidrhost(var.vm-network-cidr, count.index + 100)
ipv4_netmask = var.vm-network-mask
}
ipv4_gateway = var.vm-network-gateway
dns_server_list = var.vm-dns-servers
dns_suffix_list = [var.vm-domain]
}
}
connection {
type = "ssh"
user = "root"
password = "VMware1!"
host = self.default_ip_address
}
provisioner "remote-exec" {
inline = [
"echo hello > /root/test.txt"
]
}
}
 
resource "null_resource" "post_deploy" {
count = var.vm-count
 
triggers = {
vm_id = vsphere_virtual_machine.vm[count.index].id
}
 
connection {
type = "ssh"
user = "root"
password = "VMware1!"
host = vsphere_virtual_machine.vm[count.index].default_ip_address
}
 
provisioner "remote-exec" {
inline = [
"echo 'tf applyed' >> /root/test.txt"
]
}
}
 
# limit the terraform version
terraform {
required_version = ">= 0.12.20"
}
 
resource "local_file" "ansible_inventory" {
content = templatefile("${path.module}/inventory.tmpl", {
vm_inventory = [
for idx, v in vsphere_virtual_machine.vm :
{
name = "${var.vm-name}-${idx + 1}"
ip = v.default_ip_address
}
]
})
filename = "${path.module}/inventory.ini"
}

 

#### 1.4 定义output.tf
这个文件定义我们的虚拟机信息的最后输出,可以用来生成ansible inventory或者是CMDB信息

Shell
1
2
3
4
5
6
7
8
9
output "vm_inventory" {
value = [
for idx, v in vsphere_virtual_machine.vm :
{
name = "${var.vm-name}-${idx + 1}"
ip = v.default_ip_address
}
]
}

#### 1.5 定义ansible_inventory的输出模板
如果需要输出ansible inventory格式的虚拟信息,可以参考这个模板

Shell
1
2
3
4
5
[all]
%{ for vm in vm_inventory ~}
 
${vm.name} ansible_host=${vm.ip} ansible_user=root ansible_password=VMware1!
%{ endfor ~}

 

### 2. 开始创建虚拟机
#### 2.1 初始化
初始化工作目录,下载插件等

Shell
1
terraform init

 

#### 2.2 dry run测试
查看即将运行的terraform剧本可能带来的改变信息

Shell
1
terraform plan

 

#### 2.3 创建
执行剧本,最好不要设置自动同意,terraform启动的虚拟机一般来说应该通过terraform来管理其信息例如增加磁盘、加内存等,但是实践中为了解决我们的问题使用它用来创建虚拟机已经满足我们的需求了。如果你需要保存terraform的state信息,并想要通过terraform来管理虚拟机的生命周期则需要更加详细的学习一下terraform。(这里需要注意的是修改名称后在此apply可能导致虚拟机删除后重建,因为我认为我们仅仅使用创建功能即可,管理由我们手动的去做)

Shell
1
terraform apply

 

下面是最终的效果,一台虚拟机大约100秒能够创建成功:
[![](https://typora-pics.432000.xyz/uPic/20250930111656_sgZ7t2.jpg)](https://https://typora-pics.432000.xyz/uPic/20250930111656_sgZ7t2.jpg)

## 参考资料
1. https://knowledge.broadcom.com/external/article?legacyId=2007240
2. https://www.guoqiangli.com/2020/02/20/template-automation-series-iii-use-terraform-and-packer-to/?swcfpc=1
3. https://www.guoqiangli.com/2020/02/16/template-automation-series-1-use-packer-automation-to-build/
4. https://www.guoqiangli.com/2020/02/20/template-automation-series-iii-use-terraform-and-packer-to/?swcfpc=1
5. https://github.com/6547709/packer-vsphere/blob/master/CentOS7/ks.cfg
6. https://wghdr.top/?p=3564
7. https://manual.sakura.ad.jp/cloud/server/os-packages/archive-iso/rocky88-kickstart.html
8. https://manual.sakura.ad.jp/cloud/server/os-packages/archive-iso/ubuntu22045-releasenote.html

标签: ansible iac packer terraform vcenter
最后更新:2025/10/22

牛牛很忙

这个人很懒,什么都没留下

点赞
文章目录
  • 环境准备
  • 1. 编写kickstart文件
标签聚合
ansible fcfs cad vcenter iac terraform eda k8s

COPYRIGHT © 2025 KVM虚拟化日志. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang