VMWare PowerCLI – Backup ESXi Host Configuration
I had been using a version of this script to backup my VMWare ESXi host configurations for several years and across several versions of ESXi and vCenter server.
This script was connecting to my vCenter Server (at 192.168.0.9) and downloading the configurations to a network share (at 192.168.0.62). It would also cycle the files in and out (deleting old backups) in a First-In-First-Out scheme.
Param ( [string]$VIServer = "192.168.0.9", [string]$ConfigPath = "\\192.168.0.62\backups\ESXiHosts\", [int]$Days = 21 ) If (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) { Try { Add-PSSnapin VMware.VimAutomation.Core -ErrorAction Stop } Catch { Write-Host "Unable to load PowerCLI, is it installed?" -ForegroundColor Red; Break } } $Time = Get-Date -Format "MM-dd-yy-hhmm" If (-not (Test-Path $ConfigPath\Old)) { New-Item $ConfigPath\Old -ItemType Directory } #Add date/time to the configuration file name and move to archive ForEach ($File in (Get-ChildItem $ConfigPath\*.tgz)) { $NewName = "$($File.Basename)-$Time.tgz" Copy-Item $File.FullName "$ConfigPath\Old\$NewName" Remove-Item $File.FullName } #Download latest configuration files Connect-VIServer $VIServer Get-VMHost | Get-VMHostFirmware -BackupConfiguration -DestinationPath $ConfigPath #Remove older files in archive ForEach ($File in (Get-ChildItem $ConfigPath\Old\*.tgz | Where LastWriteTime -LE (Get-Date).AddDays(-$Days))) { Remove-Item $File.FullName }
However, I just upgraded and migrated my vCenter Server from Windows (version 6.5) to the vCenter Server Appliance version 6.7 U2c. After the migration and upgrade, the backups stopped working. These are the things I had to change to get it to work again.
I had to upgrade from old application version of vSphere PowerCLI (that was called via PSSnapin) to the newer 11.3 version, which is now a PowerShell module.
First, I had remove the old version of VMware PowerCLI from the server I was using to run the script.
Next, I had to run this command to install the current version of VMware PowerCLI, which is now a PowerShell module, not standard application.
Install-Module -Name VMware.PowerCLI
Once I had done that, I had to remove the PSSnapin references and replace them with the import module line (make sure you don’t put anything before your param section)-
Import-Module VMware.PowerCLI
When you run VMware PowerCLI for the first time, you are greeted with a request from VMware to join their customer experience improvement plan.
If you don’t want to join, run this command to stop getting prompted-
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $false.
At this point, I thought I was in the clear. As it turned out, I still had some modifications to perform. The next one was an invalid certificate error.
Connect-VIServer : 7/19/2019 1:28:50 PM Connect-VIServer Error: Invalid server certificate. Use Set-PowerCLIConfiguration to set the value for the InvalidCertificateAction option to Prompt if you'd like to connect once or to add a permanent exception for this server. Additional Information: Could not establish trust relationship for the SSL/TLS secure channel with authority '192.168.0.9'.
After some Googling, I found the command to tell PowerCLI to ignore any certificate errors.
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false
Moving on, I tried the script again. This time, I received another connection error-
Connect-VIServer “The underlying connection was closed: An unexpected error occurred on a send.”
After some further Googling, I discovered that the server needed to be told to use TLS 1.2 to connect to the vCenter Server Appliance. To accomplish this, I needed to add this command to the script, just prior to the Connect-VIServer command.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
I re-ran the script and was prompted for credentials. After entering them in, I got another error. Are you sensing a theme here?
Get-VMHostFirmware : 7/19/2019 1:31:29 PM Get-VMHostFirmware A general system error occurred: Internal error At C:\apps\VMWareHostBackupScripts\ESXi Host Config backup PowerCLI Script-FIFONew.ps1:20 char:14 + ... et-VMHost | Get-VMHostFirmware -BackupConfiguration -DestinationPath ...
…but… I was surprised and elated to see that I was getting backup tgz files for 2 of my 3 ESXi hosts. Now, I was onto something. I did some further Googling and found a post on vmware.com’s forums that the error could be related to a downloads folder on the ESXi host not being present in the /scratch directory.
I connected to the host via SSH and logged in. I ran-
ls /scratch
Notice something missing?
I created the downloads directory on the problem server by running-
mkdir /scratch/downloads
Again, I ran the script. It prompted me for my credentials, and finally…
… or so I thought. After attempting to run the scheduled task (via Windows Task Scheduler), I found that the target directory was only being emptied and the backups weren’t actually being created there. Some fo the script was running, just not the PowerCLI part.
After a brief break, I realized that it had to be the credentials prompt that was creating the issue. Some additional Googling led me to the -Force parameter for the Connect-VIServer command. Adding it, bypassed the credentials request and allowed the script to run via scheduled task.
Mission accomplished!
Param ( [string]$VIServer = "192.168.0.9", [string]$ConfigPath = "\\192.168.0.62\backups\ESXiHosts\", [int]$Days = 21 ) Import-Module VMware.PowerCLI $Time = Get-Date -Format "MM-dd-yy-hhmm" If (-not (Test-Path $ConfigPath\Old)) { New-Item $ConfigPath\Old -ItemType Directory } #Add date/time to the configuration file name and move to archive ForEach ($File in (Get-ChildItem $ConfigPath\*.tgz)) { $NewName = "$($File.Basename)-$Time.tgz" Copy-Item $File.FullName "$ConfigPath\Old\$NewName" Remove-Item $File.FullName } #Download latest configuration files [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Connect-VIServer $VIServer -Force Get-VMHost | Get-VMHostFirmware -BackupConfiguration -DestinationPath $ConfigPath #Remove older files in archive ForEach ($File in (Get-ChildItem $ConfigPath\Old\*.tgz | Where LastWriteTime -LE (Get-Date).AddDays(-$Days))) { Remove-Item $File.FullName }