Thursday, November 29, 2012

Citrix XenApp 6.x Application Report

Here's a quick and useful one-liner for pulling an Application Report from Citrix XenApp 6.x+. No longer do you pull it from Report Center/Summary database like in Presentation Server now we get to use Powershell!

First run powershell as administrator from a XA65 server, and type in this first:

Add-PSSnapin Citrix*

Then :

Get-XAApplicationReport * | select ApplicationType, DisplayName, FolderPath, 
Enabled, HideWhenDisabled,ContentAddress, CommandLineExecutable, WorkingDirectory,
AnonymousConnectionsAllowed, AddToClientStartMenu, ClientFolder,
StartMenuFolder, AddToClientDesktop, ConnectionsThroughAccessGatewayAllowed,
OtherConnectionsAllowed, AccessSessionConditionsEnabled,
@{n="AccessSessionConditions";e={[string]::join(" ; ",
$_.AccessSessionConditions)}}, InstanceLimit, MultipleInstancesPerUserAllowed,
CpuPriorityLevel, AudioType, AudioRequired, SslConnectionEnabled,
EncryptionLevel, EncryptionRequired, WaitOnPrinterCreation, WindowType,
ColorDepth, TitleBarHidden, MaximizedOnStartup, OfflineAccessAllowed,
CachingOption, AlternateProfiles, RunAsLeastPrivilegedUser,
@{n="Servers";e={[string]::join(" ; ", $_.ServerNames)}},
@{n="WorkerGroups";e={[string]::join(" ; ",
$_.WorkerGroupNames)}}, @{n="Users";e={[string]::join(" ;
", $_.Accounts)}} | Export-Csv c:\app-report.csv
 
Check for your report at c:\app-report.csv

Tuesday, September 11, 2012

XenApp Powershell Script to Publish Apps from CSV

As part of this Citrix migration project I am currently working on I had the necessity to publish multiple apps and the process was very time consuming and tedious. In order to accelerate and make the process more accurate I developed this script to publish apps from a CSV file. I also included some error checking/handling in the script for errors that I had encountered. Also be aware that for the icons to be correctly identified you will need to run this off the XA server that hosts the exe file with the path as per the "CommandLineExecutable" field.

The CSV file structure was setup as follows:
BrowserName,DisplayName,WorkerGroupNames,Accounts,CommandLineExecutable,WorkingDirectory,FolderPath,ClientFolder
Example App, Example, Test Worker Group, mshorrosh, C:\windows\system32\notepad.exe, c:\windows\system32\, Applications/Test, Test\


And the script....

# Load the snapins 
Add-PSSnapin citrix* -ErrorAction SilentlyContinue
#import the csv file
$apps = Import-Csv C:\apps.csv
#each line in the csv
foreach($app in $apps)
{
    #test if workergroup provided exists
    try{
        $WG = Get-XAWorkerGroup $app.WorkerGroupNames -ErrorAction Stop
     } catch {
        Write-Host "Error: " $app.BrowserName " resulted in " $_.Exception.Message " please create worker group first"
        break
     }
    #trying to get the executables icon from the CommandLineExecutable
    try{
        $EncodedIconData = Get-CtxIcon $app.CommandLineExecutable -index 0
    } catch [Exception] {
        Write-Host "Error: Obtaining the icon failed: " $_.Exception.Message
    }
    #checking browsername length, found out it has a limit
    if($app.BrowserName.length -gt 36)
    {
        Write-Host "Error: BrowserName for " $app.BrowserName " length is to long, must be less than 36 characters, please correct"
    }
    else
    {
   
        $success = $FALSE
       
        #try to publish new app
        try {
       
            $NewApp = New-XAApplication  `
            -ApplicationType "ServerInstalled" `
            -EncodedIconData $EncodedIconData `
            -WorkerGroupNames $app.WorkerGroupNames `
            -BrowserName $app.BrowserName `
            -Enabled $TRUE `
            -Accounts $app.Accounts `
            -WorkingDirectory $app.WorkingDirectory `
            -CommandLineExecutable $app.CommandLineExecutable `
            -FolderPath $app.FolderPath `
            -ClientFolder $app.ClientFolder  `
            -WindowType "100%" `
            -ColorDepth Colors32Bit `
            -InstanceLimit "-1" `
            -AddToClientStartMenu $false `
            -AnonymousConnectionsAllowed $false `
            -DisplayName $app.DisplayName `
            -ErrorAction Stop
            $success = $TRUE
        } catch {
             Write-Host "Error: " $_.Exception.Message
        } finally {
            if($success)
            {
                Write-Host $app.BrowserName "added successfully."
            }
        }
       
       
    }

   
}

Friday, September 7, 2012

VBScript to rename Program Neighborhood Paths

The client I was working at had a multi-farm XenApp environment so I ended up needing a script that would run on XA 4.5 as well. This is essentially the same script I wrote in powershell translated to vbscript, edit appropriately as you need for your environment.


option explicit
Const MetaFrameWinAppObject = 3
Const MetaFrameWinFarmObject = 1
Dim aMFApp, hMFAppTemp, aMFTemp
Dim MFFarm : Set MFFarm = WScript.CreateObject ("MetaFrameCOM.MetaFrameFarm")
MFFarm.Initialize(MetaFrameWinFarmObject)
Dim mode, count, strReplace
mode = "Debug"
count = 0
Dim objECA, objAPAC, objAGLNG
Set objECA = New RegExp
Set objAPAC = New RegExp
Set objAGLNG = New RegExp
With objECA
                .Pattern = "ECA\\"
                .IgnoreCase = True
End With
With objAGLNG
                .Pattern = "AGLNG\\"
                .IgnoreCase = True
End With
With objAPAC
                .Pattern = "APAC\\"
                .IgnoreCase = True
End With
For Each aMFApp in MFFarm.Applications
Set hMFAppTemp = CreateObject("MetaFrameCOM.MetaFrameApplication")
hMFAppTemp.Initialize MetaFrameWinAppObject,aMFApp.DistinguishedName
hMFAppTemp.LoadData(True)
Set aMFTemp = hMFAppTemp.WinAppObject2
                If objAGLNG.Test(aMFTemp.PNFolder) = True Then
                                count = count + 1
                                strReplace = Replace(aMFTemp.PNFolder,"AGLNG\","")
                                If mode = "Debug" Then
                                                Wscript.Echo aMFTemp.AppName & " - old: " & aMFTemp.PNFolder & " new: " & strReplace
                                Elseif mode = "Real" Then
                                                aMFTemp.PNFolder = strReplace
                                                aMFTemp.SaveData()
                                End If   
                ElseIf objAPAC.Test(aMFTemp.PNFolder) = True Then
                                count = count + 1
                                strReplace = Replace(aMFTemp.PNFolder,"APAC\","")
                                If mode = "Debug" Then
                                                               
                                                Wscript.Echo aMFTemp.AppName & " - old: " & aMFTemp.PNFolder & " new: " & strReplace
                                Elseif mode = "Real" Then
                                                aMFTemp.PNFolder = strReplace
                                                aMFTemp.SaveData()
                                End If   
                ElseIf objECA.Test(aMFTemp.PNFolder) = True Then
                                count = count + 1
                                strReplace = Replace(aMFTemp.PNFolder,"ECA\","")
                                If mode = "Debug" Then
                                                Wscript.Echo aMFTemp.AppName & " - old: " & aMFTemp.PNFolder & " new: " & strReplace
                                Elseif mode = "Real" Then
                                                aMFTemp.PNFolder = strReplace
                                                aMFTemp.SaveData()
                                End If   
                End If
Set aMFTemp = Nothing
Set hMFAppTemp = Nothing
Next
Wscript.Echo "Total applications updated : " & count

Friday, August 31, 2012

Powershell script to rename XenApp Program Neighborhood Paths

Recently there was a organizational change in a company I was working at that required us to change all the program neighborhood paths for all the apps in the XenApp environment. The script below iterates through all the apps in the farm and examines their initial folder in the path for the "Client Folder" aka Program Neighborhood folder and removes the folder from their path. You may edit the different fields that we were inspecting for to suite changes you may need to make in your farm. This script should work for XenApp 6.0+.


Add-PSSnapin Citrix.XenApp.Commands 

$apps = Get-XAApplication
$mode = "Debug" #Change to Real to make changes
$count = 0

foreach($app in $apps)
{
                if($app.ClientFolder -match "AGLNG\\")
                {
                                $original = $app.ClientFolder
                                $temp = $original -replace "AGLNG\\", ""
                                $count +=1

                                if($mode -eq "Debug")
                                {
                                                Write-Host $app.DisplayName "would have " $original " changed to " $temp
                                               
                                }
                                elseif($mode -eq "Real")
                                {
                                                Write-Host "Changing " $app " to " $temp
                                                Set-XAApplication $app -ClientFolder $temp
                                               
                                }             

                }
                if($app.ClientFolder -match "ECA\\")
                {
                                $original = $app.ClientFolder
                                $temp = $original -replace "ECA\\", ""
                                $count +=1

                                if($mode -eq "Debug")
                                {
                                                Write-Host $app.DisplayName "would have " $original " changed to " $temp
                                               
                                }
                                elseif($mode -eq "Real")
                                {
                                                Write-Host "Changing " $app " to " $temp
                                                Set-XAApplication $app -ClientFolder $temp
                                               
                                }

                }
                if($app.ClientFolder -match "APAC\\")
                {
                                $original = $app.ClientFolder
                                $temp = $original -replace "APAC\\", ""
                                $count +=1

                                if($mode -eq "Debug")
                                {
                                                Write-Host $app.DisplayName "would have " $original " changed to " $temp
                                               
                                }
                                elseif($mode -eq "Real")
                                {
                                                Write-Host "Changing " $app " to " $temp
                                                Set-XAApplication $app -ClientFolder $temp
                                               
                                }

                }
}

Write-Host "Total of" $count "applications effected by the PN Folder change."

Wednesday, August 8, 2012

Uninstall IE9 and install IE8 on Windows Server 2008 R2

I had to do this recently on a XenApp server to publish IE8 for compatibility support for legacy web apps and found out that this was not the most straight forward thing to do, however I found a really simple solution for it.

Simply go to Control Panel -> Windows Update -> Installed Updates -> and scroll down to find the IE9 update and click remove, it's as easy as that, reboot and IE8 is back.

Hope this saves someone the headache.

Monday, August 6, 2012

XenApp 6.5 Load Balancing Policies applied to specific applications

I spent some time working with XenApp 6.x's feature of application load balancing that allows you to route users based on certain filters to specific worker groups of servers. The interface is fairly simple and it that allows you to first pick how you want to filter the users, which is done by either Access Control, Client IP Address, Client Name, or User. After you've chosen your filter you simply pick the Load Balancing Policies you want to set, which is either by Worker Group Preference or Streamed App Delivery. All of this is managed via a node in AppCenter called  Load Balancing Policies. There are some benefits and some disadvantages I've found through using this feature which I will explain through my implementation scenario.

In my scenario I was working to implement a Global Office 2010 Silo distributed over 3 data centers, for this post I'll just name them worker groups DC1, DC2, and DC3 which are all servers containing office 2010. I setup the policies as follows:

Policy 1 
Name: DC1 Priority
Filter: Filter based on user, with user location based AD groups with fastest connection to DC1
Policy: Worker Group Preference, with priority in order lowest to highest DC1, DC2, DC3 (based on link speeds between the three).

Policy 2
Name: DC2 Priority
Filter: Filter based on user, with user location based AD groups with fastest connection to DC2
Policy: Worker Group Preference, with priority in order lowest to highest DC2, DC1, DC3.

Policy 3
Name: DC3 Priority
Filter: Filter based on user, with user location based AD groups with fastest connection to DC3
Policy: Worker Group Preference, with priority in order lowest to highest DC3, DC2, DC1.

After getting everything setup I quickly found out that the Application Load Balancing had no way to apply the policy to specific applications, meaning that it affected all applications that were published. So if a user's account was in a AD Group "DC1 Employees" he was subject to Policy 1 and XenApp would try to search the Office 2010 worker groups even if he was trying to run something like FireFox, this was causing applications to fail that were processing these policies. This was going to be a problem because I wanted to get away from having to publish different applications for different regions which makes administration a nightmare. Digging around on google I found this which Citrix gives mention of the idea to add a Worker Group with all the servers added at the last priority for the apps, this way if the application is not contained as part of the load balancing worker group set it will still launch where it is available. After making the "All Servers" worker group and adding that as last priority on all the LB Policies, everything was working just as planned.

Monday, June 25, 2012

One-liner to enumerate all members of an AD Group and retrieve their e-mail addresses

Here is some useful one-liners using dsquery to enumerate all the members of an AD security group and retrieve their e-mail addresses or retrieve an e-mail address from AD via a single user account. These come in handy when you need to create distribution lists for notifications or announcements. You can wrap these queries in batch scripts and pipe out to a file and import in to excel to cleanup.

dsquery group -Name <AD Group Name> | dsget group -members | dsget user -email | findstr ".com" > output.txt

Also to retrieve e-mail addresses from single users use the following:

dsquery user -samid <username> | dsget user -email | findstr ".com" > output-user.txt

Wednesday, June 13, 2012

VBScript to Enumerate Installed Applications and Versions

Here is a script I wrote to discover the installed application versions across a list of servers. I have it formatted so it will export a csv file with the information. Note: I also wrote some conditional statements to exclude some Windows update patches/hotfixes/security updates, etc.

To use: "cscript script.vbs" and will generate a results.csv file, also provide a ServerList.txt filled with the list of servers (by line) which you wish to scan the applications and their version numbers.

Here it is:

Dim args,computername, count
count = 0
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile1 = objFSO.OpenTextFile("ServerList.txt", ForReading)
Const ForReading = 1
'Read Compare To List
Dim ListToProcess()
i = 0
Do Until objFile1.AtEndOfStream
Redim Preserve ListToProcess(i)
ListToProcess(i) = objFile1.ReadLine
i = i + 1
Loop
objFile1.Close
For Each strLine in ListToProcess
 if strLine <> "" then
 computername = strLine
 Call enumApps(strLine,1)
 End if
Next

Function enumApps(computername,flag)
On Error Resume Next
Dim strComputer, strLine

strComputer = computername
Const HKLM        = &H80000002
Const strKeyPath  = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
Wscript.echo "===============" & "Parsing " & computername & "==============="
Dim oReg, arrSubKeys, strProduct, strDisplayName, strVersion
Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" &  strComputer & "\root\default:StdRegProv")
oReg.EnumKey HKLM, strKeyPath, arrSubKeys
For Each strProduct In arrSubKeys
    oReg.GetStringValue HKLM, strKeyPath & "\" & strProduct, "DisplayName", strDisplayName
    oReg.GetStringValue HKLM, strKeyPath & "\" & strProduct, "DisplayVersion", strVersion

 Dim Filter
 If InStr(1, strDisplayName, "", vbTextCompare) > 0 Then
  If InStr(1, strDisplayName, "Security", vbTextCompare) = 0 Then
   If InStr(1, strDisplayName, "Update", vbTextCompare) = 0 Then
    If InStr(1, strDisplayName, "Hotfix", vbTextCompare) = 0 Then
     strVersion = Replace(strVersion, ",", "")
     strLine = strLine & strDisplayName & "," & strVersion & "," & strComputer & vbCrLf
     'Wscript.echo strLine
    End If
   End If
  End If
 End If


Next

Dim ObjFso
Dim StrFileName
Dim ObjFile
StrFileName = "results.csv"
Set ObjFso = CreateObject("Scripting.FileSystemObject")
'Opening the file
Set ObjFile = ObjFso.OpenTextFile (StrFileName, 8, True)
If count = 0 then
 objFile.WriteLine "Application Name, Version,Server"
 count = 1
end if
objFile.WriteLine strLine
objFile.Close
End Function

Citrix Script to Enumerate Program Neighborhood Folders

Here's a MFCOM script I wrote up really quick that will output all the applications in a farm including their name, citrix management console path, and program neighborhood path. I wrote this because I was working on a customer site where the path between the PN and Citrix console differed causing big support headaches when users called in about apps not working and having trouble identifying which app they were talking about. We used this to discover what everything looked like and to design a new structure. This worked against a Citrix Presentation Server 4.5 Farm. Thanks to http://community.citrix.com/display/xa/Code+Share for providing me the base code to get this working.

Here it is, you can use it by doing cscript script.wsf > output.csv

<package>
    <job id=" ListPNFolderPath">
        <comment>
       By Matt Shorrosh
        </comment>
        <runtime>
            <description>
               Enumerates the Application Name, CMC Path, PN Folder Path
            </description>
        
        </runtime>
        <reference object="MetaFrameCOM.MetaFrameFarm"/>
        <script language="VBScript">
       
            Dim theFarm, aServer
           
            '
            ' Create MetaFrameFarm object
            '
            Set theFarm = CreateObject("MetaFrameCOM.MetaFrameFarm")
            if Err.Number <> 0 Then
                WScript.Echo "Can't create MetaFrameFarm object"
                WScript.Echo "(" & Err.Number & ") " & Err.Description
                WScript.Echo ""
                WScript.Quit Err.Number
            End if
            '
            ' Initialize the farm object.
            '
            theFarm.Initialize(MetaFrameWinFarmObject)
            if Err.Number <> 0 Then
                'WScript.Echo "Can't  Initialize MetaFrameFarm object"
                'WScript.Echo "(" & Err.Number & ") " & Err.Description
                'WScript.Echo ""
                'WScript.Quit Err.Number
            End if
            '
            ' Are you Citrix Administrator?
            '
            if theFarm.WinFarmObject.IsCitrixAdministrator = 0 then
                'WScript.Echo "You must be a Citrix admin to run this script"
                'WScript.Echo ""
                'WScript.Quit 0
            End If
            '
   
             For Each anApp In theFarm.Applications

  anApp.loaddata(true)
  if Err.Number <> 0 Then
       WScript.Echo "Can't enumerate applications"
       WScript.Echo "(" & Err.Number & ") " & Err.Description
       WScript.Echo ""
       WScript.Quit Err.Number
  End if

  Wscript.echo anApp.AppName & "," & anApp.DistinguishedName & "," & anApp.PNFolder


  Next
           
          
      
        </script>
    </job>
</package>

Monday, April 2, 2012

Publishing Sharepoint 2007 w/ Infopath Forms via UAG 2010

Problem:
Accessing all the Sharepoint content through UAG seemed fine with just AAM setup on the Sharepoint side except when we attempted to open up Infopath forms. We had issues with the GetUserProfile() service not working and/or other Web services connections for populating the other drop downs in the Infopath forms failing. We were greeted with an error that said "Some rules were not applied" and "An error occured accessing a data source" or event id 5566.



Environment:
This setup included 2 load balanced UAG 2010 SP1 Update 1 boxes with a Cisco ACE NLB, 4 Sharepoint 2007 FE Farm servers, and 2 backend Web Services IIS boxes.

Solution:

The solution ended up requiring us to implement Kerberos authentication because we were faced with the "double hop" issue (See http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx ). To successfully implement Kerberos required a lot of changes to our environment. I'll describe below in more detail what was done. 
  • Create Sharepoint Alternate Access Mappings

    Navigate to Sharepoint Central Administration->Operations->Alternate Access Mappings. Create AAMs that map a new dummy url to the public facing URL that is set for Sharepoint 2007 in UAG. Place the AAM in the internet zone and make sure that the Public URL for the zone is set to HTTPS.
  • Extend Web Application

    Next, extend the web application (Application Management > Create or Extend Web Application > Extend Web Application to Another IIS Web Site ). Make sure that Negotiate (Kerberos) is configured as the authentication provider and allow anonymous is set to No. Map the Zone to Internet.
  • Add Host Entries on Sharepoint Front End hosts

    Next add an entry to the hosts file that points the dummy URL to loopback IP address (127.0.0.1). Apply this to every Sharepoint Front end server.
  • Add IIS Host Header Binding 

    In IIS add a host header binding for the dummy URL, to the original web application (the one on port 80). For example if your real site name is partners.company.com and your dummy url is partnersuag.company.com, make sure to add the host header binding to the partners.company.com site in IIS.
  • Install SSL Cert on IIS SSL Binding

    Install an SSL certificate on the newly created SSL web application in IIS. Validate that the certificate is correctly installed and not showing any errors from the Certificates MMC or via IIS view certificate dialog screen.Validate the root cert is in the Trusted Root Authorities certificate store. We ran in to an issue where even though the certificate showed no errors, we received errors in Sharepoint about trouble making an SSL connection and this was because the certificates whole chain was in the Personal store instead of placing the root certs in the Trusted Root Auth store.

**Notes about InfoPath:  In order for InfoPath Forms to work via UAG, all data connections must be .udcx files placed in a Trusted Data Connection Library and Forms must be "Fully Trusted"  and Administrator Approved.
  • UAG Web Servers Config

    Make sure to check the replace host header in the Web Servers tab for the application and apply the “bogus” url in the text box. We matched the internal URL and the public URL in our configuration.
     
  • UAG Authentication Configuration
Check use Kerberos Constrained Delegation for single sign on and type in http/*
  • Validate SPNs in environment
·         Validate SPNs for Sharepoint Service Account for IIS Front End Application Pool
o    Make sure every SP FE server has a corresponding HTTP/<FQDN> and HTTP/<Netbios name> via Setspn -L sharepointsvcaccount
o    If missing an SPN add via setspn –A HTTP/<FQDN> sharepointsvcaccount
o    Also add setspn –A HTTP/<netbios name> sharepointsvcaccount

·         Validate SPNS for Service Account for Web services IIS Application Pool
o    Make sure every IIS box in the pool has a corresponding HTTP/<FQDN> and HTTP/<Netbios name> via Setspn -L svc-account
o    If missing an SPN add via setspn –A HTTP/<FQDN> svc-account
o    Also add setspn –A HTTP/<netbios name> svc-account
  • Set Delegation for UAG computer account(s)
Add the delegation in AD Users and Computers to the UAG computer accounts, go to the Delegation tab and click add and type in Sharepoint svc account and IIS Web Farm service account and make sure to add all related SPNs that show up to the delegation. Reboot UAG servers.

  • Set Delegation to Sharepoint Farm Service Account

Delegation should be set by adding the next service hop in the path for authentication, in this case add the Web Service IIS Farm svc account so that Sharepoint can impersonate users coming from UAG to the web services. Allow time for replication.
 
  • Set Delegation to IIS Web Services Farm Service Account

    Set the Delegation to Trust this computer for delegation to any service ( Kerberos only ). Allow time for replication.
  • Set Delegation for Sharepoint/Web Service Front End IIS Computers

    Validate that all corresponding Sharepoint Front End boxes and Web Services Farm IIS boxes have the following delegation set: "Trust this computer for delegation to any service ( Kerberos only )"
  • Set Component Services to Delegate on all Sharepoint Front End

    Go to Start -> Administrative Tools -> Component Services,Open the Computers node and right click on My Computer and select Properties. Set the Default Impersonation Level to Delegate.
  • Add Registry Key to support Kerberos in Server 2003
    Add a DWORD 32bit to HKLM\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters\MaxPacketSize and set the value to 1