Friday, February 28, 2014

Microsoft Access 2013 on Server 2008 R2 SP 1 remote access through XenDesktop Screen Freeze

Solution

New Solution:

"Legacy Graphics Mode" is an option is Citrix Policy. Enabling this for the desktop group will stop Access from freezing, and doesn't break 'browse' 'save-as' etc. Unsure exactly what legacy graphics mode does, still trying to get someone to answer that for me, or why it doesn't work in the first place, but this is a less bad work around.


Appears to be some visual theme here that messes up the ICA connection. The "fix" - probably more of a workaround - is to run the program in compatibility mode (don't forget to set for all users). You'll want to select compatibility mode for windows 7 + disable visual themes.

Edit: People on Citrix forums claim that this effects office 2010 as well; I can't confirm that it does, or that this fix works for 2010 if the problem does exist, but there you go.

Warning: This workaround has been found to cause some other undesirable behavior. Notably: "browse" buttons will no longer launch the file browser to allow you to select files. This effects a number of things such as the "save as..." feature and "import from (excel/text/etc)" wizards. This is arguably less detrimental than the screen freeze but please be aware.


The Problem / Full story

As you may guess from the title, this is an insanely specific bug that we ran into in one of our labs. Students were trying to use MS Access (because we teach that for some reason) and their sessions kept on locking up. Here's the sequence of events:

Users log into Wyse Xenith 2 thin clients connecting to a 2008 R2 SP 1 terminal server type environment through XenDesktop (Citrix seems to refer to it as ServerOS connection, or Shared Published Desktops). This works great.

Users then open Access and everything seems to be working fine, but when they try to open a table in design view the screen freezes after a few seconds. At this point the machine is completely unresponsive, the mouse still moves, but no mouse clicks or keystrokes appear to have any effect.

I say "appear to have any effect" because they actually do the user just can't see them. If I use lanschool to remotely watch/control the user's session I can see them clicking around and typing things, and when I control I can interact with the session just fine; The user cannot see anything changing on the screen. It's totally bizarre. 

The only way I've found to restore functionality is to remotely log the user off so they could log in again. 

The really weird thing is that none of the factors individually cause the problem. Using Access on the same server but over remote desktop (RDP) works just fine. Using Access on a windows 7 machine over xendesktop works just fine. No other program I've used on the server over XenDesktop has exhibited this behavior.

I figured out the workaround above pretty much by trial and error. Then figured out the correct solution from citrix forums; Yay citrix forums!


Adventures in Powershell: User account locked out continuously

One problem we run into from time to time is when a user's account keeps getting locked out without any obvious reason (They're not typing in the password wrong - though this is probably the number 1 cause). Usually this is the result of some saved credentials somewhere that are no longer valid; They've expired, or there was a password change, or something. However, if the user logs into multiple machines, or has network resources mapped to a personal device, it can be really hard to tell where the bad login attempts are coming from.

So I wrote a neat little powershell function to help out with this. Bad log on attempts are logged in the domain controllers security logs. However, for various reasons, they can be really hard to find (reasons include username not being in the username field, and domain controller security logs having about a billion events at any given time). This powershell function does a little WMI call to each domain controller you specify and looks for these events so you don't have to.

It's a little messy and slow, but can save a great amount of time whenever these events come up.

I've put in some Get-Help features to which explains how to use it. After you import it run "Get-Help Get-WhyIsThisAccountLocked". Or alternatively just read the first 30 lines or so of the code.


The code:
function Get-WhyIsThisAccountLocked
{
    <#
        .SYNOPSIS
            Querys Domain Conrollers for specific log events looking for failed kerberos authentication.
        .Description
            Method can be used to discover where a specific account is being locked out from. This will return a list of log events related to failed authentications which you can use to further troubleshoot. Because of how these events are formated, it is difficult to select specific information out of them - So this returns the whole event. The important fields to look for are 
            AccountName: (verify it returned the correct user - for various reasons it only does a 'like' comparision here)
            Client Address: (Where the bad requests are coming from - note you will probably see some coming from the domain controller(s) - unless the user is acutally tying to log into/acess the domain controller, you can ignore these.)
            Failure Code: (Tells you more about what sort of authentication was being attempted - google "kerberos autentication failure code")
            Service Name: (Can tell you what service was trying to use the account - krbtgt/domain is typically standard login attempt, if it's something else, google it)
        .PARAMETER UserID
            UserID that is being locked out
        .PARAMETER DomainController
            Domain controller(s) to query for event logs. This may be either a single hostname/IP or a comma seperated list.
        .PARAMETER HoursBack
            Number of hours back to search, default is 1. Using large search periods can exponentially slow down the script.
        .PARAMETER OutFile
            File path to output text to - default is to output to command window
        .PARAMETER YesIAmSure
            If OutFile is specified, does not ask to confirm file write/overwrite. Otherwise does nothing.
        .Example
            Get-WhyIsThisAccountLocked -UserID myuser -DomainController "mydomain1,mydomain2" -HoursBack 2 -OutFile c:\temp\output.txt -YesIAmSure
        .Notes
            Author: Keith Ballou
            Date: Feb 28, 2014


    #>
       [CmdletBinding()] 
       param( 
      [Parameter(Mandatory=$true)][string]$UserID,
      [parameter(Mandatory=$true)][string]$DomainController,
      [Parameter(Mandatory=$false)][int]$HoursBack=1,
      [Parameter(Mandatory=$false)][string]$Outfile,
      [Parameter(Mandatory=$false)][switch]$YesIAmSure
)
       $ListofEvents=""
       $SearchTime=(get-date).addhours(-$HoursBack).touniversaltime().tostring("yyyyMMddHHmmss.000000-000")
       Write-Verbose "Searching between $SearchTime and Current Time"
       $ListofDCs=$DomainController.split(',')

       if ($HoursBack -gt 1)
       {
        write-host "WARNING: Using Larger Serach periods can cause this script to take a very long time" -ForegroundColor "Magenta" -BackgroundColor "Black"
       }

    foreach ($DC in $ListofDCs) 
      {
        write-host "Querying... " $DC
        $Temp=Get-WmiObject -ComputerName $DC -Query "Select * from win32_ntlogevent Where LogFile='Security' AND (Eventcode='4771' OR Eventcode='4769') And Message Like '%$UserID%' AND timegenerated > '$SearchTime'"
        foreach($Entry in $Temp)
        {
            Write-Verbose "Processing Entry" 
            $ListofEvents+="-------------------------------------------------------------------`n"
            $ListofEvents+=$Entry.ComputerName + "`n" 
            $ListofEvents+=$Entry.Message + "`n"            
        }
    }
       
    if($OutFile -ne "")
    {
        write-host "`n`nWarning: About to write to $OutFile - This will overwrite any existing data" -ForegroundColor "Magenta" -BackgroundColor "Black"

        if($YesIAmSure)
            {
                out-file -InputObject $ListofEvents -FilePath $OutFile
            }
        else {
            {
                out-file -InputObject $ListofEvents -FilePath $OutFile -Confirm
            }
        }
    }   

    else {
        write-host $ListofEvents
    }
    

}