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.


How to use dynamic scripting in your APPV packages:

Backstory:

If you work with a company that have several different locations/divisions and sub company’s, but they all use the same programs and versions but with different configurations, how do you manage to make only one APPV package for that scenario?

The answer is dynamic package scripting, there may be other and maybe better ways to do this, but I will share my own method so you can compare and maybe get some new ideas.

Prerequirements:

For the concept of my method to work your first pre-requirement for this scenario is to tag all your computers with at least one unique location environment variable.

Today we use a script that looks into the computer OU in AD for this but you can also make this from your pcname  if it reflects location.
Unique pc-names can be set during imaging. Naming convention on our computers consist of the company short name and a location short name, and a unique running number (company-location-12345).
Our Location and company name is scripted from the ip range it’s been imaged from. You can then extract the location and companyname and store as a system variable in Windows with a script.

This should be fairly basic stuff so I won’t go into much detail of the process.

But one way is using something like powershell, cmd or autoit and stringsplit the name.

When it’s done it should be stored under the following place in Windows Registry.

"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", "Company_Location" and
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", "Company_Division

We can then look for those during our APPV package import on a computer.

How to make dynamic scripting:

Most (ALL?) .NET programs today use a config file called something like “programname.exe.Config” where they store things like db connections and other unique stuff.  
For this to work, you have to set all possible configs and test them first. Then copy out all the .config files into company subfolders and delete the file from APPV package.
The next step is to create the config files in your APPV package.

Like all other folders the scriptfolder in APPV can only consist of unique filenames you can either call them different names in the scriptfolder and rename them on import with a script, or you can use my neat script folder hack during APPV sequencing:
Picture1


Add your first config file into scriptfolder. Right click on a file and select edit button:

Picture2


Add your desired folder name into the the dialog box and select ok.
Picture3


Now you have a folder in your script directory:

Picture4


You can then use the same default name for all config files and reference the correct folder later in your import script.

Picture5

In this example I have made 3 COMPANY folders and one Location folder in APPV scriptfolder.

The following simple IF ELSE powershellscript will then compare Environment variables on computer and copy the selected folders into base layer (Local c:\) of the computer.
DISCLAIMER: I have not tested this script after renaming names from Norwegian to English, so it could contain some typos, but the logic should be fairly easy to understand.

# getting a system variable

$PC_LOCATION = (Get-Content env:PC_LOCATION)

$PC_COMPANY  = (Get-Content env:PC_COMPANY)

 

 

$AmisAmbulansestasjon = "AmisAmbulansestasjon.exe.config"

 

 

# set programfiles path after determination of operating system

# Set %programfiles(x86)% for W7 systems

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

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

    #THIS FIRST IF LOOP USING A LOCATION AND THE LAST 3 IS USING COMPANY CONFIG

    if ($PC_LOCATION -eq "LOCATION1") {

         Copy-Item -Path "ConfigFiles\LOCATION1\$AmisAmbulansestasjon" -Destination "$programfiles\AMIS AS\AMIS Ambulansestasjon\"

         } else {

            if ($PC_COMPANY -eq "COMPANY2") {

            Copy-Item -Path "ConfigFiles\COMPANY2\$AmisAmbulansestasjon" -Destination "$programfiles\AMIS AS\AMIS Ambulansestasjon\"

            } else {

                if ($PC_COMPANY -eq "COMPANY3") {

                    Copy-Item -Path "ConfigFiles\COMPANY3\$AmisAmbulansestasjon" -Destination "$programfiles\AMIS AS\AMIS Ambulansestasjon\"

                    } else {

                           if ($PC_COMPANY -eq "COMPANY1") {

                            Copy-Item -Path "ConfigFiles\COMPANY1\$AmisAmbulansestasjon" -Destination "$programfiles\AMIS AS\AMIS Ambulansestasjon\"

                            } else {

                                  Exit                                   

                                  }

                        }

             }

       }

       }

 

 

 

    

 

   

 

 

 

 

Another sample script from another almost identical program, but this time in BATCH (CMD) if you like that better:

SET AMIS_CONFIG="%ProgramData%\AMIS\Client\LocalConfig"

 

 

IF %PC_LOCATION% EQU PC_LOCATION1 (

SET AMIS_SOURCE=PC_LOCATION1

GOTO DATAHANDLER

)

 

IF %PC_LOCATION% EQU PC_LOCATION2(

SET AMIS_SOURCE=PC_LOCATION2

GOTO DATAHANDLER

)

 

IF %PC_LOCATION% EQU PC_LOCATION3 (

SET AMIS_SOURCE=PC_LOCATION3

GOTO DATAHANDLER

)

 

IF %PC_LOCATION% EQU PC_LOCATION4 (

SET AMIS_SOURCE=PC_LOCATION4

GOTO DATAHANDLER

)

 

:DATAHANDLER

XCOPY "%~dp0ConfigFiles\%AMIS_SOURCE%\*.*" "%AMIS_CONFIG%" /E /H /I /Y

 

 

 

Change folder visibility in package.

Since APPV is almost totally isolated as default we have to change our package so it can see the local filesystem on c: drive where we copy our dynamic config files. At first we tried to make all the scripts work totally inside our package VE bubble. But even with allow full VFS write it would not create and copy data inside of VE. From a security perspective and best practise you should normally not write data into os folders, but these APPV scripts all have admin access that’s what got it working for me (please comment under article if you know a better method). Redirecting data to a share would be better from a security perspective, but in our company that is a lot of extra overhead so we tend to not use it.

Picture6

Just right click on program folder and select “Merge with Local Directory”.

IMPORTANT NOTE:
If you use several versions of a program at the same time on a computer, this could be a possible problem if they use the same folders and file names. But if you only use one version at the same time it should be no problem at all. If you use several versions you could install it into another program folder to avoid this.

Sample scripts for Adding package and removing of package and script content:

We have developed our own standalone APPV program based on Tweakappv that has a script parser so we can run several cmd, powershell and Vbs scripts on each event and avoid the awful scriptrunner and adding scripts into package automatically.

We have done this in batch since it will work on all Windows versions and it has a fairly simple syntax. I have provided two sample scripts when we add package and remove package. I have not added Unpublish and Publish scripts since this example package does not use them.

We have a naming name convention so our main scripts are always called “AddPackage.cmd”, “RemovePackage.cmd”, “PublishPackage.cmd” and “UnPublishPakage.cmd”.

I have provided the default script settings we push into our packages from AVE (An excellent but not free APPV editor)

Picture7

 


You can probably skip Script 4 since that is an automated detection rule made for ALTIRIS detection of our APPV package.

AddPackage.cmd

@echo off

set hh=%time:~0,2%

if "%time:~0,1%"==" " set hh=0%hh:~1,1%

set dt=%date:/=.% %hh%%time:~2,6%

if not exist "C:\COMPANY\Appv\Scriptlog\" (mkdir "C:\COMPANY\Appv\Scriptlog\")

 

:Script_1

If EXIST "%programfiles(x86)%\AMIS AS\AMIS Ambulansestasjon\" PING 127.0.0.1 -n 3 && RMDIR /S /Q "%programfiles(x86)%\AMIS AS\AMIS Ambulansestasjon\"

If not %ERRORLEVEL% == 0 (

Set ErrorKode=%ERRORLEVEL%

set Scriptnr=1

Set Info=EXITING!!!

Set Go=Ende

Goto ErrorHandler

)

rem ---------------------------------

 

:Script_2

If NOT EXIST "%programfiles(x86)%\AMIS AS\AMIS Ambulansestasjon\" MKDIR "%programfiles(x86)%\AMIS AS\AMIS Ambulansestasjon\"

If not %ERRORLEVEL% == 0 (

Set ErrorKode=%ERRORLEVEL%

set Scriptnr=2

Set Info=EXITING!!!

Set Go=Ende

Goto ErrorHandler

)

rem ---------------------------------

 

:Script_3

powershell.exe -executionpolicy bypass -file "amis.config.lokasjon.ps1"

If not %ERRORLEVEL% == 0 (

Set ErrorKode=%ERRORLEVEL%

set Scriptnr=3

Set Info=EXITING!!!

Set Go=Ende

Goto ErrorHandler

)

rem ---------------------------------

 

:Script_4

Rem --- AltirisAddPackage Rule ---

reg add "hklm\software\wow6432node\COMPANY\Appv\Amis Ambulanse 1.3.0" /v AddPackage /t reg_sz /d 0.0.0.1/893de8df-2447-4950-a0ca-a3f75546ca1a/3b2cdab1-b7ac-4d9f-b60d-6f155f248d29 /f

If not %ERRORLEVEL% == 0 (

Set ErrorKode=%ERRORLEVEL%

set Scriptnr=4

Set Go=Ende

Goto ErrorHandler

)

rem ---------------------------------

 

Goto ende

 

:ErrorHandler

Echo %dt% >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Echo %Info% >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Echo PakkeID:893de8df-2447-4950-a0ca-a3f75546ca1a Versjonsid:3b2cdab1-b7ac-4d9f-b60d-6f155f248d29 Versjon:0.0.0.1 >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Echo AddPackage.cmd: Error in script nr %Scriptnr% Errorcode: %Errorkode% >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Goto %Go%

rem ---------------------------------

 

 

:Ende

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

                                                                                                                                                               


RemovePackage.cmd

@echo off

set hh=%time:~0,2%

if "%time:~0,1%"==" " set hh=0%hh:~1,1%

set dt=%date:/=.% %hh%%time:~2,6%

if not exist "C:\COMPANY\Appv\Scriptlog\" (mkdir "C:\COMPANY\Appv\Scriptlog\")

 

:Script_1

If EXIST "%programfiles(x86)%\AMIS AS\AMIS Ambulansestasjon\" PING 127.0.0.1 -n 3 && RMDIR /S /Q "%programfiles(x86)%\AMIS AS\AMIS Ambulansestasjon\"

If not %ERRORLEVEL% == 0 (

Set ErrorKode=%ERRORLEVEL%

set Scriptnr=1

Set Info=EXITING!!!

Set Go=Ende

Goto ErrorHandler

)

rem ---------------------------------

 

:Script_2

Rem --- AltirisRemovePackage Roule ---

reg delete "hklm\software\wow6432node\COMPANY\Appv\Amis Ambulanse 1.3.0" /f

If not %ERRORLEVEL% == 0 (

Set ErrorKode=%ERRORLEVEL%

set Scriptnr=2

Set Go=Ende

Goto ErrorHandler

)

rem ---------------------------------

 

Goto ende

 

:ErrorHandler

Echo %dt% >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Echo %Info% >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Echo PakkeID:893de8df-2447-4950-a0ca-a3f75546ca1a Versjonsid:3b2cdab1-b7ac-4d9f-b60d-6f155f248d29 Versjon:0.0.0.1 >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Echo RemovePackage.cmd: Feiler i script nr %Scriptnr% Errorkode: %Errorkode% >>"C:\COMPANY\Appv\Scriptlog\Amis Ambulanse 1.3.0.log"

Goto %Go%

rem ---------------------------------

 

 

:Ende

 

That’s it for now, hope this will give you some pointers for automation of APPV packaging.