Chapter 10
Using vApps

In this chapter, you will learn to:

  • Import Virtual Appliances
  • Create Your Own vApps
  • Maintain vApps
  • Setting the Start Order
  • Power Operations
  • Using Network Protocol Profiles
  • Using IP Assignment
  • Modifying vApp Product Information

As vSphere has developed, the term vApp has meant many different things. In the past many would refer to any virtualized workload as a virtual application, or vApp, whereas others use vApp to refer to a VM that has been exported to an Open Virtualization Format (OVF) template. As of vSphere 4.1, the term vApp has referred to a virtual container similar to a resource pool. A vApp can contain one or more VMs and is managed as a single logical unit. The modern vApp can be powered on or off and even cloned like a standard VM. vApps allow you to simplify complex applications by providing vSphere with valuable metadata about a group of VMs. For instance, you can capture the startup/shutdown sequence for a web farm. Then, from within vSphere, you simply power on or off the vApp and vSphere handles the rest. In this chapter you will learn to import virtual appliances, create and manage vApps, and automate some of the more advanced vCenter features surrounding vApps.

Import Virtual Appliances

In 2006, VMware opened the VMware Virtual Appliance Marketplace. The idea was that third-party developers could provide whole VMs preconfigured, thereby eliminating complex installers and simplifying the deployment of new applications. The Marketplace has proven to be a little hit or miss, but the concept itself has been a smash hit. Virtual appliances have become mainstream, and virtually all software/hardware vendors offer them. vApps are distributed in either Open Virtual Appliance (OVA) or Open Virtualization File (OVF) formats. In reality, they are identical as every OVA contains a tape archive (TAR)-compressed copy of an OVF.

The installation of a vApp is straightforward. Simply download the zip file containing the OVF, or the self-contained OVA, and then import the appliance. However, this does bring a slight complication. Most OVA/OVF appliances contain configuration data that is used at the initial power-on to automatically configure the vApp. When importing through the vSphere web client, this is all done in one step, but when importing from PowerCLI you must first inspect the configuration data to determine the settings. This is accomplished with the Get-OvfConfiguration cmdlet. Listing 10-1 will read the metadata and return an object with all the configurable parameters in the OVF/OVA.

Listing 10-1: Getting the OVF metadata

Get-OvfConfiguration .VMware-vCenter-Server-Appliance-6.0.0.ova

Common           : System.Object
DeploymentOption : VMware.VimAutomation.ViCore.Impl.V1.Ovf.OvfPropertyImpl
IpAssignment     : System.Object
NetworkMapping   : System.Object
vami             : System.Object

You can then leverage the metadata to programmatically deploy a complete vApp from any OVA/OVF. Listing 10-2 fully automates the deployment of the vCenter Server appliance.

Listing 10-2: Deploying vCenter Server appliance from PowerCLI

# Read Metadata and create the configuration object
$Path = ".VMware-vCenter-Server-Appliance-6.0.0.ova"
$OvfConfig = Get-OvfConfiguration -Ovf $Path

# Set all the required configuration parameters.
$OvfConfig.NetworkMapping.Network_1.Value = 'Public'
$OvfConfig.DeploymentOption.Value = 'tiny'
$OvfConfig.Common.guestinfo.cis.appliance.net.addr.family.Value = "ipv4"
$OvfConfig.Common.guestinfo.cis.appliance.net.mode.Value = "static"
$OvfConfig.Common.guestinfo.cis.appliance.net.addr_1.Value = "192.168.2.8"
$OvfConfig.Common.guestinfo.cis.appliance.net.prefix.Value = "24"
$OvfConfig.Common.guestinfo.cis.appliance.net.gateway.Value = "192.168.2.1"
$OvfConfig.Common.guestinfo.cis.appliance.net.dns.servers.Value = "192.168.2.1"
$OvfConfig.Common.guestinfo.cis.vmdir.password.Value = "VMware1!"
$OvfConfig.Common.guestinfo.cis.appliance.root.passwd.Value = "VMware1!"
$OvfConfig.Common.guestinfo.cis.appliance.time.tools_sync.Value = "True"
$OvfConfig.Common.guestinfo.cis.appliance.ssh.enabled.Value = "True"

# create a hash table with all the configuration
$Splat = @{
    'Name' = 'VCSA'
    'VMHost' = (Get-VMHost ESX1)
    'Source' = $Path

    'OvfConfig' = $OvfConfig
    'DiskStorageFormat' = 'Thin'
}

#Import the vApp using Splatting to inject parameters
Import-VApp @Splat

Virtual appliances can simplify the deployment of a new VM. vApps bring that simplicity at a scale that was previously only available to the largest hosting providers.

Create Your Own vApps

vApps are a resource pool at their base, so when creating a new vApp, resource guarantees are where it all starts. There are three ways to create a new vApp in your environment:

  • Create a new vApp.
  • Clone an existing vApp.
  • Import a vApp from an OVF/OVA.

Listing 10-3 creates a new vApp that conforms to the following specification:

  • Name: App01
  • Location: Cluster Prod01
  • Reserve 4 GHz CPU
  • Reserve 6 GB of RAM

Listing 10-3: Creating a new vApp

New-VApp -Name App01 `
    -Location (Get-Cluster prod01) `
    -CpuExpandableReservation $true `
    -CpuReservationMhz 4000 `
    -MemExpandableReservation $true `
    -MemReservationGB 6

vApps can also be cloned with the New-vApp cmdlet. As shown in Listing 10-4, App01 is cloned to a new vApp named App02. Be aware that when a vApp is cloned, the clone includes any child VMs or vApps.

Listing 10-4: Cloning an existing vApp

New-VApp -Name App02 `
    -Location (Get-Cluster prod01) `
    -VApp (Get-VApp App01) `
    -Datastore datastore1

The third way to create a vApp is to import one from an OVF. This is often used either as an inexpensive vApp backup or to copy vApps between vCenter Servers. Before a vApp can be imported, it must first be exported. For that, PowerCLI provides the Export-VApp cmdlet. Like cloning, exporting a vApp results in the VMs being exported as well. This is very useful because it enables entire systems to be transported using one logical container. Nothing is lost in the move—settings such as the startup order, IP allocation, and resource allocation are all encompassed in the exported OVF. Listing 10-5 exports App02 into an OVF container.

Listing 10-5: Exporting an existing vApp

Export-VApp -VApp (Get-VApp App02) `
    -Name "app02_$(Get-Date -Format MM_dd_yyyy)"

Once the vApp is exported, importing is similar to importing a virtual appliance. The difference is that when you import an OVF, the whole vApp is re-created along with its child vApps and VMs. To reimport the vApp named App02 that was exported in Listing 10-5, simply point the Import-vApp cmdlet to the OVF file, VMHost, and a datastore (Listing 10-6).

Listing 10-6: Importing a vApp from OVF

Import-VApp -Source .app02_01_10_2015.ovf `
    -Name 'App02' `
    -VMHost 'ESX1*' `
    -Datastore datastore1

To remove an existing vApp from the system, the Remove-vApp cmdlet is provided within PowerCLI. By default the cmdlet will merely remove the vApp from inventory. To permanently delete the vApp and delete the data from disk, you must apply the DeletePermanently switch. Listing 10-7 removes App02 from the environment, deleting its metadata and removing the child VMs from the datastores.

Listing 10-7: Removing an existing vApp

Remove-VApp -VApp App02 -DeletePermanently

Over the last 5 years, vApps have grown in importance and are now integral to more advanced systems such as vCloud Director. They are on par with VMs when it comes to deployment options, with one notable exception: there is no way to target a specific hard drive or VM to a datastore when deploying a vApp. This leads to a two-step process when there is a need to spread the I/O load of a vApp. Deploy to a datastore large enough to hold the entire vApp, and then, after it’s been deployed, SVMotion individual hard disks and VMs to different datastores.

Maintain vApps

With the basics of vApps established, the fun really starts: maintaining and modifying existing vApps, using them not only as a provisioning mechanism, but as a logical management container to group a service into a single container regardless of the number of VMs that comprise said service. That is where the true power of vApps begins to shine. To add an existing VM to an existing vApp, use the standard VM mobility cmdlet Move-VM and target the vApp as the destination (Listing 10-8).

Listing 10-8: Adding VMs to an existing vApp

Get-VM Web01, Web02, SQL01 |
    Move-VM -Destination (Get-VApp -Name 'App01')

Setting the Start Order

Start order sequencing is perhaps the most powerful feature of a vApp. By setting the start order, you ensure that any administrator can safely power on or off an application no matter now complicated. Unfortunately, PowerCLI doesn’t currently contain any cmdlets for managing the startup order. But Listing 10-9 provides the Get-vAppStartOrder function for that purpose.

Listing 10-9: The Get-vAppStartOrder function

<#
  .SYNOPSIS
    Get the vApp Startup Order for a given VM
  .DESCRIPTION
    Get the vApp Startup Order for a given VM if no VM is
    provided will return the startup order for every VM
    in the vApp.
  .PARAMETER VM
    VM to retrieve the startup order for.
  .PARAMETER vApp
    vApp to retrieve the startup order from.
  .EXAMPLE
    Get-vApp | Get-vAppStartOrder
  .EXAMPLE
    Get-vAppStartOrder -VM (get-vm sql01)
  #>
function Get-vAppStartOrder {
  [CmdletBinding()]
  Param(
    [parameter(ValueFromPipeline=$true)]
    [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]
    $VM
  ,
    [parameter(ValueFromPipeline=$true)]
    [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VAppImpl]
    $vApp
  )
  Process
  {
    if ($VM)
    {
      try
      {
        $vApp = Get-VIObjectByVIView $VM.ExtensionData.ParentVApp
      }
      catch
      {
        Write-Warning "$($VM.name) doesn’t belong to a vApp."
        continue;
      }
    }
    elseif (-not $vApp)
    {
      Write-Warning 'vApp was not specified'
      break;
    }
    $vApp.ExtensionData.VAppConfig.EntityConfig |
      Where-Object {$_.Key -match $VM.Id} |
      Select-Object @{
        Name='VM'
        Expression={Get-VIObjectByVIView $_.Key}
      },
      @{
        Name='vApp'
        Expression={$vApp.name}
      },
      'StartOrder','StartDelay',
      'WaitingForGuest','StartAction',
      'StopDelay','StopAction',
      @{
        Name='DestroyWithParent'
        Expression={if ($_.DestroyWithParent -eq $null){
            $false
          }
          else
          {
            $_.DestroyWithParent
          }
        }
      }
  }
}

Listing 10-10 retrieves the current start order settings using the Get-vAppStartOrder function.

Listing 10-10: Getting the start order settings for the App01 vApp

Get-VApp App01 | Get-vAppStartOrder

VM                : Web01
vApp              : App01
StartOrder        : 1
StartDelay        : 120
WaitingForGuest   : False
StartAction       : powerOn
StopDelay         : 120
StopAction        : powerOff
DestroyWithParent : False

VM                : Web02
vApp              : App01
StartOrder        : 2
StartDelay        : 120
WaitingForGuest   : False
StartAction       : powerOn
StopDelay         : 120
StopAction        : powerOff
DestroyWithParent : False

VM                : SQL01
vApp              : App01
StartOrder        : 3
StartDelay        : 120
WaitingForGuest   : False
StartAction       : powerOn
StopDelay         : 120
StopAction        : powerOff
DestroyWithParent : False

By default a vApp powers on and off based on the order in which the VMs were added. This will likely not be the desired order. To adjust these settings, use the Set-vAppStartOrder function (Listing 10-11).

Listing 10-11: The Set-vAppStartOrder function

<#
  .SYNOPSIS
    Set the vApp Startup Order for a given VM
  .DESCRIPTION
    Set the vApp Startup Order for a given VM
  .PARAMETER VM
    VM to modify the startup order for.
  .PARAMETER StartOrder
    Specifies the start order for this entity. Entities are
    started from lower numbers to higher-numbers and
    reverse on shutdown. Multiple entities with the same
    start-order can be started in parallel and the order is
    unspecified. This value must be 0 or higher.
  .PARAMETER StartDelay
    Delay in seconds before continuing with the next entity
    in the order of entities to be started
  .PARAMETER WaitingForGuest
    Determines if the virtual machine should start after
    receiving a heartbeat, from the guest. When a virtual
    machine is next in the start order, the system either
    waits a specified period of time for a virtual machine
    to power on or it waits until it receives a successful
    heartbeat from a powered on virtual machine. By
    default, this is set to false
  .PARAMETER StartAction
    How to start the entity. Valid settings are none or
    powerOn. If set to none, then the entity does not
    participate in auto-start.
  .PARAMETER StopDelay
    Delay in seconds before continuing with the next entity
    in the order sequence. This is only used if the stopAction
    is guestShutdown.
  .PARAMETER StopAction
    Defines the stop action for the entity. Can be set to none,
    powerOff, guestShutdown, or suspend. If set to none, then
    the entity does not participate in auto-stop.
  .PARAMETER DestroyWithParent
    ethier the entity should be removed, when this vApp is
    removed. This is only set for linked children.
  .PARAMETER PassThru
    return the vApp object
  .EXAMPLE
    Get-vAppStartOrder -VM (get-vm sql01)
  #>
function Set-vAppStartOrder {
  [CmdletBinding(SupportsShouldProcess=$true)]
  Param(
    [parameter(Mandatory=$true
    ,   ValueFromPipelineByPropertyName=$true
    ,   ValueFromPipeline=$true)]
    [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]
    $VM
  ,
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [int]
    $StartOrder
  ,
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [int]
    $StartDelay
  ,
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [bool]
    $WaitingForGuest
  ,
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [ValidateSet("none","powerOn")]
    [string]
    $StartAction
  ,
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [int]
    $StopDelay
  ,
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [ValidateSet('none','powerOff','guestShutdown','suspend')]
    [string]
    $StopAction
  ,
    [parameter(ValueFromPipelineByPropertyName=$true)]
    [bool]
    $DestroyWithParent
,
    [Switch]
    $PassThru
  )
  process
  {
    try
    {
      $vApp = Get-VIObjectByVIView $VM.ExtensionData.ParentVApp
    }
    catch
    {
      Write-Warning "$($VM.name) doesn't belong to a vApp."
      continue;
    }
    $EntityConfig = $vApp.ExtensionData.VAppConfig.EntityConfig

    $spec = New-Object VMware.Vim.VAppConfigSpec
    $spec.EntityConfig = `
      foreach ($Conf in ($EntityConfig.GetEnumerator()))
      {
        if ($Conf.Key.ToString() -eq $VM.Id.ToString())
        {
          $msg = "Setting $($VM.Name) start order to:"
          Switch ($PSCmdlet.MyInvocation.BoundParameters.Keys)
          {
            'StartOrder'
            {
              $msg = "{0} StartOrder:{1}" -f $msg, $StartOrder
              $Conf.StartOrder = $StartOrder
            }
            'StartDelay'
            {
              $msg = "{0} StartDelay:{1}" -f $msg, $StartDelay
              $Conf.StartDelay = $StartDelay
            }
            'WaitingForGuest'
            {
              $msg = "{0} WaitingForGuest:{1}" -f $msg, $WaitingForGuest
              $Conf.WaitingForGuest = $WaitingForGuest
            }
            'StartAction'
            {
              $msg = "{0} StartAction:{1}" -f $msg, $StartAction
              $Conf.StartAction = $StartAction
            }
            'StopDelay'
            {
              $msg = "{0} StopDelay:{1}" -f $msg, $StopDelay
              $Conf.StopDelay = $StopDelay
            }
            'StopAction'
            {
              $msg = "{0} StopAction:{1}" -f $msg, $StopAction
              $Conf.StopAction = $StopAction
            }
            'DestroyWithParent'
            {
              $msg = "{0} DestroyWithParent:{1}" -f $msg,
                $DestroyWithParent
              $Conf.DestroyWithParent = $DestroyWithParent
            }
          }
        }
        $Conf
      }
   if ($PSCmdlet.ShouldProcess($vApp.Name, $msg))
    {
      $vApp.ExtensionData.UpdateVAppConfig($spec)
        if ($PassThru)
      {
         Get-vAppStartOrder -VM $VM
      }
    }

  }
}

Using this new function, the startup order can now be fully configured from PowerCLI. Listing 10-12 sets the startup order to the following specification:

Startup Group 1

  • SQL01
    • Startup Action: PowerOn
    • Startup Delay: 120 sec or VMware Tools
    • Shutdown Action: Shutdown Guest

Startup Group 2

  • Web01
    • Startup Action: PowerOn
    • Startup Delay: 120 sec or VMware Tools
    • Shutdown Action: Shutdown Guest
    • Shutdown Delay: 120 sec
  • Web02
    • Startup Action: PowerOn
    • Startup Delay: 120 sec or VMware Tools
    • Shutdown Action: Shutdown Guest
    • Shutdown Delay: 120 sec

Listing 10-12: Setting the start order for the VMs in App01

Get-VApp App01 | Get-VM 'SQL01' |
  Set-vAppStartOrder -StartOrder 1 `
    -StartAction 'powerOn' `
    -StartDelay 120 `
    -WaitingForGuest $true `
    -StopAction 'guestShutdown'

Get-VApp App01 | Get-VM Web0[12] |
  Set-vAppStartOrder -StartOrder 2 `
    -StartAction 'powerOn' `
    -StartDelay 120 `
    -WaitingForGuest $true `
    -StopAction 'guestShutdown' `
    -StopDelay 120

Power Operations

As mentioned previously, one of the advantages of vApps is how they natively include startup sequencing. Once configured, power operations are accomplished using the native cmdlets. For instance, Listing 10-13 powers on the App01 vApp that has recently been configured. Listing 10-14 powers it down. Notice that the power operation is run at the vApp, not on the individual VMs.

Listing 10-13: Powering on a vApp

Start-VApp –Vapp App01 | Format-Table Name, Status

Name   Status
----   ------
App01  Started

Listing 10-14: Powering off a vApp

Stop-VApp –Vapp App01 | Format-Table Name, Status

Name   Status
----   ------
App01  Stopped

Using Network Protocol Profiles

Of course, power management is only part of vApps. To ease deployment of application, vApps can also supply the IP information for the VM(s) within a vApp. This is accomplished by passing the request to an external DHCP server, or a vCenter Server can issue an IP from a network protocol profile (formerly known as an IP pool). Although the name has changed in the vSphere Web Client, the underlying API has not—for that reason, we’ll continue to use the old name. Listing 10-15 obtains a list of existing IP pools by running the Get-IPPool function.

Listing 10-15: The Get-IPPool function

<#
    .SYNOPSIS
        Get existing IP Pools from vCenter
    .DESCRIPTION
        Get existing IP Pools from vCenter
    .PARAMETER Datacenter
        Datacenter to query for IP Pools.
    .PARAMETER Name
        Name of the IP Pool to retrieve.
    .EXAMPLE
        Get-Datacenter | Get-IPPool
    #>
function Get-IPPool {
    [CmdletBinding()]
    Param(
        [parameter(ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [VMware.VimAutomation.ViCore.Impl.V1.Inventory.DatacenterImpl[]]
        $Datacenter = (Get-Datacenter)
    ,
        [parameter(ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [String]
        $Name = "*"
    )
    Process
    {
        foreach ($dc in $Datacenter)
        {
            $IPPoolManager = Get-View -Id 'IpPoolManager-IpPoolManager'
            $IPPoolManager.QueryIpPools($dc.Id) |
                Where-Object {$_.Name -like $Name} | Foreach-Object {
                New-Object PSObject -Property @{
                    'Name' = $_.Name
                    'DnsDomain' = $_.DNSDomain
                    'DNSSearchPath' = $_.DNSSearchPath
                    'HostPrefix' = $_.HostPrefix
                    'HttpProxy' = $_.HttpProxy
                    'NetworkAssociation' = $_.NetworkAssociation
                    'IPv4SubnetAddress' = $_.Ipv4Config.SubnetAddress
                    'IPv4Netmask' = $_.Ipv4Config.Netmask
                    'IPv4Gateway' = $_.Ipv4Config.Gateway
                    'IPv4Range' = $_.Ipv4Config.Range
                    'IPv4DNS' = $_.Ipv4Config.DNS
                    'IPv4DHCP' = $_.Ipv4Config.DhcpServerAvailable
                    'IPv4IpPoolEnabled' = $_.Ipv4Config.IpPoolEnabled
                    'IPv6SubnetAddress' = $_.Ipv6Config.SubnetAddress
                    'IPv6Netmask' = $_.Ipv6Config.Netmask
                    'IPv6Gateway' = $_.Ipv6Config.Gateway
                    'IPv6Range' = $_.Ipv6Config.Range
                    'IPv6DNS' = $_.Ipv6Config.DNS
                    'IPv6DHCP' = $_.Ipv6Config.DhcpServerAvailable
                    'IPv6IpPoolEnabled' = $_.Ipv6Config.IpPoolEnabled
                    'Datacenter' = $dc
                }
            }
        }
    }
}

By default there are no IP pools configured. To create a new IP pool within vCenter Server, use the New-IPPool function in Listing 10-16.

Listing 10-16: The New-IPPool function

<#
    .SYNOPSIS
        Create a new IP Pool within vCenter
    .DESCRIPTION
        Create a new IP Pool within vCenter
    .PARAMETER Datacenter
        Datacenter to create the new IP Pool in.
    .PARAMETER Name
        Pool name. Must be unique.
    .PARAMETER DnsDomain
        DNS Domain. For example, vmware.com. This can be an
        empty string if no domain is configured.
    .PARAMETER DNSSearchPath
        DNS Search Path. For example, eng.vmware.com;vmware.com
    .PARAMETER HostPrefix
        Prefix for hostnames.
    .PARAMETER HttpProxy
        The HTTP proxy to use on this network
    .PARAMETER NetworkAssociation
        The networks that are associated with this IP pool.

        Use the Get-Network function to get the objects this
        parameter requires.
    .PARAMETER IPv4SubnetAddress
        Address of the subnet.
    .PARAMETER IPv4Netmask
        Netmask
    .PARAMETER IPv4Gateway
        Gateway. This can be an empty string
    .PARAMETER IPv4Range
        IP range. This is specified as a set of ranges
        separated with commas. One range is given by a start
        address, a hash (#), and the length of the range.
        For example:
        192.0.2.235#20 = IPv4 range 192.0.2.235-192.0.2.254
        192.0.2.0#24 = IPv4 range 192.0.2.1-192.0.2.254
    .PARAMETER IPv4DNS
        DNS servers
    .PARAMETER IPv4DHCP
        Whether a DHCP server is available on this network.
    .PARAMETER IPv4IpPoolEnabled
        IP addresses can only be allocated from the range if
        the IP pool is enabled.
    .PARAMETER IPv6SubnetAddress
        Address of the subnet.
    .PARAMETER IPv6Netmask
        Netmask
    .PARAMETER IPv6Gateway
        Gateway. This can be an empty string
    .PARAMETER IPv6Range
        IP range. This is specified as a set of ranges
        separated with commas. One range is given by a start
        address, a hash (#), and the length of the range.
        For example:
        2001::7334 # 20 = IPv6 range 2001::7334 - 2001::7347
    .PARAMETER IPv6DNS
        DNS servers
    .PARAMETER IPv6DHCP
        Whether a DHCP server is available on this network.
    .PARAMETER IPv6IpPoolEnabled
        IP addresses can only be allocated from the range if
        the IP pool is enabled.
    #>
function New-IPPool {
    [CmdletBinding()]
    Param(
        [parameter(Mandatory=$true
        ,   ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [VMware.VimAutomation.ViCore.Impl.V1.Inventory.DatacenterImpl]
        $Datacenter
    ,   [parameter(Mandatory=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [String]
        $Name
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $DnsDomain = ""
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String[]]
        $DNSSearchPath = ""
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $HostPrefix = ""
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $HttpProxy = ""
    ,   [parameter(ValueFromPipeline=$true
        ,    ValueFromPipelineByPropertyName=$true)]
        [VMware.Vim.IpPoolAssociation[]]
        $NetworkAssociation
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4SubnetAddress = ''
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4Netmask = '255.255.255.0'
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4Gateway = ''
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4Range
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String[]]
        $IPv4DNS = @("")
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv4DHCP = $false
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv4IpPoolEnabled = $false
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6SubnetAddress = ''
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6Netmask = "ffff:ffff:ffff:ffff:ffff:ffff::"
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6Gateway = ""
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6Range
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String[]]
        $IPv6DNS = @()
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv6DHCP = $false
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv6IpPoolEnabled = $false
    )
    Process
    {
        $pool = New-Object VMware.Vim.IpPool
        $pool.Name = $Name
        $pool.Ipv4Config = New-Object VMware.Vim.IpPoolIpPoolConfigInfo
        $pool.Ipv4Config.SubnetAddress = $IPv4SubnetAddress
        $pool.Ipv4Config.Netmask = $IPv4Netmask
        $pool.Ipv4Config.Gateway = $IPv4Gateway
        $pool.Ipv4Config.Dns = $IPv4DNS
        $pool.Ipv4Config.DhcpServerAvailable = $IPv4DHCP
        $pool.Ipv4Config.IpPoolEnabled = $IPv4IpPoolEnabled
        $pool.Ipv6Config = New-Object VMware.Vim.IpPoolIpPoolConfigInfo
        if ($IPv4Range)
        {
            $pool.Ipv4Config.Range = $IPv4Range
        }
        $pool.Ipv6Config.SubnetAddress = $IPv6SubnetAddress
        $pool.Ipv6Config.Netmask = $IPv6Netmask
        $pool.Ipv6Config.Gateway = $IPv6Gateway
        $pool.Ipv6Config.Dns = $IPv6DNS
        $pool.Ipv6Config.DhcpServerAvailable = $IPv6DHCP
        $pool.Ipv6Config.IpPoolEnabled = $IPv6IpPoolEnabled
        if ($IPv6Range)
        {
            $pool.Ipv6Config.Range = $IPv6Range
        }
        $pool.DnsDomain = $DnsDomain
        $pool.DnsSearchPath = $DNSSearchPath
        $pool.HostPrefix = $HostPrefix
        $pool.HttpProxy = $HttpProxy
        if ($NetworkAssociation)
        {
            $pool.NetworkAssociation = $NetworkAssociation
        }
        $IpPoolManager = Get-View 'IpPoolManager-IpPoolManager'
        $IpPoolManager.CreateIpPool($DataCenter.Id, $pool) | Out-Null
        if ($?)
        {
            Get-IPPool -Datacenter $DataCenter -Name $Name
        }
        Else
 {
             Write-Warning "Failed to create IP Pool, check vCenter tasks"
        }
    }
}

Using the New-IPPool function, create a new IP pool to be allocated to the vApp. For instance, the code in Listing 10-17 creates such a pool.

Listing 10-17: Creating a new IP pool

Get-Datacenter 'DC1' |
    New-IPPool -Name '10.10.10.0' `
    -IPv4SubnetAddress '10.10.10.0' `
    -IPv4Gateway '10.10.10.1' `
    -IPv4Netmask '255.255.255.0' `
    -IPv4Range '10.10.10.11#244' `
    -IPv4DNS '10.10.10.5','10.10.10.6' `
    -DnsDomain 'vSphere.local' `
    -DNSSearchPath 'prod.vSphere.local','dev.vSphere.local' `
    -IPv4IpPoolEnabled $true

With the new IP pool created, the next step is to associate a virtual network with the new IP pool. To assist in this task, Listing 10-18 contains the Get- NetworkAssociation function. This function retrieves the information needed to perform the network association.

Listing 10-18: The Get-NetworkAssociation function

<#
    .SYNOPSIS
        Get networks registered in vCenter
    .DESCRIPTION
        Get networks registered in vCenter
    .PARAMETER Name
        Only return networks that match name
    .PARAMETER Network
        Retrieve the Network Association for a specified network MoRef.
    .EXAMPLE
        Get-NetworkAssociation -Name 10.10.10.0
#>
function Get-NetworkAssociation {
    [CmdletBinding(DefaultParameterSetName='name')]
    Param(
        [parameter(ParameterSetName='name'
        ,   ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [String]
        $Name = "*"
    ,   [parameter(ParameterSetName='MoRef'
        ,   ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [VMware.Vim.ManagedObjectReference]
        $Network
    )
    Process
    {
        if ($PSCmdlet.ParameterSetName -eq 'name')
        {
            $net = Get-View -ViewType Network -Property Name |
                Where-Object {$_.Name -like $Name}
        }
        else
        {
            $net = Get-View -Id $Network -Property Name
        }
        if ($net)
        {
            foreach ($N in $net)
            {
                New-Object VMware.Vim.IpPoolAssociation `
                    -Property @{
                        Network=$N.MoRef
                        NetworkName=$N.Name
                    }
            }
        }
    }
}

Using the Get-NetworkAssociation function, get the networks that need to be configured for the IP pool (Listing 10-19).

Listing 10-19: Getting networks that need to be configured

Get-VApp App01 |
    Get-VM |
    Get-NetworkAdapter |
    Select-Object -ExpandProperty NetworkName -Unique |
    Get-NetworkAssociation

Network                                         NetworkName
-------                                         -----------
DistributedVirtualPortgroup-dvportgroup-109     vDS01-10.10.10.0

In this case, only one network needs to be configured. For that task use the Set-IPPool function shown in Listing 10-20.

Listing 10-20: The Set-IPPool function

<#
    .SYNOPSIS
        Modify an existing IP Pool within vCenter
    .DESCRIPTION
        Modify an existing IP Pool within vCenter
    .PARAMETER Datacenter
        Datacenter to create the new IP Pool in.
    .PARAMETER Name
        Pool name.
    .PARAMETER Name
        New pool name. Must be unique.
    .PARAMETER DnsDomain
        DNS Domain. For example, vmware.com. This can be an
        empty string if no domain is configured.
    .PARAMETER DNSSearchPath
        DNS Search Path. For example, eng.vmware.com;vmware.com
    .PARAMETER HostPrefix
        Prefix for hostnames.
    .PARAMETER HttpProxy
        The HTTP proxy to use on this network
    .PARAMETER NetworkAssociation
        The networks that are associated with this IP pool.

        Use the Get-Network function to get the objects this
        parameter requires.
    .PARAMETER IPv4SubnetAddress
        Address of the subnet.
    .PARAMETER IPv4Netmask
        Netmask
    .PARAMETER IPv4Gateway
        Gateway. This can be an empty string
    .PARAMETER IPv4Range
        IP range. This is specified as a set of ranges
        separated with commas. One range is given by a start
        address, a hash (#), and the length of the range.
        For example:
        192.0.2.235#20 = IPv4 range 192.0.2.235-192.0.2.254
        192.0.2.0#24 = IPv4 range 192.0.2.1-192.0.2.254
    .PARAMETER IPv4DNS
        DNS servers
    .PARAMETER IPv4DHCP
        Whether a DHCP server is available on this network.
    .PARAMETER IPv4IpPoolEnabled
        IP addresses can only be allocated from the range if
        the IP pool is enabled.
    .PARAMETER IPv6SubnetAddress
        Address of the subnet.
    .PARAMETER IPv6Netmask
        Netmask
    .PARAMETER IPv6Gateway
        Gateway. This can be an empty string
    .PARAMETER IPv6Range
        IP range. This is specified as a set of ranges
        separated with commas. One range is given by a start
        address, a hash (#), and the length of the range.
        For example:
        2001::7334 # 20 = IPv6 range 2001::7334 - 2001::7347
    .PARAMETER IPv6DNS
        DNS servers
    .PARAMETER IPv6DHCP
        Whether a DHCP server is available on this network.
    .PARAMETER IPv6IpPoolEnabled
        IP addresses can only be allocated from the range if
        the IP pool is enabled.
#>
function Set-IPPool {
    [CmdletBinding()]
    Param(
        [parameter(Mandatory=$true
        ,   ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [VMware.VimAutomation.ViCore.Impl.V1.Inventory.DatacenterImpl]
        $Datacenter
    ,   [parameter(Mandatory=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [String]
        $Name
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $NewName
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $DnsDomain
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String[]]
        $DNSSearchPath
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $HostPrefix
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $HttpProxy
    ,   [parameter(ValueFromPipeline=$true
        ,    ValueFromPipelineByPropertyName=$true)]
        [VMware.Vim.IpPoolAssociation[]]
        $NetworkAssociation
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4SubnetAddress
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4Netmask
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4Gateway
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv4Range
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String[]]
        $IPv4DNS
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv4DHCP
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv4IpPoolEnabled
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6SubnetAddress
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6Netmask
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6Gateway
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String]
        $IPv6Range
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [String[]]
        $IPv6DNS
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv6DHCP
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [bool]
        $IPv6IpPoolEnabled
    )
    Process
    {
        $IPPoolManager = Get-View 'IpPoolManager-IpPoolManager'
        $pool = $IPPoolManager.QueryIpPools($Datacenter.Id)|
            Where-Object {$_.Name -eq $Name}
        Switch ($PSCmdlet.MyInvocation.BoundParameters.Keys)
        {
            'NewName' {
                $pool.Name = $Name
            }
            'IPv4SubnetAddress' {
               $pool.Ipv4Config.SubnetAddress = $IPv4SubnetAddress
            }
            'IPv4Netmask' {
                $pool.Ipv4Config.Netmask = $IPv4Netmask
            }
            'IPv4Gateway' {
                $pool.Ipv4Config.Gateway = $IPv4Gateway
            }
            'IPv4DNS' {
                $pool.Ipv4Config.Dns = $IPv4DNS
            }
            'IPv4DHCP' {
                $pool.Ipv4Config.DhcpServerAvailable = $IPv4DHCP
            }
            'IPv4IpPoolEnabled' {
               $pool.Ipv4Config.IpPoolEnabled = $IPv4IpPoolEnabled
            }
            'IPv4Range' {
                $pool.Ipv4Config.Range = $IPv4Range
            }
            'IPv6SubnetAddress' {
               $pool.Ipv6Config.SubnetAddress = $IPv6SubnetAddress
            }
            'IPv6Netmask' {
                $pool.Ipv6Config.Netmask = $IPv6Netmask
            }
            'IPv6Gateway' {
                $pool.Ipv6Config.Gateway = $IPv6Gateway
            }
            'IPv6DNS' {
                $pool.Ipv6Config.Dns = $IPv6DNS
            }
            'IPv6DHCP' {
                $pool.Ipv6Config.DhcpServerAvailable = $IPv6DHCP
            }
            'IPv6IpPoolEnabled' {
               $pool.Ipv6Config.IpPoolEnabled = $IPv6IpPoolEnabled
            }
            'IPv6Range' {
                $pool.Ipv6Config.Range = $IPv6Range
            }
            'DnsDomain' {
                $pool.DnsDomain = $DnsDomain
            }
            'DNSSearchPath' {
                $pool.DnsSearchPath = $DNSSearchPath
            }
            'HostPrefix' {
                $pool.HostPrefix = $HostPrefix
            }
            'HttpProxy' {
                $pool.HttpProxy = $HttpProxy
            }
            'NetworkAssociation' {
                $pool.NetworkAssociation = $NetworkAssociation
            }
        }

        $IpPoolManager.UpdateIpPool($DataCenter.Id, $pool) | Out-Null
        if ($?)
        {
            Get-IPPool -Datacenter $DataCenter -Name $Name
        }
        Else
 {
            Write-Warning "Failed to associate IP Pool, check vCenter tasks"
        }

    }
}

At this point the vApp is ready to associate the new IP pool with the virtual network that the VM(s) are using, as shown in Listing 10-21.

Listing 10-21: Associating the IP pool to a virtual network

Get-VApp App01 | Get-VM | Get-NetworkAdapter |
    Select-Object -ExpandProperty NetworkName -Unique |
    Get-NetworkAssociation |
        Set-IPPool -Name '10.10.10.0' `
            -Datacenter (Get-Datacenter DC1)

Using IP Assignment

With the IP pool configured, vApps can now be configured to use pools for IP assignment. By default, a new vApp does not have any IP protocols or allocation methods enabled, and its IP allocation policy is set to fixedPolicy. This means that, out of the box, vApp IP allocation is disabled. Enabling IP assignment is a two-step process.

  1. Enable the IP allocation/protocol.
  2. Set the IP allocation/protocol.

To retrieve the current IP assignment configuration of a vApp, use the Get-vAppIPAssignment function in Listing 10-22.

Listing 10-22: The Get-vAppIPAssignment function

<#
    .SYNOPSIS
        Get the IP assignment for the specified vApp.
    .DESCRIPTION
        Get the IP assignment for the specified vApp.
    .PARAMETER vApp
        vApp to retrieve the IP Assignment settings.
    .EXAMPLE
        Get-vApp | Get-vAppIPAssignment
#>
function Get-vAppIPAssignment {
    [CmdletBinding()]
    Param(
        [parameter(ValueFromPipeline=$true)]
        [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VAppImpl]
        $vApp
    )
    Process
    {
        $vapp.ExtensionData.VAppConfig.IpAssignment | Foreach-Object {
            New-Object PSObject -Property @{
                'vApp' = $vApp
                'IpProtocol' = $_.IpProtocol
                'IpAllocationPolicy' = $_.IpAllocationPolicy
                'SupportedIpAllocation' = $_.SupportedAllocationScheme
                'SupportedIpProtocol' = $_.SupportedIpProtocol
            }
        }
    }
}

Use the Get-vAppIPAssignment function retrieves the current settings for any given vApp. Out of the box, a new vApp is configured to use static IP assignment, as shown in Listing 10-23.

Listing 10-23: Getting the current IP assignment for App01

Get-VApp App01 | Get-vAppIPAssignment

IpProtocol            : IPv4
IpAllocationPolicy    : fixedPolicy
SupportedIpAllocation :
SupportedIpProtocol   :
vApp                  : App01

Therefore, to enable this powerful feature, you have to enable IP assignment and specify which protocols will be used. This is broken up into a two-step process in the vSphere Web Client, but the Set-vAppIPAssignment function (Listing 10-24) enables the configuration of IP assignment in one line of PowerCLI (after the function is defined in the session, of course).

Listing 10-24: The Set-vAppIPAssignment function

<#
    .SYNOPSIS
        Set the IP assignment for the specified vApp.
    .DESCRIPTION
        Set the IP assignment for the specified vApp.  These
        Settings control how the guest software gets
        Configured with IP addresses, including protocol type
        (IPv4 or IPv6) and the lifetime of those IP addresses.
    .PARAMETER vApp
        vApp to modify the IP Assignment settings.
    .PARAMETER IpProtocol
        Specifies the chosen IP protocol for this deployment.
        This must be one of the values in the
        SupportedIpAllocation
    .PARAMETER IpAllocationPolicy
        Specifies how IP allocation should be managed by the VI
        Platform. This is typically specified by the deployer.
        Valid options are 'dhcpPolicy','transientPolicy', and
        'fixedPolicy'
    .PARAMETER SupportedIpAllocation
        Specifies the IP allocation schemes supported by the
        guest software. When updating this field, an array of
        the form "" will clear all settings.

        Otherwise, the supplied value will overwrite the
        current setting.
    .PARAMETER SupportedIpProtocol
        Specifies the IP protocols supported by the guest
        software. When updating this field, an array in the
        form "" will clear all settings.

        Otherwise, the supplied value will overwrite the
        current setting.
#>
function Set-vAppIPAssignment {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [parameter(ValueFromPipeline=$true)]
        [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VAppImpl]
        $vApp
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('IPv4','IPv6')]
        [string]
        $IpProtocol
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('dhcpPolicy',
                  'transientPolicy',
                  'fixedPolicy')]
        [string]
        $IpAllocationPolicy
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('ovfenv','dhcp')]
        [string[]]
        $SupportedIpAllocation
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('IPv4','IPv6')]
        [string[]]
        $SupportedIpProtocol
    )
    Process
    {
        $spec = New-Object VMware.Vim.VAppConfigSpec
        $spec.IpAssignment = $vApp.ExtensionData.VAppConfig.IpAssignment
        $msg = "Modifying $($vApp.Name)"
        Switch ($PSCmdlet.MyInvocation.BoundParameters.Keys)
        {
            'IpProtocol'
            {
                $msg = "{0} IP protocol:{1}" -f $msg, $IpProtocol
                $spec.IpAssignment.IpProtocol = $IpProtocol
            }
            'IpAllocationPolicy'
            {
                $msg = "{0} IP allocation policy:{1}" -f $msg,
                    $IpAllocationPolicy
                $spec.IpAssignment.IpAllocationPolicy = `
                    $IpAllocationPolicy
            }
            'SupportedIpAllocation'
            {
                $msg = "{0} supported allocation policy:{1}" -f `
                    $msg, $($SupportedIpAllocation -join ',')
                $spec.IpAssignment.SupportedAllocationScheme = `
                    $SupportedIpAllocation
            }
            'SupportedIpProtocol'
            {
                $msg = "{0} supported IP protocol:{1}" -f $msg,
                    $($SupportedIpProtocol -join ',')
                $spec.IpAssignment.SupportedIpProtocol = `
                    $SupportedIpProtocol
            }
        }
        if ($PSCmdlet.ShouldProcess($vApp.Name,$msg))
        {
          $vApp.ExtensionData.UpdateVAppConfig($spec)
          if ($?)
          {
              Get-vAppIPAssignment -vApp $vApp
          }
          Else
   {
     Write-Warning "Failed to set vApp IP Assignment check vCenter tasks"
          }

        }
    }
}

Listing 10-25 uses the Set-vAppIPAssignment function to enable both DHCP and IP pools for IPv4 and IPv6 protocols and then set the vApp to obtain its IP from the IP pool using a temporary IPv4 address.

Listing 10-25: Configuring IP assignment for App01

Get-VApp App01 | Set-vAppIPAssignment `
    -SupportedIpAllocation ovfenv,DHCP `
    -SupportedIpProtocol IPv4,IPv6 `
    -IpProtocol IPv4 `
    -IpAllocationPolicy transientPolicy

IpProtocol            : IPv4
IpAllocationPolicy    : transientPolicy
SupportedIpAllocation : {ovfenv, DHCP}
SupportedIpProtocol   : {IPv4, IPv6}
vApp                  : App01

Modifying vApp Product Information

Digging deeper into vApps, there is additional metadata in the form of product information. To get the product information for an existing vApp, the Get-vAppProductInfo function (Listing 10-26) is provided.

Listing 10-26: The Get-vAppProductInfo function

<#
    .SYNOPSIS
        Get the vApp Product Information
    .DESCRIPTION
        Get the vApp Product Information
    .PARAMETER vApp
        vApp to retrieve the Product Information for.
    .EXAMPLE
        Get-VApp | Get-vAppProductInfo
#>
function Get-vAppProductInfo {
    [CmdletBinding()]
    Param(
        [parameter(ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VAppImpl]
        $vApp
    )
    Process
    {
        $vApp.ExtensionData.VAppConfig.Product |
            Select-Object -Property @{
                    Name='vApp'
                    Expression={$vApp}
                },'Name','Vendor','Version','FullVersion',
                'VendorUrl','ProductUrl','AppUrl'
    }
}

Of course, when dealing with a brand-new vApp, the product information will likely be blank. The Set-vAppProductInfo function (Listing 10-27) can be used to supply that information. Note that the Get-vAppProductInfo function (Listing 10-26) must be loaded into your PowerCLI session before you use the Set-vAppProductInfo function.

Listing 10-27: The Set-vAppProductInfo function

<#
    .SYNOPSIS
        Set the vApp Product Information
    .DESCRIPTION
        Set the vApp Product Information

        Information that describes what product a vApp
        contains, e.g., what software that is installed in
        the contained virtual machines.
    .PARAMETER vApp
        vApp to set the product information for.
    .PARAMETER Name
        Name of the product
    .PARAMETER Vendor
        Vendor of the product.
    .PARAMETER Version
        Short version of the product , e.g., 1.0.
    .PARAMETER FullVersion
        Full-version of the product, e.g., 1.0-build 12323.
    .PARAMETER VendorUrl
        URL to vendor homepage.
    .PARAMETER ProductUrl
        URL to product homepage.
    .PARAMETER AppUrl
        URL to entry-point for application. This is often
        specified using a macro, e.g., http://${app.ip}/,
        where app.ip is a defined property on the virtual
        machine or vApp container.
    .EXAMPLE
        Get-VApp App01| Set-vAppProductInfo `
            -Vendor 'VMware' -Version '4' -FullVersion '4.1'
#>
function Set-vAppProductInfo {
    [CmdletBinding()]
    Param(
        [parameter(ValueFromPipeline=$true
        ,   ValueFromPipelineByPropertyName=$true)]
        [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VAppImpl]
        $vApp
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [string]
        $Name
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [string]
        $Vendor
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [string]
        $Version
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [string]
        $FullVersion
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [string]
        $VendorUrl
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [string]
        $ProductUrl
    ,   [parameter(ValueFromPipelineByPropertyName=$true)]
        [string]
        $AppUrl
    )
    Process
    {
        $spec = New-Object VMware.Vim.VAppConfigSpec
        $spec.Product = New-Object VMware.Vim.VAppProductSpec[] (1)
        $spec.Product[0] = New-Object VMware.Vim.VAppProductSpec
        $spec.Product[0].Operation = "edit"
        $spec.Product[0].Info = New-Object VMware.Vim.VAppProductInfo
        $spec.Product[0].Info.Key = `
            $vApp.ExtensionData.VAppConfig.Property |
            Select-Object -ExpandProperty Key -First 1
        $msg = "Modifing Advanced Properties "
        Switch ($PSCmdlet.MyInvocation.BoundParameters.Keys)
        {
            'Name'
            {
                $spec.Product[0].Info.Name = $Name
                $msg = "{0} Name:{1}" -f $msg,$Name
            }
            'Vendor'
            {
                $spec.Product[0].Info.Vendor = $Vendor
                $msg = "{0} Vendor:{1}" -f $msg,$Vendor
            }
            'Version'
            {
                $spec.Product[0].Info.Version = $Version
                $msg = "{0} Version:{1}" -f $msg,$Version
            }
            'FullVersion'
            {
                $spec.Product[0].Info.FullVersion = $Fullversion
                $msg = "{0} Full version:{1}" -f $msg,
                    $Fullversion
            }
            'VendorUrl'
            {
                $spec.Product[0].Info.VendorUrl = $vendorURL
                $msg = "{0} vendor URL:{1}" -f $msg,$vendorURL
            }
            'ProductUrl'
            {
                $spec.Product[0].Info.ProductUrl = $productUrl
                $msg = "{0} product Url:{1}" -f $msg,
                    $productUrl
            }
            'AppUrl'
            {
                $spec.Product[0].Info.AppUrl = $AppUrl
                $msg = "{0} App Url:{1}" -f $msg,$AppUrl
            }
        }

        if ($PSCmdlet.ShouldProcess($vApp.Name,$msg))
        {
            $vApp.ExtensionData.UpdateVAppConfig($spec)
            if ($?)
            {
                 Start-Sleep -Milliseconds 500
                 Get-vAppProductInfo -vApp $vApp
            }
            Else
            {
            Write-Warning "Failed to set vApp product info check vCenter tasks"
            }	
        }
    }
}

Using the Set-vAppProductInfo function, you can configure the product information on a custom or home-grown vApp, as seen in Listing 10-28.

Listing 10-28: Configuring product information for App01

Set-vAppProductInfo -Name App01 `
    -Vendor Acme -version 1.1 `
    -FullVersion 1.1.0.1 `
    -VendorURL www.acme.com `
    -ProductUrl www.acme.com/go/App01

vApp                  : App01
Name                  : App01
Vendor                : Acme
Version               : 1.1
FullVersion           : 1.1.0.1
VendorURL             : www.acme.com
ProductUrl            : www.acme.com/go/App01
AppUrl                :

Although vApps haven’t quite taken over the world as we predicted in the previous edition, they are still extremely powerful and should be part of any vSphere infrastructure—if for nothing else than to control the startup and shutdown order of a complex virtual application. Either way, using the tools provided in this chapter, any administrator can easily automate vApp operations in a repeatable means with PowerCLI.

..................Content has been hidden....................

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