Tuesday, February 25, 2014

Help!!!! Troubleshooting accessing sites with AAMs, Host Name Site Collections, IIS Bindings/Protocols/Ports/Host Headers

I wrote up this post to help clarify one of the hardest grasped concepts in regards to SharePoint that I've seen trip SharePoint Admins up, which is how IIS / SharePoint handle incoming requests and how it is all processed. In specific this in regards to IIS Site IP Bindings/Host Headers/Ports/Protocols and SharePoint AAM settings as well as host name based site collections. Once the general flow of processing is understood troubleshooting issues with accessing sites will become a lot easier which is the goal of this post. I figure the easiest way to explain this is by scenarios and break it down and explain the outcome.
 
KEY CONCEPTS TO UNDERSTAND FIRST
  • HTTP/HTTPS requests are first processed at the IIS level meaning the incoming request must satisfy the configured settings on the site for the configured Protocol/IP/Port/Host Header. Two IIS sites can never have all 4 settings the same configuration and run at the same time, at least one of the 4 must be different.
  • After the site has been processed at the IIS level and it starts executing SharePoint's site code (assuming its a SharePoint site....) SharePoint will match the Host Header request to current configured AAMs and capture authentication protocol assigned for that zone. Then SharePoint will prompt for credentials via that protocol, and validate the login as mapped to the site you are attempting to access. The incoming URL/Host header must match a configured Alternate Access Mapping and you must be authenticated for the site or you will receive 401 UNAUTHORIZED.
  • SharePoint automatically creates an AAM for 1) the manual host header you configured or 2) if the host header is blank when you created your web app, it will automatically create an AAM for the http://servername:port the web app was created on.
  • Host Named Site Collections require IIS Sites configured with a blank host header. AAMs will be auto created for that site in the Default zone when using New-SPSite and the -HostHeaderWebApplication parameter.
SCENARIOS ENVIRONMENT 
 
For simplicity's sake I will only be working with HTTP requests below (No HTTPs). We are assuming all URLs I type in the post below have a load balancer (just for fun) in front forwarding the request to our single SharePoint server with an IP of 192.168.27.1. Also we are assuming our DNS works just fine. :)
 
Scenario 1: Web Application created, Blank Host Header, * IP Bindings, Port 80, no additional AAM configured (Basic host name site collection web app structure set up, no sites created only the root site).
 
For this scenario I type http://site1.com in the browser which takes me to our VIP and then forwarded to the SharePoint server via the load balancer. Following that I am prompted to login. So this means I am actually hitting the site, the load balancer sent me to the IIS server, and it is processing my connection. I type in my credentials however I receive 401 Unauthorized. Why is this?
 
First at the IIS site level, IIS is seeing a host header come in for site1.com and the IP it is directed in for as 192.168.27.1:80. The site I created accepts any host header, directed at any IP configured on the IIS/SharePoint server, matches port 80 so it starts it begins launching SharePoint's Web App. This is where SharePoint examines the HTTP header and sees that you are trying to access site1.com which is not associated with the web app at all so it will reject your login.
 
Scenario 2: Web Application created, Blank Host Header, * IP Bindings, Port 80, site1.com additional AAM configured for this web application (Host named site collection created).
 
For this scenario I type http://site1.com in the browser which takes me to the VIP and the load balancer and I am prompted to login. So this means I am actually hitting the site and the IIS server is processing my connection. I type in my credentials and I am able to login. Why is this?
 
First at the IIS site level, IIS is seeing a host header come in for site1.com and the IP it is directed in for 192.168.27.1:80. The site I created accepts any host header, directed at any IP configured on the IIS server, matches port 80 so it starts it begins launching SharePoint's site files. This is where SharePoint examines the HTTP header and sees that you are accessing site1.com which is a configured alternate access mapping for this site. It recognizes your credentials via the authentication provider and accepts.
 
This is an important concept to note because this is how Host named site collections work. Each site collection essentially creates another AAM for the web application. Host named site collections require a blank host header so they can accept any hostname coming in on the IIS level and then SharePoint does the site direction on it's side.
 
Scenario 3: Web Application created, Blank Host Header, 192.168.27.1 IP Bound (manually configured in IIS), Port 80, no additional AAM configured.
 
For this scenario I type http://site1.com in the browser which takes me to the VIP and the load balancer and I am prompted to login. So this means I am actually hitting the site and the load balancer sent me to the IIS/SharePoint server and it is processing my connection. I type in my credentials and I am unable to login. Why is this?
 
First at the IIS site level, IIS is seeing a host header come in for site1.com and the IP it is directed in for 192.168.27.1:80. The site I created accepts any host header, directed at 192.168.27.1 on the IIS server (which matches the IP the load balancer directed me to), matches port 80 so it starts it begins launching SharePoint's site files. This is where SharePoint examines the HTTP header and sees that you are accessing site1.com which is not configured as an AAM for this site so it will say unauthorized and reject your connection.
 
Scenario 4: Web Application (simple path based) created, site1.com as the configured Host Header, 192.168.27.1 IP Bound (manually configured), Port 80, no additional AAM configured besides auto generated.
 
For this scenario I type http://site1.com in the browser which takes me to the VIP and the load balancer and I am prompted to login. So this means I am actually hitting the site and the load balancer sent me to the IIS server and it is processing my connection. I type in my credentials and I am to login. Why is this?
 
First at the IIS site level, IIS is seeing a host header come in for site1.com and the IP it is directed in for 192.168.27.1:80. The site I created accepts ONLY site1.com, directed at 192.168.27.1 on the IIS server (which matches the IP the load balancer directed me to), matches port 80 so it starts it begins launching SharePoint's site files. This is where SharePoint examines the HTTP header and sees that you are accessing site1.com which matches the AUTOMATICALLY generated AAM configured.
 
The important concept here is that my criteria matched the IIS level and then also matched the AAM criteria. Additionally important to understand when you specify a host header when you create a web application the url you use it is automatically generated via an AAM.
 
Summary: I know this doesn't cover every scenario, however I hope this helps you better understand the process flow of requests to SharePoint/IIS sites.

Quick guide to Implementing Host Name Site Collections in SharePoint 2013

In this post I am going to explain a little bit about the new site collection model in SharePoint 2013 called host name site collections and how to implement it via PowerShell (which is currently the only supported method).

First off, what are host named site collections? Host named site collections are simply site collections that are created based off of unique FQDNs instead of the previous model of path based site collections that followed a basic root URL. This provides several benefits which I won't go in to detail but of the biggest benefit is scalability ( Ex. I can host multiple sites now under one web application aka one iw3p.exe process instead of creating new web applications for new FQDN sites)

As an example, with host name site collections I can now create several SharePoint sites with the following names all under one web application (one iw3p.exe process from IIS):
  • www.mattsharepoint.com
  • sharepoint.hostnamedsitecollection.com
  • hnsc.org
This is cool right? So how do we go about doing this. Below I've created an example script that you can follow to create 2 host name site collection sites. Hope this helps you get started!
#"Add SharePoint Cmdlets"
add-pssnapin microsoft.sharepoint.powershell


# Web App Variables
$WebAppDefault = "SharePoint - HSNC Example"
$Port = "80"
$AppPool = "HSNCAppPool"
$Account = "domain\svc-apppoolaccount"

# Root Site Variables'
$RootHHDefault = "myrootsite.com"
$RootURLDefault = "http://myrootsite.com"
$Owner = "domain\svc-farmaccount"
$RootDB = "RootDB"
$Lang = "1033"
$Template = "blankinternetcontainer#0"

# HSNC Site Variables
$HNSCSITE1 = "http://hnsc1.com"
$HNSCSITE2 = "http://hnsc2.com"


# Create Web App
New-SPWebApplication -Name $WebAppDefault -hostHeader $RootHHDefault -Port $port -ApplicationPool $AppPool -ApplicationPoolAccount (Get-SPManagedAccount $Account) -AuthenticationProvider (New-SPAuthenticationProvider –UseWindowsIntegratedAuthentication) -DatabaseName $RootDB -AllowAnonymousAccess
echo "Web App created"

# Create Root Site Collection 
New-SPSite $RootURLDefault -Name 'Root Site' -Description 'External Root Site Collection' -OwnerAlias $Owner -language $Lang -Template $Template
echo "Root Site Collection created"

# Create HNSC 1
New-SPSite $HNSCSITE1 -HostHeaderWebApplication (get-spwebapplication $RootURLDefault) -Name 'Site 1' -Description 'HNSC Site1' -OwnerAlias $Owner -language $Lang -Template $template
echo "HNSC 1 Site Collection created"

# Create HNSC 2
New-SPSite $HNSCSITE2 -HostHeaderWebApplication (get-spwebapplication $RootURLDefault) -Name 'Site 2' -Description 'HNSC Site2' -OwnerAlias $Owner -language $Lang -Template $template
echo "HNSC 2 Site Collection created"



 

PowerShell Script to backup files based on particular date

Here is a nice short PowerShell script I wrote up that can help system admins with backing up files based on their modified date. What this script does is recurse the directory you wish to scan for files based on a particular modified date and export their paths to a text file. The second line in the script parses the text file and removes all white spaces so that it is cleaned up to throw in to 7z, which is used to create a compressed zipped up file. Feel free to throw this in your scheduled tasks and test it out for your backups!
Get-ChildItem -Path E:\FilesToBackUp -Recurse | Where{$_.LastWriteTime -gt (get-date).AddDays(-1)} | Where{$_.PSIsContainer -ne $true} | Select FullName | format-table -HideTableHeaders | out-file E:\file_backup.txt
Select-String -Pattern "\w" -Path E:\file_backup.txt | ForEach-Object { $_.line} | Set-Content -Path E:\file_backup_cleaned.txt

# Use 7z Version 9.25 alpha or newer
7z a G:\BackupLocation\Daily%date:~4,2%-%date:~7,2%-%date:~10%.7z @E:\file_backup_cleaned.txt -spf 
 

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."