Using PowerShell to Self Deliver RunVirtual

Share on facebook
Share on twitter
Share on linkedin
Share on reddit
Share on stumbleupon

In previous blog posts I have talked about various models for RunVirtual detailing how we can deliver and manage the feature in conjunction with connection groups. In this post I would like to share with you a simple script which you can include in your package delivery to provision the RunVirtual registry values. This ‘self delivery’ approach will allow you to include this script as part of a ‘connector’ package within your connection group to make integration of local to virtual even easier. I have recently worked with a handful of clients for who this script has come in handy for, most recently for a financial organisation who wanted to move away from a virtualised Office (due to a raft of various issues) and go back to having it locally installed. They however wanted to still keep their various office plugins virtualised and user targeted:

In this design the connector package is a member of the connection group, it also contains a script which queries its own GUIDs (from the manifest) which it uses to populate the RunVirtual registry keys under the local Office process names:

The Script

The simple PowerShell script below will query its own package and version GUID and populate RunVirtual keys for any processes listed in the ApplicationExes variable (pre-populated with Office exes). The script can be called with either a -Publish to populate the keys or a -Unpublish to clean up the keys. The script itself is of course sequenced into the package under the scripts folder.

# Set Parameters
param
([switch]$Publish = $false,[switch]$Unpublish = $false)

#Set Variables
$ConnectorScriptPath = (Get-Variable MyInvocation).Value
$ConnectorScriptRootPath = Split-Path -Path $ConnectorScriptPath.MyCommand.Path -Parent
$ConnectorRootPath = Split-Path -Path $ConnectorScriptRootPath -Parent
$ManifestPath = $ConnectorRootPath + '\AppxManifest.xml'
$AppVRegistryPath = "HKCU:\Software\Microsoft\AppV\Client"
$RunVirtualRegistryPath = "HKCU:\Software\Microsoft\AppV\Client\RunVirtual"
$ApplicationExes = @("ACCICONS.EXE","AppSharingHookController.exe","CLVIEW.EXE","CNFNOT32.EXE","EXCEL.EXE","excelcnv.exe","FIRSTRUN.EXE","GRAPH.EXE","GROOVE.EXE","IEContentService.exe","INFOPATH.EXE","lync.exe","lynchtmlconv.exe","misc.exe","MSACCESS.EXE","msoev.exe","MSOHTMED.EXE","msoia.exe","MSOSREC.EXE","MSOSYNC.EXE","msotd.exe","MSOUC.EXE","MSPUB.EXE","MSQRY32.EXE","NAMECONTROLSERVER.EXE","OcPubMgr.exe","ONENOTE.EXE","ONENOTEM.EXE","ORGCHART.EXE","ORGWIZ.EXE","OUTLOOK.EXE","PDFREFLOW.EXE","POWERPNT.EXE","PPTICO.EXE","PROJIMPT.EXE","protocolhandler.exe","SCANPST.EXE","SELFCERT.EXE","SETLANG.EXE","SPDESIGN.EXE","TLIMPT.EXE","UcMapi.exe","VISICON.EXE","VISIO.EXE","VPREVIEW.EXE","WINPROJ.EXE","WINWORD.EXE","Wordconv.exe","WORDICON.EXE","XLICONS.EXE")

#Load Manifest.xml
$ManifestXML = New-Object System.XML.XMLDocument
$ManifestXML.Load($ManifestPath) 

#Get GUIDs
$ConnectorPackageGUID = $ManifestXML.Package.Identity.PackageId
$ConnectorVersionGUID = $ManifestXML.Package.Identity.VersionId

#Manipulate RunVirtual
Function RunVirtual
{
    If ((!(Test-Path $RunVirtualRegistryPath)) -and $Publish)
    {
    New-Item -Path $AppVRegistryPath -Name RunVirtual –Force
    }

    ForEach($ApplicationExe in $ApplicationExes)
    {
        If ($Publish)
        {
        New-Item -Path $RunVirtualRegistryPath -Name $ApplicationExe –Force
        Set-Item -Path $RunVirtualRegistryPath\$ApplicationExe -Value ($ConnectorPackageGUID+"_"+$ConnectorVersionGUID)
        }

    If (($Unpublish) -and (Test-Path $RunVirtualRegistryPath\$ApplicationExe))
    {
    $RunVirtualRegistryValue=(Get-ItemProperty -LiteralPath $RunVirtualRegistryPath\$ApplicationExe).'(default)'
        If($RunVirtualRegistryValue -eq ($ConnectorPackageGUID+"_"+$ConnectorVersionGUID))
        {
        Remove-Item $RunVirtualRegistryPath\$ApplicationExe
        }
    }
}

    If (!(Test-Path "$RunVirtualRegistryPath\*") -and $Unpublish)
    {
    Remove-Item $RunVirtualRegistryPath
    }
}

RunVirtual $ApplicationExes 

Once Connector.ps1 above has been saved into the package the script can be called within the deployment or user configuration files for the publish / unpublished actions as shown:

<UserScripts>
<PublishPackage>
<Path>powershell.exe</Path>
<Arguments>[{AppVPackageRoot}]\..\Scripts\Connector.ps1 -Publish</Arguments>
<Wait Timeout="30" RollbackOnError="true"/>
</PublishPackage>

<UnpublishPackage>
<Path>powershell.exe</Path>
<Arguments>[{AppVPackageRoot}]\..\Scripts\Connector.ps1 -Unpublish</Arguments>
<Wait Timeout="30" RollbackOnError="false"/>
</UnpublishPackage>
</UserScripts>

The great thing about the approach above is it doesn’t matter if you ever happen to update the connector package, the script will always lay down the correct GUIDs, so what you end up with is a self contained ‘worker package’ that just sits inside your connection groups ready to do any necessary RunVirtual work on behalf of its other members. Feel free to use the script above and tailor it to your needs. For further reading check out this post here where I further discuss real world RunVirtual.

Leave a Reply.

6 thoughts on “Using PowerShell to Self Deliver RunVirtual”

  1. Nice article and ps script 🙂 I’m about to make my own little tool for my own organization, doing much of the same stuff with a dummy package for runvirtual. But also going to add the possibility to choose and read all *.exe files from a certain filestructure: Like %programfiles%\program\ with our without the flag on to look trough subfolders. That way we can make it even more dynamical if needed. And also the ability to have a registry key or xml file with certain excluded exe files that won’t ever be added if we don’t remove this flag first.

  2. Hi Tham, great work usual. Just wanted to get your thoughts as to whether this solution is suitable for Internet Explorer plugins or do you just tend to go with Dynamic Virtualization where possible. (One limitation being that they must be globally published which is a pain)

    • Hi Mark,

      Most implementations I have seen tend to go with dynamic virtualisation although I agree the global publishing is a pain. However there is no reason you couldn’t take iexplore.exe out of DV registry and utilise the model described above in place.

  3. Hi, great post!

    By assigning the powershell script uing , will it run silently or will you see the powershell blue popup screens when publising takes place?

  4. Hi Thamim, great post and very interesting to read. I did create this script a bit another way, but basically does the same. I have a question how you assigned the security groups and how you did configure the connection group.

    Connnection group: do you enable the “use any version” for the Connector package?

    Security: for every security group you assigned to a addon-plugin you assign the same group to the Connector package and Connector connection group?
    Or do you by default assign all users to the connector package and connector connection group?

Comments are closed.