{"id":661,"date":"2021-03-20T15:20:40","date_gmt":"2021-03-20T15:20:40","guid":{"rendered":"http:\/\/192.168.8.14\/?p=661"},"modified":"2021-03-20T15:20:40","modified_gmt":"2021-03-20T15:20:40","slug":"esxi-host-config-backup","status":"publish","type":"post","link":"https:\/\/www.jasonstreet.com\/?p=661","title":{"rendered":"ESXi host config backup"},"content":{"rendered":"\n<p>If you look after ESXi hosts that are installed to SD cards then you will know the constant pain of replacing failed cards. You may know the pain of losing both cards in this process and having to reinstall from scratch. ESXi hosts are not the hardest thing to rebuild but for me. its a job I don&#8217;t have time for.<\/p>\n\n\n\n<p>So this is a script that basically connects to a list of vCenters, loops through each host and runs the following<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\nGet-VMHostFirmware -VMHost $vHost -BackupConfiguration -DestinationPath $BackupFolder\n<\/pre><\/div>\n\n\n<p>It will then append the date to the backup file name and delete any old\/unneeded backup files.<br>It will also produce a text file of the host basic config details. name, mgt IP, mgt vlan, version etc.<br>So in the event you lost the host you will to reinstall ESXi to the same version. connect to it and import the backup. Job done. <\/p>\n\n\n\n<p>To set the script up all you need is a CSV file with a list of vCenters and if needed path to exported credentials and a location for the backups. <\/p>\n\n\n\n<p>The vCenter CSV should be in the following format<\/p>\n\n\n\n<p>Location,vCenterIP,vCenterName,Enable,CredFile<br>eg<br>Production-London, 10.10.10.10, prod-vc01.domain.name , y, c:\\script\\SavedCreds\\BackupAccountCred.xml<\/p>\n\n\n\n<p>Here is the script. It has saved me a lot of rebuild \/ reconfig time. It even works for hosts is a cluster using NSX.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\nImport-module VMware.VimAutomation.Core\n\n\n$vCenterCSVFile =         &quot;c:\\Scripts\\Backup\\Locations\\vcenters.csv&quot;\n$ResultFile =             &quot;c:\\Scripts\\Backup\\Logs\\BackupResult.csv&quot;\n$LogFile =                &quot;c:\\Scripts\\Backup\\Logs\\HostBackup.log&quot;\n$BackupBaseDirectory =    &quot;\\\\BackupServer\\Share\\BackupPath\\ESXiBackups&quot;\n$KeepOldBackups =         4\n\n$BackupResult = @()\n$vCenters = import-csv $vCenterCSVFile\n\n\n\n\n# loop through each vCenter listed in the csv file\nforeach ($vCenter in $vCenters)\n{\n  # disconnect if connected to VC\n  if ($global:DefaultVIServers){disconnect-viserver * -Confirm:$false}\n  \n  # connect to VC\n  $UseCreds = $false\n  try\n  {\n    if(test-path $vCenter.CredFile){$UseCreds = $true}\n  }\n  catch\n  {\n    $UseCreds = $false\n  }\n\n  # if we have a valid cred file path, import creds and use them to connect to vCenter\n  if ($useCreds)\n  {\n    $Credentials = Import-CliXml -Path $vCenter.CredFile\n    $ConnectionDetails = connect-viserver $vCenter.vCenterIP -Credential $Credentials\n  }else{\n    $ConnectionDetails = connect-viserver $vCenter.vCenterIP\n  }\n  \n  # test connection to VC\n  if (($global:DefaultVIServers).name -eq $vCenter.vCenterIP)\n  {\n     # check\/create backup paths\n    if(!(test-path $BackupBaseDirectory)){New-Item -ItemType directory -Path $BackupBaseDirectory}\n    $BackupPODFolder = $BackupBaseDirectory + &quot;\\&quot; + $vCenter.Location\n    if(!(test-path $BackupPODFolder)){New-Item -ItemType directory -Path $BackupPODFolder}\n\n    # loop through each host in the vCenter\n    foreach($vHost in Get-VMHost)\n    {\n      # check backup path exists, create if not.\n      $BackupFolder = $BackupBaseDirectory + &quot;\\&quot; + $vCenter.Location + &quot;\\&quot; + $vHost.name\n      if(!(test-path $BackupFolder)){New-Item -ItemType directory -Path $BackupFolder}\n\n      # get the date as a string so we can append it to the backup file name later.\n      $BackUpDate = get-date -f &quot;yyyy-MM-dd&quot;\n\n\n      # get host info (This will be used to build the $NetFile) \n      # The NetFile is a summery of the host config (IP details. version etc)\n      $HostNetworking = $vHost | Get-VMHostNetwork\n      $MGTnet = @($HostNetworking.VirtualNic | where {$_.ManagementTrafficEnabled -eq $true})&#x5B;0]\n      $HostIP = $MGTnet.IP\n\n      # get result object ready\n      $tmpResult = &#039;&#039; | select Location,Host,Backup\n      $tmpResult.Location = $vCenter.Location\n      $tmpResult.Host = $vHost.name\n      \n      $BackupStatus = Get-VMHostFirmware -VMHost $vHost -BackupConfiguration -DestinationPath $BackupFolder\n      if ($?)\n      {\n        $tmpResult.Backup = &quot;Success&quot;\n        $BackupFilePath = $BackupStatus.Data\n\n        #write host info to file\n        $NetFile = $BackupFolder + &quot;\\NetworkInfo.txt&quot;\n        &quot;HostName:      &quot; + $vHost.name | out-file $NetFile\n        &quot;HostIP:        &quot; + $MGTnet.IP | out-file $NetFile -Append\n        &quot;Subnet mask:   &quot; + $MGTnet.SubnetMask | out-file $NetFile -Append\n        &quot;MAC address:   &quot; + $MGTnet.MAC | out-file $NetFile -Append\n        &quot;Gateway:       &quot; + $HostNetworking.VMKernelGateway | out-file $NetFile -Append\n        &quot;Port Group:    &quot; + $MGTnet.PortGroupName | out-file $NetFile -Append\n        &quot;DNS Servers:   &quot; + @($HostNetworking.DnsAddress) -join &quot; &quot; | out-file $NetFile -Append\n        &quot;Domain Name:   &quot; + $HostNetworking.DomainName | out-file $NetFile -Append\n        &quot;Version:       &quot; + $vHost.Version + &quot;  &quot; + $vHost.Build | out-file $NetFile -Append\n        &quot;Model          &quot; + $vHost.Manufacturer + &quot;  &quot; + $vHost.Model | out-file $NetFile -Append\n        &#039;&#039; | out-file $NetFile -Append\n        &quot;To restore. Open PowerCLI and run&quot; | out-file $NetFile -Append\n        &quot;connect-viserver $HostIP&quot; | out-file $NetFile -Append\n        &quot;Set-VMHost -VMHost $HostIP -State &#039;Maintenance&#039;&quot; | out-file $NetFile -Append \n        &quot;Set-VMHostFirmware -VMHost $HostIP -Restore -SourcePath $BackupFilePath&quot; | out-file $NetFile -Append\n\n\n        # get the file object of the most recent backup file (this one) and append the data on the end\n        $CurrentBackUpFile = get-item ($BackupFolder + &quot;\\config*&quot;) | sort lastwritetime | select -last 1 \n        rename-item ($CurrentBackUpFile.FullName) -NewName ($CurrentBackUpFile.name).Replace(&quot;.tgz&quot;,(&quot;-&quot; + $BackUpDate + &quot;.tgz&quot;)) -Confirm:$false\n\n        # remove any old backups over the file keep limit\n        $FileToDelete = get-item ($BackupFolder + &quot;\\config*&quot;) | sort lastwritetime -Descending | select -Skip $KeepOldBackups\n        if ($FileToDelete){remove-item $FileToDelete -Confirm:$false}\n\n      }else{\n        $tmpResult.Backup = &quot;Failed&quot;\n      }\n\n      $BackupResult += $tmpResult\n      &quot;Backup of host  &quot; + $tmpResult.Host +  &quot; in POD  &quot; + $tmpResult.Location + &quot;  &quot; + $tmpResult.Backup\n    } # end loop through each host\n\n    Disconnect-VIServer * -confirm:$false\n  }else{\n    # not connected to the correct VC. do nothing.\n  }\n}\n\n$BackupResult | export-csv $ResultFile -NoTypeInformation\n\n# number of successful and failed backups. currently not doing anything with them\n$SuccBackups = @($BackupResult | where{$_.backup -eq &quot;Success&quot;}).count\n$failedBackups = @($BackupResult | where{$_.backup -eq &quot;Failed&quot;}).count\n\n\n\n\n<\/pre><\/div>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you look after ESXi hosts that are installed to SD cards then you will know the constant pain of replacing failed cards. You may know the pain of losing both cards in this process and having to reinstall from scratch. ESXi hosts are not the hardest thing to rebuild but for me. its a&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[129,5,4,43],"tags":[65,66,131,15,130],"class_list":["post-661","post","type-post","status-publish","format-standard","hentry","category-dr","category-powercli","category-powershell","category-vmware","tag-backup","tag-config","tag-dr","tag-host","tag-recovery"],"_links":{"self":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts\/661","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=661"}],"version-history":[{"count":5,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts\/661\/revisions"}],"predecessor-version":[{"id":666,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=\/wp\/v2\/posts\/661\/revisions\/666"}],"wp:attachment":[{"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=661"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=661"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jasonstreet.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=661"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}