Chapter 12
Organize Your Disaster Recovery

In this chapter, you will learn to:

  • Back Up Your vCenter Server Database
  • Backing Up Your vCenter Server Database
  • Restore Your vCenter Server
  • Restoring Your vCenter Server Database
  • Reconnecting ESXI Hosts
  • Export vCenter Server Inventory Items
  • Folders
  • Datacenters
  • Clusters
  • Roles
  • Permissions
  • VM Locations
  • Hosts
  • Tags
  • Networking
  • Import vCenter Server Inventory Items
  • Folders and Datacenters
  • Datacenter Folders
  • Clusters
  • Hosts
  • VM Locations
  • Roles
  • Permissions
  • Networking
  • Tags
  • Recover Virtual Machines

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.

Back Up Your vCenter Server Database

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.

Backing Up Your vCenter Server Database

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.

Creating a Full Database Backup

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:

  1. Server Instance The name of the server that is hosting the database
  2. Database The name of the database you plan to back up
  3. Backup File Location The target location for the backup file
  4. Backup Action A specification that defines whether you are backing up the database, files, or logs
  5. Credentials The username and password needed to connect to the database.

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.

c12f001.tif

Figure 12-1: Invalid location error

If the backup was successful, you will be able to see the file in the location specified in the command.

Creating a Differential Backup

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)

Creating a Log Backup

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)

Restore Your vCenter Server

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.

Restoring Your vCenter Server Database

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.

Reconnecting ESXI Hosts

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
     }
}

Export vCenter Server Inventory Items

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.

Folders

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

Datacenters

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

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

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"

Permissions

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"

VM Locations

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"

Hosts

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"

Tags

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
  }
}

Networking

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
}
}

Import vCenter Server Inventory Items

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.

Folders and 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:

  1. Import the datacenter folders.
  2. Import the datacenter objects.
  3. Import the VM and host folders.

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)
}

Datacenter Folders

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)

Datacenter Objects

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
}

VM and Host Folders

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)
  }
}

Clusters

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
}

Hosts

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.

c12f002.tif

Figure 12-2: Default Credential Request dialog box

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", "")
c12f003.eps

Figure 12-3: Custom credential dialog box

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
  }
}

VM Locations

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))
}

Roles

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})
}

Permissions

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)
}

Networking

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

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 }
}
}
}

Recover Virtual Machines

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()
            }
        }
    }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset