In this chapter, you will learn to:
When it comes to designing your disaster recovery strategy, there are several aspects you should take into account. The vCenter Server installation consists of two parts: an application server and a backend database. While the application server services the user interface, the heart of the vCenter Server is stored in the backend database. In this chapter, you’ll learn how to back up and restore your vCenter Server database when you don’t have SQL Server Management Studio available. We’ll explore methods to export and import specific items of your vCenter Server inventory.
Once upon a time, there was a system administrator. Every day, he verified his backup logs. He always checked with the people responsible for changing the backup tapes at the branch offices. Had the backup run? Had they changed the tapes properly? He never experienced a problem with restoring files that had been erased by some miscreant. He thought everything was taken care of until the phone rang. A fire had broken out in the server room of a branch office; the file server was lost. “A problem, but not a serious one,” the system administrator thought as he left to set up a replacement server.
After the server was set up, he contacted the person who had so diligently run the backup and changed the tapes, day in and day out.
“Where are the backup tapes?” asked the administrator.
“I keep them on top of the file server, why?”
Is your backup secured? This example, based on a true story, is particularly gruesome, but it illustrates that a good backup system is more than just the technology alone.
Some see backup as a necessary evil and do only a simple file backup of the vCenter Server. You should always remember that the backup is the first step to being able to restore your infrastructure; make sure that you know what elements you’ll need for your environment.
The heart of your vCenter Server is stored in its backend database. This database is an essential part of your backup strategy, and a simple file backup of your vCenter Server is insufficient. Let’s see how to properly back up the vCenter Server database.
As a best practice, schedule your database backup of the vCenter Server database according to your database management system’s vendor recommendations. For a Microsoft SQL database, that involves using SQL Server Management Studio to schedule backups. For Oracle, you’ll be using the Oracle Recovery Manager (RMAN). Luckily, you have PowerShell to help you.
Although this chapter focuses on SQL Server 2014, you can use the steps and functions outlined here for any SQL Server 2012 edition or later. We recommend using SQL Server Management Studio to schedule maintenance tasks (including backups). However, because of the nature of this book, we will show you how you can do this using PowerShell.
Microsoft has come a long way in simplifying SQL backups via PowerShell in the last few years. Originally, users would have to work solely with SQL Server objects (similar to PowerShell objects), which was not as intuitive as the current method. Microsoft has since introduced a Backup-SqlDatabase
cmdlet to simplify the backup process.
To use this cmdlet, you will first need to import the SQLPS
module into your PowerShell session:
Import-Module "SQLPS"
There are a few cmdlets in the SQLPS
module that do not follow the normal cmdlet naming convention and cause a warning message when importing the module. If you do not wish to see this warning, you can use the -DisableNameChecking
parameter to suppress the warnings. After you import the module, you can work with the Backup-SqlDatabase
cmdlet.
The Backup-SqlDatabase
cmdlet has numerous parameters for various use cases. We will focus on only a small portion of them; just be aware that you can leverage this cmdlet for more than backing up SQL databases the way we do here.
To create a full database backup, you need the following information:
In an example from Chapter 1, “Automating vCenter Server Deployment and Configuration,” we used a remote SQL database with SQL authentication rather than Windows-based authentication. Because we used SQL authentication, we had to specify credentials in the command. If we had used Windows-based authentication and were performing these commands from a Windows account with permissions to our vCenter Server database, we would not have been required to specify our credentials.
Backup-SqlDatabase -ServerInstance "MGMT-DB" -Database vCenter6 `
-BackupFile C:TempMybackup.bak -BackupAction Database `
-Credential (Get-Credential)
If no value is specified for the backup action, the cmdlet defaults to a full database backup.
Once the backup is complete, you can take a look at the file and verify that it was successful. The backup file specified in the command is a location on the SQL server. If an invalid location is specified, an error will be returned prior to execution of the database backup, as shown in Figure 12-1.
If the backup was successful, you will be able to see the file in the location specified in the command.
You can also create a differential backup. Differential backups are particularly useful if you have a large database with minimal changes. A differential backup stores only the changes since the last full backup of your database. This approach decreases the backup window, but that decrease comes at a cost. If you are using differential backups and need to restore your database, you have to restore the last full backup, followed by the last differential backup. Differential backups can only be performed on databases that are not in Simple recovery mode. By default, the vCenter Server installation installs the database in Simple recovery mode.
To create a differential backup, you must set the -BackupAction
parameter to Database
. Once that has been set, add the -Incremental
parameter to the command:
Backup-SqlDatabase -ServerInstance "MGMT-DB" -Database vCenter6 `
-BackupFile C:TempMybackup.bak -BackupAction Database `
-Incremental -Credential (Get-Credential)
The third type of backup you can make is a log backup. With log backups you can recover the database to a specific point in time. Log backups generally use fewer resources than database backups, and as a result, you can create them more often. Note that you can only perform log backups on databases that are not in Simple recovery mode. To perform a backup of your database transactional log files, change the -BackupAction
parameter to Log
:
Backup-SqlDatabase -ServerInstance "MGMT-DB" -Database vCenter6 `
-BackupFile C:TempMybackup.bak -BackupAction Log `
-Credential (Get-Credential)
When it comes to restoring your vCenter Server environment, there are several possibilities depending on your original setup. If you used an external database, recover your database first. Follow your database vendor’s guidelines. After your database is online, follow the steps as outlined in the next section to install and recover your vCenter Server environment.
Before restoring your database, you need to stop the vCenter Server service using the Stop-Service
cmdlet. Because the vCenter Server service has dependent services, you must provide the -Force
parameter:
Stop-Service vpxd -Force
To restore the database, use the SQL Server cmdlets. The restore is similar to the backup except now we will use the Restore-SqlDatabase
cmdlet. Just like the backup cmdlet, the restore cmdlet has some properties that you’ll need to define. First, provide the restore type you want to perform. In this case, you’ll want to restore the database and overwrite it if the database already exists:
Restore-SqlDatabase -ServerInstance MGMT-DB -Database VCENTER6 `
-BackupFile "C:TempMyBackup.bak" `
-ReplaceDatabase -Credential (Get-Credential)
If you receive an error message in your PowerShell console that “Exclusive access could not be obtained because the database is in use,” you may have to also stop the vmware-cis-config
service and try again:
Stop-Service vmware-cis-config -Force
After the restore is finished, you can start vCenter Server again using the Start-Service
cmdlet:
Start-Service vpxd
If you stopped the vmware-cis-config
service to restore the database, you will need to make sure that you start the service back up as well.
When you restore your vCenter Server without a backup of the original SSL certificate, vCenter Server won’t be able to decrypt the ESXi host passwords. All ESXi hosts will show up as disconnected. In this case, you’ll have to reauthenticate every host by reconnecting them. When replacing the vCenter Server certificates, you must also reconnect all ESXi hosts to update the ESXi host passwords.
You can reconnect an ESXi host using the Set-VMHost
cmdlet. This cmdlet is used to change the configuration of the host. Set the –State
parameter to Connected
to reconnect a disconnected host. To disconnect a host, set the –State
parameter to Disconnected
.
Set-VMHost esx01 –State Connected
You should understand that if the SSL certificates aren’t valid, the Set-VMHost
cmdlet doesn’t let you reconnect the server and the following error is thrown: “Cannot complete login due to an incorrect user name or password.”
When you have to deal with invalid SSL certificates and you’re solely working with PowerCLI cmdlets, your only option is removing and adding the host back again using the Remove-VMHost
and Add-VMHost
cmdlets.
Remove-VMHost -VMHost esx01 -Confirm:$false
Add-VMHost -Name esx01 -User root -Password VMware1! `
-Location MainDC -Force
However, there is a drawback to this approach. Removing an ESXi host will remove all of the host’s configuration, including resource pools, VM folders, and the like. This is something you definitely don’t want to happen in a production environment or during a restore operation.
The good news is that there is one other option. It relies on the vSphere API objects that are retrieved using the Get-View
cmdlet:
$vmHostView = Get-View -ViewType HostSystem -Filter @{"Name" = "esx01"}
The VMware.Vim.HostSystem
object contains a ReconnectHost_Task()
method. If you view the method’s definition, you will see that the ReconnectHost_Task()
method accepts a VMware.Vim.HostConnectSpec
object as input. This object lets you specify the username and password to authenticate the ESXi host, as shown next:
$objHostConnectSpec = New-Object VMware.Vim.HostConnectSpec
$objHostConnectSpec.userName = $user
$objHostConnectSpec.password = $password
To reconnect the ESXi host, you just need to call the ReconnectHost_Task()
method using the $objHostConnectSpec
variable you just defined:
$vmHostView.ReconnectHost_Task($objHostConnectSpec, $null)
To assist you in reconnecting an ESXi host, you can use the Connect-VMHost
function we created in Listing 12-1. This function accepts a VMHost
object from the pipeline or through the -VMHost
parameter. Because each ESXi host needs to be reauthenticated, you must specify the -User
and -Password
parameters. The optional -Reconnect
switch forces an ESXi host to disconnect first, which is ideal for testing purposes.
Listing 12-1: Reconnecting ESXi hosts
function Connect-VMHost {
<#
.SYNOPSIS
Connect a disconnected ESXi host
.DESCRIPTION
This function (re)connects a disconnected ESXi host.
.NOTES
.PARAMETER vmHost
The VMHost object to connect
.PARAMETER credential
A PSCredential object used to authenticate the VMHost server
.PARAMETER user
The user account used to authenticate the VMHost server
.PARAMETER password
The password for the account specified by the -User parameter
.PARAMETER Reconnect
An optional switch parameter to force a disconnect first
.EXAMPLE
Connect-VMHost -VMHost MyESX -User root -Password password
.EXAMPLE
Get-VMHost myESX | Connect-VMHost -User root -Password password -Reconnect
.EXAMPLE
Get-VMHost myESX | Connect-VMHost -Credential (Get-Credential)
#>
Param (
[Parameter(ValueFromPipeline = $true, Position = 0,
Mandatory = $true,
HelpMessage = "Enter an ESX(i) host entity")]
[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]$vmHost,
[Parameter(Mandatory = $true, ParameterSetName = "cred",
HelpMessage = "Enter a PSCredential object")]
[System.Management.Automation.PSCredential]$credential,
[Parameter(ParameterSetName = "user")]
[ValidateNotNullOrEmpty()]
[string]$User = "root",
[Parameter(Mandatory = $true, ParameterSetName = "user",
HelpMessage = "Enter the root account password")]
[string]$Password,
[switch]$Reconnect)
Process {
$vmHostView = $vmHost | Get-View –Property Name
# Create a new HostConnectSpec object
$objHostConnectSpec = New-Object VMware.Vim.HostConnectSpec
if ($credential) {
$objHostConnectSpec.userName =
$credential.GetNetworkCredential().UserName
$objHostConnectSpec.password =
$credential.GetNetworkCredential().Password
} else {
$objHostConnectSpec.userName = $user
$objHostConnectSpec.password = $password
}
# if Reconnect switch is specified disconnect host first
if ($Reconnect) {
Write-Host "Disconnecting $($vmHost.Name) " -NoNewline
$taskMoRef = $vmHostView.DisconnectHost_Task()
$task = Get-View $taskMoRef
while ("running", "queued" -contains $task.Info.State) {
Write-Host "." -NoNewline
Start-Sleep 2
$task.UpdateViewData("Info.State")
}
Write-Host "Done"
$task.UpdateViewData("Info.Result")
$task.Info.State
}
# Connect host
Write-Host "Connecting $($vmHost.Name) " -NoNewline
$taskMoRef = `
$vmHostView.ReconnectHost_Task($objHostConnectSpec, $null)
$task = Get-View $taskMoRef
while ("running", "queued" -contains $task.Info.State) {
Write-Host "." -NoNewline
Start-Sleep 2
$task.UpdateViewData("Info.State")
}
Write-Host "Done"
$task.UpdateViewData("Info.Result")
$task.Info.State
}
}
An alternate approach to backing up your complete vCenter Server database is exporting the specific database objects that are of importance in your environment. Using this approach, you can restore your inventory (or maybe just a part of your inventory) to a separate database for testing purposes or create a disaster recovery (DR) replica of your inventory on another vCenter Server. Using this approach, you can even restore your vCenter Server environment to a vCenter Server that is using a different database platform for the backend database.
We once encountered an environment where, due to a database move, the SQL agent rollup jobs weren’t available. The person moving the database was unaware of the existence of any SQL agent rollup jobs. As a result, the real-time performance statistics weren’t consolidated. By the time someone noticed that there was something wrong with the performance charts, the database had grown very, very large. Performance was degrading, and statistics were being deleted from the database. Using a SQL script provided by VMware would have required vCenter Server to be offline for a number of days. Restoring the database from backup was not an option either, since it was a very dynamic environment. The backup was several weeks out-of-date. A restore would result in the loss of many, many changes.
In the end, we used the functions discussed next to re-create the database overnight at the cost of losing all the performance data. But, since the performance statistics were corrupted, we were more than willing to pay that price. It beat having the vCenter Server down for several days. After the rebuild process was done, these functions were still being used to replicate some parts (like roles, permissions, and folders) to a DR environment on a regular basis.
Although this sounds great, the structure of the vCenter Server inventory is complex and contains many different object types and configuration items that are linked to each other one way or the other. To show you how to export all possible objects in your inventory would make this chapter probably 100 pages or more, so we’ll stick to the most common objects to give you an idea of the possibilities.
Let’s start at the top of the inventory, the root folder, and retrieve the complete folder structure of your environment. The folder structure is the logical layout of your environment, and because folders function as containers for other objects, they are an essential part of your environment. The root folder object is the only object that’s always present in your inventory. You can retrieve all the folders in your inventory using the Get-Folder
cmdlet. To retrieve the root folder only, use the -NoRecursion
parameter.
Get-Folder -NoRecursion
Name Id
---- --
Datacenters Folder-group-d1
As you can see, the root folder is always called Datacenters
. This is because at this level the only objects you can create (other than subfolders) are datacenter objects. To retrieve the subfolders, you use the root folder object and pass it along the pipeline to the Get-folder
cmdlet again:
Get-Folder -NoRecursion | Get-Folder -NoRecursion
Name Id
---- --
EMEA Folder-group-d2058
US Folder-group-d2059
You can continue this recursive process for every child folder over and over again to retrieve the full folder structure. This is illustrated in the Get-FolderStructure
function we created in Listing 12-2.
Listing 12-2: Retrieving the folder structure
function Get-FolderStructure {
<#
.SYNOPSIS
Retrieve folder structure
.DESCRIPTION
This function retrieves the folder structure beneath
the given container
.NOTES
Source: Automating vSphere Administration
.EXAMPLE
Get-Datacenter DC01 | Get-FolderStructure
#>
process {
$folder = "" | select Name,Children
$folder.Name = $_.Name
$folder.Children = @($_ | Get-Folder -NoRecursion | `
Get-FolderStructure)
$folder
}
}
You only need to feed this function a folder or datacenter object, and it will return the full folder structure beneath it. Let’s try this and store the root folder’s structure in a hash table called $folderStructure
:
$folderStructure=@{}
Get-Folder –NoRecursion | Get-FolderStructure | `
%{$folderStructure[$_.name] = $_.Children}
Because you supplied the root folder as an input object, you only retrieved the datacenter folders, but not the folders beneath the datacenters. Now, use the same function to retrieve the folder structure beneath each datacenter in your inventory. To retrieve the datacenters, use the Get-Datacenter
cmdlet. First, take a look at the root folders directly beneath the datacenter:
Get-Datacenter DC01 | Get-Folder -NoRecursion
Name Id
---- --
vm Folder-group-v2081
host Folder-group-h2082
datastore Folder-group-s2083
Notice that the three folders returned aren’t visible in your inventory. These are special root folders, just like the datacenter root folder. Virtual machines go in the vm
root folder. Subfolders of a vm
folder are represented as blue folders inside your inventory. The host
folder contains your clusters and hosts, and its subfolders are represented as yellow folders inside your inventory. The datastore
folder is for datastores.
Now, let’s use the Get-FolderStructure
function again to retrieve the folder structure beneath the datacenter and store it in the $folderStructure
hash table:
Get-Datacenter | Get-FolderStructure | %{
$folderStructure[$_.name] = $_.Children
}
All folder structures are now captured into the $folderStructure
hash table. You now only need to export this hash table to a file. The most appropriate format for storing objects is XML. Therefore, use the Export-Clixml
cmdlet to export the $folderStructure
object to an XML file:
$folderStructure | Export-Clixml C:ExportFolders.xml
The datacenter is a container in your inventory, just like a folder. It can contain folders, clusters, and hosts. Although in most cases there will be only one or two datacenter objects in the inventory and re-creating them by hand is quick and painless, using a PowerCLI script to export them enables you to automate the complete rebuild process without user intervention. To retrieve the datacenters, use the Get-Datacenter
cmdlet again:
Get-Datacenter
Name Id
---- --
DC01 Datacenter-datacenter-2080
DC02 Datacenter-datacenter-2
Like datacenters, all inventory objects have a specific location in your inventory. When you want to re-create any object later, you can specify the object’s inventory location using the –Location
parameter. To retrieve the object’s full path you can use something similar to the Get-FolderStructure
function in Listing 12-2, except that you need to work your way up to the root folder this time. In Listing 12-3 you’ll find a function to retrieve the path called Get-VIPath
.
Listing 12-3: Retrieving the object path
function Get-VIPath {
<#
.SYNOPSIS
Retrieve the full path of an inventory object
.DESCRIPTION
This function retrieves the full path of the given
inventory object
.NOTES
Source: Automating vSphere Administration
.PARAMETER inputObject
The inventory object to retrieve the full path from
.PARAMETER childPath
Optional parameter used by the function when calling itself
recursively
.EXAMPLE
Get-Datacenter DC01 | Get-VIPath
#>
Param (
[parameter(valuefrompipeline = $true, mandatory = $true, `
HelpMessage = 'Enter an inventory object entity')]
$InputObject
)
Begin {
$exclude = 'Datacenters', 'vm', 'host', `
'datastore', 'network'
$excludePattern = ($exclude | %{ "^$_$" }) -join "|"
}
process {
if ($InputObject -is [VMware.Vim.ManagedEntity]) {
$obj = $InputObject
} else {
$obj = $InputObject.ExtensionData
}
$path = @($obj.Name)
while ($obj.Parent) {
$obj = Get-View -Id $obj.Parent -Property Name, Parent
$path += $obj.Name
}
$path = $path | Select-String -Pattern `
$excludePattern -NotMatch
[array]::Reverse($path)
$path -join '/'
}
}
You can use the Get-VIPath
function to retrieve the datacenter’s path and add a VIPath
property to the datacenter object using the Add-Member
cmdlet. Let’s put this in a small ForEach
loop and store the datacenters in an array. When all is processed, simply export the array using the Export-Clixml
cmdlet.
$datacenters = @()
ForEach ($dc in Get-Datacenter) {
$dc | Add-Member -MemberType Noteproperty -Name VIPath `
-Value ($dc | Get-View | Get-VIPath)
$datacenters += $dc
}
$datacenters | Export-Clixml "C:ExportDatacenters.xml"
Clusters function as a pool of resources and are the home location of your ESXI hosts, which in turn provide the resources to the clusters’ root resource pool. If you need to re-add your ESXI hosts in your environment, you need to have the clusters in place. Exporting them makes for an easy, fully automated restore. Clusters are retrieved using the Get-Cluster
cmdlet.
Get-Cluster
Name HAEnabled HAFailover DrsEnabled DrsAutomationLe
Level vel
---- --------- ---------- ---------- ---------------
CL01 True 1 False FullyAutomated
CL02 True 1 True FullyAutomated
CL03 False 1 False FullyAutomated
In addition to all the cluster properties, you’ll need to know to which datacenter the cluster belongs so that you can re-create the cluster in the right datacenter. To accomplish this, iterate through the datacenters first and then retrieve the clusters. The datacenter can then be added to the cluster object as a new property using the Add-Member
cmdlet. You can also use the Get-VIPath
function from Listing 12-3 again to retrieve the cluster’s path.
$clusters = @()
ForEach ($dc in Get-Datacenter) {
ForEach ($cluster in ($dc | Get-Cluster)) {
$cluster | Add-Member -MemberType Noteproperty `
-Name Datacenter -Value $dc.name
$cluster | Add-Member -MemberType Noteproperty `
-Name VIPath -Value ($cluster | Get-View | Get-VIPath)
$clusters += $cluster
}
}
$clusters | Export-Clixml "C:ExportClusters.xml" -force
Roles are a collection of privileges and provide a way to aggregate all the individual privileges required to perform a specific task in your infrastructure. When assigning permissions, you assign a role to a specific user or group. If you’ve created custom roles, you probably don’t want to re-create them by hand in case of disaster. Exporting them also makes it possible to import these custom roles into another vCenter installation. You could, for example, create and test new custom roles in a test environment and, when you’re done, export them to a file and import them in the production environment. Roles are retrieved using the Get-VIRole
cmdlet.
Get-VIRole
Name IsSystem
---- --------
NoAccess True
Anonymous True
View True
ReadOnly True
Admin True
VirtualMachineAdminist... False
DatacenterAdministrator False
VirtualMachinePowerUser False
VirtualMachineUser False
…
Because you don’t want to export the default system roles, you’ll need to filter these out before exporting them to a file:
Get-VIRole | Where-Object {-not $_.IsSystem} | `
Export-Clixml "C:ExportRoles.xml"
After defining roles, the next step in setting up your security is assigning permissions to users and groups. In case of disaster, it’s always nice to be able to restore permissions to a recent state. You can quickly restore access to the vCenter Server without making everyone an administrator (temporarily) until you sort out the permissions. To retrieve permissions, you can use the Get-VIPermission
cmdlet:
Get-VIPermission
EntityId Role Principal IsGroup Propagate
-------- ---- --------- ------- ---------
Folder-group-d1 ReadOnly VIReport False True
Folder-group-d1 Admin Administrators True True
Because you can put permissions on a lot of different object types, you’ll want to know what object type the permission is granted to. Therefore, you’ll need to retrieve the SDK object using the EntityId
property and use the GetType()
method to retrieve the object type:
(Get-View $permission.EntityID).GetType().Name
You can simply add the object type to the permission object using the Add-Member
cmdlet:
$permissions=@()
ForEach ($permission in Get-VIPermission) {
$permission | Add-Member -MemberType Noteproperty `
-Name EntityType `
-Value (Get-View $permission.EntityID).GetType().Name
$permissions += $permission
}
$permissions | Export-Clixml "C:ExportPermissions.xml"
Just like datacenters and clusters, your virtual machines have a specific location. This is represented in your infrastructure by the blue folders. The blue folders are perfect candidates for assigning permissions, so be sure to export every VM’s locations. If you don’t, users might not be able to access the VMs if they’re not restored to the correct location. To export each VM’s location, use the Get-VIPath
function from Listing 12-3. In addition to the location, you must know which datacenter the VM belongs to. Use the Add-Member
cmdlet to add both properties to the VM object you’re retrieving using the Get-VM
cmdlet. When all VM objects are retrieved, you can then simply export them to an XML file.
$vmLocations = @()
foreach ($vm in Get-VM) {
$vm | Add-Member -MemberType Noteproperty -Name Datacenter `
-Value $($vm | Get-Datacenter).Name
$vm | Add-Member -MemberType Noteproperty -Name VIPath `
-Value ($vm | Get-View | Get-VIPath)
$vmLocations += $vm
}
$vmLocations | Export-Clixml "C:ExportVMLocations.xml"
The hosts in your inventory can be either part of a cluster or a stand-alone host. You want to make sure that you can import the hosts to their original locations. Failing to do so might put the host’s resources in the wrong cluster and might impact the resources available to your VMs. If you have a stand-alone host, you are interested in the location of that host in your inventory. Here again the Get-VIPath
function from Listing 12-3 can be helpful. If the host is part of a cluster, you’ll only need to know the cluster name.
$vmHosts = @()
foreach ($DC in Get-Datacenter) {
foreach ($machine in ($DC | Get-VMHost)) {
$machine | Add-Member -MemberType Noteproperty -Name `
Datacenter -Value $DC -Force
$machine | Add-Member -MemberType Noteproperty -Name Cluster `
-Value $($machine | Get-Cluster).Name -force
if (-not $_.Cluster) {
$machine | Add-Member -MemberType Noteproperty -Name VIPath `
-Value ($machine | Get-View | Get-VIPath)
}
$vmHosts += $machine
}
}
$vmHosts | Export-Clixml "C:ExportVMHosts.xml"
In vSphere 5.1, VMware introduced a tagging feature to vCenter Server. Tags allow administrators to add metadata to objects in their environment. Administrators now have the ability to create tags and tag categories to organize their virtual environment and allow for easier searching and sorting of objects based on the tagging criteria specified.
Tags can be used on a number of objects within the vSphere environment. Objects such as virtual machines, clusters, datastores, hosts, networks, and more can be tagged with additional metadata. The tag feature allows administrators to choose whether a given tag can be used on more than one type of vSphere object as well as if multiple tags from a single tag category can be used on the same object.
Tagging is a fantastic feature, but the problem with tags is that in vCenter Server 5.1 through vCenter Server 5.5, the tags are stored in the Inventory Service Database and therefore do not carry over if a host is connected to a different vCenter Server. In vSphere 6, tagging has a dedicated database. With this knowledge, you can leverage PowerCLI to create backups of the tag categories, tags, and the relationships between vSphere objects and their attached metadata. Listing 12-4 can be used to export these objects.
Listing 12-4: Exporting tags and tag categories
function Export-Tag {
<#
.SYNOPSIS
Export Tags and Tag Categories
.DESCRIPTION
This function exports Tags and Tag Categories from vCenter
.NOTES
Source: Automating vSphere Administration
.PARAMETER Path
Mandatory parameter used by the function to determine
the destination of the Tags file.
.EXAMPLE
Export-Tag -path C:Tempexportedtags.xml
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, Position = 1)]
[string]$Path
)
# Retrieve all categories vCenter
$categories = Get-TagCategory
# Retrieve all tags from vCenter
$tags = Get-Tag
# Bundle tags and categories in a list
$export = @($categories, $tags)
# Export to desired file
Export-Clixml -InputObject $export -Path $path
}
Once you have exported the tag and tag category information, you can export the relationships between the vSphere objects and their respective tags. We have created a nice function to collect this information, shown in Listing 12-5.
Listing 12-5: Exporting object/tag relationships
function Export-TagRelationship {
<#
.SYNOPSIS
Export Object relationships to their tags
.DESCRIPTION
This function exports Object relationships to tags
.NOTES
Source: Automating vSphere Administration
.PARAMETER Path
Mandatory parameter used by the function to determine
the destination of the Tags file.
.EXAMPLE
Export-TagRelationship -Path c: empTagRelationship.csv
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, Position = 1,
HelpMessage = "Enter a Destination File Path")]
[string]$Path
)
process {
# Create an array to be used
$outputs = @()
# Retrieve all Tag assignments and parse info
$tagentities = Get-TagAssignment | Foreach-Object {
$Uid = $_.Uid.split("/")
# Type of object being associated to tag
$type = $Uid[2].split('=')
# Tag Category
$tagcat = $_.Tag.Category
# Tag Name
$tagname = $_.Tag.Name
# MoRef
$objectID = $type[1]
# Create PSObject of parsed data
$output= New-Object -TypeName PSObject -Property @{
Type = $type[0]
Entity = $_.Entity
Category = $tagcat
Tag = $tagname
ObjectID = $objectID
}
# Add PSObject to array
$outputs += $output
}
# Export array contents to CSV
$outputs | Export-Csv -Path $Path -NoTypeInformation
}
}
When it comes to virtual networking, the virtual standard switch (vSS) information is stored locally on each ESXI host. The virtual distributed switch (vDS), on the other hand, is stored on the vCenter Server; it spans multiple hosts.
VMware makes it easy to export a vDS configuration using either the vSphere Web Client or PowerCLI. During the export, an administrator can decide whether to export the vDS and all port groups associated with that switch or just the vDS. An export allows administrators to easily re-import the exported vDS for use at a later time should something happen to its current configuration.
Get-VDSwitch -Name "vDS-Primary" | Export-VDSwitch -Destination `
"c: empvDS-Primary.zip"
If an administrator wants to export a vDS without the port groups, they can add the -WithoutPortGroups
parameter to the end of the line of code.
Get-VDSwitch -Name "vDS-Primary" | Export-VDSwitch -Destination `
"c: empvDS-Primary.zip" -WithoutPortGroups
If a distributed switch is imported back into the same vCenter from which it was exported, the vmnics of the hosts that were connected to that vDS will reconnect automatically, providing that those vmnics have not been reconfigured and attached to a different switch. However, if the exported vDS is imported into a different vCenter and the original hosts that had been connected to the original vDS are also connected to this new vCenter, the host’s vmnics will need to be re-added to their uplink port groups. Listing 12-6 is a function that exports information on every vDS, including which uplinks each ESXI host’s vmnics are connected to. This ensures that the vmnics connected to each port group before the export of this information will be re-added to the correct port groups if the networking has to be restored at a later date.
Listing 12-6: Exporting vDS vmnic information
function Export-VMHostVMnicvDSConfiguration {
<#
.SYNOPSIS
Export ESXI Host and vmnic connection information
about each vDSwitch and port group.
.DESCRIPTION
This function exports information about each ESXI
Host, its vmnics, and which Distributed switches
and port groups each is connected to.
.NOTES
Source: Automating vSphere Administration
.PARAMETER Path
Specifies the destination path of the CSV file.
.EXAMPLE
Export-VMHostVMnicvDSConfiguration -Path C:TempNICs.csv
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, Position = 1,
HelpMessage = "Enter The Destination Path of the CSV")]
. [string]$Path
. )
process {
$outputs = @()
..
foreach ($switch in (Get-VDSwitch)) {
# get the uplink portgroup of each switch
$PG = Get-VDPortgroup -VDSwitch $switch | Where-Object `
{ $_.isUplink -and $_.NumPorts -gt "0" }
..
#get the vmnics
$PG | Get-VMHostNetworkAdapter -VirtualSwitch `
. $switch.name -Physical | Group-Object -Property VMHost | `
. Foreach {
$output = New-Object -TypeName Psobject -Property @{
.switch = $switch
.PG = $PG.name
.Host = $_.Group[0].VMHost.Name
.vmnic = ([string]::Join(',', ($_.Group | Select `
.-ExpandProperty DeviceName)))
.}
.
.$outputs += $output
}
}
$outputs | Export-Csv -Path $Path -NoTypeInformation
}
}
In the previous section, you learned how you can export inventory items to an XML file. In this section you’ll learn how you can use these files to rebuild your inventory. Let’s start with an empty inventory. As you’ll recall from the “Export vCenter Server Inventory Items” section, an empty inventory only contains the root folder named Datacenters
.
The first items you need to restore are your folders and datacenters beneath the root folder. Three steps are involved in the restore process:
To import the XML files, use the Import-Clixml
cmdlet:
$folderStructure = Import-Clixml "C:ExportFolders.xml"
If you followed the process we outlined in the last section, you exported the folders using nested objects, so you’ll need a special helper filter to re-create the folders recursively. (A filter is nothing more than a function with only a process script block.) The filter first checks if the folder exists. If the folder doesn’t exist, it creates that folder and passes the child property to the filter recursively. Folders are created using the New-Folder
cmdlet, as shown in Listing 12-7.
Listing 12-7: Creating a folder structure
filter New-FolderStructure {
param($parent)
if (-not($folder = Get-Folder $_.name -Location $parent `
-ErrorAction:SilentlyContinue)) {
$folder = New-Folder $_.name -Location $parent
}
$_.children | New-FolderStructure($folder)
}
Let’s start by creating the datacenter folders. Because the $folderStructure
variable is a hash table, you can simply retrieve the datacenter folders item using its name, Datacenters
:
$folderStructure["Datacenters"]
Name Children
---- --------
EMEA {@{Name=BE; Children=}
US {}
Now just use the pipeline to pass the objects to the New-FolderStructure
filter and pass the root folder as the parent object:
$folderStructure["Datacenters"] | `
New-FolderStructure(Get-Folder -NoRecursion)
With the datacenter folders in place, you can now create the datacenters. Because the datacenter is the root of the vm
and host
folders, you’ll have to create the datacenters before you can continue creating folders. Datacenters are created using the New-Datacenter
cmdlet.
Again you’ll need a special helper filter, Get-VILocation
(Listing 12-8), to retrieve the location of the datacenters that you’ve exported using the Get-VIPath
function to the VIPath
property in the export section.
Listing 12-8: Retrieving locations
filter Get-VILocation {
param($parent)
if ($_.child) {
$_.child | Get-VILocation(Get-Folder -Location $parent `
$_.Name)
} else {
Get-Folder -Location $parent $_.Name
}
}
The Get-VILocation
filter retrieves the parent folder (location) of the object using the nested objects stored in the VIPath
property. You need to use this filter in the –Location
parameter of the New-Datacenter
cmdlet:
ForEach ($dc in Import-Clixml "C:ExportDatacenters.xml") {
New-Datacenter -Location ($dc.VIPath | Get-VILocation) `
-Name $dc.Name
}
Creating the vm
and host
folders is similar to creating the datacenter folders, except that you need to provide the datacenter object as the parent object. Use the GetEnumerator()
method to put all the hash table items on the pipeline. This, however, will also put the datacenter folders on the pipeline and you already imported them, so you need to filter them out:
$folderStructure.GetEnumerator() | %{
if ($_.Name –ne "Datacenters") {
$_.Value | New-FolderStructure(Get-Datacenter $_.Name)
}
}
You create clusters by using the New-Cluster
cmdlet. Because this cmdlet only accepts DRS parameters when the -DrsEnabled
parameter is set to true, you’ll need to make this a multistep action in case one of your clusters has DRS (temporarily) disabled. First, you’ll need to create the clusters and set the –DrsEnabled
parameter to $true
. After the cluster is created, you’ll use the Set-Cluster
cmdlet to set DRS and HA according to the exported settings. The Set-Cluster
cmdlet is used to change cluster settings.
ForEach ($cluster in Import-Clixml "C:ExportClusters.xml") {
$newCluster = New-Cluster -Location ($cluster.VIPath | `
Get-VILocation(Get-Datacenter $cluster.Datacenter)) `
-Name $cluster.Name
Set-Cluster -Cluster $newCluster -DrsEnabled:$True `
-VMSwapfilePolicy $cluster.VMSwapfilePolicy `
–DrsAutomationLevel $cluster.DrsAutomationLevel.value `
-HAAdmissionControlEnabled `
$cluster.HAAdmissionControlEnabled `
-HAFailoverLevel $cluster.HAFailoverLevel `
-HAIsolationResponse $cluster.HAIsolationResponse `
-HARestartPriority $cluster.HARestartPriority `
-Confirm:$false
Set-Cluster –Cluster $newcluster `
-EVCMode $cluster.EVCMode `
-VsanEnabled $cluster.VsanEnabled `
-VsanDiskClaimMode $cluster.VsanDiskClaimMode -Confirm:$false
Set-Cluster -Cluster $newCluster `
-DrsEnabled $cluster.DrsEnabled `
-HAEnabled $cluster.HAEnabled -Confirm:$false
}
Now that your clusters are created, you can add your hosts. Hosts can be added using the Add-VMHost
cmdlet. To add a host, you must also provide a user account with sufficient permissions to log on to that specific host. Usually, this is the root account. You can specify the username and password as a string using the -User
and -Password
parameters, or you can ask for credentials at runtime using the Get-Credential
cmdlet:
Get-Credential root
The only drawback to using the Get-Credential
cmdlet is that you can’t specify the window title in the login dialog box, shown in Figure 12-2.
As an alternative, you can use the PromptForCredential()
method of the $host
object, which lets you specify the window caption, message, and default username that appear in the message box. The method’s syntax is $Host.UI.PromptForCredential($Caption,$Message,$UserName,$Domain)
. The $Domain
parameter is not visible in the dialog box but will be prepended to the username in the credential object that’s returned in the form of "$Domain$UserName"
. Figure 12-3 shows a custom credential dialog box.
$hostCredential = $Host.UI.PromptForCredential( `
"Please enter host credentials", `
"Enter ESXI host credentials", "root", "")
If the host isn’t part of a cluster, the host’s location can be retrieved using the Get-VILocation
filter from Listing 12-8:
ForEach ($vmhost in Import-Clixml "C:ExportVMHosts.xml") {
if ($vmhost.Cluster) {
Add-VMHost $vmhost.Name `
-Location (Get-Cluster $vmhost.Cluster `
-Location (Get-Datacenter $vmhost.Datacenter)) `
-Credential $hostCredential -Force
}
else {
Add-VMHost $vmhost.Name -Location ($vmhost.VIPath | `
Get-VILocation(Get-Datacenter $vmhost.Datacenter)) `
-Credential $hostCredential -Force
}
}
If the VMs are still registered on your ESXI hosts, adding the hosts also imported the registered VMs. The VMs are all imported into the default Discovered virtual machine
folder, and luckily you’ve exported each VM’s location. To move a VM to a folder, you’ll have to use the Move-VM
cmdlet. You can easily retrieve the original location using the Get-VILocation
function from Listing 12-8 again:
ForEach ($vm in Import-Clixml "C:ExportVMLocations.xml") {
$dc = Get-Datacenter $vm.Datacenter
Move-VM ($dc | Get-VM $vm.Name) -Destination ($vm.VIPath | `
Get-VILocation($dc))
}
New roles are created using the New-VIRole
cmdlet. Before restoring your roles, you should remove all existing roles, including the default sample roles that came with your vCenter Server installation. Otherwise, you’ll receive errors when trying to import a role that already exists. System roles can’t be deleted, so filter them out first:
Get-VIRole | Where-Object {-not $_.IsSystem} | `
Remove-VIRole -Confirm:$false
When restoring a role, you’ll need to specify which privileges are attached to the role. These privileges have to be specified as privilege objects. When exporting roles, however, the privileges are returned as strings. So before adding privileges to the new role, you’ll need to find a way to convert the string values to privilege objects. This can be achieved by retrieving all privileges first using the Get-VIPrivilege
cmdlet and then matching the privilege IDs against the privilegelist
property of the role you want to import. Let’s import the roles:
$privileges = Get-VIPrivilege -PrivilegeItem
ForEach ($role in Import-Clixml "C:ExportRoles.xml") {
New-VIRole -Name $role.Name -Privilege ($privileges | `
? {$role.PrivilegeList -contains $_.id})
}
Now that the roles are in place, you can start to restore the permissions throughout your inventory. New permissions can be assigned using the New-VIPermission
cmdlet. Because you can assign permissions to all kinds of objects in your inventory, you’ll first need to determine what object type you want to restore permissions to in order to use the right cmdlet to retrieve that object. If the object is a folder, you’ll need to use the Get-Folder
cmdlet. If the object is a datacenter, you’ll need to use the Get-Datacenter
cmdlet, and so forth.
However, this can be more easily achieved using the Get-View
cmdlet and making use of the full SDK objects, because this cmdlet allows you to specify the entity type and hence can be used to retrieve all kinds of objects. When using the Get-View
cmdlet, you must specify a filter to indicate the objects you want to return. For example, to retrieve a VM called VM001
, you would use the following:
Get-View -ViewType "VirtualMachine" -Filter @{"Name" = "VM001"}
One important thing to know is that Get-View
filters are regular expressions. Regular expressions can contain metacharacters that serve special uses. Because these characters can also be part of the object’s name, you’ll have to escape them using the character. We’ve created a small helper function for this in Listing 12-9.
Listing 12-9: Escaping regular expression metacharacters
filter Escape-MetaCharacter {
ForEach($MetaChar in '^','$','{','}','[',']','(',')','.', `
'*','+','?','|','<','>','-','&') `
{$_=$_.replace($MetaChar,"$($Metachar)")
}
$_
}
To assign permissions using the SDK, use AuthorizationManager
and its SetEntityPermissions()
method. This method accepts a ManagedObjectReference
to an inventory object and a VMware.Vim.Permission
object:
ForEach ($permission in Import-Clixml `
"C:ExportPermissions.xml") {
$entityView = Get-View -ViewType $permission.EntityType `
-Property Name -Filter `
@{"Name" = $($permission.Entity.Name | `
Escape-MetaCharacters)}
$newVIPermission = New-Object VMware.Vim.Permission
$newVIPermission.principal = $permission.Principal
$newVIPermission.group = $permission.IsGroup
$newVIPermission.propagate = $permission.Propagate
$newVIPermission.roleId = $(Get-VIRole $permission.Role).id
$authMgr.SetEntityPermissions($entityView.MoRef, `
$newVIPermission)
}
Once your permissions are set, your folders are in place, and the virtual structure is set up, you can restore the virtual distributed switch. If you are restoring the distributed switch back onto the original vCenter server from which it was exported, the ESXI hosts should reconnect to the switch automatically. If you are importing the distributed switch configuration onto a new vCenter server, you will need to reconnect the vmnics of the ESXI hosts to each switch.
Start by importing the virtual distributed switch; if you have more than one virtual datacenter, you will need to specify the correct folder using the Get-Datacenter
cmdlet at the beginning of the Get-Folder
cmdlet:
$Folder = Get-Datacenter Main-DC | Get-Folder -Name Network
New-VDSwitch -Name "vDS-Primary" -Location $Folder `
-BackupPath "c:TempvDS-Primary.zip"
If you are restoring a distributed switch that already exists in vSphere, restoring the switch overwrites the current settings of the distributed switch and its port groups. Note that it does not delete existing port groups that are not part of the configuration file.
Once you have imported your distributed switch and are ready to reconnect your host’s vmnics to their respective port groups, you can leverage the Import-VMHostVMnicvDSConfiguration
cmdlet in Listing 12-10 to restore the configuration that you had exported earlier in the chapter.
Listing 12-10: Importing ESXI host vmnic configuration
function Import-VMHostVMnicvDSConfiguration {
<#
.SYNOPSIS
Attach ESXI Host to vDS and connect each vmnic to
its corresponding Uplink portgroup
.DESCRIPTION
This function uses the information that was
exported using the Export-VMHostVMnicvDSConfiguration
function to connect each ESXI host to the correct
vDS and each vmnic to its corresponding Uplink PG
.NOTES
Source: Automating vSphere Administration
.PARAMETER Path
Specifies the path to the CSV file.
.EXAMPLE
Import-VMHostVMnicvDSConfiguration -Path C:TempVMnicConfig.csv
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, Position = 1,
HelpMessage = "Enter The Path To The VMNicConfig.csv file")]
[string]$Path
)
process {
$csv = Import-Csv -Path $Path
foreach ($row in $csv) {
Write-Host $row.Switch -ForegroundColor DarkGreen
Write-Host ("... " + $row.PG) -ForegroundColor Green
Write-Host (" . " + $row.Host) -ForegroundColor DarkCyan
# Check for VMhost and vDS
if (!(Get-VMhost -Name $row.Host -ErrorAction SilentlyContinue)) `
{Write-Host "Warning $($row.Host) does not exist, moving to next `
row" -ForegroundColor Red; Continue }
if (!(Get-VDSwitch -Name $row.Switch -ErrorAction SilentlyContinue))`
{ Write-Host "Warning $($row.switch) does not exist, moving to next `
row" -ForegroundColor Red; Continue }
# Add Host to vDS
Write-Host "Adding Host $($row.Host) to vDS Switch $($row.Switch)" `
-ForegroundColor Yellow
Get-VDSwitch -Name $row.Switch | Add-VDSwitchVMHost `
-VMHost $row.Host
if (Get-VDSwitch -Name $row.Switch) { Write-Host "Host `
$($row.Host) added Successfully" -ForegroundColor Green } else `
{ Write-Host "It Appears that $($row.Host) was not added `
successfully to $($row.Switch)" -ForegroundColor Red }
# Add vmnics to vDS
$vmnics = ($row.vmnic).Split(",")
$vmnics | Foreach-Object {
# Clear past configuration
Get-VMHost -Name $row.Host | Get-VMHostNetworkAdapter -Physical `
-Name $_ | Remove-VDSwitchPhysicalNetworkAdapter `
-Confirm:$false -ErrorAction SilentlyContinue
#Get the Hosts network adapters
Write-Host "Adding $($_) on $($row.Host) to vDS Switch `
$($row.Switch)" -ForegroundColor Yellow
$HostNetworkAdapter = Get-VMHost $row.Host | `
Get-VMHostNetworkAdapter -Physical -Name $_
Get-VDSwitch $row.Switch | Add-VDSwitchPhysicalNetworkAdapter `
-VMHostPhysicalNic $HostNetworkAdapter -Confirm:$false
if (Get-VDSwitch -Name $row.Switch | Get-VMHostNetworkAdapter `
| Where-Object { $_.VMHost -like $row.host -and $_.Name -eq $_ }) `
{ Write-Host "$_ on $($row.Host) added Successfully" `
-ForegroundColor Green } else { Write-Host "It Appears `
that $($_) on $($row.Host) was not added successfully to `
$($row.Switch)" -ForegroundColor Red }
Write-Host (" .... " + $_) -ForegroundColor Cyan
}
}
}
}
Tags and tag categories can be imported into vCenter Server at any time. However, you will want to make sure that all of your virtual objects are populated in vCenter before you try to re-create the tag relationships; otherwise you will receive errors. The tags will not be able to find their associated objects. Listing 12-11 contains a function that will help you import the tags and tag categories that you exported earlier in this chapter.
Listing 12-11: Importing tags and tag categories
function Import-Tag {
<#
.SYNOPSIS
Import Tags and Tag Categories
.DESCRIPTION
This function exports Tags and Tag Categories from vCenter
.NOTES
Source: Automating vSphere Administration
.PARAMETER Path
Mandatory parameter used by the function to determine
the source of the Tags file.
.EXAMPLE
Import-Tag -Path C:TempTagData.xml
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, Position = 1)]
[string]$Path
)
# Import data from file
$data = Import-Clixml -Path $Path
# Divide the input in separate lists for tags and categories
$categoryList = $data[0]
$tags = $data[1]
# Array of categories
$categories = @()
# Import each category
foreach ($category in $categoryList) {
$categories += New-TagCategory `
-Name $category.Name `
-Description $category.Description `
-Cardinality $category.Cardinality `
-EntityType $category.EntityType `
| Out-Null
}
# Create each tag
foreach ($tag in $tags) {
# Add to corresponding Tag Category
$category = $categories | Where-Object { $_.Name -eq $tag.Category.Name }
if ($category -eq $null) { $category = $tag.Category.Name }
New-Tag -Name $tag.Name -Description $tag.Description `
-Category $category
}
}
Once the tags and tag categories have been created, you can use the function in Listing 12-12 to associate objects with their original tags. As of this writing, vCOWorkflow
and vCOScheduledWorkflow
object tags cannot be imported. If you have tags on either of those two objects within vCenter Server, you will need to add tags manually.
Listing 12-12: Adding tag-object relationship
function Import-TagRelationship {
<#
.SYNOPSIS
Imports Tag-Object relationship for vSphere objects
.DESCRIPTION
This function restores Tags to vSphere objects
.NOTES
Source: Automating vSphere Administration
.PARAMETER Path
Parameter used by the function to locate the
import file
.EXAMPLE
Import-TagRelationship -Path c: empTagRelationship.csv
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, Position = 1)]
[string]$Path
)
#import data file
$csv = Import-Csv -Path $Path
$csv | ForEach-Object {
$tagcat = $_.Category
$tag = Get-Tag -Name $_.Tag | Where-Object { $_.Category -like $tagcat }
$entity = $_.Entity
switch ($_.type) {
"Cluster" { Get-Cluster -Name $Entity | `
New-TagAssignment -Tag $tag }
"Datacenter" { Get-Datacenter -Name $Entity | `
New-TagAssignment -Tag $tag }
"Datastore" { Get-Datastore -Name $Entity | `
New-TagAssignment -Tag $tag }
"DatastoreCluster" { Get-DatastoreCluster `
-Name $Entity | New-TagAssignment -Tag $tag }
"DistributedPortGroup" { Get-VDPortgroup `
-Name $Entity | New-TagAssignment -Tag $tag }
"DistributedSwitch" { Get-VDSwitch `
-Name $Entity | New-TagAssignment -Tag $tag }
"Folder" { Get-Folder -Name $Entity | `
New-TagAssignment -Tag $tag }
"Host" { Get-VMHost -Name $Entity | `
New-TagAssignment -Tag $tag }
"Network" { Get-VirtualSwitch -Name $Entity | `
New-TagAssignment -Tag $tag }
"ResourcePool" { Get-ResourcePool -Name $Entity | `
New-TagAssignment -Tag $tag }
"vApp" { Get-VApp -Name $Entity | `
New-TagAssignment -Tag $tag }
"vCOScheduledWorkflow" { Write-Host "$Entity is a `
vCOScheduledWorkflow and will need to be tagged `
manually with: ($tag)" -ForegroundColor Red }
"vCOWorkflow" { Write-Host "$Entity is a vCOWorkflow `
and will need to be tagged manually with: `
($tag)" -ForegroundColor Red }
"VirtualMachine" { Get-VM -Name $Entity | `
New-TagAssignment -Tag $tag }
}
}
}
If you’ve lost your ESXI host and have to reinstall or restore it, you’ve also lost all VM registrations. So, you’ll need to register your VMs before you can restore your VMs to their original location. To register a VM, you’ll need the exact path of its VMX file. You could browse your datastores using the vSphere Client and register every VMX file found one at a time, or you can use PowerCLI to do this daunting task for you.
Browsing the datastores from the PowerCLI console is very easy, because PowerCLI includes a datastore provider. To view all installed providers, use the Get-PSProvider
cmdlet, and to view all mapped drives, use the Get-PSDrive
cmdlet.
Get-PSDrive
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
A FileSystem A:
Alias Alias
C 10,89 1,09 FileSystem C:
…
vi VimInventory LastConnected
VCenterServer
vis VimInventory
vmstore VimDatastore LastConnected
VCenterServer
vmstores VimDatastore
…
Notice the vmstore
drive. You can use this drive to browse the datastores just like you’d browse your local hard drive. (Note that the drive names are case-sensitive.) So, just use the Get-ChildItem
cmdlet, its alias dir,
or even ls
if you prefer the Unix style:
dir vmstore:
Name Type Id
---- ---- --
DC01 Datacenter Datacenter-d...
Get-ChildItem vmstore:DC01
Name FreeSpaceMB CapacityMB
---- ----------- ----------
ESXTST01_Local_01 419497 476672
ESX02_Local_Datastore 143127 151296
VSAPROD1_NotReplicated 65626 99840
To find all VMX files, you just have to provide the -Include *.vmx
and -Recurse
parameters. To register the found VMX files, you’ll use the New-VM
cmdlet.
Get-ChildItem vmstore: -Include *.vmx -Recurse | %{
New-VM -VMHost (Get-Datastore $_.Datastore | Get-VMHost | `
Get-Random) -VMFilePath $_.DatastoreFullPath
}
To assist in the registration process, you can use the Register-VMX
function, which is shown in Listing 12-13. This function provides much more flexibility because you can search for one or more datastores, a host, a cluster, or a datacenter. Also, it speeds things up by using SDK objects instead of the datastore provider, which is more than welcome in a larger environment.
Listing 12-13: Searching datastores for VMX files
function Register-VMX {
<#
.SYNOPSIS
Finds and registers VMX's that are not currently in vCenter.
.DESCRIPTION
Find and register VM's that are on a datastore but not
registered in vCenter
.NOTES
Source: Automating vSphere Administration
Authors: Luc Dekens, Arnim van Lieshout, Jonathan Medd,
Alan Renouf, Glenn Sizemore, Brian Graf,
Andrew Sullivan
.PARAMETER Cluster
Name of Cluster to be searched (searches all datastores
associated to that cluster)
.PARAMETER Datastore
Name of Datastore to search for VMX files
.PARAMETER Folder
Name of Target VM folder in vCenter
.EXAMPLE
Register-VMX -Cluster Main-CL -Verbose -Confirm:$false
.EXAMPLE
Register-VMX -Datastore iSCSI-900a -Verbose
.EXAMPLE
Get-Datastore MyDS | Register-Vmx -WhatIf -Verbose
#>
[CmdletBinding(SupportsShouldProcess = $True)]
[OutputType([String[]])]
param (
[parameter(ParameterSetName = 'Cluster')]
[PSObject]$Cluster,
[parameter(ParameterSetName = 'Datastore', `
ValueFromPipeline)]
[PSObject[]]
$Datastore,
[PSObject]$Folder
)
Begin {
$StartTime = Get-Date
$fn = $MyInvocation.MyCommand
$dsProvider = 'TgtDS'
if (!$Folder) {
$Folder = 'Discovered virtual machine'
}
}
Process {
if ($PSCmdlet.ParameterSetName -eq 'Cluster') {
$Datastore = Get-Cluster -Name $cluster | `
Get-Datastore
}
foreach ($ds in $Datastore) {
if ($ds -is [System.String]) {
$ds = Get-Datastore -Name $ds
}
Write-Verbose "$((Get-Date).ToString())`t$($fn)`t `
Looking at datastore $($ds.Name)"
$registered = @{ }
# Get all registered VM on the datastore
Get-VM -Datastore $ds | %{
$_.Extensiondata.LayoutEx.File | where { `
$_.Name -like "*.vmx" } | %{
Write-Verbose "$((Get-Date).ToString())`t `
$($fn)`tFound registered VMX file $($_.Name)"
$registered.Add($_.Name, $true)
}
}
# Set up a PSDrive for the datastore
if (Test-Path -Path "$($dsProvider):") {
Write-Verbose "$((Get-Date).ToString())`t `
$($fn)`tCleaning Up PSDrive Connections for $dsProvider"
Remove-PSDrive -Name $dsProvider -Confirm:$false
}
New-PSDrive -Name $dsProvider -Location $ds `
-PSProvider VimDatastore -Root '' `
-WhatIf:$false | Out-Null
# Find all unregistered VMX files on datastore
Write-Verbose "$((Get-Date).ToString())`t `
$($fn)`tSearching for Unregistered VM's. This May Take a While"
$unregistered = @()
Get-ChildItem -Path "$($dsProvider):" -Filter `
"*.vmx" -Recurse |
where { $_.FolderPath -notmatch ".snapshot" -and `
!$registered.ContainsKey($_.DatastoreFullPath) } | `
%{
Write-Verbose "$((Get-Date).ToString())`t `
$($fn)`tFound unregistered VMX $($_.DatastoreFullPath)"
$unregistered += $_
}
# Drop the PSDrive
Remove-PSDrive -Name $dsProvider -WhatIf:$false
#Register all .vmx Files as VMs on the datastore
$esx = Get-VMhost -Datastore $ds | Get-Random
$fld = Get-Folder -Name $Folder -Location `
(Get-Datacenter -VMHost $esx)
foreach ($VMXFile in $unregistered) {
Write-Verbose "$((Get-Date).ToString())`t `
$($fn)`tRegistering $($VMXFile.DatastoreFullPath) in folder `
$($Folder) on $($esx.Name)"
New-VM -VMFilePath $VMXFile.DatastoreFullPath `
-VMHost $esx -Location $fld -RunAsync | Out-Null
}
}
}
End {
# Remove innaccessible VMs registered in another vCenter
$inaccessiblevms = Get-VM | where { `
$_.ExtensionData.Summary.OverallStatus -eq "gray" } | `
Get-View
# Wait until the RegisterVM_Task completes
Do { Start-Sleep -Seconds 5 } while (Get-Task | where `
{ $_.Name -eq "RegisterVM_Task" -and $_.State -eq `
"Running" -and $_.StartTime -gt $StartTime })
# Store Task ID's in array
$taskIDs = @()
$tasks = Get-Task | where { $_.StartTime -gt `
$StartTime -and $_.Name -eq "RegisterVM_Task" }
foreach ($task in $tasks) {
$taskIDs += $task.Result.Value
}
# If vCenter Task ID matches VM ID, Unregister
foreach ($vm in $inaccessiblevms) {
if ($taskIDs -contains $vm.Moref.Value) {
Write-Verbose "$((Get-Date).ToString())`t `
$($fn)`tInaccessible VM found $($vm.Name)... `
Removing From Inventory"
$vm.unregisterVM()
}
}
}
}