During the back-end of 2012 and earlier this year, I was involved in a fairly substantial VMware vCloud Plan and Design engagement that required integration of a number of different VMware and 3rd Party products.  In particular a level of integration was required between VMware vCloud Director (vCD), vCloud Automation Center (vCAC) and consumer virtual machine workloads.  This was required in order to offer advanced management capabilities.

One of the challenges that came to light during my involvement with this project was that the vCAC design depended upon the use of vCAC agents for installing software packages in virtual machine workloads.  In effect it was assumed TCP/IP connectivity was available between the management infrastructure and consumer virtual machines – not common practice in a multi-tenant environment.  Without going in to specific details of the project, what was needed was a mechanism to install software in consumer virtual machines without compromising security between the provider and the consumer(s).  A number of options were considered, including:

  • Multiple options around the concept of secured networking (using tools such as Private VLANs, vShield Edge, vShield App etc…)

I was personally an advocate of the second option as it involved not TCP/IP connectivity between consumer’s and/or the provider infrastructure.  Furthermore it would work for virtual machines running on isolated networks too.  During the various design discussions that ensued I produced a quick example script to prove the concept of connecting to vCloud Director, identifying a specific vApp and its constituent virtual machines, and then locating the same virtual machines in vSphere and installing the required software.  The following video and script is an updated version of that initial test, which I enhanced with the ability to cope with multiple virtual machines, Powered Off virtual machines and packaged as an advanced function.

function Install-WinPkgInvCloudVM {
<#
.SYNOPSIS
    Install a software package in vCloud Director managed virtual machine.
.DESCRIPTION
    Identify virtual machine(s) within a vCloud Director vApp and install a package in the guest operating system
.PARAMETER CIServer
    The hostname of a vCloud Director Server managing the vApp(s).
.PARAMETER CIUser
    The username of a valid user to connect to the vCloud Director Server managing the vApp(s).
.PARAMETER CIPassword
    The password for the user CIUser to connect to the vCloud Director Server managing the vApp(s).
.PARAMETER VApp
    The name or object or collection of objects of a vApp in which to search for virtual machines.
.PARAMETER Package
    The full path to a software package to be installed in the windows guest.
.PARAMETER Command
    The command required to install the software package.
.PARAMETER VIUser
    The username of a valid user to connect to the vCenter Server managing ESXi Server hosts on which to search for volumes.
.PARAMETER VIPassword
    The password for the user VIUser to connect to the vCenter Server managing ESXi Server hosts on which to search for volumes.
.PARAMETER GuestUser
    The username of a valid user to connect to the vCenter Server managing ESXi Server hosts on which to search for volumes.
.PARAMETER GuestPassword
    The password for the user VIUser to connect to the vCenter Server managing ESXi Server hosts on which to search for volumes.
.EXAMPLE
    PS C:\ Install-WinPkgInvCloudVM -CIServer "mycloud.myurl.com" -CIUser "MyvCDUser" -CIPassword "MyvCDPassword" -CIVApp "MyVApp" `
    -Org "MyOrg" -SrcPath "C:\Users\myuser\mypackage.msi" -DstPath "C:\Users\myuser\" -Command "C:\Users\myuser\mypackage.msi /S" `
    -VIUser "MyVCUser" -VIPassword "MyVCPassword" -GuestUser "MyGuestUser" -GuestPassword "MyGuestPassword"
#>
[CmdletBinding()]
param(
    [parameter(Mandatory=$true,
        HelpMessage="Supply the hostname of the vCloud Director Server managing the vApp(s).",
        ValueFromPipeline=$false)]
    [String]
    $CIServer
    ,
    [parameter(Mandatory=$true,
        HelpMessage="Supply a valid username for the supplied CIServer.",
        ValueFromPipeline=$false)]
    [String]
    $CIUser
    ,
    [parameter(Mandatory=$true,
        HelpMessage="Supply a valid password for the supplied CIServer.",
        ValueFromPipeline=$false)]
    [String]
    $CIPassword
    ,
    [parameter(Mandatory=$true,
        HelpMessage="Supply a vApp name.",
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$true)]
    [String[]]
    $CIVapp
    ,
    [parameter(Mandatory=$true,
        HelpMessage="Supply an Org name to narrow search",
        ValueFromPipeline=$false,
        ValueFromPipelineByPropertyName=$true)]
    [String]
    $Org
    ,
    [parameter(Position=0,
        Mandatory=$false,
        HelpMessage="Supply a OrgVdc name to narrow search.",
        ValueFromPipeline=$false,
        ValueFromPipelineByPropertyName=$true)]
    [String]
    $OrgVdc
    ,
    [parameter(Mandatory=$false,
        HelpMessage="Supply a the full path to a local software package to be copied to the vApp.",
        ValueFromPipeline=$false)]
    [String]
    $SrcPath
    ,
    [parameter(Mandatory=$false,
        HelpMessage="Supply a the full path to a destination folder for the software package.",
        ValueFromPipeline=$false)]
    [String]
    $DstPath
    ,
    [parameter(Mandatory=$false,
        HelpMessage="Supply a command to start the installation of the software package.",
        ValueFromPipeline=$false)]
    [String]
    $Command
    ,
    [parameter(Mandatory=$true,
        HelpMessage="Supply a valid username for the supplied VIServer.",
        ValueFromPipeline=$false)]
    [String]
    $VIUser
    ,
    [parameter(Mandatory=$true,
        HelpMessage="Supply a valid password for the supplied VIServer.",
        ValueFromPipeline=$false)]
    [String]
    $VIPassword
    ,
    [parameter(Position=3,
        Mandatory=$false,
        HelpMessage="Supply a user for guest operating system.",
        ValueFromPipeline=$false)]
        [ValidateNotNull()]
    $GuestUser
    ,
    [parameter(Position=4,
        Mandatory=$false,
        HelpMessage="Supply a password for the guest operating system.",
        ValueFromPipeline=$false)]
    [ValidateNotNull()]
    $GuestPassword
    ,
    [parameter(Mandatory=$false,
        HelpMessage="Specify if installation should continue in supported VMs, when other un-supported guest operating systems are detected.",
        ValueFromPipeline=$false)]
    [Switch]
    $NonSupportedGuestOveride = $false
)
Begin {
    # Define supported virtual machines guest operating systems for this function
    $SuppOSFullNames = "Microsoft Windows XP Professional (32-bit)",
    "Microsoft Windows Server 2003 (64-bit)",
    "Microsoft Windows Server 2003 (32-bit)",
    "Microsoft Windows 7 (64-bit)",
    "Microsoft Windows Server 2008 R2 (64-bit)",
    "Microsoft Windows Server 2008 (64-bit)",
    "Microsoft Windows Server 2008 (32-bit)"
}
Process {
    # Connect to the vCloud Director Server
    Write-Host Connecting to vCloud Director Server $CIServer
    Connect-CIServer -Server $CIServer -User $CIUser -Password $CIPassword | Out-Null

    # Locate the vApp within vCloud Director (assumes no duplicate names can existing within a given Organisation)
    Write-Host "Searching for the vApp" $CIVapp
    if ($OrgVdc){
        $vCDvApp = Get-CIVApp -Name $CIVApp -Org $Org -OrgVdc $OrgVdc
    }else{
        $vCDvApp = Get-CIVApp -Name $CIVApp -Org $Org
    }

    if (!$vCDvApp){
        Write-Host -ForegroundColor Red "The vApp" $CIVApp "could not be found"
    }

    # Identify the virtual machine(s) located within the vApp
    Write-Host Identifying vCloud virtual machines within vApp $CIVapp
    $vCDVms = Get-CIVM -VApp $vCDvApp
    if (!$vCDVms){
        Write-Host -ForegroundColor Yellow "vApp" $CIVapp "does not contain any virtual machines"
        Write-Host -ForegroundColor Yellow "Package will not be copied and installed - skipping vApp"
    }
    Write-Host $CIVapp "contains" ($vCDVms | Measure-Object).Count "virtual machines:"
    $vCDVms | Select Name, Status, GuestOSFullName, Href | Format-Table -AutoSize | Out-String

    # Identify the vCenter Server, MoRef and other useful properties of the virtual machine(s) located within the vApp
    Write-Host "Retrieving vSphere properties for vCloud virtual machines:
    $vSphereMaps = @()
    $vCDVms | foreach {
        # Retrieve CIVm ExtensionData
        $vCloudExtData = $_.ExtensionData.VCloudExtension

        # Start building a custom object containing useful vSphere VM information
        $vSphereMap = New-Object System.Object
        $vSphereMap | Add-Member -MemberType NoteProperty -Name VmName -Value $_.Name
        $vSphereMap | Add-Member -MemberType NoteProperty -Name Status -Value $_.Status
        $vSphereMap | Add-Member -MemberType NoteProperty -Name Href -Value $_.Href
        $vSphereMap | Add-Member -MemberType NoteProperty -Name vCenterServer -Value
        $vCloudExtData.SyncRoot[0].Any.SyncRoot[0].VmVimObjectRef.VimServerRef.name
        $vSphereMap | Add-Member -MemberType NoteProperty -Name VmMoRef -Value
        $vCloudExtData.SyncRoot[0].Any.SyncRoot[0].VmVimObjectRef.MoRef
        Write-Host vCloud virtual machine $vSphereMap.VmName is managed by vCenter Server $vSphereMap.vCenterServer and has the MoRef
        $vSphereMap.VmMoRef

        # Check if the identified Guest OS is supported for this function and flag if not supported
        $SuppCount = 0
        $GuestOsSupported = "Yes"
        foreach ($SuppOSFullName in $SuppOSFullNames) {
            if ($_.GuestOsFullName -eq $SuppOSFullName) {
                $SuppCount ++
            }
        }
        if ($SuppCount -lt 1) {
            Write-Host -ForegroundColor Red "Unsupported guest operating system detected in virtual machine" $_.Name
            Write-Host -ForegroundColor Red "OSFullName must be one of:" ($SuppOSFullNames | Format-Table | Out-String)
            $GuestOsSupported = "No"
            if (!$NonSupportedGuestOveride){
                Write-Host -ForegroundColor Red "unction aborted. Use the 'NonSupportedGuestOveride' switch in order to overide and install packages in any supported virtual machines"
                exit 1
            }
        }

        # Add VM Guest OS information to the custom object
        $vSphereMap | Add-Member -MemberType NoteProperty -Name GuestOsFullName -Value $_.GuestOsFullName # May add multiple OS support later.
        $vSphereMap | Add-Member -MemberType NoteProperty -Name GuestOsSupported -Value $GuestOsSupported # May add multiple OS support later.

        # Add custom object to an array
        $vSphereMaps += $vSphereMap
    }

    # Summarise the mapping information rerieved
    Write-Host vCloud Director Server to vCenter Server mapping information is as follows
    $vSphereMaps | Format-Table @{Expression={$_.VmName};Label="Name"},Href, Status,@{Expression={$_.vCenterServer};Label="vCenter Server"},@{Expression={$_.VmMoRef};Label="MoRef"},@{Expression={$_.GuestOsFullName};Label="Guest OS"},@{Expression={$_.GuestOsSupported};Label="Supported"}  | Out-String

    # Disconnect from the vCloud Director Server
    Write-Host Disconnecting from vCloud Director Server $CIServer
    Disconnect-CIServer -Server $CIServer -Force -Confirm:$false

    # Connect to the first identified vCenter Server (assume all VMs in a vApp are managed by a common vCenter Server)
    $VIServer = $vSphereMaps[0].vCenterServer
    Write-Host Connecting to vCenter Server $VIServer
    Connect-VIServer -Server $VIServer -User $VIUser -Password $VIPassword | Out-Null

    # Loop through each discovered VM in the vApp
    $vSphereMaps | foreach {
        Write-Host "Processing virtual machine" $_.VmName

        # Verify the VM has a supported Guest OS
        if (($_.GuestOsSupported -eq "Yes") -and ($_.Status -eq "PoweredOn")){
            Write-Host "Virtual machine Guest OS is supported for package copy and install"

            # Identify the vSphere VM
            Write-Host Locating vSphere virtual machine object using MoRef $_.VmMoRef
            $VIVm = Get-VM -id ("VirtualMachine-" + $_.VmMoRef)
            Write-Host "Located vSphere virtual machine" $VIVm.Name "in $VIServer "inventory"

            # Copy install package to the virtual machine
            Write-Host Copying install package to vSphere virtual machine $VIVm.Name
            Write-Host "Transfering local package" $SrcPath "to virtual machine destination" $DstPath
            Copy-VMGuestFile -VM $VIVm -GuestUser $GuestUser -GuestPassword $GuestPassword -Source $SrcPath -Destination $DstPath -LocalToGuest -Force

            # Execute install package in the virtual machine
            $FileName = $SrcPath.Split("\")[(($SrcPath.Split("\")).Length -1)]
            Write-Host Executing package $DstPath within guest operating system of vSphere virtual machine $VIVm.Name
            $Result = Invoke-VMScript -VM $VIVm -GuestUser $GuestUser -GuestPassword $GuestPassword -ScriptText $Command
            $ExitCode = $Result.ExitCode
            Write-Host vSphere virtual machine $VIVm.Name returned exit code $ExitCode
        }elseif ($_.Status -eq "PoweredOff"){
            Write-Host -ForegroundColor Yellow "Virtual machine is powered off"
            Write-Host -ForegroundColor Yellow "Package cannot be copied and installed - skipping virtual machine" $_.VmName
        }else{
            Write-Host -ForegroundColor Yellow "Virtual machine Guest OS not supported for package copy and install"
            Write-Host -ForegroundColor Yellow "Package will not be copied and installed - skipping virtual machine" $_.VmName
        }
    }

    # Disonnect from the identified vCenter Server
    Write-Host Disconnecting from vCenter Server $VIServer
    Disconnect-VIServer -Server $VIServer -Force -Confirm:$false
}
End{}
}

When I get some more free time, I might take a look at adding some enhancements, such as support for both Windows and Linux based virtual machine guest operating systems and/or enhanced support for handling virtual machines in different power states.  Feel free to leave comments and provide some suggestions.

No Responses to “Install Software in Isolated Virtual Machines”

Trackbacks/Pingbacks

  1. Virtual Blog - vCloud PowerCLI scripts - Virtual Blog - [...] Install software in Isolated Machines Aidan Dalgleish [...]

Leave a Reply

Your email address will not be published. Required fields are marked *