How to automate App-V runvirtual keys:


If you want to automate App-v’s “runvirtual” function in a large Enterprise, Ms has not provided you with any central tools to enable it for you.
Most examples online use the following two methods:
Add the runvirtual keys with a GPO or a Script in every package and deliver it during package deployment.
This is working fairly good, but it’s not very dynamic if you have a large and complex company and you need different runvirtual keys for different locations or computers you need to update GPO’s or roll out new packages if you want to change it.
The way we are doing it is fairly simple, but it does provide us with the ability to set and update all our runvirtual keys at once from a central location.
Our method was inspired by a blogpost by Thamim Karim. But we wanted to make it more dynamic so me and a member of my team scripted our own variant in 2017, and we have been using it ever since in our company.

How our script works:

Starting with the basics of our INI file on a machine with all empty runvirtual keys.


This is our sample ini file:



The [Ini] version part.

Just to make it easy for us we normally just put in the date of INI file creation and the last -1 part makes it easier if you have to update it several times that day, if it is different from the version on our server it will be replaced by the newest INI file.
IMPORTANT: You have to create a new INI file with the [Ini] and iniversion part to work on your computer before running the powershell script!

The [Forbidden] part.

 This is the programs we DON’T want added as runvirtual keys (if users or helpdesk has been fiddling around for them self to get stuff on the computer).
This is executed first in our script and will remove all added exe files if they exist from the list.

The [PACKAGE] part.

In the example we are using the App-V package [CLUE]
Here you just insert your package name into the brackets [].
The next part 001 is the path to your exe file.
In the example I showed that it will support wildcards from a folder, so *.exe will add all exe files in the office directory. Or you can add one and one if you like to on a new line starting with 002=windord.exe
003=excel.exe and you get the draft
You can add as many entry’s as you want.
In this example we place our empty runvirtual ini file at the following location.
“c:\company\appv\” on our testmachine.
The location can be altered in the powershell script if you want to.

Then we run the following powershell commandline from the directory the powershell resides in:
powershell.exe -executionPolicy bypass -file .\runvirtual.ps1 -inifile "RunVirtual_product.ini" -Rule "IngenRule" -Serverini "c:\temp\runvirtual_product.ini"
First you run the powershell script using powershell.exe -executionPolicy bypass -file .\runvirtual.ps1
Then you specify what you want your local ini file to be called: -inifile "RunVirtual_product.ini"  (by default it resides under c:\company\appv, must be changed in powershell file for now)
Then we tattoo our registry with a detectionrule so Altiris would know it has been run when using policys under "HKLM:\Software\company\RunVirtual_Version" -Name 'Version' -Value $Version
The last part is the path to our master ini file: Serverini "c:\temp\runvirtual_product.ini" This could be changed to a share \\server\dfs\runvirtual_product.ini or something else you want.
You could also use the runvirtual.cmd file. It will provide you with errorcodes if  something is missing using: %ERRORLEVEL%
if($inifile -eq ''){exit 16000}
if($serverini -eq ''){exit 16001}
if($Rule -eq ''){exit 16002}
NOTE: If our script can’t access or find the specified server ini file it will try to read runvirtual.ini from the same directory as our powershell script is running from, this could be altered in the powershell scriptfile.


This is the result. As we can see it added all exe files from the “c:\programfiles  (x86)\Microsoft Office\Office15\ folder” and the correct packageguid and version guid
Then we want to test the forbidden part. We now add outlook.exe in that part and run the powershellscript again.

Now we can see that outlook.exe got removed from our runvirtual keys


Now we add a new App-V package Wisc-IV in the INI file.
Then we set chrome.exe as a runvirtual key an it gets added when running the script


What do you need to use this script?

Well first you have to download it from my Github here.
Then you modify the powershellscript after your own liking.
#Edit the name of your ini file to suit your productname if you want. Scripts could be modified for several ini files for different locations/computers
[string]$inifile = 'RunVirtual_product.ini'

The part over here is the filename of your ini file.
# Edit this part to a share for central managment "\\share\folder\"    
[string]$serverIni = "c:\temp\"

In the example I just use a local file in c:\temp, but in production we use a DFS share where all our clients can read it. Using a central location makes it easy to update and be sure that not everyone can modify it.
The next part you need to modify is:
#Edit this part to suit your companys wanted ini file location we just use c:\company in our example
       if (!(Test-Path -Path "c:\Company\Appv")) { new-item -Path"c:\Company\Appv" -ItemType directory | Out-Null }
       $LocalIni = "c:\Company\Appv\" + $inifile
I guess most of you dont want the ini file in a folder called c:\company\appv

IMPORTANT: As stated before in the ini example, you have to create a new INI file in the scriptdir the first time with the [Ini] and Iniversion= part to work on your computer before running the powershell script!


After that has been done, you need to run it at a schedule on your computers.
We use Altiris and deliver it as a package from there on a schedule, or a task if we want it running instantly.
But you could just put up a scheduled task that’s running with an account with correct execution rights to DFS share and the powershell script running. Or build a Windows service if you want.
If I remember correct it will just use the local INI file copy if it lost connection to the DFS share.
So I hope you will enjoy our script.
Updated 25.07.19:
I apologize there is still some Norwegian parts in examples. Have updated the githubscript with an all English version J





A faster and more modern method for setting company and location variables


An even better, faster and more modern method for setting company and location variables is just using the registry. In my previous article about dynamic scripting I was using environment variables, now I just use the registry.

A sample powershell script:

 

$PC_COMPANY = (Get-ItemProperty HKLM:\SOFTWARE\COMPANYVAR\PC_COMPANY).PC_COMPANY

Write-Output "Original registry key: $($PC_COMPANY)"

#$PC_LOCATION = (Get-ItemProperty HKLM:\SOFTWARE\COMPANYVAR\PC_LOCATION).PC_LOCATION

#Write-Output "Original registry key: $($PC_LOCATION)"

 

if ((Get-WmiObject Win32_OperatingSystem).name -notmatch "XP") {

    $programfiles = (Get-Content env:"ProgramFiles(x86)")

}

 

$Destination = "$ProgramFiles\Elements\ElementsOutlook\ElementsOutlook\Config\"

New-Item -ItemType Directory -Path $Destination

 

    if ($PC_COMPANY -eq "COMPANY1") {

        Copy-Item -Path "ConfigFiles\ COMPANY1\*" $Destination -Recurse -Force

        } else {

        if ($PC_COMPANY -eq "COMPANY2") {

            Copy-Item -Path "ConfigFiles\COMPANY2\*" $Destination -Recurse -Force

            } else {

                if ($PC_COMPANY -eq "COMPANY3") {

                    Copy-Item -Path "ConfigFiles\COMPANY3\*" $Destination -Recurse -Force

                    } else {

                            if ($PC_COMPANY -eq "COMPANY4") {

                                Copy-Item -Path "ConfigFiles\COMPANY4\*" $Destination -Recurse -Force

                                } else {

                                    if ($PC_COMPANY -eq "COMPANY5") {

                                        Copy-Item -Path "ConfigFiles\COMPANY5\*" $Destination -Recurse -Force

                                        } else {

                                            if ($PC_COMPANY -eq "COMPANY6") {

                                                Copy-Item -Path "ConfigFiles\COMPANY6\*" $Destination -Recurse -Force

                                                } else {

                                                    Exit

                                                   }        

                                            }

                                    }

                            }

                    }

                }

 

 

 

How you can manage to view and edit all Advanced APPV package settings from commandline using Tweakappv

Backstory:

If you work with APPV you probably used the ability to connect several APPV packages with a Connection Group (CG).

We all know it’s time consuming when there is conflicting advanced COM and isolation settings between packages already produced since they all need the same settings to work in a CG.
First you have to open all packages, and then you have to see what’s different before you can make the same adjustment to all the needed packages, it takes time and it easy to forget something.

What we wanted to do:

So I have been working on an idea of developing a small  in-house PowerShell tool based on the fine work and ideas of Advanced Installers “tweakappv”with a colleague. 

Tweakappv works basically like a small APPV api, so if you can crack the commands and syntax you can basically edit everything in one or more packages without doing manual editing.

What we managed to do:

Using a lot of time on trial and error we did manage to change all advanced isolation settings in all tested packaged, even that of Appv Office  2013\365 without breaking it (We don’t change those in Office, but it’s fun to test complex packages just because you can).
Picture 1
In this first beta we first select the working folder with all our packages going into a CG, then the program will extract and open all packages Appv manifest xml files and list out the individual package settings they have in a table view in the GUI.
We can then select the same default settings we want to set on all the packages in that CG and compile them.

Picture 2
This second Log window just show the PowerShell output and if there is some errors in there.
It can take several minutes to compile a lot of big packages if you edit a lot of functions, since one settings has to be applied at the time then compiled on each package before the next will start, but you can put it to work while you fetch a cup of coffee.
Some settings have to be done using xsl transforms, here is the XSL for disabling Brower plugins:
<?xml version="1.0" encoding="utf-8"?>

<!--
  Update appv package manifest using the following command line:
  TweakAppv.exe /update "Your Application.appv" /transformmanifest AppxManifest.xml "DisableBrowserHelperObject.xsl"
-->
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                             xmlns:appx="http://schemas.microsoft.com/appx/2010/manifest"
                             xmlns:appv="http://schemas.microsoft.com/appv/2010/manifest"
                             xmlns:appv1.1="http://schemas.microsoft.com/appv/2013/manifest"
                             xmlns:appv1.2="http://schemas.microsoft.com/appv/2014/manifest">

  <xsl:output method="xml" version="1.0" omit-xml-declaration="no" encoding="UTF-8" standalone="yes" indent="yes"/>
 
   <!-- Copy nodes that are not BrowserPlugin -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <!-- Comment BrowserPlugin -->
  <xsl:template match="/appx:Package/appv:Extensions/*[local-name()='Extension' and namespace-uri()='http://schemas.microsoft.com/appv/2013/manifest' and @Category='AppV.BrowserPlugin' and *[@Subcategory='BrowserHelperObject']]">
    <xsl:text disable-output-escaping="yes"> &lt;!-- </xsl:text>
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    <xsl:text disable-output-escaping="yes"> --&gt; </xsl:text>
  </xsl:template>
 
</xsl:transform>
Here is the xsl to disable all objects from isolation:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://schemas.microsoft.com/appx/2010/manifest"
xmlns:appx="http://schemas.microsoft.com/appx/2010/manifest"
xmlns:appv="http://schemas.microsoft.com/appv/2010/manifest"
xmlns:appv1.1="http://schemas.microsoft.com/appv/2013/manifest"
xmlns:appv1.2="http://schemas.microsoft.com/appv/2014/manifest"
version="1.0"
exclude-result-prefixes="appx">

<xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

<xsl:template match='/appx:Package/appv:Extensions/appv:Extension[@Category="AppV.Objects"]' />

<xsl:template match="/appx:Package/appv:Extensions">
    <xsl:copy>
        <xsl:copy-of select="@*|node()"/>
               <appv:Extension Category="AppV.Objects">
                               <appv:Objects>
                              <appv:NotIsolate>
                                              <appv:Object Name="*" />
                              </appv:NotIsolate>
                               </appv:Objects>
               </appv:Extension>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
Here is the xsl to delete all objects from isolation.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://schemas.microsoft.com/appx/2010/manifest"
xmlns:appx="http://schemas.microsoft.com/appx/2010/manifest"
xmlns:appv="http://schemas.microsoft.com/appv/2010/manifest"
xmlns:appv1.1="http://schemas.microsoft.com/appv/2013/manifest"
xmlns:appv1.2="http://schemas.microsoft.com/appv/2014/manifest"
version="1.0"
exclude-result-prefixes="appx">

<xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

<xsl:template match='/appx:Package/appv:Extensions/appv:Extension[@Category="AppV.Objects"]' />

</xsl:stylesheet>
Here is the xsl to change VFS settings:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://schemas.microsoft.com/appx/2010/manifest"
xmlns:appx="http://schemas.microsoft.com/appx/2010/manifest"
xmlns:appv="http://schemas.microsoft.com/appv/2010/manifest"
xmlns:appv1.1="http://schemas.microsoft.com/appv/2013/manifest"
xmlns:appv1.2="http://schemas.microsoft.com/appv/2014/manifest"
version="1.0"
exclude-result-prefixes="appx">

<xsl:param name="VFS" select="true"/>

<xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="appx:Package/appx:Properties/appv1.2:FullVFSWriteMode">
  <xsl:copy>
      <xsl:value-of select="$VFS"/>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

Com settings can be set directly using tweakappv commands:
Here is the list of COM settings we use.
;twc


;Appv:ComIsolated 6
;appv:SoftwareClients 7
;appv:Objects 8
Del-Nodes "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM"
Del-Nodes "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:SoftwareClients"
Del-Nodes "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:Objects"

;end

;Appv:COMOFF 6,23,24,25
New-Element "AppxManifest.xml" -elementname "appv:ExtensionsConfiguration" -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
New-Element "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration" -elementname "appv:COM" -elementtext " " -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM" -AttributeName "Mode" -AttributeValue "Off"
;end

;Appv:COMIntegrated 6,23,24,31,32,33-34,35-36
;New-Element "AppxManifest.xml" -elementname "appv:ExtensionsConfiguration" -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
;New-Element "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration" -elementname "appv:COM" -elementtext " " -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
New-Element "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM" -elementname "appv:IntegratedCOMAttributes" -elementtext " " -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM" -AttributeName "Mode" -AttributeValue "Integrated"
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM/appv:IntegratedCOMAttributes" -AttributeName "InProcessEnabled" -AttributeValue "true"
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM/appv:IntegratedCOMAttributes" -AttributeName "InProcessEnabled" -AttributeValue "false"
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM/appv:IntegratedCOMAttributes" -AttributeName "OutOfProcessEnabled" -AttributeValue "true"
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:COM/appv:IntegratedCOMAttributes" -AttributeName "OutOfProcessEnabled" -AttributeValue "false"
;end

;appv:SoftwareClients 7,23,24,42,43,44-45
;New-Element "AppxManifest.xml" -elementname "appv:ExtensionsConfiguration" -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
;New-Element "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration" -elementname "appv:SoftwareClients" -elementtext " " -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
New-Element "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:SoftwareClients" -elementname "appv:ClientConfiguration" -elementtext " " -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:SoftwareClients" -AttributeName "Enabled" -AttributeValue "false"
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:SoftwareClients/appv:ClientConfiguration" -AttributeName "EmailEnabled" -AttributeValue "true"
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:SoftwareClients/appv:ClientConfiguration" -AttributeName "EmailEnabled" -AttributeValue "false"
;end

;appv:Objects 8,23,50,51,52
;New-Element "AppxManifest.xml" -elementname "appv:ExtensionsConfiguration" -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
New-Element "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration" -elementname "appv:Objects" -elementtext " " -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
New-Element "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration" -elementname "appv:ApplicationCapabilities" -elementtext " " -namespaceURI "http://schemas.microsoft.com/appv/2010/manifest" -createifnotexist
Set-ElementAttribute "AppxManifest.xml" -xpath "appv:ExtensionsConfiguration/appv:Objects" -AttributeName "Enabled" -AttributeValue "false"
;end

Update 15.01.2020. Program now available for download on Github.