Tag Archives: DPAPI

App-v Staging script

We are having performance issues with some virtualising some packages in App-v 5. There’s a package that needs it’s entire registry before it performs. When a user runs the starts the packages 3 to 4 times it starts to perform better. We have seen that the registry increased by 200 MB of plain text size. This is quit a lot. Since all our Xenapp servers are virtualised and provisioned will every reboot remove the changes in the registry. So the package remains very slow. We created a script that runs on all servers and starts a CMD.exe in a virtualised process. This will trigger staging and thus loading the entire registry. When this script is ran before any users connect, users will always have a good performance.

#import AppVClient module
ipmo *appv*

#get all appv packages
$apps = Get-AppvClientPackage
$obj = @()

#Get connection group applications
$connApps = (Get-AppvClientConnectionGroup).GetPackages()

#remove connecion-group applications from the list
[System.Collections.ArrayList]$appList = $apps
foreach ($connApp in $connApps) {$appList = $appList | ? { $_.name -ne $connApp.Name }}


#for each (non-connection group) appv package...
foreach ($app in $appList) {
$prop = New-Object System.Object

#get each appv package ID and version ID
$package = ($app.packageID).ToString()
$version = ($app.versionID).ToString()

#start a blank cmd.exe in the environment (this kicks off the AppV5 registry staging)
Start-AppvVirtualProcess -AppvClientObject (Get-AppvClientPackage $app.name) cmd.exe


    do {
    write-host "Testing: " $app.name
        sleep 1
       }
       until (Test-Path "HKLM:\SOFTWARE\Microsoft\AppV\Client\Packages\$package\Versions\$version\RegistryStagingFinished")
       write-host $app.name
       $appvProcess = get-appvVirtualProcess
       stop-process $appvProcess -force
    }

#for each connection group package...
$conGroups = Get-AppvClientConnectionGroup
foreach ($group in $conGroups) {
$prop = New-Object System.Object

#get each connection group package ID and version ID
$package = ($group.groupID).ToString()
$version = ($group.versionID).ToString()

#start a blank cmd.exe in the environment (this kicks off the AppV5 registry staging)
Start-AppvVirtualProcess -AppvClientObject ($group) cmd.exe


    do {
    write-host "Testing: " $group.name
        sleep 1
       }
       until (Test-Path "HKLM:\SOFTWARE\Microsoft\AppV\Client\PackageGroups\$package\Versions\$version\RegistryStagingFinished")
       write-host $group.name
       $appvProcess = get-appvVirtualProcess
       stop-process $appvProcess -force
    }
    

In order to start the above script I used DPAPI to encrypt the password of the service account. I configured a scheduled task to run every day at 6:30 AM. I wrote a article or two about his some time ago. It is important to encrypt the password when logged in to that account. The profile of that service account should not be removed!

The script used to start the script above is shown below

<#Custom Module#>
Import-Module "LocationWhenTheFunctionIsLocated\Function.psm1" -force
<#Script Start#>
Add-PSSnapin Citrix*
#Note that the username is without the domain name
$UserName = "ServiceAccount"
$SecureString = Get-Content -Path "LocationOfTheStoredPassword.txt" | ConvertTo-SecureString -Entropy ([Math]::PI)
$SecureString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString))
#If the server that launch the script is not a Citrix server you have to add -ComputerName COMPUTERNAME
$OnlineServers = Get-XAServer -ZoneName FarmName -OnlineOnly  | ?{$_.ServerName -match "PrefixFromProductionServers" -and $_.ServerName -notmatch "tst" -and $_.ServerName -notmatch "DEV"}
#Line below is for testing purposes
Add-content -Value $OnlineServers -Path "G:\Scheduler_Scripts\GMN\App-V Prelaunch\Test.csv"
Foreach ($Server in $Onlineservers){

$ServerName = $Server.ServerName
$Date = (Get-Date).AddMinutes(30)
schtasks /create /S $ServerName /RU DOMAIN\$Username /RP $SecureString /SC Once /SD $date.ToString('dd"/"MM"/"yyy') /ST $date.ToString('HH":"mm') /TN "App-V 5 Prelaunch Script" /TR "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe \\SharedLocationOfScriptAbove.ps1" /F 
}

A few things are important to make this work. First you need to share the first script on a shared location, let’s say the netlogon or whatever directory works for you. Next you’ll need to have the Xenapp SDK installed on the server that runs the script. This is needed. Only the Powershell CMDlets are required. You need to have a Xenapp server that can be reached over the network. You’ll also need to know the farm name. And of course you’ll need the have the function loaded. I added the function below this post. Check you Execution policy settings before scheduling.

Function

Encrypt Values using DPAPI

You can encrypt passwords for your account only using DPAPI. You need the function on this site.

Import the module. On some hardened systems using:

Import-Module '.\Modulename.psm1' -Force

Next encrypt the password using the following command:

$secureString = Read-Host -AsSecureString "Enter a secret password." 

# You can pass basically anything as the Entropy value, but I'd recommend sticking to simple value types (including strings),
# or arrays of those types, to make sure that the binary serialization of your entropy object doesn't change between
# script executions.  Here, we'll use Pi.  Edit:  The latest version of the code enforces the use of the recommended simple
# types, unless you also use the -Force switch.

$secureString | ConvertFrom-SecureString -Entropy ([Math]::PI) | Out-File .\storedPassword.txt

You can use it by decrypting the password as a secure string using the following code:

$newSecureString = Get-Content -Path .\storedPassword.txt | ConvertTo-SecureString -Entropy ([Math]::PI)

You can decrypt the password as plain text using the following code:

$newSecureString = Get-Content -Path .\storedPassword.txt | ConvertTo-SecureString -Entropy ([Math]::PI)
$newSecureString | ConvertFrom-SecureString -AsPlainText -Force