Update (October 24th, 2014): I modified the code slightly to support special characters in the user’s displayname attribute.
This is another addition to the “Customize RD Web Access 2012 R2” series. You can start this series here in case you want to read up or see what else I customized in this series.
This post is inspired by comments in the series, in which readers asked if it was possible to show the logged in user’s displayname as it appears in Active Direcory.
I did a little digging in the code and found a way to accomplish this.
I’m not going to detail the setup I have here. If you’re interested I suggest you read up on the series.
Logging into the RD Web Access shows this:
This is actually the top of the Default.aspx page.
Just basic stuff here, nothing customized, it shows a basic navigation bar. I’m going to show you how you can add the displayname right after the “Sign out” button on the navigation bar.
The files that make up RD Web Access are in C:\Windows\Web\RDWeb\Pages so make a copy of that folder structure just to make sure we have a backup should we break something.
We are going to use native Windows code from an assembly that is already present on the server that holds the RD Web Access role, but is not known to the RD Web Access application yet. So the first step is to tell the application that we are going to add code.
Open web.config from C:\Windows\Web\RDWeb\Pages in an editor.
Find line 52:
And add the following code right after that:
<compilation defaultLanguage="c#" debug="true"> <assemblies> <add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/> </assemblies> </compilation>
The result must look like this:
This tells the RD Web Access application that we need extra code. The extra code is in the .NET Assembly, and specifically in System.DirectoryServices.dll.
Save and close web.config, we don’t need it anymore.
Now open Default.aspx in an editor, which is in C:\Windows\Web\RDWeb\Pages\en-us.
Find line 225:
Insert the following code before line 225:
private static string GetDisplayName(string strUserName) { string strLDAPPath = "LDAP://dc=itw,dc=test"; string strFilter = string.Empty; if(strUserName.Contains("\\")){strUserName = strUserName.Substring(1 + strUserName.IndexOf("\\"));} strFilter = "(SAMAccountName=" + strUserName + ")"; if(strUserName.Contains("@")){strFilter = "(UserPrincipalName=" + strUserName + ")";} System.DirectoryServices.DirectoryEntry de = new System.DirectoryServices.DirectoryEntry(strLDAPPath); System.DirectoryServices.DirectorySearcher ds = new System.DirectoryServices.DirectorySearcher(de); ds.Filter = strFilter; ds.PropertiesToLoad.Add("DisplayName"); System.DirectoryServices.SearchResultCollection results = ds.FindAll(); return (results != null && results.Count > 0) ? Uri.EscapeDataString(results[0].Properties["DisplayName"][0].ToString()) : string.Empty; }
Remember to replace “LDAP://dc=itw,dc=test” with your own domain name. So for example, if your domain name is “contoso.local”, this code would be “LDAP://dc=contoso,dc=local”.
The result must look like this (I have wordwrap on in my editor):
This shows line 225-242 in my file.
And add the following line of code right after that:
userdisplayname="<%=GetDisplayName(strDomainUserName)%>"
The result must look like this:
This last modification tells the xsl template that it can expect a new variable, and it’s called “userdisplayname”. The value of that variable is the result of the GetDisplayName code you inserted earlier.
Save and close Default.aspx.
Note that if you want to show the displayname on other pages than Default.aspx you need to make these two changes in those files as well! The modifications above in Default.aspx will only work on Default.aspx.
Now open Site.xsl in an editor, which is in C:\Windows\Web\RDWeb\Pages.
Find line 15 (empty line) and insert the following code:
<xsl:variable name="userdisplayname" select="/RDWAPage/@userdisplayname"/>
The result will look like this:
Here we grab the new variable which we defined in Default.aspx (and now you see why we need to do this on every page that we want to show the displayname on, because the variable is supplied by the actual page).
Add the following code on the end of that line:
<xsl:if test="$userdisplayname"> (document.write(decodeURIComponent(''));)</xsl:if>
The result must look like this:
Save and close Site.xsl.
Open your RD Web Access page, login using valid credentials:
Or, with special characters:
Nice! We now have the displayname displayed on the navigation bar, right next to the Sign out button.
I have tested this in my very simple lab setup. I have only one domain, with a single UPN context, so I couldn’t test this with aliases and such.
I have tested this customization using DOMAIN\username, UPN and just username, and that all works.
Till next time,
Arjan
Thanks a lot Arjan. It works as a charm.
It is possible to do the same thing if WINDOWS AUTHENTICATION is enabled instead of the Form based?
I think it is, but you’d probably need to program that in aspx using c# yourself.
The way I show you to do it here is based on the cookie and variables that are used throughout your forms based session.
Time is relatively sparse for me at the moment, but I’ll put it on my list “to find out”.
There is a mistake in the blog, you say that the site.xsl file is in the Language dir but on my system it’s in de Pages dir.
Corrected.
Good catch, thank you sir!
I keep getting a runtime error each time I modify the changes for the default.aspx file.
Hi Xa,
My Windows 2012 R2 template contains .Net Framework 3.5, which is a good thing to do.
Install .Net Framework 3.5 and I think that will solve your runtime error.
I do have .net framework 3.5 installed. No luck.
If I ignore everything in the Default.aspx file, and modify everything else in the web.config and site.xsl files, it worked.
XA, did you ever resolve this? I’ve having the same problem. When I insert the code at 225 (script function) I get a runtime error. Any ideas?
Ben
Ben, I am still getting the runtime error and have not had a chance to look into this further.
Ben, XA,
Sorry for the late reply, was on vacation :)
Can you check the version of System.DirectoryServices.dll on your Web Access machine?
Arjan,
I am not sure how to verify the version of this. I tried googling, but could not come up with a method. Please advise.
Ben
Ben,
Find the dll on your system. It’s probably in C:\Windows\Microsoft.NET somewhere. Check the properties, last tab, file version.
Like XA if I edit default.aspx I get runtime errors. I have installed Net 3.5 but this did not help! If I dont edit default. all is OK but it does not display anything which defeats the purpose.
Hi, this is good. Can you we do the same thing for Server 2008 R2?
Thanks
Hi Jonathan,
Sorry for the late reply, was on vacation :)
I’m sure the same thing can be accomplished on Windows 2008 R2, but not with the steps I provide.
As my focus is not on Windows 2008 R2 anymore, I cannot help you with this.
Hi, I find out that if user’s display contains special characters e.g. “&” this won’t work. Any ideas? Thanks!
Hi omgsone,
Do you get an error? If so, can you mail that to me?
Hi Arjan,
Unfortunately I had to switch it back to defaults, as we had a users with special characters on their names. I cannot test that right now, so I don’t have an error messages to give you. However error was from IIS that page cannot be displayed and includes errors. So I mean, after user log in, whole page is invisible.
I tried this one as well and didn’t work for me. The page gave me an error. I’ll try again in a bit and let you know. thank you.
Re-tried and double checked and I got a 500 error when opening the page. Double checked everything and still no GO!
I was able to fix this. Thank you.
Hi X2Dojo,
Glad you were able to fix it. Can you share what was the problem in your situation? Maybe it will help others as well.
I found mismatch in code and result overview:
ds.PropertiesToLoad.Add(“DisplayNameSystem.DirectoryServices.SearchResultCollection results = ds.FindAll();
I edit code like in result screeinshot, and it work.
My version:
ds.PropertiesToLoad.Add(“DisplayName”);
System.DirectoryServices.SearchResultCollection results = ds.FindAll();
return (results != null && results.Count > 0) ? Uri.EscapeDataString(results[0].Properties[“DisplayName”][0].ToString()) : string.Empty;
}
Thanks!
Hi,
Can I make RD web access to use my active directory to sign in instead of this form authentication.
Hi Joseph,
If you change the authentication method on the IIS app I think you can.
Not sure if you also need to modify the authentication section in the web.config though..
I think there is a misstake in the Code for the default.aspx on line 225.
The code as is, didn´t work for me. I correct it into this:
…
ds.PropertiesToLoad.Add(“DisplayName”);
System.DirectoryServices.SearchResultCollection results = ds.FindAll();
…
now it works.
Hi Henrik,
Thanks for that, but I don’t see a difference with the original code?
Yes this is the fix that I made to make it work (at my previous post)
thank you for your advise here, I couldn’t get mine to work! then I noticed your comment and it worked!
Yes, current code works, exect if user’s account information includes & or any other special characters.
Hi again omgsone,
I updated the post with a fix for users with special characters in their displayname attribute.
Thanks a million! Will try it.
Hi Sir.
How can i remove the special character?
thank you
Thanks a lot!
Since I had a little problem with “document.write” in Firefox, here my solution:
@site.xsl
Line 331, change
to:
var PORTAL_SIGNOUT = document.getElementById(“PORTAL_SIGNOUT”);
var DisplayUser = decodeURIComponent(”);
PORTAL_SIGNOUT.innerHTML = ” (” + DisplayUser + “)”;
Maybe it es usefull for someone
Thanks!
Could you elaborate on how to implement this? Does it enable Firefox and IE or is it either/or? What specifically needs to be changed?
I publish my RDS farm through TMG 2010. In Intranet displaying user work correct, but from Internet, user name don’t display. Have any ideas?
Hi,
I think that has to do with whether or not the TMG forwards the original request or not (impersonation?).
In your case the page tries to find the TMG user instead of the actual user :(
hello Arjan,
I have a couple of domain / forest related questions. and I cannot find them on the internet. do you have time to answer them or show me where I can find the answers?
Q1. is i possible to have a Windows 2012 R2 RDS vdi implementation and provision Windows 7 VDI machines in another domain?
Q2. is i possible to have a Windows 2012 R2 RDS vdi implementation and provision Windows 7 VDI machines in another Forest?
Q3. is i possible to have a Windows 2012 R2 RDS vdi implementation with Windows 7 personal VDI machines in 1 AD/Forest. and have users from another AD/Forest to login and use them?
Q4. what is the best practises for production environments? having the RDS servers in one domain and the Windows 7 VDI machine in the same or another AD?
I hope you have some answers,
Andre
Hi André,
I can only answer Q3 with a definite Yes. Q4: I prefer the same, there’s no best practice really.
Q1 and Q2 are best found out in a Lab environment, if you really want to know.
I was able to get this by following the replies. One thing I ran into is AD accounts without a display name generate an error:
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
I’m going to take a crack at reverting to no username next to sign in if the display name is null but if you have any hints on how to accomplish it I’d appreciate it. If I figure out a fix I’ll post it.
Hi Drew,
A quick and dirty fix would be to replace the last line in the GetDisplayName function.
return (results != null && results.Count > 0) ? Uri.EscapeDataString(results[0].Properties[“DisplayName”][0].ToString()) : string.Empty
Would need to replaced by several lines.
if (results == null || results.Count < 1) { return ""; }
strDisplay = Uri.EscapeDataString(results[0].Properties["DisplayName"][0].ToString());
if (strDisplay == null || strDisplay == String.Empty()) { return "": }
return strDisplay;
Now this is without testing and might need some tweaking to get it to work, but something like that would fix your problem.
That, or just powershell your AD to give everyone a displayname (preferred method actually).
Hi Arjan,
First of all thanks for your job its awesome.
I confirm what Henrik said, there is a mistake in Default.aspx.
This section:
ds.PropertiesToLoad.Add(“DisplayNameSystem.DirectoryServices.SearchResultCollection results = ds.FindAll();
Should be:
ds.PropertiesToLoad.Add(“DisplayName”);
System.DirectoryServices.SearchResultCollection results = ds.FindAll();
I was doin this over and over without any succes then when i tried the following it started to work.
In order to help others i tought it would be cool to inform you and maybe update you already awesome job :)
Thanks
Hi Christian,
The code still looks fine in my browser. I tested this with some colleagues, and they notice the errors as well. If you look in the sourcecode of this page, you’ll notice the code is ok there as well. Looks like a wordpress theme issue..
Arjan,
Your article is great. In my site.xsl the line 323 edit, I did not have that many lines, I had to add in two extra lines just to insert line 323 at the end.
So my real problem is that I have two web access servers and I’m using microsoft’s NLB between them. The rd web access is accessed by that. when I made the changes in this article above, they apply properly going to each of the local servers. Once you use the NLB name to access the rd web access site, the sign out name does not show.
is there something else that needs to be added for these changes to show using the NLB name instead of just on the local servers?
RDWEB1: shows “username (sign out)”
RDWEB2: shows “username (sign out)”
RDWEB NLB Name: does not show “username (sign out)”
Arjan,
I just blew away the domain user folder for that user I could not see the signout name with and went to a completely different computer (other than the two test machines I was using) and the username (sign out) showed up internally. also showed externally, thank you!!!!
I am still seeing this :
Sign out(document.write(decodeURIComponent(”));)
Is there a way to fix this? I am on Windows 2012R2
Thanks
Digvijay
Hi Digvijay,
It looks like WordPress has changed something in how the code is displayed.
The textversion of the code is missing some pieces :(
Please refer to my screenshots of my code to fix your problem. In your case it’s the piece of code that is show where it says Find line 323 in this article.
Sorry for that.
Hi Arjan,
I followed your suggestion and made the text look exaclty as in the screenshot
and now line 323 looks like this –
(document.write(decodeURIComponent(‘xls:value-of select=”$userdisplayname”/>’));)
However i am not seeing the login name of the user , but this –
Sign out (xls:value-of select=”$userdisplayname”/>)
Seems like wordpress also changed the text that i entered. But still, my line 323 looks exactly like yours in the screenshot, but i am still unable to get the username displayed correctly.
This is my xsl code for the Site.xsl and my usernames show up perfectly.
(document.write(decodeURIComponent(”));)
Hmmm…Wordpress takes out the scripting. Email me at marv3k1983@gmail.com and I will gladly share my files with you.
I followed this article to the “T,” but when page loads I get: “Sign Out ()” instead of Sign Out (User Name). Ialready checked everything and all code is exactly like screenshot examples. Where is the mistake?
Ok. I think I found solution to my own problem. Not only code displayed on this web page is garbled up by WordPress, but also it seems that code displayed on the last screenshot is wrong is well. I’m not sure why it was necessary to do “document.write(decodeURIComponent”….etc.
So what I did is replaced this code (see screenshot): https://msfreaks.files.wordpress.com/2014/03/2014-10-24_15-12-38.png
with this one (see screenshot):
And everything started to work like it should. I hope it helps someone.
Hi Sirs. Can we remove the special character on the user name? example: Logout(Mark%20Anderson).
Any other wayto remove this special character? appreciate your help.
Thank you.
Hi How did you remove the special character? I have the same issue. Sign Out (Daniel%20Prasanna)
Hi Sir,
May I know how can I display “Domain\username” instead of displayname?
Reason being is some of the users sign in with UPN often and they tend to forget their “Domain\username”.
So I want to display it so that when they need to change password on the RDWeb, they know what is their “Domain\username”
I’m not a developer, I tried to understand your below coding and see how i can modify but I really can’t figure it out. haha.
private static string GetDisplayName(string strUserName)
{
string strLDAPPath = “LDAP://dc=itw,dc=test”;
string strFilter = string.Empty;
if(strUserName.Contains(“\\”)){strUserName = strUserName.Substring(1 + strUserName.IndexOf(“\\”));}
strFilter = “(SAMAccountName=” + strUserName + “)”;
if(strUserName.Contains(“@”)){strFilter = “(UserPrincipalName=” + strUserName + “)”;}
System.DirectoryServices.DirectoryEntry de = new System.DirectoryServices.DirectoryEntry(strLDAPPath);
System.DirectoryServices.DirectorySearcher ds = new System.DirectoryServices.DirectorySearcher(de);
ds.Filter = strFilter;
ds.PropertiesToLoad.Add(“DisplayName”);
System.DirectoryServices.SearchResultCollection results = ds.FindAll();
return (results != null && results.Count > 0) ? Uri.EscapeDataString(results[0].Properties[“DisplayName”][0].
ToString()) : string.Empty;
}
Hope to hear from you soon.
Thanks in advance.
Hi Andrew,
Try replacing “Display Name” with “samAccountName” in the code you quoted.
Hi Arjan,
Thanks for the quick response.
it manage to display the Username But without the domain.
is it possible to make it display Domain\username?
I’m thinking maybe we can hard code it somewhere on the following code.
E.g “contoso” + $userdisplaname (then it will display “contoso\username”)
(document.write(decodeURIComponent(”));)
Is it possible to specify different attributes other than displayName?
I have tried several from the list of AD attribute names from here: https://www.manageengine.com/products/ad-manager/help/csv-import-management/active-directory-ldap-attributes.html
However, everytime I save and refresh the page, it throws an exception and won’t load.
Thanks
James
Ignore me, didn’t realise DisplayName was mentioned twice in the code changes on default.aspx
After changing both to just “name” it works as expected.
still getting errors but i will figure it out. really like this ability
i tried this a couple times now and i am getting this error
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
i worked through with some of the comments to line 238
Line 238:System.DirectoryServices.SearchResultCollection results = ds.FindAll();
the compiler errors
c:\Windows\Web\RDWeb\Pages\en-US\Default.aspx(71,26): warning CS0168: The variable ‘objException’ is declared but never used
c:\Windows\Web\RDWeb\Pages\en-US\Default.aspx(183,46): warning CS0168: The variable ‘wue’ is declared but never used
c:\Windows\Web\RDWeb\Pages\en-US\Default.aspx(238,49): error CS0128: A local variable named ‘results’ is already defined in this scope
thanks john
default.aspx
I know this is an old thread, but i am trying to get this to work for RDS Web 2016. Everything works the same except that any space or special character is displayed using the ASCII code instead of the space or special character.
Any ideas how to fix that?
you got this working on 2016 ? what about the line 225 is 245 and that one piece of code breaks RDWEB for me. How did you get around that part?
and Forms authentication what does that mean is that another method of query to AD ?
I can get it working for IE and Chrome, but FireFox does not show the username–just blank
Bazinga. Got it…thank you all for reading my insaanity
Work on 2k19 ! Thank You
Hello Arjan,
I am getiing this error
CS0120: An object reference is required for the non-static field, method, or property ‘ASP.en_us_default_aspx.LDAPserver’.
I have configured my ldap in web.config
But in this line I’m getting the error pls help me asap.
System.DirectoryServices.DirectoryEntry entry = new System.DirectoryServices.DirectoryEntry(@”LDAP://” + LDAPserver, LDAPid, LDAPpw);
Thanks
Vignesh
Hi everyone,
Has anyone got this to work on Server 2019 or Server 2022? I have a server 2022 setup and it actually got it working. Unfortunately, I did not take a snapshot before I broke it and have not been able to get it back again.
I seem to be stuck here:
Server Error in ‘/RDWeb/Pages’ Application.
The (sAMAccountName=) search filter is invalid.
LDAP is working properly. Feel free to share any of your config files if you have it working.
Thanks for any assistance.