Tag Archives: App-V

App-v 4.6 large cache detection and alert script

App-v 4.6 generates sometime a large sftfs.fsd file. Users are experiencing performance issues when the file exceeds 15 GB. To fix this problem administrators remove the file by rebooting the Xenapp in safe mode. No user should be logged in of course. The Citrix servers therefore need to be empty. My colleagues ran a VB script that did the job of detecting but nothing else. I have made a script in Powershell that does the same but also set the server in ProhibitLogOns logon mode. The implement this script the server must have the Xenapp SDK installed. The server which runs the script need to have relay rights on the SMTP server. You’ll also need to have a Xenapp Server with PSRemoting enabled or the server itself should be member of the Xenapp farm.

#Load Modules and Snapins
Import-Module ActiveDirectory
Add-PSSnapin Citrix*
#Opens array for servers that apply on the conditions specified below
$ProhibitLogonServers =@()
$SMTP = "SMTPSERVER"
#Get all Xenapp server based on OU
$list = Get-ADComputer -Filter * -SearchBase "OU=Citrix Servers,OU=Servers,DC=Domain,DC=LOCAL"
Foreach ($Computer in $List){$Server = $Computer.DNSHostName 
$Result = GI "\\$Server\d$\AppVClientcache\Global\SoftGrid Client\sftfs.fsd"
If ($result.length -ne 0){$Outinfo = $Server+";"+$Result.length+" Bytes"
AC -Value $Outinfo -Path ".\log.csv"
#The conditions are met if the file is greater and engels 15 GB
If (($Result.length / 1073741824 ) -ge 15){$ProhibitLogonServers += $Computer}}}

#Mail message if no server are found                                                                                                                                                                            
If ($ProhibitLogonServers.length -eq 0){
$Mail = "Er is geen server gevonden die voldoet aan de voorwaarden om op ProhibitLogons te worden gezet" + "`n`n" + "Met Vriendelijke groet," +  "`n" + "Ochtend Controle Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Report" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

#Mail message one server is found
If ($ProhibitLogonServers.count -eq 1){
$Mail = "Er is een server gevonden die voldoet aan de voorwaarden om op ProhibitLogons te worden gezet. De server heeft als naam:" + "`n`n" + "$ProhibitlogonServers.Name" +"`n`n"+ "Met vriendelijke groet," + "`n`n" + "Ochtendcontrole Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Report" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

#Mail message if more than one server is found
If ($ProhibitLogonServers.count -gt 1){
$Mail = "Er zijn servers gevonden die voldoen aan de voorwaarden om op ProhibitLogons te worden gezet. De servers hebben de naam:" + "`n`n"
foreach ($server in $prohibitlogonservers){$Mail = $Mail + $Server.Name + "`r`n"}
$Mail = $Mail  + "`n" + "Met vriendelijke groet," + "`n`n" + "Ochtend Controle Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Report" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

#Opens array to check if the server has been set to prohibit logons. If servers are found a retry will be executed
$FailedServers = @()
If ($Prohibitlogonservers -ne $null){
Foreach ($Server in $Prohibitlogonservers){Set-XAServerLogOnMode -ServerName $Server.Name -LogOnMode Prohibitlogons -ComputerName CitrixServer}
Foreach ($Server in $Prohibitlogonservers){If ((Get-XAServer -ServerName $Server.Name -ComputerName CitrixServer).LogonMode -notmatch "ProhibitLogonOns"){$FailedServers += $Server}}}

#If a server is rebooting during the process you do want to make sure that server is set to Prohibit Logons. 
Start-sleep -s 900

#Retry setting the server to prohibit logons
If ($Failedservers.count -ne 0){
Foreach ($Server in $FailedServers){Set-XAServerLogOnMode -ServerName $Server.Name -LogOnMode Prohibitlogons -ComputerName CitrixServer}
$Failedservers = @()
Foreach ($Server in $FailedServers){If ((Get-XAServer -ServerName $Server.Name -ComputerName CitrixServer).LogonMode -notmatch "ProhibitLogonOns"){$FailedServers += $Server}}}

#Mail message if servers aren't set to prohibit logons
If ($Failedservers.count -ne 0){$Mail = "Er zijn servers niet succesvol op prohibitlogons gezet. Deze servers zijn:" + "`n`n"
foreach ($server in $Failedservers){$Mail = $Mail + $Server.Name + "`r`n"}
$Mail = $Mail  + "`n" +'Zet deze server(s) handmatig op ProhibitLogons' + "`n`n" + "Met vriendelijke groet," + "`n`n" + "Ochtend Controle Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Failed" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

 

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

App-v 5 and Citrix Provisioning

An administrator here came to me and asked me about how to Powershell around a problem with the App-V 5.0 Client Cache directory. The implementation configured is that App-V uses a folder on the D disk. This disk is dedicated. It is not on the distributed Vdisk. When the server reboots the cache becomes corrupted and the App-V service does not start. A sync is necessary in order to start the App-V 5.0 service. I made a script that clears a few reg values, deletes the App-v Client Connection Groups, App-V Client Packages, the AppV folder in Programdata and the entire folder on the D disk.

I handed over the script to the administrator and asked him to create a scheduled task that run at system startup. The reason for  running the script this way, is that creating a GPO that run a Powershell script will not statisfy the required security context. The SYSTEM account doesn’t have enought permissions to complete the script. PSRemoting will not work either. When running the script on another server with the Invoke-Command -Scriptblock {} fails. This is probably due to a bug in App-V. The account has enough rights and UAC is disabled. Yet all attempts fail. Please review the script before implementing

 

<#Script Variables#>
$ServiceName = "AppVClient"
$ProgramDataAppV = "C:\ProgramData\Microsoft\AppV"
$AppVData = "D:\App-V 5.0"

<#Regvalues#>
$Regvalues=@()
$Regvalues +=@{Regvalue="HKLM:\SOFTWARE\Microsoft\AppV\Client\PackageGroups"}
$Regvalues +=@{Regvalue="HKLM:\SOFTWARE\Microsoft\AppV\Client\Packages"}
$Regvalues +=@{Regvalue="HKLM:\SOFTWARE\Microsoft\AppV\Client\Streaming\Packages"}

#Service stoppen om Program Data te verwijderen.
$State = Get-Service $ServiceName
If ($State.status -NotMatch "Stopped"){Stop-Service $ServiceName
Start-Sleep -s 5}
Remove-Item $ProgramDataAppV -force -recurse

#Verwijderen van de Registersleutels
Foreach ($item in $Regvalues){If ((Test-Path $item.Regvalue) -ne $True){Remove-item $item.regvalue -force -Recurse}}

#Service Starten om initiele synch uit te voeren zodat de D schijf wordt vrijgegeven.
$State = Get-Service $ServiceName
If ($State.status -Match "Stopped"){Start-Service $ServiceName
Start-Sleep -s 5}
Get-AppvPublishingServer | Sync-AppvPublishingServer

#Service Stoppen om Packages en AppvClientConnectionGroup te verwijderen.
$State = Get-Service $ServiceName
If ($State.status -NotMatch "Stopped"){
Write-Host Stoppen
Stop-Service $ServiceName
Start-Sleep -s 5}
Get-AppvClientConnectionGroup -all | Remove-AppvClientConnectionGroup
Get-AppvClientPackage -all | Remove-AppvClientPackage
Start-Sleep -s 10

#Service stoppen om App-v folder op de D schijf te verwijderen.
If ($State.status -NotMatch "Stopped"){Stop-Service $ServiceName
Start-Sleep -s 5}
Remove-Item $AppVData -Force -Recurse
Start-Sleep -s 10

#Check of de folder ook daadwerkelijk verwijderd is.
If ((Test-Path $AppVData) -eq $True){

#Proberen de D schijf leeg te krijgen door eerst te synchen
Get-AppvPublishingServer | Sync-AppvPublishingServer
Start-Sleep -s 5
$State = Get-Service $ServiceName
If ($State.status -NotMatch "Stopped"){Stop-Service $ServiceName
Start-Sleep -s 5}
Get-AppvClientConnectionGroup -all | Remove-AppvClientConnectionGroup
Get-AppvClientPackage -all | Remove-AppvClientPackage
Start-Sleep -s 10
Remove-Item $AppVData -Force -Recurse

#Laatste check op de D schijf om te kijken of de folder nu daadwerkelijk weg is.
If ((Test-Path $AppVData) -eq $True){
$Outinfo = "De App-V Data op de D Schijf is na twee pogingen niet succesvol verwijderd. Bekijk het logboek."
Add-Content -Value $Outinfo -Path "C:\ScriptFout.txt"
$Outinfo = Get-Date
Add-Content -Value $Outinfo -Path "C:\ScriptFout.txt"
}}

#Laatste Sync uitvoeren
Get-AppvPublishingServer | Sync-AppvPublishingServer