At one of our clients, TFS build server was choking for no good reason with the following error “Please contact your administrator. There was an error contacting the server. Technical information (for administrator): System.ServiceModel.ServiceActivationException.” Not very useful error message, isn’t it? TFS logs were a bit more informative and had the following error:
WebHost failed to process a request.
Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/4342953
Exception: System.ServiceModel.ServiceActivationException: The service '/tfs/queue/DefaultCollection/Services/v4.0/MessageQueueService2.svc' cannot be activated due to an exception during compilation. The exception message is: This collection already contains an address with scheme http. There can be at most one address per scheme in this collection. If your service is being hosted in IIS you can fix the problem by setting 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' to true or specifying 'system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters'.
Parameter name: item. ---> System.ArgumentException: This collection already contains an address with scheme http. There can be at most one address per scheme in this collection. If your service is being hosted in IIS you can fix the problem by setting 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' to true or specifying 'system.serviceModel/serviceHostingEnvironment/baseAddressPrefixFilters'.
Parameter name: item
at System.ServiceModel.UriSchemeKeyedCollection.InsertItem(Int32 index, Uri item)
at System.Collections.Generic.SynchronizedCollection`1.Add(T item)
at System.ServiceModel.UriSchemeKeyedCollection..ctor(Uri[] addresses)
at System.ServiceModel.ServiceHost..ctor(Type serviceType, Uri[] baseAddresses)
at System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(Type serviceType, Uri[] baseAddresses)
at System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(ServiceActivationInfo serviceActivationInfo, EventTraceActivity eventTraceActivity)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
--- End of inner exception stack trace ---
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
at System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String relativeVirtualPath, EventTraceActivity eventTraceActivity)
Process Name: w3wp
Now, that’s a much better error message. It actually tells us that the problem is caused by multiple bindings in IIS on TFS website and it tells us how this error can be fixed. I love such errors. Anyway, to fix the problem we need to add the following line to web.config file on TFS server:
<serviceHostingEnvironment
aspNetCompatibilityEnabled=”true"
multipleSiteBindingsEnabled="true"
/>
Then restart TFS Build Service, and it’s all good again…
Recently, I have been getting a lot of questions about what are the things we should consider before upgrading to SharePoint 2010. Here is my list:
- Ensure your environment is fully functioning before you perform an upgrade. No need to carry over any old issues into your new SharePoint 2010 environment
- Make sure you meet hardware requirements: 64-bit hardware, 4 cores CPU or better, 8Gb of RAM or better, enough disk storage, et cetera.
- Make sure you meet software requirements Windows Server 2008 SP2 or better, SQL Server 2005 SP3 or better, SharePoint prerequisites installed, member of Active Directory domain, and so on. Everything must be 64 bit.
- Plan browser support (IE6 is not supported) and Office client upgrade
- Get all your SharePoint servers to Service Pack 2 or later
- Run Pre-Upgrade check to identify potential issues that will prevent us from successfully upgrading to SharePoint 2010. Review the report. Fix the errors. Re-run pre-upgrade check utility. Repeat, if needed.
- Identify all customizations such as 3rd party webparts or look-n-feel changes. Make sure those will work properly in SharePoint 2010
- Backup all SharePoint databases. Seriously. Backup all SharePoint databases.
- Choose upgrade approach: in-place approach or database attach upgrade. Or hybrid approach.
- Test the upgrade process. Before you perform an upgrade in production environment, test the upgrade process and address any issues you found during testing
- UPGRADE all your SharePoint servers to SharePoint 2010 (finally)
- Evaluate the upgrade. Review logs, check update status troubleshoot issues and errors.
- Use Visual Upgrade to convert site collections to the SharePoint 2010 product look
- Completing the upgrade: configure service applications, update database permissions, configure authentication, validate the upgrade, etc.
- Enjoy the SharePoint 2010 awesomeness.
To learn more about how to build a sound SharePoint environment, check out our Upcoming Courses
At a presentation a few weeks ago someone asked me about capturing session details during authentication at an STS by way of frames and JavaScript. To paraphrase the question: “What prevents a malicious developer from sticking an RP within an iframe, cause a redirect to an STS, get some user to log in, and then capture the details through JavaScript from the parent page?” There are a couple of ways this problem can be solved. It’s a defense-in-depth problem where on their own, each piece won’t close every attack vector, but when used together you end up with a pretty solid solution.
- First, a lot of new browsers will actually prevent cross-frame JavaScript calls when SSL is involved. Depending on the browser, the JavaScript will throw the equivalent of an Access Denied exception. This is not the case with all browser versions though. Older browsers may not do this.
- Second, some browsers will not allow you to host an SSL page in a frame if the parent page is not using SSL. The easy fix for the malicious developer is to simply use SSL for the parent site, but that could be problematic as the CA’s theoretically verify the sites requesting certificates.
- Third, you could write some JavaScript for the STS to bust out of the frame. It would look something like this:
if (top != self)
{
try
{
top.location.replace(self.location.href);
}
catch (e)
{
}
}
The problem with this is that it wouldn’t work if the browser has JavaScript disabled.
- Fourth, there is a new HTTP header that Microsoft introduced in IE 8 that tells the browser that if the requested page is hosted in a frame to simply stop processing the request. Safari and Chrome support it natively, and Firefox supports it with the NoScript add on. The header is called X-Frame-Options and it can have two values: “DENY” which prevents all requests, and “SAMEORIGIN” which allows a page to be rendered if the parent page is the same page. E.g. the parent is somesite.com/page and the framed page is somesite.com/page.
There are a couple of ways to add this header to your page. First you can add it via ASP.NET:
Context.Response.AddHeader("x-frame-options", "DENY");
Or you could add it to all pages via IIS. To do this open the IIS Manager and select the site in question. Then select the Feature “HTTP Response Headers”:

Select Add… and then set the name to x-frame-options and the value to DENY:

By keeping in mind these options you can do a lot to prevent any exploits that use frames.
Well, I was looking through SharePoint web.config file one time and asked myself how SharePoint knows where to find its contents. I know, it's a weird question to ask, but what can I say… I am that kind of person. Anyways, obviously looking through web.config did not yield any results, so I looked googled it. By the way, I do not know why, but I keep calling searching online as "googling" even though I switched to Bing a while ago. Old habits die hard, I guess, but I digress… After a little of research, I have found this blog post that has explained how SharePoint 2010 or 2007 finds its content. Basically, this is how it works:
- User types in the URL of the SharePoint website
- IIS gets a HTTP request, then handles it based on port number or a host header specified in the request
- Matching SharePoint website hands all requests to ASP.NET (thanks to wild card ISAPI mappings), which does all kinds of magic with the request
- Then, SPRequest HTTP module (defined in the web.config) gets into the play. SPRequest module does its magic too, and eventually request the location of the SharePoint configuration database from the registry. Connection string for the SharePoint configuration database is set using DSN key at HKLM\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\Secure\ConfigDB (for SharePoint 2007) or HKLM\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\14.0\Secure\ConfigDB (for SharePoint 2010). So, this is the registry value, you need to change, if you ever need to move configuration database.
- SharePoint configuration database knows where everything about the SharePoint farm, so it is (relatively speaking) "no brainer" from that point on… and the user sees an awesome SharePoint page on the screen.
It's that easy J
In a domain environment it is really useful to switch user contexts in a web application.
This could be if you are needing to log in with credentials that have elevated permissions
(or vice-versa) or just needing to log in as another user.
It’s pretty easy to do this with Windows Identity Foundation and Claims Authentication.
When the WIF framework is installed, a service is installed (that is off by default)
that can translate Claims to Windows Tokens. This is called (not surprisingly)
the Claims to Windows Token Service or (c2WTS).
Following the deploy-with-least-amount-of-attack-surface methodology, this
service does not work out of the box. You need to turn it on and enable which
user’s are allowed to impersonate via token translation. Now, this doesn’t mean
which users can switch, it means which users running the process are allowed to switch.
E.g. the process running the IIS application pools local service/network service/local
system/etc (preferably a named service user other than system users).
To allow users to do this go to C:\Program Files\Windows Identity Foundation\v3.5\c2wtshost.exe.config
and add in the service users to <allowedCallers>:
<windowsTokenService>
<!--
By default no callers are allowed to use the Windows
Identity Foundation Claims To NT Token Service.
Add the identities you wish to allow below.
-->
<allowedCallers>
<clear/>
<!-- <add value="NT AUTHORITY\Network Service"
/> -->
<!-- <add value="NT AUTHORITY\Local Service" />
–>
<!-- <add value="nt authority\system" /> –>
<!-- <add value="NT AUTHORITY\Authenticated Users"
/> -->
</allowedCallers>
</windowsTokenService>
You should notice that by default, all users are not allowed. Once you’ve done
that you can start up the service. It is called Claims to Windows Token
Service in the Services MMC snap-in.
That takes care of the administrative side of things. Lets write some code.
But first, some usings:
using System;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.WindowsTokenService;
The next step is to actually generate the token. From an architectural perspective,
we want to use the UPN claims type as that’s what the service wants to see.
To get the claim, we do some simple LINQ:
IClaimsIdentity identity = (ClaimsIdentity)Thread.CurrentPrincipal.Identity;
string upn = identity.Claims.Where(c => c.ClaimType == ClaimTypes.Upn).First().Value;
if (String.IsNullOrEmpty(upn))
{
throw new Exception("No UPN claim found");
}
Following that we do the impersonation:
WindowsIdentity windowsIdentity = S4UClient.UpnLogon(upn);
using (WindowsImpersonationContext ctxt = windowsIdentity.Impersonate())
{
DoSomethingAsNewUser();
ctxt.Undo(); // redundant with using { } statement
}
To release the token we call the Undo() method, but if you are within a using { }
statement the Undo() method is called when the object is disposed.
One thing to keep in mind though. If you do not have permission to impersonate
a user a System.ServiceModel.Security.SecurityAccessDeniedException will be thrown.
That’s all there is to it.
Implementation Details
In my opinion, these types of calls really shouldn’t be made all that often.
Realistically you need to take a look at how impersonation fits into the application
and then go from there. Impersonation is pretty weighty topic for discussion,
and frankly, I’m not an expert.
From Microsoft Marketing, ADFS 2.0 is:
Active Directory Federation Services 2.0 helps IT enable users to collaborate across
organizational boundaries and easily access applications on-premises and in the cloud,
while maintaining application security. Through a claims-based
infrastructure, IT can enable a single sign-on experience for end-users to
applications without requiring a separate account or password, whether applications
are located in partner organizations or hosted in the cloud.
So, it’s a Token Service plus some. In a previous post I had said:
In other words it is a method for centralizing
user Identity information, very much like how the Windows Live and OpenID systems
work. The system is reasonably simple. I have a Membership data store
that contains user information. I want (n) number of websites to use that membership
store, EXCEPT I don’t want each application to have direct access to membership data
such as passwords. The way around it is through claims.
The membership store in this case being Active Directory.
I thought it would be a good idea to run through how to install ADFS and set up an
application to use it. Since we already discussed how to federate an application
using FedUtil.exe, I will let you go through the steps
in the previous post. I will provide information on where to find the Metadata
later on in this post.
But First: The Prerequisites
-
Join the Server to the Domain. (I’ve started the installation of ADFS three times
on non-domain joined systems. Doh!)
-
Install the latest .NET Framework. I’m kinda partial to using SmallestDotNet.com created
by Scott Hanselman. It’s easy.
-
Install IIS. If you are running Server 2008 R2 you can follow these
steps in another post, or just go through the wizards. FYI: The post installs
EVERY feature. Just remember that when you move to production. Surface
Area and what not…
-
Install PowerShell.
-
Install the Windows Identity Foundation: http://www.microsoft.com/downloads/details.aspx?FamilyID=eb9c345f-e830-40b8-a5fe-ae7a864c4d76&displaylang=en
-
Install SQL Server. This is NOT required. You only need
to install it if you want to use a SQL Database to get custom Claims data. You
could also use a SQL Server on another server…
-
Download ADFS 2.0 RTW: http://www.microsoft.com/downloads/details.aspx?familyid=118c3588-9070-426a-b655-6cec0a92c10b&displaylang=en
The Installation
Read the terms and accept them. If you notice, you only have to read half of
what you see because the rest is in French. Maybe the lawyers are listening…these
things are getting more readable.
Select Federation Server. A Server Proxy allows you to use ADFS on
a web server not joined to the domain.
We already installed all of these things. When you click next it will check
for latest hotfixes and ask if you want to open the configuration MMC snap-in.
Start it.
We want to start the configuration Wizard and then create a new Federation Service:
Next we want to create a Stand-alone federation server:
We need to select a certificate for ADFS to use. By default it uses the SSL
certificate of the default site in IIS. So lets add one. In the IIS Manager
select the server and then select Server Certificates:
We have a couple options when it comes to adding a certificate. For the sake
of this post I’ll just create a self-signed certificate, but if you have a domain
Certificate Authority you could go that route, or if this is a public facing service
create a request and get a certificate from a 3rd party CA.
Once we’ve created the certificate we assign it to the web site. Go to the website
and select Bindings…
Add a site binding for https:
Now that we’ve done that we can go back to the Configuration Wizard:
Click next and it will install the service. It will stop IIS so be aware of
that.
You may receive this error if you are installing on Server 2008:
The fix for this is here: http://www.syfuhs.net/2010/07/23/ADFS20WindowsServiceNotStartingOnServer2008.aspx
You will need to re-run the configuration wizard if you do this. It may complain
about the virtual applications already existing. You two options: 1) delete
the applications in IIS as well as the folder C:\inetpub\adfs; 2) Ignore the warning.
Back to the installation, it will create two new Virtual Applications in IIS:
Once the wizard finishes you can go back to the MMC snap-in and fiddle around.
The first thing we need to do is create an entry for a Relying Party. This will
allow us to create a web application to work with it.
When creating an RP we have a couple options to provide configuration data.
Since we are going to create a web application from scratch we will enter in manual
data. If you already have the application built and have Federation Metadata
available for it, by all means just use that.
We need a name:
Very original, eh?
Next we need to decide on what profile we will be using. Since we are building
an application from scratch we can take advantage of the 2.0 profile, but if we needed
backwards compatibility for a legacy application we should select the 1.0/1.1 profile.
Next we specify the certificate to encrypt our claims sent to the application.
We only need the public key of the certificate. When we run FedUtil.exe we can
specify which certificate we want to use to decrypt the incoming tokens. This
will be the private key of the same certificate. For the sake of this, we’ll
skip it.
The next step gets a little confusing. It asks which protocols we want to use
if we are federating with a separate STS. In this case since we aren’t doing
anything that crazy we can ignore them and continue:
We next need to specify the RP’s identifying URI.
Allow anyone and everyone, or deny everyone and add specific users later? Allow
everyone…
When we finish we want to edit the claim rules:
This dialog will allow us to add mappings between claims and the data within Active
Directory:
So lets add a rule. We want to Send LDAP Attributes as Claims
First we specify what data in Active Directory we want to provide:
Then we specify which claim type to use:
And ADFS is configured! Lets create our Relying Party. You can follow
these steps: Making
an ASP.NET Website Claims Aware with the Windows Identity Foundation. To
get the Federation Metadata for ADFS navigate to the URL that the default website
is mapped to + /FederationMetadata/2007-06/FederationMetadata.xml. In my case
it’s https://web1.nexus.internal.test/FederationMetadata/2007-06/FederationMetadata.xml.
Once you finish the utility it’s important that we tell ADFS that our new RP has Metadata
available. Double click on the RP to get to the properties. Select Monitoring:
Add the URL for the Metadata and select Monitor relying party. This will periodically
call up the URL and download the metadata in the event that it changes.
At this point we can test. Hit F5 and we will redirect to the ADFS page.
It will ask for domain credentials and redirect back to our page. Since I tested
it with a domain admin account I got this back:
It works!
For more information on ADFS 2.0 check out http://www.microsoft.com/windowsserver2008/en/us/ad-fs-2-overview.aspx or
the WIF Blog at http://blogs.msdn.com/b/card/
Happy coding!
This is more of a place for me to store something I use fairly often, but can never
remember off the top of my head. This script, when run as administrator, will
install all the features of IIS for developing on Windows 7. Mind you, this
is the prettified* version so it’s web-readable.
START /WAIT DISM /Online /Enable-Feature
/FeatureName:IIS-ApplicationDevelopment
/FeatureName:IIS-ASP
/FeatureName:IIS-ASPNET
/FeatureName:IIS-BasicAuthentication
/FeatureName:IIS-CGI
/FeatureName:IIS-ClientCertificateMappingAuthentication
/FeatureName:IIS-CommonHttpFeatures
/FeatureName:IIS-CustomLogging
/FeatureName:IIS-DefaultDocument
/FeatureName:IIS-DigestAuthentication
/FeatureName:IIS-DirectoryBrowsing
/FeatureName:IIS-FTPExtensibility
/FeatureName:IIS-FTPServer
/FeatureName:IIS-FTPSvc
/FeatureName:IIS-HealthAndDiagnostics
/FeatureName:IIS-HostableWebCore
/FeatureName:IIS-HttpCompressionDynamic
/FeatureName:IIS-HttpCompressionStatic
/FeatureName:IIS-HttpErrors
/FeatureName:IIS-HttpLogging
/FeatureName:IIS-HttpRedirect
/FeatureName:IIS-HttpTracing
/FeatureName:IIS-IIS6ManagementCompatibility
/FeatureName:IIS-IISCertificateMappingAuthentication
/FeatureName:IIS-IPSecurity
/FeatureName:IIS-ISAPIExtensions
/FeatureName:IIS-ISAPIFilter
/FeatureName:IIS-LegacyScripts
/FeatureName:IIS-LegacySnapIn
/FeatureName:IIS-LoggingLibraries
/FeatureName:IIS-ManagementConsole
/FeatureName:IIS-ManagementScriptingTools
/FeatureName:IIS-ManagementService
/FeatureName:IIS-Metabase
/FeatureName:IIS-NetFxExtensibility
/FeatureName:IIS-ODBCLogging
/FeatureName:IIS-Performance
/FeatureName:IIS-RequestFiltering
/FeatureName:IIS-RequestMonitor
/FeatureName:IIS-Security
/FeatureName:IIS-ServerSideIncludes
/FeatureName:IIS-StaticContent
/FeatureName:IIS-URLAuthorization
/FeatureName:IIS-WebDAV
/FeatureName:IIS-WebServer
/FeatureName:IIS-WebServerManagementTools
/FeatureName:IIS-WebServerRole
/FeatureName:IIS-WindowsAuthentication
/FeatureName:IIS-WMICompatibility
/FeatureName:WAS-ConfigurationAPI
/FeatureName:WAS-NetFxEnvironment
/FeatureName:WAS-ProcessModel
/FeatureName:WAS-WindowsActivationService
*Interesting that “prettified” is a word according to Live Writer.
If you're getting a blank page when you are trying to access SharePoint Central Administration site, then check your IIS settings. To be more specific, check the authentication settings for SharePoint Central Administration website in IIS. Make sure that you have Windows Authentication enabled and Anonymous Authentication disabled.
This must be the shortest blog post I have ever written. But taking into consideration that it was written on the first day of work after the holiday season, it's not so bad. Happy New Year everyone!!! J
Over the last few months I have been collecting best practices for deploying ASP.NET
applications to production. The intent was to create a document that described
the necessary steps needed to deploy consistent, reliable, secure applications that
are easily maintainable for administrators. The result was an 11 page document.
I would like to take a couple excerpts from it and essentially list what I believe
to be key requirements for production applications.
The key is consistency.
-
Generate new encryption keys
The benefit to doing this is that internal hashing and encrypting schemes use different
keys between applications. If an application is compromised, the private keys that
can get recovered will have no effect on other applications. This is most important
in applications that use Forms Authentication such as the member’s section. This Key
Generator app is using built-in .NET key generation code in the RNGCryptoServiceProvider.
-
Version and give Assemblies Strong Names
Use AssemblyInfo.cs file:
[assembly: AssemblyTitle("NameSpace.Based.AssemblyTitle")]
[assembly: AssemblyDescription("This is My Awesome Assembly…")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("My Awesome Company")]
[assembly: AssemblyProduct("ApplicationName")]
[assembly: AssemblyCopyright("Copyright © 2009")]
[assembly: AssemblyTrademark("TM Application Name")]
[assembly: AssemblyCulture("en-CA")]
Strong names and versioning is the backbone of .NET assemblies. It helps distinguish
between different versions of assemblies, and provides copyright attributes to code
we have written internally. This is especially helpful if we decide to sell any of
our applications.
-
Deploy Shared Assemblies to the GAC
-
Assemblies such as common controls
-
gacutil.exe -I "g:\dev\published\myApp\bin\myAssembly.dll"
If any assemblies are created that get used across multiple applications they should
be deployed to the GAC (Global Assembly Cache). Examples of this could be Data Access
Layers, or common controls such as the Telerik controls. The benefit to doing this
is that we will not have multiple copies of the same DLL in different applications.
A requirement of doing this is that the assembly must be signed and use a multipart
name.
-
Pre-Compile Site: [In Visual Studio] Build > Publish Web Site
Any application that is in production should be running in a compiled state. What
this means is that any application should not have any code-behind files or App_Code
class files on the servers. This will limit damage if our servers are compromised,
as the attacker will not be able to modify the source.
-
Encrypt SQL Connections and Connection Strings
Encrypt SQL Connection Strings
Aspnet_regiis.exe -pe connectionStrings -site myWebSite -app /myWebApp
Encrypt SQL Connections
Add ‘Encrypt=True’ to all connection strings before encrypting
SQL Connections contain sensitive data such as username/password combinations for
access to database servers. These connection strings are stored in web.config files
which are stored in plain-text on the server. If malicious users access these files
they will have credentials to access the servers. Encrypting the strings will prevent
the ability to read the config section.
However, encrypting the connection string is only half of the issue. SQL transactions
are transmitted across the network in plain-text. Sensitive data could be acquired
if a network sniffer was running on a compromised web server. SQL Connections should
also be encrypted using SSL Certificates.
-
Use key file generated by Strong Name Tool:
C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\sn.exe
“sn.exe -k g:\dev\path\to\app\myAppKey.snk”
Signing an assembly provides validation that the code is ours. It will also allow
for GAC deployment by giving the assembly a signature. The key file should be unique
to each application, and should be kept in a secure location.
-
Set retail=”true” in machine.config
<configuration>
<system.web>
<deployment retail="true"/>
</system.web>
</configuration>
In a production environment applications do not want to show exception errors or trace
messages. Setting the retail property to true is simple way to turn off debugging,
tracing, and force the application to use friendly error pages.
In part 2 I continue my post on more best practices for deployment to a production
environment.
The BBSM (Building Broadband Service Manager) is a Windows 2000 box that acts as a
gateway to the internet for customer access. It handles that login page when
you connect to the open WiFi network. It is the most convoluted piece of [insert
noun here]. The guy who signs my paycheck had asked me a few weeks back to redesign
said login page in keeping with corporate designs. It was also requested that
it be mobile browser friendly. Classic ASP, running JScript (yes, JScript),
in IIS 5 on Windows 2000 behind ISA Server 2000. The new layout was done in
about an hour, and it looks pretty good. It has been 3 weeks and I still can't
get the freakin mobile code working.
In a moment of insanity (clarity?) I got the bright idea to install .NET on the box
and rewrite all the pages from scratch. Rewriting took a couple hours, and the
mobile support works. Go to set it up on the box (which must be done via USB
key, via Ops guy, via physically walking to box in DataCentre {which I don't have
access to}) and come to find permission errors for the ASPNET account doing COM stuff.
Needless to say I hate COM Interop with a passion. I even sunk to the level
of giving the ASPNET account full admin privileges. Turns out Windows 2000 does
not like COM Interop either.
"It looks nice if you use a laptop" was my statement to the boss.
His response was "everyone is using PDA's and their iPhones. Maybe
10 customers use laptops."
Moral of the story: If the original code was written in the same year you turned 11,
run. Quickly.