Pages

Showing posts with label View. Show all posts
Showing posts with label View. Show all posts

Friday, April 26, 2019

Attempting to rebalance VMware Horizon View linked-clones desktop pool datastore fails with: "Refit operation rebalance failed"

Problem

You attempt to migrate a linked-clone pool’s storage from one datastore to another by using the rebalance feature as outlined in the following KB:

Migrating linked clone pools to a different or new datastore (1028754)
https://kb.vmware.com/s/article/1028754

… but notice that the operation attempts the migration on a few desktops but fails with the error: Refit operation rebalance failed in the Events log of the linked-clones desktop pool:

Clicking on the ellipsis icon for the error for more information does not reveal additional information:

Navigating outside of the Events log of the linked-clones desktop pool then into the Inventory view and clicking on the ellipsis icon for the desktop in error displays the following message:

Apr 5, 2019 8:34:03 PM ADT: View Composer Invalid Parent VM Fault: View Composer Invalid Parent VM Fault: Selected snapshot on parent VM is not found on VC server

Pairing state:

Configured by:

Attempted theft by:

Solution

This error is usually caused by situations where the master image for the linked-clones pool no longer have the snapshots that the desktops being moved are associated with.  The rebalance operation requires access to the snapshots in order to re-clone the replicas (snapshots) to the new datastore so if they no longer exist then this error would be thrown.  In the case of the environment, the pool of 200 desktops had a mix of 5 different snapshots where only 1 of them was still available on the master image.  To view the linked-clones desktops and the associated snapshot that are being used, navigate to Inventory > Machines (View Composer Details) and review the Image tab:

If the snapshots have already been deleted then the way to remediate the error is to recompose all the snapshots with an existing snapshot on the master image or create a new one and recompose with it.  The rebalance operation should complete successfully once all the linked-clone pools are using a snapshot that exists.

Monday, February 11, 2019

Using InstallSoftwareRemotely.ps1 to upgrade VMware Horizon View Agent

I’ve had a lot of colleagues in the past ask me what I would use for smaller clients who did not have a software deployment tool such as SCCM to install or upgrade applications and the solution I typically present is the InstallSoftwareRemotely.ps1 PowerScript found here:

Install software on multiple computers remotely with PowerShell
https://gallery.technet.microsoft.com/scriptcenter/Install-software-on-9278d883

I’ve always used this script to perform small deployments and this blog post serves as a demonstration of how to use it.

Prerequisites for InstallSoftwareRemotely.ps1

The items that you’ll need to prepare as prerequisites before executing the command are as follows:

  1. An administrator account on the target computers that you’ll be installing applications on (a domain admin would suffice)
  2. Download Psexec.exe from https://docs.microsoft.com/en-us/sysinternals/downloads/psexec and place the executable C:\Windows\System32 so the PS script could execute the command to enable PSRemoting
  3. Place the software installation file, in this case the VMware Horizon View agent, into a network share accessible by the target computer and the account you will be running the PS script and ensure that it is not blocked (see one of my previous blog posts for more information about why this is important http://terenceluk.blogspot.com/2018/05/attempting-to-use-invoke-command.html)
  4. Arguments (if reqiured) for the installation executable
  5. A CSV file containing the computers that the software will be installed on (see the TechNet article’s description of the ComputerList for more information about the accepted sources)
  6. Amount numeric value for the number of retries for the installation
  7. The application name to check and see if it exists before installing the application (more information provided below)
  8. The application version to check and see if it exists before installing the application (more information provided below)
  9. Determine whether the target computer is 32-bit or 64-bit

There are several other switches available for the InstallSoftwareRemotely.ps1 but the ones mentioned above or the ones I typically use.  Please refer to the TechNet article for more information about the other ones available.

-----------------------------------------------------------------------------------------------------------------------------

Determining the application name and application version to check before installing

One of the things you can do with the InstallSoftwareRemotely.ps1 PS script is to have it check to see if the software you’re attempting to install already exists on the target computer.  The information required can be found via the following registry path:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{IdentifyingNumber}

DisplayName

DisplayVersion

Here is an example of where the VMware Horizon View Agent 6.2.1.3284564 can be found:

Note that you can also use PowerShell to required information as described here:

Use PowerShell to Find and Uninstall Software
https://blogs.technet.microsoft.com/heyscriptingguy/2011/12/14/use-powershell-to-find-and-uninstall-software/

----------------------------------------------------------------------------------------------------------------------------

Using a CSV for the target computers

Below is a screenshot of the format for a CSV file if you choose to use it as a list of the target computers:

Modification to the PS Script (for VMware Horizon View Agent only)

The slight modification I made to the PS script includes a restart command that executes when the installation of the application is completed.  The reason why chose to suppress the reboot with the switch that the VMware Horizon View Agent accepts is because allow the agent installer to restart the target computer would interrupt the InstallSoftwareRemotely.ps1 script before it completes thus causing errors to be thrown.  This should be applied to any applications that automatically restart upon completing its install.

Simply insert the Restart-Computer -ComputerName $Computer as shown in the screenshot below to achieve this:

The PowerShell script and required parameters

The following is the final PS script and its required parameters for the VMware Horizon View Agent install:

.\InstallSoftwareRemotely.ps1 -AppPath '\\fp09\tech\Software\VMware\VMware Horizon View\VMware Horizon View 6.2.1\Agent\VMware-viewagent-x86_64-6.2.1-3284564.exe' -AppArgs '/S /V"/qn REBOOT=Reallysuppress' -CSV 'C:\Scripts\VDIs.csv' -Retries 2 -AppName 'VMware Horizon View Agent' -AppVersion '6.2.1.3284564' -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"' -EnablePSRemoting -Credential

Note that the:

WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"'

… is to determine whether the target computer is a 32-bit or 64-bit Windows OS.  This is necessary because the agent we’re installing is for a 64-bit OS.

The resulting output of the script should look similar to the screenshot below:

Note that I am unable to determine what causes the error code 1641 during the VMware Horizon View Agent install even though it is a successful as this does not happen to other applications I’ve used this PS script with so be aware of the following:

  • 1641 is success
  • 1603 is failure

-----------------------------------------------------------------------------------------------------------------------------

InstallSoftwareRemotely.ps1 Script with modification

Below is a copy and paste of the actual script I used for this demonstration.  Be advised that the script would likely receive updates and changes in the future.

<#

.SYNOPSIS

Install software remotely in a group of computers and retry the installation in case of error.

.DESCRIPTION

This script install software remotely in a group of computers and retry the installation in case of error.

It uses PowerShell to perform the installation. Target computer must allow Windows PowerShell Remoting.

Script can try to enable Windows PowerShell Remoting using Microsoft Sysinternals Psexec with the paramenter -EnablePSRemoting.

If PSExec is not found on computer, script asks to the user for download it and extract in system folder.

.PARAMETER AppPath

Path to the application executable, It can be a network or local path because entire folder will be copied to remote computer before installing and deleted after installation.

Example: 'C:\Software\TeamViewer\TeamvieverHost.msi' (Folder TeamViewer will be copied to remote computer before run ejecutable)

.PARAMETER AppArgs

Application arguments to perform silent installation.

Example: '/S /R settings.reg'

.PARAMETER LocalPath

Local path of the remote computer where copy application directory.

Default: 'C:\temp'

.PARAMETER Retries

Number of times to retry failed installations.

Default: 5

.PARAMETER TimeBetweenRetries

Seconds to wait before retrying failed installations.

Default: 60

.PARAMETER ComputerList

List of computers in install software. You can only use one source of target computers: ComputerList, OU or CSV.

Example: Computer001,Computer002,Computer003 (Without quotation marks)

.PARAMETER OU

OU containing computers in which install software.

RSAT for AD module for PowerShell must be installed in order to query AD.

If you run script from a Domain Controller, AD module for PowerShell is already enabled.

You can only use one source of target computers: ComputerList, OU or CSV.

Example: 'OU=Test,OU=Computers,DC=CONTOSO,DC=COM'

.PARAMETER CSV

CSV file containing computers in which install software. You can only use one source of target computers: ComputerList, OU or CSV.

Example: 'C:\Scripts\Computers.csv'

CSV Format:

Name

Computer001

Computer002

Computer003

.PARAMETER LogPath

Path where save log file.

Default: My Documents

.PARAMETER Credential

Script will ask for an account to perform remote installation.

.PARAMETER EnablePSRemoting

Try to enable PSRemoting on failed computers using Psexec. Psexec has to be on system path.

If PSExec is not found. Script ask to download automatically PSTools and copy them to C:\Windows\System32.

.PARAMETER AppName

App name as shown in registry to check if app is installed on remote computer and not reinstall it.

You can check app name on a computer with it installed looking at:

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\'

Example: 'TightVNC'

Default: None

.PARAMETER AppVersion

App name as shown in registry to check if app is installed on remote computer and not reinstall it.

If not specified and AppName has a value, version will be ignored.

You can check app version on a computer with it installed looking at:

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\'

Example: '2.0.8.1'

Default: all

.PARAMETER WMIQuery

WMI Query to execute in remote computers. Software will be installed if query returns values.

Example: 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"' (64 bit computers)

Example: 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="32"' (32 bit computers)

Default: None

.EXAMPLE

TightVNC -> .\InstallSoftwareRemotely.ps1 -AppPath 'C:\Scripts\TightVNC\tightvnc-2.8.8-gpl-setup-64bit.msi' -AppArgs '/quiet /norestart ADDLOCAL="Server" SERVER_REGISTER_AS_SERVICE=1 SERVER_ADD_FIREWALL_EXCEPTION=1 SERVER_ALLOW_SAS=1 SET_USEVNCAUTHENTICATION=1 VALUE_OF_USEVNCAUTHENTICATION=1 SET_PASSWORD=1 VALUE_OF_PASSWORD=Password.01 SET_USECONTROLAUTHENTICATION=1 VALUE_OF_USECONTROLAUTHENTICATION=1 SET_CONTROLPASSWORD=1 VALUE_OF_CONTROLPASSWORD=3digits.01' -OU 'OU=Central,OU=Computers,DC=Contoso,DC=local' -Retries 2 -AppName 'TightVNC' -AppVersion '2.8.8.0' -EnablePSRemoting -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"'

.EXAMPLE

TightVNC Mirage Driver -> .\InstallSoftwareRemotely.ps1 -AppPath 'C:\Scripts\TightVNC\dfmirage-setup-2.0.301.exe' -AppArgs '/verysilent /norestart' -OU 'OU=Central,OU=Computers,OU=MyBusiness,DC=Contoso,DC=local' -Retries 2 -AppName 'DemoForge Mirage Driver for TightVNC 2.0' -AppVersion '2.0' -EnablePSRemoting -WMIQuery 'select * from Win32_Processor where DeviceID="CPU0" and AddressWidth="64"'

.EXAMPLE

InstallSoftwareRemotely.ps1 -AppPath "C:\Temp\Software\Miranda\miranda-im-v0.10.75-unicode.exe" -AppArgs "/S" -ComputerList Computer001,Computer002,Computer003 -AppName "Miranda IM 0.10.75" -AppVersion "0.10.75"

.EXAMPLE

InstallSoftwareRemotely.ps1 -AppPath "C:\Temp\Software\Miranda\miranda-im-v0.10.75-unicode.exe" -AppArgs "/S" -CSV "C:\Computers.csv" -Credential -EnablePSRemoting

.EXAMPLE

InstallSoftwareRemotely.ps1 -AppPath "\\Server01\Software\Miranda\miranda-im-v0.10.75-unicode.exe" -AppArgs "/S" -OU "OU=Test,OU=Computers,DC=CONTOSO,DC=COM"

.NOTES

Author: Juan Granados

Date: November 2017

#>

Param(

[Parameter(Mandatory=$true,Position=0)]

[ValidateNotNullOrEmpty()]

[string]$AppPath,

[Parameter(Mandatory=$false,Position=1)]

[ValidateNotNullOrEmpty()]

[string]$AppArgs="None",

[Parameter(Mandatory=$false,Position=2)]

[ValidateNotNullOrEmpty()]

[string]$LocalPath="C:\temp",

[Parameter(Mandatory=$false,Position=3)]

[ValidateNotNullOrEmpty()]

[int]$Retries=5,

[Parameter(Mandatory=$false,Position=4)]

[ValidateNotNullOrEmpty()]

[int]$TimeBetweenRetries=60,

[Parameter(Mandatory=$false,Position=5)]

[ValidateNotNullOrEmpty()]

[string[]]$ComputerList,

[Parameter(Mandatory=$false,Position=6)]

[ValidateNotNullOrEmpty()]

[string]$OU,

[Parameter(Mandatory=$false,Position=7)]

[ValidateNotNullOrEmpty()]

[string]$CSV,

[Parameter(Mandatory=$false,Position=7)]

[ValidateNotNullOrEmpty()]

[string]$LogPath=[Environment]::GetFolderPath("MyDocuments"),

[Parameter(Position=9)]

[switch]$EnablePSRemoting,

[Parameter(Position=10)]

[switch]$Credential,

[Parameter(Mandatory=$false,Position=11)]

[ValidateNotNullOrEmpty()]

[string]$AppName="None",

[Parameter(Mandatory=$false,Position=12)]

[ValidateNotNullOrEmpty()]

[string]$AppVersion="all",

[Parameter(Mandatory=$false,Position=13)]

[ValidateNotNullOrEmpty()]

[string]$WMIQuery="None"

)

#Functions

Add-Type -AssemblyName System.IO.Compression.FileSystem

function Unzip

{

param([string]$zipfile, [string]$outpath)

[System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)

}

Function Copy-WithProgress

{

Param([string]$Source,[string]$Destination)

$Source=$Source.tolower()

$Filelist=Get-Childitem $Source –Recurse

$Total=$Filelist.count

$Position=0

If(!(Test-Path $Destination)){

New-Item $Destination -Type Directory | Out-Null

}

foreach ($File in $Filelist){

$Filename=$File.Fullname.tolower().replace($Source,'')

$DestinationFile=($Destination+$Filename)

try{

Copy-Item $File.FullName -Destination $DestinationFile -Force

}catch{throw $_.Exception}

$Position++

Write-Progress -Activity "Copying data from $source to $Destination" -Status "Copying File $Filename" -PercentComplete (($Position/$Total)*100)

}

}

Function Set-Message([string]$Text,[string]$ForegroundColor="White",[int]$Append=$True){

if ($Append){

$Text | Out-File $LogPath -Append

}

else {

$Text | Out-File $LogPath

}

Write-Host $Text -ForegroundColor $ForegroundColor

}

function Get-InstalledApps

{

if ([IntPtr]::Size -eq 4) {

$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

}

else {

$regpath = @(

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'

)

}

Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} |

Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |

Sort DisplayName

#$result = Get-InstalledApps | where {$_.DisplayName -like $appToMatch}

}

Function CheckSoftwareInstalled([string]$Computer){

If ($Cred){

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$AppName = $args[0]

$AppVersion = $args[1]

if ([IntPtr]::Size -eq 4) {

$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

}

else {

$regpath = @(

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'

)

}

$InstalledApps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} |

Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |

Sort DisplayName

If ($AppVersion -ne "all"){

Return $InstalledApps | where {$_.DisplayName -eq $AppName -and $_.DisplayVersion -eq $AppVersion}

}

Else{

Return $InstalledApps | where {$_.DisplayName -eq $AppName}

}

} -ArgumentList $AppName, $AppVersion -Credential $Cred

}catch{throw $_.Exception}

}

else{

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$AppName = $args[0]

$AppVersion = $args[1]

if ([IntPtr]::Size -eq 4) {

$regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

}

else {

$regpath = @(

'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'

)

}

$InstalledApps = Get-ItemProperty $regpath | .{process{if($_.DisplayName -and $_.UninstallString) { $_ } }} |

Select DisplayName, Publisher, InstallDate, DisplayVersion, UninstallString |

Sort DisplayName

If ($AppVersion -ne "all"){

Return $InstalledApps | where {$_.DisplayName -eq $AppName -and $_.DisplayVersion -eq $AppVersion}

}

Else{

Return $InstalledApps | where {$_.DisplayName -eq $AppName}

}

} -ArgumentList $AppName, $AppVersion

}catch{throw $_.Exception}

}

}

Function CheckWMIQuery([string]$Computer){

If ($Cred){

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$WMIQuery = $args[0]

Write-Host "Executing $($WMIQuery)"

Return gwmi -Query $WMIQuery

} -ArgumentList $WMIQuery -Credential $Cred

}catch{throw $_.Exception}

}

else{

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$WMIQuery = $args[0]

Write-Host "Executing $($WMIQuery)"

Return gwmi -Query $WMIQuery

} -ArgumentList $WMIQuery

}catch{throw $_.Exception}

}

}

Function InstallRemoteSoftware([string]$Computer){

If ($Cred){

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$Application = $args[0]

$AppArgs = $args[1]

$ApplicationName = $Application.Substring($Application.LastIndexOf('\')+1)

$ApplicationFolderPath = $Application.Substring(0,$Application.LastIndexOf('\'))

$ApplicationExt = $Application.Substring($Application.LastIndexOf('.')+1)

Write-Host "Installing $($ApplicationName) on $($env:COMPUTERNAME)"

If($ApplicationExt -eq "msi"){

If ($AppArgs -ne "None"){

Write-Host "Installing as MSI: msiexec /i $($Application) $($AppArgs)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) $($AppArgs)" -Wait -Passthru

}

else{

Write-Host "Installing as MSI: msiexec /i $($Application)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) /quiet /norestart" -Wait -Passthru

}

}

ElseIf ($AppArgs -ne "None"){

Write-Host "Executing $Application $AppArgs"

$p = Start-Process $Application -ArgumentList $AppArgs -Wait -Passthru

}

Else{

Write-Host "Executing $Application"

$p = Start-Process $Application -Wait -Passthru

}

$p.WaitForExit()

if ($p.ExitCode -ne 0) {

Write-Host "Failed installing with error code $($p.ExitCode)" -ForegroundColor Red

$Return = $($env:COMPUTERNAME)

}

else{

$Return = 0

}

Write-Host "Deleting $($ApplicationFolderPath)"

Remove-Item $($ApplicationFolderPath) -Force -Recurse

Return $Return

} -ArgumentList "$($LocalPath)\$($ApplicationFolderName)\$($ApplicationName)", $AppArgs -Credential $Cred

}catch{throw $_.Exception}

}

else{

try{

Return Invoke-Command -computername $Computer -ScriptBlock {

$Application = $args[0]

$AppArgs = $args[1]

$ApplicationName = $Application.Substring($Application.LastIndexOf('\')+1)

$ApplicationFolderPath = $Application.Substring(0,$Application.LastIndexOf('\'))

$ApplicationExt = $Application.Substring($Application.LastIndexOf('.')+1)

Write-Host "Installing $($ApplicationName) on $($env:COMPUTERNAME)"

If($ApplicationExt -eq "msi"){

If ($AppArgs -ne "None"){

Write-Host "Installing as MSI: msiexec /i $($Application) $($AppArgs)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) $($AppArgs)" -Wait -Passthru

}

else{

Write-Host "Installing as MSI: msiexec /i $($Application)"

$p = Start-Process "msiexec" -ArgumentList "/i $($Application) /quiet /norestart" -Wait -Passthru

}

}

ElseIf ($AppArgs -ne "None"){

Write-Host "Executing $Application $AppArgs"

$p = Start-Process $Application -ArgumentList $AppArgs -Wait -Passthru

}

Else{

Write-Host "Executing $Application"

$p = Start-Process $Application -Wait -Passthru

}

$p.WaitForExit()

if ($p.ExitCode -ne 0) {

Write-Host "Failed installing with error code $($p.ExitCode)" -ForegroundColor Red

$Return = $($env:COMPUTERNAME)

}

else{

$Return = 0

}

Write-Host "Deleting $($ApplicationFolderPath)"

Remove-Item $($ApplicationFolderPath) -Force -Recurse

Return $Return

} -ArgumentList "$($LocalPath)\$($ApplicationFolderName)\$($ApplicationName)", $AppArgs

}catch{throw $_.Exception}

}

}

Function CheckPSRemoting([string]$Computer){

If ($EnablePSRemoting){

Set-Message "Enabling PSRemoting on computer: psexec.exe /accepteula -h -d \\$($Computer) -s powershell Enable-PSRemoting"

try{

psexec.exe /accepteula -h -d "\\$($Computer)" -s powershell Enable-PSRemoting -Force 2>&1 | Out-Null

}catch{

Set-Message "PSExec running on background. Continue with next computer."

}

}

Else{

Set-Message "You can try to enable PowerShell Remoting on computer using parameter -EnablePSRemoting" -ForegroundColor DarkYellow

}

}

$ErrorActionPreference = "Stop"

#Initialice log

$LogPath += "\InstallSoftwareRemotely_" + $(get-date -Format "yyyy-mm-dd_hh-mm-ss") + ".txt"

Set-Message "Start remote installation on $(get-date -Format "yyyy-mm-dd hh:mm:ss")" -Append $False

#Initial validations.

If (!(Test-Path $AppPath)){

Set-Message "Error accessing $($AppPath). The script can not continue"

Exit 1

}

If ($EnablePSRemoting){

if (!(Get-Command "psexec.exe" -ErrorAction SilentlyContinue)){

Set-Message "Error. Microsoft Psexec not found on system. Download it from https://download.sysinternals.com/files/PSTools.zip and extract all in C:\Windows\System32" -ForegroundColor Yellow

$Answer=Read-Host "Do you want to download and install PSTools (y/n)?"

if (($Answer -eq "y") -or ($Answer -eq "Y")){

Set-Message "Downloading PSTools"

If (Test-Path "$($env:temp)\PSTools.zip"){

Remove-Item "$($env:temp)\PSTools.zip" -Force

}

(New-Object System.Net.WebClient).DownloadFile("https://download.sysinternals.com/files/PSTools.zip", "$($env:temp)\PSTools.zip")

if (Test-Path "$($env:temp)\PSTools.zip"){

Set-Message "Unzipping PSTools"

If (Test-Path "$($env:temp)\PSTools"){

Remove-Item "$($env:temp)\PSTools" -Force -Recurse

}

Unzip "$($env:temp)\PSTools.zip" "$($env:temp)\PSTools"

Copy-Item "$($env:temp)\PSTools\*.exe" "$($env:SystemRoot)\System32" -Force

if (Test-Path "$($env:SystemRoot)\System32\psexec.exe"){

Set-Message "PSTools installed" -ForegroundColor Green

}

else{

Set-Message "Error unzipping PSTools" -ForegroundColor Red

Remove-Item "$($env:temp)\PSTools.zip" -Force

Exit 1

}

}

else{

Set-Message "Error downloading PSTools" -ForegroundColor Red

Exit 1

}

}

else{

Exit 1

}

}

}

If ($OU){

if (!(Get-Command "Get-ADComputer" -ErrorAction SilentlyContinue)){

Set-Message "Error. Get-ADComputer not found on system. You have to install the PowerShell Active Directory module order to query Active Directory. https://4sysops.com/archives/how-to-install-the-powershell-active-directory-module/" -ForegroundColor Red

Exit 1

}

try{

$ComputerList = Get-ADComputer -Filter * -SearchBase "$OU" | Select-Object -Expand name

}catch{

Set-Message "Error querying AD: $($_.Exception.Message)" -ForegroundColor Red

Exit 1

}

}

ElseIf ($CSV){

try{

$ComputerList = Get-Content $CSV | where {$_ -notmatch 'Name'} | Foreach-Object {$_ -replace '"', ''}

}catch{

Set-Message "Error getting CSV content: $($_.Exception.Message)" -ForegroundColor Red

Exit 1

}

}

ElseIf(!$ComputerList){

Set-Message "You have to set a list of computers, OU or CSV." -ForegroundColor Red

Exit 1

}

If ($Credential){

$Cred = Get-Credential

}

If(!$Cred -or !$Credential){

Set-Message "No credential specified. Using logon account"

}

Else{

Set-Message "Using user $($Cred.UserName)"

}

$ApplicationName = $AppPath.Substring($AppPath.LastIndexOf('\')+1)

$ApplicationFolderPath = $AppPath.Substring(0,$AppPath.LastIndexOf('\'))

$ApplicationFolderName = $ApplicationFolderPath.Substring($ApplicationFolderPath.LastIndexOf('\')+1)

$ComputerWithError = [System.Collections.ArrayList]@()

$ComputerWithSuccess = [System.Collections.ArrayList]@()

$ComputerSkipped = [System.Collections.ArrayList]@()

$TotalRetries = $Retries

$TotalComputers = $ComputerList.Count

Do{

Set-Message "-----------------------------------------------------------------"

Set-Message "Attempt $(($TotalRetries - $Retries) +1) of $($TotalRetries)" -ForegroundColor Cyan

Set-Message "-----------------------------------------------------------------"

$Count = 1

ForEach ($Computer in $ComputerList){

Set-Message "COMPUTER $($Computer.ToUpper()) ($($Count) of $($ComputerList.Count))" -ForegroundColor Yellow

$Count++

If($AppName -ne "None"){

Set-Message "Checking if $($AppName) version $($AppVersion) is installed on remote computer."

try{

If(CheckSoftwareInstalled $Computer){

Set-Message "Software found on computer. Skipping installation." -ForegroundColor Green

$ComputerSkipped.Add($Computer) | Out-Null

Continue

}

Else{

Set-Message "Software not found on remote computer."

}

}catch{

Set-Message "Error connecting: $($_.Exception.Message)" -ForegroundColor Red

CheckPSRemoting $Computer

$ComputerWithError.Add($Computer) | Out-Null

Continue

}

}

If($WMIQuery -ne "None"){

Set-Message "Checking WMI Query on remote computer."

try{

If(!(CheckWMIQuery $Computer)){

Set-Message "WMI Query result is false. Skipping installation."

$ComputerSkipped.Add($Computer) | Out-Null

Continue

}

Else{

Set-Message "WMI Query result is true. Continue installation."

}

}catch{

Set-Message "Error connecting: $($_.Exception.Message)" -ForegroundColor Red

CheckPSRemoting $Computer

$ComputerWithError.Add($Computer) | Out-Null

Continue

}

}

Set-Message "Coping $($ApplicationFolderPath) to \\$($Computer)\$($LocalPath -replace ':','$')"

try{

Copy-WithProgress "$ApplicationFolderPath" "\\$($Computer)\$("$($LocalPath)\$($ApplicationFolderName)" -replace ':','$')"

}catch{

Set-Message "Error copying folder: $($_.Exception.Message)" -ForegroundColor Red

$ComputerWithError.Add($Computer) | Out-Null

Continue;

}

try{

$ExitCode = InstallRemoteSoftware $Computer

If ($ExitCode){

$ComputerWithError.Add($Computer) | Out-Null

Set-Message "Error installing $($ApplicationName)." -ForegroundColor Red

}

else{

Set-Message "$($ApplicationName) installed successfully." -ForegroundColor Green

$ComputerWithSuccess.Add($Computer) | Out-Null

}

}catch{

Set-Message "Error on remote execution: $($_.Exception.Message)" -ForegroundColor Red

$ComputerWithError.Add($Computer) | Out-Null

try{

Set-Message "Deleting \\$($Computer)\$($LocalPath -replace ':','$')\$($ApplicationFolderName)"

}catch{

Set-Message "Error on remote deletion: $($_.Exception.Message)" -ForegroundColor Red

}

Remove-Item "\\$($Computer)\$($LocalPath -replace ':','$')\$($ApplicationFolderName)" -Force -Recurse

CheckPSRemoting $Computer

}

}

If ($ComputerWithError.Count -eq 0){

break

}

$Retries--

If ($Retries -gt 0){

$ComputerList=$ComputerWithError

$ComputerWithError = [System.Collections.ArrayList]@()

If ($TimeBetweenRetries -gt 0){

Set-Message "Waiting $($TimeBetweenRetries) seconds before next retry..."

Sleep $TimeBetweenRetries

}

}

}While ($Retries -gt 0)

If($ComputerWithError.Count -gt 0){

Set-Message "-----------------------------------------------------------------"

Set-Message "Error installing $($ApplicationName) on $($ComputerWithError.Count) of $($TotalComputers) computers:"

Set-Message $ComputerWithError

$csvContents = @()

ForEach($Computer in $ComputerWithError){

$row = New-Object System.Object

$row | Add-Member -MemberType NoteProperty -Name "Name" -Value $Computer

$csvContents += $row

}

$CSV=(get-date).ToString('yyyyMMdd-HH_mm_ss') + "ComputerWithError.csv"

$csvContents | Export-CSV -notype -Path "$([Environment]::GetFolderPath("MyDocuments"))\$($CSV)" -Encoding UTF8

Set-Message "Computers with error exported to CSV file: $([Environment]::GetFolderPath("MyDocuments"))\$($CSV)" -ForegroundColor DarkYellow

Set-Message "You can retry failed installation on this computers using parameter -CSV $([Environment]::GetFolderPath("MyDocuments"))\$($CSV)" -ForegroundColor DarkYellow

}

If ($ComputerWithSuccess.Count -gt 0){

Set-Message "-----------------------------------------------------------------"

Set-Message "$([math]::Round((($ComputerWithSuccess.Count * 100) / $TotalComputers), [System.MidpointRounding]::AwayFromZero) )% Success installing $($ApplicationName) on $($ComputerWithSuccess.Count) of $($TotalComputers) computers:"

Set-Message $ComputerWithSuccess

}

Else{

Set-Message "-----------------------------------------------------------------"

Set-Message "Installation of $($ApplicationName) failed on all computers" -ForegroundColor Red

}

If ($ComputerSkipped.Count -gt 0){

Set-Message "-----------------------------------------------------------------"

Set-Message "$($ComputerSkipped.Count) skipped of $($TotalComputers) computers:"

Set-Message $ComputerSkipped

}

Friday, November 2, 2018

Attempting to authenticate with SecurEnvoy passcode for VMware Horizon View fails with: “Access Denied” and “Incorrect Soft Token Code Received From Client”

Problem

You’ve completed configuring VMware Horizon View with SecurEnvoy but when authentication fails with Access Denied:

image

Reviewing the SecurEnvoy logs reveal the following error:

Incorrect Soft Token Code Received From ClientIP=10.34.30.58 RemoteID=

image

Solution

One of the possible reasons why authentication would not work and this message is logged in the Log Viewer is if the Shared Secret configured on the VMware Horizon View Connection Server does not match the one configured in the corresponding Radius server in SecurEnvoy:

imageimage

The following message should be logged once the authentication succeeds:

Access Accepted with Soft Token From ClientIP=10.34.30.58 RemoteID=

image

Monday, July 2, 2018

Poor multi-monitor performance using Dell Wyse 7020 Windows 10 IoT with VMware Horizon View 6.x to 7.x

This is a follow up to a previous post I wrote about poor VMware Horizon View video, keyboard and mouse performance when adding a 3rd monitor to the Dell Wyse 7020 Windows 10 IoT thin client:

Adding a 3rd monitor to a Dell Wyse Z90QQ10 thin client connecting to a VMware Horizon View 6.2 virtual desktop causes slow video performance with mouse movement and typing delays

http://terenceluk.blogspot.com/2018/04/adding-3rd-monitor-to-dell-wyse-z90qq10.html

The poor performance issue began affecting our Microsoft Windows 2016 RDS services, which escalated the priority so I was finally able to take 2 days to troubleshoot the problem. I had also opened up a ticket with VMware and Dell prior to beginning troubleshooting the issue myself and the VMware EUC engineering said this was likely a Dell Wyse issue since it does not happen on a full thick PC while Dell never got back to me at all after a ticket was opened. I’ve never been impressed with Dell support for the Wyse devices but figured I’d give them a try but they have yet to call me days after the ticket was opened and I don’t have much confidence that I would receive a call back soon.

Before I begin, the following are the environment details:

Hardware

Thin Client: Dell Wyse Z90QQ10 Thin Client

image

Operation System: Windows 10 Enterprise 2015 LTSB

Monitors: 3 x Dell P2414Hb (1920 x 1080) and 3 x Dell U2417H (1920 x 1080)

View Horizon Client: VMware Horizon View Client 4.8.0 build-8547331

Troubleshooting

The first item I looked at the start of the troubleshooting was the PCoIP Client Session Variables configuration provided by VMware to apply to the Horizon View Client and in particular the Configure PCoIP client image cache size policy and Configure the PCoIP session bandwidth floor settings:

image

Both of these settings did not help with the problem so I moved on to upgrading the VDI’s virtual machine version (no improvement), increasing resources (no improvement) then reviewing the drivers available for the two video cards in the thin client but noticed that there was an 2 month newer update for the AMD Radeon HD 8330E but no update for the AMD Radeon E6460:

image

Realizing I’m not going to have much luck with the drivers or the Horizon View Client application tweaking, I started reviewing the display output connections of the thin client:

image

After trying different combinations to connect the 3 monitors, I concluded that the degrade in performance happens when both of the video cards are used. Independently using the video cards to display 2 monitors at a time was fine but using 1 output connector from each video card for 2 monitors degraded performance immediately. Having ran out of ideas, I decided to review the specifications to see if I had missed something:

https://www.dell.com/en-us/work/shop/wyse-endpoints-and-software/wyse-7000-series-thin-clients-high-performance-virtual-desktop/spd/wyse-z-class

What I immediately noticed was that the thin client actually supported 6 displays as indicated under the Display specifications:

Wyse 7020
DisplayPort: 2560 x 1600 @32bpp
Dual DisplayPort: 2560 x 1600 @32bpp
DVI-I: 1920 x 1200 @32bpp
Dual Display: 1920 x 1200 @32bpp
Four Displays (DVI & DisplayPort): (1) 1920x1200@32bpp, (3) 2560x1600 @32bpp
Four Displays (DisplayPort daisy-chain): 3840x2160@32bpp
Six Displays (DisplayPort daisy-chain): 2560x1600@32bpp

image

I have to admit that I don’t really follow the advancements of monitor outputs so I was unfamiliar with DisplayPort daisy-chain but taking it literally made me realize that I could potentially connect more than 2 monitors to the same video card. The Dell P2414Hb (1920 x 1080)

monitors we had in the office did not support daisy chaining so we ended up purchasing 3 x Dell U2417H (1920 x 1080) to test this capability and I immediately noticed that daisy chaining 3 monitors to the AMD Radeon HD 8330E video card provided optimal performance in Horizon View. I then went and tried to the do the same with the AMD Radeon E6460 and received an error indicating it only supported 2 monitors via a single daisy chained display port.

It was a bit absurd to find that this was the problem and I could not find any information about this anywhere on the internet so I hope this post would help anyone who may encounter the same problem. Below is a photo of the Dell Wyse 7020 back panel with the video cards labeled and a table of the test results:

image

AMD Radeon E6460

Port

Connection

Monitors

Resolution

Performance Results

Display Port

Daisy-Chain DP

2

1920 x 1080

Poor with mouse delays

Display Port

Daisy-Chain DP

3

1920 x 1080

Unsupported as only a maximum of 2 monitors is supported

DVI and Display Port

Direct connection

2 (1 on DP and 1 on DVI)

1920 x 1080

Good as expected

AMD Radeon HD 8330E

Port

Connection

Monitors

Resolution

Performance Results

Display Port

Daisy-Chain DP

2

1920 x 1080

Good

Display Port

Daisy-Chain DP

3

1920 x 1080

Good

Display Port

Daisy-Chain DP

4

1920 x 1080

Good

Display Port

Daisy-Chain DP

4 (1 on DP and 3 on other DP)

1920 x 1080

Good

**Any combination of mixing the AMD Radeon E6460 and HD 8330E resulted in poor performance and would generated the following low on memory message for the VMware Remote MKS service:

Close programs to prevent information loss

Your computer is low on memory. Save your files and close these programs:

VMware Remote MKS

image

Wednesday, May 9, 2018

Recomposing a VMware Horizon View virtual desktop fails with the error: "No network communication between the View Agent and Connection Server. Please verify that the virtual desktop can ping the Connection Server via the FQDN"

Problem

You attempt to recompose a VMware Horizon View virtual desktop but notice that it fails with the following error:

No network communication between the View Agent and Connection Server. Please verify that the virtual desktop can ping the Connection Server via the FQDN

image

image

Solution

There could be various causes for this error but one of the common ones I’ve come across is when there is a problem with the virtual desktop’s network adapter.  For this recent environment I worked on, I noticed that the Summary tab of the virtual desktop did not have a value for the IP Addresses field:

image

Logging into the virtual desktop allowed me to immediately recognize that Windows no longer recognized the network adapter and therefore none were displayed in the Network Connections console:

image

Device Manager also displayed the Ethernet Controller in the Other Devices node indicating it was not installed:

image

To correct the issue, I simply right clicked on the Ethernet Controller item, selected Update Driver Software…:

image

Selected Search automatically for updated driver software:

image

Allowed Windows to search and install the vmxnet3 driver:

image

image

Verified the ethernet adapter is now listed under Network Adapters:

image

Restart desktop now that it has network connectivity, verified that vCenter began displaying the tasks required for completing the recompose and that the View Administrator console indicated that customizations are being applied:

image

Monday, May 7, 2018

Attempting to launch a VMware Horizon View virtual desktop after successfully authenticating fails with the error: “Your user account is disabled.”

This blog post serves as an update to the following I wrote back in April 2018:

"Your user account is disabled" error is thrown after upgrading VMware Horizon View to 6.2.0 or 6.2.1
http://terenceluk.blogspot.com/2016/04/your-user-account-is-disabled-error-is.html

I finally got the go-ahead to upgrade the environment and did not want to move from 6.0.1 to the latest 7.4.0 in one shot so I decided to upgrade to 6.2.0, then upgrade the agents to 6.2.1, upgrade servers to 7.4.0 and finally upgrade the agents to the latest version. Knowing that I was going to run into the error as described in the post above, I allocated extra time for troubleshooting because I wasn’t convinced that this was a bug due to the limited information available and the comments people left on the post. What I ended up discovering through troubleshooting was that this wasn’t a bug but rather an Active Directory domain issue so I hope to provide this update in case someone runs into the same issue.

Problem

You’ve completed an upgrade of VMware Horizon View and noticed all functions operate as expected aside from users trying to connect from external via the Security Server, who are from another domain in the same Active Directory forest. The error below is what they receive when they attempt to connect:

Your user account is disabled.

image

These users from another domain but in the same Active Directory forest have no issues connecting from the internal network.

Solution

Before I begin, let me provide information as to how the domains are configured.

Active Directory Forest Root Domain: contoso.com

Domain in the same forest but separate tree: fabrikam.com

The domain in which the Horizon View environment is deployed in is contoso.com but fabrikam.com users use this environment as well. Users from contoso.com can authenticate without any issues but fabrikam.com users cannot authenticate via the View Security server.

Seeing that users were able to connect from the internal network but not external, I logged onto the View Connection server that is paired with the View Security server handling external connections, reviewed the event logs and found the following error:

Log Name: System

Source: NETLOGON

Event ID: 5516

Level: Error

The computer or domain BMVV01 trusts domain fabrikam.com. (This may be an indirect trust.) However, BMVV01 and fabrikam.com have the same machine security identifier (SID). NT should be re-installed on either BMVV01 or fabrikam.com.

image

The above error log led me to believe that we may have a duplicate SID issue between the View Connection server paired with the View Security server and the fabrikam.com domain controllers (all domain controllers share the same SID) so I went ahead to PsGetsid64.exe from the SysInternals tools to compare the SIDs and confirmed that they were all identical:

image

There are several ways address this but the way I resolved the issue was deploy a new View Connection server with a unique SID, and then swap out the server with the duplicate SID.

My understanding as to why the desktop fails to launch for the user is because authentication takes place between the View Connection server and the domain controller but because both servers have the same SID, the process fails and View falsely believes the user’s account is disabled.

Hope this helps anyone who may encounter this problem.

Saturday, May 5, 2018

Launching a virtual desktop via VMware Horizon View 6.2.0 HTML Access fails with the error message: “ERR:p:bad method”

Problem

You’re able to successfully authenticate via the login page for HTML access:

image

… but notice that launching a virtual desktop will immediately fail with the error message:

ERR:p:bad method

image

Solution

This error stumped me for quite some time as I was unable to find any related forum posts or KB articles and what ended up being the issue was that the Google Chrome browser I was using could not launch the desktop:

Version: 66.0.3359.139 64-bit

image

Attempting to use an alternate browser such as Microsoft Edge successfully launched the virtual desktop:

image

I proceeded to try using the problematic Google Chrome browser to launch a VMware Horizon View 7.4.0 virtual desktop and was successful so this problem appears to be limited to 6.2.0.

Wednesday, May 2, 2018

Attempting to recompose a VMware Horizon View 6.x desktop fails with the error status: java.lang.InterruptedException

I recently had to investigate an issue with a few VMware Horizon View 6.x desktops that failed to recomposed and was stuck in an error state with the status java.lang.InterruptedException and would like to share my troubleshooting steps in this blog post.

Problem

An attempt was made to recompose a desktop by the process fails with the desktop in an Error Status:

image

Clicking on the icon beside the Error status brings up the following details:

java.lang.InterruptedException

Pairing state:

Configured by:

Attempted theft by:

image

Launching the console of the virtual desktop displays the following:

image

Solution

I’ve come across this issue twice in the past and the first option to try is manually power off the desktop and leave it for a few minutes to see if vCenter is able to proceed with the recompose.  If the recompose operation does continue and completes then one of the possible causes for this is if an administrator had inadvertently interrupted the recompose operation by manually powering the desktop on.

If shutting the virtual desktop down doesn’t work then navigate into the virtual desktop’s summary tab in the Horizon View Administrator console to see if the snapshot used for the Base Image still exists because I’ve noticed in the past that this can happen if the master image no longer has the snapshot and a recompose or refresh operation is executed for the desktop. From here, try to issue another recompose with a different snapshot that exists on the master image and if that doesn’t work then recreate the machine completely.  In situations where the machine has a profile disk for a user that needs to be preserved, proceed to detach the profile disk and recreate a desktop with the profile disk.

image

Monday, April 30, 2018

Adding a 3rd monitor to a Dell Wyse Z90QQ10 thin client connecting to a VMware Horizon View 6.2 virtual desktop causes slow video performance with mouse movement and typing delays

Update – July 2, 2018

Please see my following blog post for an update to this issue:

Poor multi-monitor performance using Dell Wyse 7020 Windows 10 IoT with VMware Horizon View 6.x to 7.x
http://terenceluk.blogspot.com/2018/07/poor-multi-monitor-performance-using.html

----------------------------------------------------------------------------------------------------------------------------------------------------

I recently ran into a strange issue with a VMware Horizon View 6.2 environment where adding a 3rd monitor caused the performance to severely degrade to a state where the virtual desktop was almost unusable.  The time constraints I had due to other various projects did not allow me to perform further troubleshooting so the solution I have isn’t what I would prefer but I’ll update this post when I have more time to thoroughly investigate the issue.

Hardware

Thin Client: Dell Wyse Z90QQ10 Thin Client

imageimage

Operation System: Windows 10 Enterprise 2015 LTSB

image

Monitors: 3 x Dell P2414Hb (1920 x 1080)

Horizon View Client: VMware Horizon View Client 4.7.0 build-7395453

image

Problem

The thin client and virtual desktop was running optimally until we attached the 3rd Dell P2414Hb monitor, which lead to noticeable keyboard and mouse lag.  The Windows 10 IOT thin client also started displaying this message in the background:

Close programs to prevent information loss

Your computer is low on memory. Save your files and close these programs:

VMware Remote MKS

image

The user wouldn’t notice this because they don’t typically minimize the virtual desktop but if they did and clicked the Close program button then the View Horizon Client would eventually close and terminate their session.

Solution

As mentioned earlier in the post, I haven’t had the time to continue troubleshooting but what allowed us to fix this performance issue while still providing 3 monitors was swap the 3rd Dell P2414Hb monitor with a lower resolution 1280 x 1024 one.  This is not ideal and isn’t a supported configuration for View 6 but isn’t explicitly mentioned as not being supported in View 7 as shown in the following VMware Horizon View documentation:

Monitors and Screen Resolution
https://docs.vmware.com/en/VMware-Horizon-7/7.5/horizon-architecture-planning/GUID-8A29C7A6-8A99-49E3-B596-4A61F969250A.html

Using Multiple Monitors (View 6)
https://docs.vmware.com/en/VMware-Horizon-6/6.2/com.vmware.horizon-view.planning.doc/GUID-FC4A3F61-7C8B-4F08-87AE-007E2A598169.html

I’ll update this post when I have the time to perform more testing but I hope this would help anyone who may face this same issue

Tuesday, May 9, 2017

Unable to change Domain field when logging into VMware Horizon View with factor authentication enabled

Problem

You’re attempting to log into VMware Horizon View where 2 factor authentication is enabled and you use your email address or UPN as the User name:

image

However, you notice that you are unable to change the Domain field as the drop down box is greyed out and locked:

image

Solution

I am unsure as to whether this problem is specific to the SecurEnvoy 2 factor authentication software this environment had but a way to get around this is to use the format domain\username for the User name field instead:

image

Using this login format will unlock the domain drop down box so you can now select the domain:

image