Application Management and Patching

Creating and Deploying a PuTTY App-V Package in Liquit Workspace

Topics: Application Management and Patching

PuTTY is a free SSH and telnet client, developed by Simon Tatham for the Windows platform. Due to its compact size, this application is ideal for demonstrating the seamless support of App-V in Liquit Workspace.

Introduction to PuTTY and App-V Package Deployment

In this blog, I’ve used an App-V package for PuTTY created using Liquit Setup Commander and the App-V Sequencer. The latter used to be part of MDOP and is now part of the Windows 10 Assessment and Deployment Toolkit (Windows ADK).

To simplify creating a Liquit Workspace package for an App-V package of PuTTY, I’ve included a PowerShell script at the end of this blog.

This PowerShell script can be changed easily. To use it with any App-V package, you would want to distribute and use it from within Liquit Workspace. It creates a Smart Icon with all actions needed to enable App-V, publish, unpublish, and remove the App-V package for PuTTY, as demonstrated in this video.

The actions which are used to support App-V have been divided into Install, Launch, and Uninstall action sets.

Install

Start Normal PuTTY

If needed, you can enable this action to launch PuTTY in a normal way for devices on which App-V is not supported, like Windows 10 Home Edition. To install PuTTY this way (without App-V), you would need to add an ‘Install package’ action that references a managed package from the Setup Store to the Install action set. However, this process is beyond the scope of this blog, as I am focusing solely on using App-V.

Start App-V PuTTY

After the App-V application has been published, the application is available as App-VE1D03455-9A27-4E26-BF4E-CAA2F2DD8A239980FADE-CD94-4C81-B9B2-32F8FF2BCACC\Root\VFS\ProgramFilesX86\PuTTY\putty.exe in the All Users Profile directory. A filter to check for ${AllUsersProfile}\App-VE1D03455-9A27-4E26-BF4E-CAA2F2DD8A239980FADE-CD94-4C81-B9B2-32F8FF2BCACC\Root\VFS\ProgramFilesX86\PuTTY\putty.exe has been added to make sure it’s only started when it has been published

Unpublish-AppvClientPackage PuTTY

This action uses the Unpublish-AppvClientPackage cmdlet to unpublish the published App-V application for all users of a device.

Remove-AppvClientPackage PuTTY

This action uses the Remove-AppvClientPackage cmdlet to unpublish the published App-V application for all users of a device.


PowerShell Script for PuTTY App-V Package Deployment

Import-Module "C:\Program Files (x86)\Liquit Workspace\PowerShell\3.0\Liquit.Server.PowerShell.dll" -Prefix "Liquit"

[System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression") | Out-Null;
[System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null;

$liquitZoneUsername = "local\admin"
$liquitZonePassword = Read-Host "Enter Password" -AsSecureString
$liquitCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $liquitZoneUsername, $liquitZonePassword
$liquitZone = "https://yourliquitzone.liquit.com/"

$puttyIconURL = "https://www.setupcommander.com/ico/putty.ico"
$puttyIconPath = "c:\windows\temp\putty.ico"
$puttyAppVPackagePath = "c:\temp\PuTTY 0.73.appv"
$puttyAppVPackageFilename = "PuTTY 0.73.appv"

#get the Version ID and Package ID of the App-V package
$AppxManifestPath = "c:\windows\temp\appxmanifest.xml"
$AppV5Package = New-Object System.IO.Compression.ZipArchive(New-Object System.IO.FileStream($puttyAppVPackagePath, [System.IO.FileMode]::Open));
$AppxManifest = $AppV5Package.GetEntry("AppxManifest.xml" );
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($AppxManifest, $AppxManifestPath, $true);
$xml = [xml](Get-Content $AppxManifestPath)
$AppV5PackageVersionId = $xml.Package.Identity.VersionId.ToUpper() 
$AppV5PackageId = $xml.Package.Identity.PackageId.ToUpper()
$AppV5Version = $xml.Package.Identity.Version
$AppV5DisplayName = $xml.Package.Properties.DisplayName
$AppV5Package.Dispose();

#connect to Liquit Workspace
$liquitContext = Connect-LiquitWorkspace -URI $liquitZone -Credential $liquitCredentials

#download the PuTTY icon
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($puttyIconURL,$puttyIconPath)
$iconContent = New-LiquitContent -Path $puttyIconPath

#define and create the package 
$packageName = "PuTTY"
$packageDisplayName = "PuTTY"
$packageDescription = "PuTTY"

$package = New-LiquitPackage -Name $packageName `
                             -Type "Launch" `
                             -DisplayName $packageDisplayName `
                             -Description $packageDescription `
                             -Priority 100 `
                             -Enabled $true `
                             -Offline $True `
                             -Web $false `
                             -Icon $iconContent

#create the snapshot for this package
$snapshot = New-LiquitPackageSnapshot -Package $package

#define install action set
$actionset_install = New-LiquitActionSet -snapshot $snapshot `
                                 -Type Install `
                                 -name 'Install' `
                                 -Enabled $true `
                                 -Frequency Always `
                                 -Process Sequential


#
#define the first install action
$actionset_install_action1 = New-LiquitAction -ActionSet $actionset_install `
                                      -Name 'Copy uploaded App-V Package to local' `
                                      -Type 'contentcopy' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{content = $puttyAppVPackageFilename; destination = '${PackageTempDir}\' + $puttyAppVPackageFilename} `
                                      -Context Device

$appvContent = New-LiquitContent -Path $puttyAppVPackagePath -FileName $puttyAppVPackageFilename
$attribute = New-LiquitAttribute -Entity $actionset_install_action1 -Link $appvContent -ID 'content' -Settings: @{filename = $puttyAppVPackageFilename}


#
#define the second install action
#
$actionset_install_action2 = New-LiquitAction -ActionSet $actionset_install `
                                      -Name 'Enable-Appv' `
                                      -Type 'contentscript' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{content = 'script.ps1'; type = 3; display = 1; engineParameters = "-NoLogo -ExecutionPolicy Bypasss"; successReturnCodes = 0, 1} `
                                      -Context Device

#add the PowerShell script for this action
$scriptLine = 'Enable-Appv'
Set-Content -Path 'c:\windows\temp\script.ps1' -Value $scriptLine
$script_content_actionset_install_action2 = New-LiquitContent -Path "c:\windows\temp\script.ps1" -FileName 'script.ps1'
$attribute = New-LiquitAttribute -Entity $actionset_install_action2 -Link $script_content_actionset_install_action2 -ID 'script' -Settings: @{filename = 'script.ps1'}

#
#define the third install action
#
$actionset_install_action3 = New-LiquitAction -ActionSet $actionset_install `
                                      -Name 'Add-AppvClientPackage' `
                                      -Type 'contentscript' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{content = 'script.ps1'; type = 3; display = 1; engineParameters = "-NoLogo -ExecutionPolicy Bypasss" ; directory = '${PackageTempDir}'; successReturnCodes = 0, 1} `
                                      -Context Device

#add the PowerShell script for this action
$scriptLine = 'Add-AppvClientPackage "' + $puttyAppVPackageFilename + '"'
Set-Content -Path 'c:\windows\temp\script.ps1' -Value $scriptLine
$script_content_actionset_install_action3 = New-LiquitContent -Path "c:\windows\temp\script.ps1" -FileName 'script.ps1'
$attribute = New-LiquitAttribute -Entity $actionset_install_action3 -Link $script_content_actionset_install_action3 -ID 'script' -Settings: @{filename = 'script.ps1'}

#
#define the fourth install action
#
$actionset_install_action4 = New-LiquitAction -ActionSet $actionset_install `
                                      -Name 'Mount-AppvClientPackage' `
                                      -Type 'contentscript' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{content = 'script.ps1'; type = 3; display = 1; engineParameters = "-NoLogo -ExecutionPolicy Bypasss" ; directory = '${PackageTempDir}'; successReturnCodes = 0, 1} `
                                      -Context Device

#add the PowerShell script for this action
$scriptLine = 'Mount-AppvClientPackage "' + $puttyAppVPackageFilename + '"'
Set-Content -Path 'c:\windows\temp\script.ps1' -Value $scriptLine
$script_content_actionset_install_action4 = New-LiquitContent -Path "c:\windows\temp\script.ps1" -FileName 'script.ps1'
$attribute = New-LiquitAttribute -Entity $actionset_install_action4 -Link $script_content_actionset_install_action3 -ID 'script' -Settings: @{filename = 'script.ps1'}


#
#define the fifth install action
#
$actionset_install_action5 = New-LiquitAction -ActionSet $actionset_install `
                                      -Name 'Publish-AppvClientPackage' `
                                      -Type 'contentscript' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{content = 'script.ps1'; type = 3; display = 1; engineParameters = "-NoLogo -ExecutionPolicy Bypasss" ; directory = '${PackageTempDir}' ; successReturnCodes = 0, 1} `
                                      -Context Device

#add the PowerShell script for this action
$scriptLine = 'Publish-AppvClientPackage -Name "' + $AppV5DisplayName + '" -Global'
Set-Content -Path 'c:\windows\temp\script.ps1' -Value $scriptLine
$script_content_actionset_install_action5 = New-LiquitContent -Path "c:\windows\temp\script.ps1" -FileName 'script.ps1'
$attribute = New-LiquitAttribute -Entity $actionset_install_action5 -Link $script_content_actionset_install_action5 -ID 'script' -Settings: @{filename = 'script.ps1'}

#define launch action set
$actionset_launch = New-LiquitActionSet -snapshot $snapshot `
                                 -Type Launch `
                                 -name 'Launch' `
                                 -Enabled $true `
                                 -Frequency Always `
                                 -Process StopAtFirstEffectiveAction
#
#define the first launch action
#
$actionset_launch_action1 = New-LiquitAction -ActionSet $actionset_launch `
                                      -Name 'Start Normal PuTTY' `
                                      -Type 'processstart' `
                                      -Enabled $false `
                                      -IgnoreErrors $false `
                                      -Settings @{name = '${ProgramFiles32}\PuTTY\putty.exe'; parameters = "";} `
                                      -Context User

#define the filter set for the first launch action
$filterset_launch_action1 = New-LiquitFilterSet -Action $actionset_launch_action1

#add a filter to the first action
$filter_launch_action1 = new-LiquitFilter -FilterSet $filterset_launch_action1 -type fileexists -Settings @{path = '${ProgramFiles32}\PuTTY\putty.exe';} -Value "true" 

#
#define the second launch action
#
$actionset_launch_action2 = New-LiquitAction -ActionSet $actionset_launch `
                                      -Name 'Start App-V PuTTY' `
                                      -Type 'processstart' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{name = '${AllUsersProfile}\App-V\' + $AppV5PackageId + '\' + $AppV5PackageVersionId + '\Root\VFS\ProgramFilesX86\PuTTY\putty.exe'; parameters = "";} `
                                      -Context User

#define the filter set for the second launch action
$filterset_launch_action2 = New-LiquitFilterSet -Action $actionset_launch_action2

#add a filter to the second action
$filter_launch_action2 = new-LiquitFilter -FilterSet $filterset_launch_action2 -type fileexists -Settings @{path = '${AllUsersProfile}\App-V\' + $AppV5PackageId + '\' + $AppV5PackageVersionId  + '\Root\VFS\ProgramFilesX86\PuTTY\putty.exe';} -Value "true" 


#define uninstall action set
$actionset_uninstall = New-LiquitActionSet -snapshot $snapshot `
                                 -Type Uninstall `
                                 -name 'Uninstall' `
                                 -Enabled $true `
                                 -Frequency Always `
                                 -Process Sequential
#
#define the first uninstall action
#
$actionset_uninstall_action1 = New-LiquitAction -ActionSet $actionset_uninstall `
                                      -Name 'Unpublish-AppvClientPackage PuTTY' `
                                      -Type 'contentscript' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{content = 'script.ps1'; type = 3; display = 1; engineParameters = "-NoLogo -ExecutionPolicy Bypasss"} `
                                      -Context Device

#add the PowerShell script for this action
$scriptLine = 'Unpublish-AppvClientPackage -Name "' + $AppV5DisplayName + '" -Version ' + $AppV5Version
Set-Content -Path 'c:\windows\temp\script.ps1' -Value $scriptLine
$script_content_actionset_uninstall_action1 = New-LiquitContent -Path "c:\windows\temp\script.ps1" -FileName 'script.ps1'
$attribute = New-LiquitAttribute -Entity $actionset_uninstall_action1 -Link $script_content_actionset_uninstall_action1 -ID 'script' -Settings: @{filename = 'script.ps1'}

#
#define the second uninstall action
#
$actionset_uninstall_action2 = New-LiquitAction -ActionSet $actionset_uninstall `
                                      -Name 'Remove-AppvClientPackage PuTTY' `
                                      -Type 'contentscript' `
                                      -Enabled $true `
                                      -IgnoreErrors $false `
                                      -Settings @{content = 'script.ps1'; type = 3; display = 1; engineParameters = "-NoLogo -ExecutionPolicy Bypasss"} `
                                      -Context Device

#add the PowerShell script for this action
$scriptLine = 'Remove-AppvClientPackage -PackageId ' + $AppV5PackageId.Tolower() + ' -VersionId ' + $AppV5PackageVersionId.ToLower()
Set-Content -Path 'c:\windows\temp\script.ps1' -Value $scriptLine
$script_content_actionset_uninstall_action2 = New-LiquitContent -Path "c:\windows\temp\script.ps1" -FileName 'script.ps1'
$attribute = New-LiquitAttribute -Entity $actionset_uninstall_action2 -Link $script_content_actionset_uninstall_action2 -ID 'script' -Settings: @{filename = 'script.ps1'}


#publish the package
Publish-LiquitPackageSnapshot -Snapshot $snapshot -stage Production 

#set the entitlement
$identity = Get-LiquitIdentity -id "LOCAL\everyone"
New-LiquitPackageEntitlement -Package $package -Identity $identity -Publish Workspace

App-V Note

App-V is only supported on Windows 10 Education and Enterprise. If you use Windows 10 Home or Professional in your organization, you can filter to the Install and Uninstall action sets. The two filters below can be used to check for ‘Windows 10.0 and the value of the CompositionEditionID in the registry, which is ‘Enterprise’ when Windows 10 Enterprise is used:

PuTTY App-V package

Conclusion and Next Steps

By following these steps, you can efficiently create and deploy a PuTTY App-V package in Liquit Workspace, streamlining your application management process. The provided PowerShell script simplifies the deployment, ensuring a smooth integration and optimal performance. For further insight or support, refer to the detailed documentation and resources available within Liquit Workspace.

Back to Top