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…)
- Leveraging the VIX API
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