PowerShell一键自动化部署ESXI及VSAN
脚本自动化部署vSphere和配置VSAN集群环境,可以完成以下一些操作:
1 部署嵌套的 ESXi 主机:通过 OVA 文件导入和配置多个嵌套 ESXi 主机,设置其网络信息、CPU、内存和磁盘大小,并将其加入到指定的集群中。
2 部署 vCenter Server Appliance(VCSA):根据预定义的 JSON 配置文件,部署 VCSA 并进行相应的配置,包括网络设置、存储、部署规模、主机名、SSO 配置等。
3 创建新的数据中心和集群:连接到新部署的 vCenter Server,创建新的数据中心和集群,并根据需要配置 vSAN 磁盘组。
4 添加 ESXi 主机到 vCenter:将嵌套的 ESXi 主机添加到新创建的集群中,也可以选择使用 DNS 名称来添加主机。
5 清除 vSAN 告警和进行最终配置:清除 vSAN 告警并进行额外的配置,例如开启 vMotion、退出维护模式等。
使用时需要注意以下几点:
脚本中使用了一些默认值和硬编码的路径,需要根据实际情况进行修改,比如 OVA 存储路径、vCenter 安装镜像解压后的文件夹路径等。
在执行脚本之前,需要确保目标环境已经满足一定的条件,比如网络设置、存储准备等。
需要对脚本中的用户名、密码等敏感信息进行适当的保护和管理,避免泄露。
在执行脚本时,建议先在测试环境进行验证,确保脚本的正确性和稳定性。
此脚本仅用于测试学习使用,代码如下:
#######################################################################
# 函数列表 #
#######################################################################
# 日志记录
Function logRecord(){
[cmdletbinding()]
Param (
# 参数列表,参数属性
# 具体日志信息
[parameter(Mandatory=$True)]
[string] $logString,
# 日志存储路径,包括路径和文件名称全路径
[parameter(Mandatory=$False)]
[string] $logPath="",
[Parameter(Mandatory=$True)]
[ValidateSet('INFO','ERROR')]
[string] $logLevel
)
$time = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
if($logPath){
echo "$time $logLevel $logString" >> $logPath
}else{
if($logLevel -eq "INFO"){
write-host -ForegroundColor Green "$time $logLevel $logString"
}else{
write-host -ForegroundColor red "$time $logLevel $logString"
}
}
}
#######################################################################
# 参数列表 #
#######################################################################
# 第一层 vCenter Server 信息
$vCenterIp = "172.17.3.110"
$vCenterUsername = "administrator@vsphere.local"
$vCenterPassword = "P@ssw0rd"
# 部署的组件
$deployNestedESXiVMs = 1 # 部署 ESXi
$deployVCSA = 1 # 部署 VCSA
$setupNewVC = 1 # 创建数据中心、集群
$configureVSANDiskGroup = 1 # 配置磁盘组
$addESXiHostsToVC = 1 # 添加主机到VC
$addHostByDnsName = 0 # 添加主机到VC使用DNS名称
$clearVSANHealthCheckAlarm = 1 # 清除vSAN告警
# 嵌套ESXi#############################################################
# 嵌套ESXi OVA存储路径
# $nestedESXiApplianceOVA = "D:\test-vm.ova" ESXI主机模板位置
# 嵌套ESXi网络信息,以及需要部署的嵌套ESXi数量
$nestedESXiHostnameToIPs = @{
"Lab-esxi-113" = "172.17.3.70"
"Lab-esxi-114" = "172.17.3.71"
"Lab-esxi-115" = "172.17.3.72"
}
# 嵌套ESXi资源配置
$NestedESXivCPU = "4"
$NestedESXivMEM = "16" # GB
$NestedESXiCachingvDisk = "8" # GB
$NestedESXiCapacityvDisk = "100" # GB
# VCSA#################################################################
# vCenter安装镜像解压后的文件夹
$VCSAInstallerPath = "E:\ISO\VC"
# VCSA部署规模
$vcsaSize2MemoryStorageMap = @{
"tiny"=@{"cpu"="2";"mem"="12";"disk"="415"};
"small"=@{"cpu"="4";"mem"="19";"disk"="480"};
"medium"=@{"cpu"="8";"mem"="28";"disk"="700"};
"large"=@{"cpu"="16";"mem"="37";"disk"="1065"};
"xlarge"=@{"cpu"="24";"mem"="56";"disk"="1805"}
}
# VCSA部署配置
$VCSADeploymentSize = "tiny" # 部署规模
$VCSADisplayName = "Lab-VCSA-112" # 虚拟机名称
$VCSAIPAddress = "172.17.3.80" # IP地址
$VCSAHostname = "172.17.3.80" # 主机名(如果没有DNS解析则使用IP地址)
$VCSAPrefix = "24" # 掩码长度
$VCSASSODomainName = "vsphere.local" # SSO域
$VCSASSOPassword = "P@ssw0rd" # SSO密码
$VCSARootPassword = "P@ssw0rd" # ROOT密码
$VCSASSHEnable = "true" # SSH
# 嵌套环境配置
$NewVCDatacenterName = "Datacenter"
$NewVCVSANClusterName = "Cluster"
# 公共配置#############################################################
# 使用的存储、端口组等信息
$vmDatacenter = "Datacenter01" # 数据中心
$vmCluster = "cluster01" # 集群
$vmNetwork = "VM Network" # 端口组
$vmDatastore = "Datastore_new" # 存储
$vmNetmask = "255.255.255.0" # 掩码
$vmGateway = "172.17.3.254" # 网关
$vmDNS = "172.17.3.10" # DNS
$vmNTP = "172.17.3.10" # NTP
$vmPassword = "P@ssw0rd" # 密码
$vmDomain = "powershell.com" # 域
$vmSyslog = "172.17.3.90" # Syslog
$vCenterConnection = $null
try{
$vCenterConnection = Connect-VIServer $vCenterIp -User $vCenterUsername -Password $vCenterPassword -WarningAction SilentlyContinue
logRecord -logLevel INFO -logString "连接 vCenter Server $vCenterIp 成功"
}catch{
logRecord -logLevel ERROR -logString "连接 vCenter Server $vCenterIp 失败"
return ;
}
# 获取存储、数据中心、ESXi主机
if(($deployNestedESXiVMs -eq 1) -or ($deployVCSA -eq 1)) {
try{
$datastore = Get-Datastore -Server $vCenterConnection -Name $vmDatastore | Select -First 1
logRecord -logLevel INFO -logString "获取存储 $vmDatastore 成功"
}catch{
logRecord -logLevel ERROR -logString "获取存储 $vmDatastore 失败"
}
try{
$cluster = Get-Cluster -Server $viConnection -Name $VMCluster
logRecord -logLevel INFO -logString "获取集群 $VMCluster 成功"
}catch{
logRecord -logLevel ERROR -logString "获取集群 $VMCluster 失败"
}
try{
$datacenter = $cluster | Get-Datacenter
logRecord -logLevel INFO -logString "获取数据中心 $($datacenter.name) 成功"
}catch{
logRecord -logLevel ERROR -logString "获取数据中心 $($datacenter.name) 失败"
}
try{
$vmHost = $cluster | Get-VMHost | Select -First 1
logRecord -logLevel INFO -logString "获取主机 $($vmHost.name) 成功"
}catch{
logRecord -logLevel ERROR -logString "获取主机 $($vmHost.name) 失败"
}
}
if($vCenterConnection){
if($deployNestedESXiVMs -eq 1) {
# 遍历嵌套ESXi信息
foreach($nestedESXiHostKey in $nestedESXiHostnameToIPs.keys) {
$vmName = $nestedESXiHostKey
$vmIpAddress = $nestedESXiHostnameToIPs[$nestedESXiHostKey]
# 获取OVA配置
# $ovfconfig = Get-OvfConfiguration $NestedESXiApplianceOVA
$ovfconfig = Get-OvfConfiguration "D:\test-vm.ova"
$networkMapLabel = ($ovfconfig.ToHashTable().keys | where {$_ -Match "NetworkMapping"}).replace("NetworkMapping.","").replace("-","_").replace(" ","_")
# 虚拟机端口组
$ovfconfig.NetworkMapping.$networkMapLabel.value = $vmNetwork
# 网络信息
$ovfconfig.common.guestinfo.hostname.value = $vmName
$ovfconfig.common.guestinfo.ipaddress.value = $vmIpAddress
$ovfconfig.common.guestinfo.netmask.value = $vmNetmask
$ovfconfig.common.guestinfo.gateway.value = $vmGateway
$ovfconfig.common.guestinfo.dns.value = $vmDNS
$ovfconfig.common.guestinfo.domain.value = $vmDomain
$ovfconfig.common.guestinfo.ntp.value = $vmNTP
$ovfconfig.common.guestinfo.syslog.value = $vmSyslog
$ovfconfig.common.guestinfo.password.value = $vmPassword
# SSH
if($VMSSH -eq "true") {
$VMSSHVar = $true
} else {
$VMSSHVar = $false
}
$ovfconfig.common.guestinfo.ssh.value = $VMSSHVar
# 自动创建datastore1
$ovfconfig.common.guestinfo.createvmfs.value = $false
$isExsit = Get-VM -Name $vmName -ErrorAction SilentlyContinue
if($isExsit){
logRecord -logLevel INFO -logString "虚拟机 $vmName 已存在,跳过该虚拟机导入"
continue
}
# 导入OVA
logRecord -logLevel INFO -logString "导入虚拟机 $vmName"
$vm = Import-VApp -Source "D:\test-vm.ova" -OvfConfiguration $ovfconfig -Name $vmName -Location $cluster -VMHost $vmhost -Datastore $datastore -DiskStorageFormat thin
# 添加网卡
New-NetworkAdapter -VM $vm -Type Vmxnet3 -NetworkName $vmNetwork -StartConnected -confirm:$false | Out-Null
New-NetworkAdapter -VM $vm -Type Vmxnet3 -NetworkName $vmNetwork -StartConnected -confirm:$false | Out-Null
New-NetworkAdapter -VM $vm -Type Vmxnet3 -NetworkName $vmNetwork -StartConnected -confirm:$false | Out-Null
$vm | New-AdvancedSetting -name "ethernet2.filter4.name" -value "dvfilter-maclearn" -confirm:$false -ErrorAction SilentlyContinue | out-null
$vm | New-AdvancedSetting -Name "ethernet2.filter4.onFailure" -value "failOpen" -confirm:$false -ErrorAction SilentlyContinue | out-null
$vm | New-AdvancedSetting -name "ethernet3.filter4.name" -value "dvfilter-maclearn" -confirm:$false -ErrorAction SilentlyContinue | out-null
$vm | New-AdvancedSetting -Name "ethernet3.filter4.onFailure" -value "failOpen" -confirm:$false -ErrorAction SilentlyContinue | out-null
$vm | New-AdvancedSetting -name "ethernet4.filter4.name" -value "dvfilter-maclearn" -confirm:$false -ErrorAction SilentlyContinue | out-null
$vm | New-AdvancedSetting -Name "ethernet4.filter4.onFailure" -value "failOpen" -confirm:$false -ErrorAction SilentlyContinue | out-null
# 设置CPU和内存大小
Set-VM -Server $viConnection -VM $vm -NumCpu $NestedESXivCPU -MemoryGB $NestedESXivMEM -Confirm:$false | out-null
# 设置磁盘大小
Get-HardDisk -Server $viConnection -VM $vm -Name "Hard disk 2" | Set-HardDisk -CapacityGB $NestedESXiCachingvDisk -Confirm:$false | out-null
Get-HardDisk -Server $viConnection -VM $vm -Name "Hard disk 3" | Set-HardDisk -CapacityGB $NestedESXiCapacityvDisk -Confirm:$false | out-null
# 开机
$vm | Start-Vm -RunAsync | Out-Null
}
}
if($deployVCSA -eq 1) {
$isExsit = Get-VM -Name $VCSADisplayName -ErrorAction SilentlyContinue
if($isExsit){
logRecord -logLevel INFO -logString "虚拟机 $VCSADisplayName 已存在,跳过该虚拟机导入"
return ;
}
$config = (Get-Content -Raw "$($VCSAInstallerPath)\vcsa-cli-installer\templates\install\embedded_vCSA_on_VC.json") | convertfrom-json
$config.'new_vcsa'.vc.hostname = $vCenterIp # 第一层vCenter IP
$config.'new_vcsa'.vc.username = $vCenterUsername # 第一层vCenter 用户
$config.'new_vcsa'.vc.password = $vCenterPassword # 第一层vCenter 密码
$config.'new_vcsa'.vc.deployment_network = $vmNetwork # 端口组
$config.'new_vcsa'.vc.datastore = $datastore # 存储
$config.'new_vcsa'.vc.datacenter = $datacenter.name # 第一层数据中心名称
$config.'new_vcsa'.vc.target = $VMCluster # 第一层集群名称
$config.'new_vcsa'.appliance.thin_disk_mode = $true # 磁盘模式
$config.'new_vcsa'.appliance.deployment_option = $VCSADeploymentSize # 部署规模
$config.'new_vcsa'.appliance.name = $VCSADisplayName # 虚拟机名称
$config.'new_vcsa'.network.ip_family = "ipv4" # 网络栈
$config.'new_vcsa'.network.mode = "static" # 网络模式
$config.'new_vcsa'.network.ip = $VCSAIPAddress # IP地址
$config.'new_vcsa'.network.dns_servers[0] = $VMDNS # DNS
$config.'new_vcsa'.network.prefix = $VCSAPrefix # 掩码长度
$config.'new_vcsa'.network.gateway = $VMGateway # 网关
$config.'new_vcsa'.os.ntp_servers = $VMNTP # NTP
$config.'new_vcsa'.network.system_name = $VCSAHostname # 计算机名
$config.'new_vcsa'.os.password = $VCSARootPassword # ROOT密码
if($VCSASSHEnable -eq "true") {
$VCSASSHEnableVar = $true
} else {
$VCSASSHEnableVar = $false
}
$config.'new_vcsa'.os.ssh_enable = $VCSASSHEnableVar # SSH
$config.'new_vcsa'.sso.password = $VCSASSOPassword # SSO密码
$config.'new_vcsa'.sso.domain_name = $VCSASSODomainName # SSO域
logRecord -logLevel INFO -logString "创建VCSA JSON部署配置文件"
$config | ConvertTo-Json | Set-Content -Path "$($ENV:Temp)\jsontemplate.json"
logRecord -logLevel INFO -logString "部署VCSA..."
# 部署VCSA(Invoke-Expression将字符串解析为命令并执行)
Invoke-Expression "$($VCSAInstallerPath)\vcsa-cli-installer\win32\vcsa-deploy.exe install --no-esx-ssl-verify --accept-eula --acknowledge-ceip $($ENV:Temp)\jsontemplate.json"
}
if($setupNewVC -eq 1) {
# 连接vCenter
try{
$vc = Connect-VIServer $VCSAIPAddress -User "administrator@$VCSASSODomainName" -Password $VCSASSOPassword -WarningAction SilentlyContinue
logRecord -logLevel INFO -logString "连接 vCenter Server $VCSAIPAddress 成功"
}catch{
logRecord -logLevel ERROR -logString "连接 vCenter Server $VCSAIPAddress 失败"
return ;
}
# 创建数据中心
$d = Get-Datacenter -Server $vc $NewVCDatacenterName -ErrorAction Ignore
if( -Not $d) {
logRecord -logLevel INFO -logString "创建数据中心 $NewVCDatacenterName"
New-Datacenter -Server $vc -Name $NewVCDatacenterName -Location (Get-Folder -Type Datacenter -Server $vc) | out-null
}
# 创建集群
$c = Get-Cluster -Server $vc $NewVCVSANClusterName -ErrorAction Ignore
if( -Not $c) {
if($configureVSANDiskGroup -eq 1) {
logRecord -logLevel INFO -logString "创建vSAN集群 $NewVCVSANClusterName"
New-Cluster -Server $vc -Name $NewVCVSANClusterName -Location (Get-Datacenter -Name $NewVCDatacenterName -Server $vc) -DrsEnabled -HAEnabled -VsanEnabled | out-null
} else {
logRecord -logLevel INFO -logString "创建vSphere集群 $NewVCVSANClusterName"
New-Cluster -Server $vc -Name $NewVCVSANClusterName -Location (Get-Datacenter -Name $NewVCDatacenterName -Server $vc) -DrsEnabled -HAEnabled | out-null
}
(Get-Cluster $NewVCVSANClusterName) | New-AdvancedSetting -Name "das.ignoreRedundantNetWarning" -Type ClusterHA -Value $true -Confirm:$false | out-null
}
# 添加主机到vCenter
if($addESXiHostsToVC -eq 1) {
foreach($nestedESXiHostKey in $nestedESXiHostnameToIPs.keys) {
$VMName = $nestedESXiHostKey
$VMIPAddress = $nestedESXiHostnameToIPs[$nestedESXiHostKey]
$targetVMHost = $VMIPAddress
if($addHostByDnsName -eq 1) {
$targetVMHost = $VMName
}
logRecord -logLevel INFO -logString "添加ESXi主机 $targetVMHost 到集群"
Add-VMHost -Server $vc -Location (Get-Cluster -Name $NewVCVSANClusterName) -User "root" -Password $VMPassword -Name $targetVMHost -Force | out-null
}
}
# 创建磁盘组
if($configureVSANDiskGroup -eq 1) {
Get-VsanClusterConfiguration -Server $vc -Cluster $NewVCVSANClusterName | Set-VsanClusterConfiguration -HealthCheckIntervalMinutes 0 | out-null
foreach ($vmhost in Get-Cluster -Server $vc | Get-VMHost) {
$luns = $vmhost | Get-ScsiLun | select CanonicalName, CapacityGB
logRecord -logLevel INFO -logString "主机 $vmhost 查询可用于创建磁盘组的磁盘"
foreach ($lun in $luns) {
if(([int]($lun.CapacityGB)).toString() -eq "$NestedESXiCachingvDisk") {
$vsanCacheDisk = $lun.CanonicalName
}
if(([int]($lun.CapacityGB)).toString() -eq "$NestedESXiCapacityvDisk") {
$vsanCapacityDisk = $lun.CanonicalName
}
}
logRecord -logLevel INFO -logString "主机 $vmhost 创建磁盘组"
New-VsanDiskGroup -Server $vc -VMHost $vmhost -SsdCanonicalName $vsanCacheDisk -DataDiskCanonicalName $vsanCapacityDisk | out-null
}
}
# 清除vSAN告警
if($clearVSANHealthCheckAlarm -eq 1) {
logRecord -logLevel INFO -logString "清除vSAN集群健康检测告警"
$alarmMgr = Get-View AlarmManager -Server $vc
Get-Cluster -Server $vc | where {$_.ExtensionData.TriggeredAlarmState} | %{
$cluster = $_
$Cluster.ExtensionData.TriggeredAlarmState | %{
$alarmMgr.AcknowledgeAlarm($_.Alarm,$cluster.ExtensionData.MoRef)
}
}
$alarmSpec = New-Object VMware.Vim.AlarmFilterSpec
$alarmMgr.ClearTriggeredAlarms($alarmSpec)
Set-VsanClusterConfiguration -Configuration $NewVCVSANClusterName -AddSilentHealthCheck controlleronhcl,vumconfig,vumrecommendation -PerformanceServiceEnabled $true
}
# 最终配置
foreach ($vmhost in Get-Cluster -Server $vc | Get-VMHost) {
# 清除告警: 未配置任何 coredump 目标。无法保存主机核心转储
Get-AdvancedSetting -Entity $vmhost -Name UserVars.SuppressCoredumpWarning | Set-AdvancedSetting -Value 1 -Confirm:$false | out-null
# 开启vMotion
$vmhost | Get-VMHostNetworkAdapter -VMKernel | Set-VMHostNetworkAdapter -VMotionEnabled $true -Confirm:$false | out-null
# 如果主机在维护模式,则退出维护模式
if($vmhost.ConnectionState -eq "Maintenance") {
Set-VMHost -VMhost $vmhost -State Connected -RunAsync -Confirm:$false | out-null
}
}
logRecord -logLevel INFO -logString "断开vCenter连接"
Disconnect-VIServer $vc -Confirm:$false
}
}1 如下图,是通过执行脚本后,在现有ESXI主机环境下,自动嵌套部署3台ESXI主机

2 如下是自动化部署第二层vCenter管理控制台界面,如图可见VSAN已按脚本要求配置完成,


