Michael's profileMike's RavingsBlogLists Tools Help

Blog


    June 17

    MOSS 2007 SP2Config wizard failure on step 8 of 9

           So after numerous installs, I ran into an issue with this install that totally hosed a number of servers. The boxes affected were all 32 bit MOSS 2007 enteprise machines, single Server dev implementations using SQL Server 2008 om Windows Server 2008.
     
          On step 8 it would consistently fail. The upgrade logs indicated a conflict in one of the search databases:
    "ReflexiveUpgrade [SPServer Name=<machinename> Parent=SPFarm Name=<configDBName>] failed.
    [SPManager] [ERROR] [6/17/2009 12:55:20 AM]: An update conflict has occurred, and you must re-try this action. The object SPSearchDatabase Name=<WSSSearchDB Name> Parent=SPDatabaseServiceInstance is being updated by domain\installaccountname, in the OWSTIMER process, on machine <machinename>.  View the tracing log for more information about the conflict.
    [SPManager] [ERROR] [6/17/2009 12:55:20 AM]:    at Microsoft.SharePoint.Administration.SPConfigurationDatabase.StoreObject(SPPersistedObject obj, Boolean storeClassIfNecessary, Boolean ensure)"
     
        I tried the solutions posted here:
     
     and other places with no luck. Could not get the config wizard to finish. Out of frustration, I ran the wizard 1 more time, as soon as it hit step 8 I clicked the "cancel" button and allow the config wizard to stop in a more controlled manner. I reran the config wizard right after that and it ran to completion!
     
      Cannot say at this point in time I have any idea wh this worked, what was broken, but in addition to the solutions provided at the link above (which do work for many folks), this is another option to try.
     
    March 16

    MOSS 2007: meeting DOD DISA STIG compliance

    MOSS 2007: Meeting DOD DISA STIG restrictions with your farm

     

                    Throughout my experiences with MOSS, I have done more architectures, configurations, installations, than I can count. I have had many challenges in farm configuration but none of these come close to the challenge of meeting DOD DISA STIG restrictions (listing: http://iase.disa.mil/stigs/stig/index.html)  for a MOSS farm. There were many challenges, some technical some organic in nature. I am going to attempt here to give you a strategy for approaching this task, some lessons learned, and where possible some areas you need to be careful of.  Anyone who has gone through this and would like to share, PLEASE DO. This can be quite a challenge.

                    First off, when it comes to STIGs. If you have looked at the list, they are not a “Change setting A at registry point A” type of listing. In many cases, there are different ways to achieve compliance with a particular item. A good example, had a client who needed to correct an issue with the VB Scripting engine on the OS. The way MS recommends doing this, is to restrict security (ACLs) on the dll. The client’s methodology was to unregister the dll. This caused serious issues for MOSS when it came to configuration and patching as this dll was accessed by the configuration manager.

                    Adding to this, currently Microsoft does not provide any official guidance on implementing the STIGs set. So most companies to not have that guidance on how to implement these without disabling operating system components.

    Preparation

                    When you start down this road, do not assume that just because in one house you ran through compliance with no issue, that you will have no problem at another one. First recommendation here, from the onset, get a virtualized environment that matches the production environment as much as possible. It MUST at least have the same operating system version and patch level.  Get this designed up from and in place ASAP. Your life will be much easier if you sort the STIG issues out up front for the particular environment than on the end of it.  It is likely, if you are in the DISA STIG environment you will have some firewall restrictions as well. It is really best if you also configure your test environment to match the same firewall restrictions you will have in production.

                    Second recommendation, patch your MOSS environment (OS and applications) up to the latest  service pack and cumulative update. There are some restrictions (such as disabling SSL v2.0) which are much easier to correct when your OS is fully patched.

                    Before we move on to planning your process keep this is mind, MOSS needs more permissions during the installation, and configuration process than it does during normal operation. The configuration wizard will utilize ports and OS components that you may not use otherwise. For this reason you need to be sure your restrictions are tested against the MOSS configuration wizard, SSP configuration, Web Application configuration, etc as well as just a normally operating MOSS environment.

     

    Process

                    Third recommendation, plan out your process for reaching compliance before you start.  You will need to plan on getting time from the networking, platform (OS), AD, and application security staff during this process. This may be 1 person or a dozen people depending on the size of your organization.

                    As for the process, one method which worked for us on a previous engagement. Apply the full set of STIGs for the company to your environment prior to installing MOSS. Once this is done, attempt to install and configure your application/Index server. Chances are this will fail in some manner. 

                    Assuming it does fail, the next step, break the STIGS up first by severity level (High, Medium, Low, Informational). For each of these groups, break the STIG up further into groups of 20-30. Try to group them into functional areas (i.e. File ACLs, IE settings, service permissions, etc). If a particular group holds lower risk of causing issues you can group more than 20-30. A good example of this. We had 80 IE settings that posed an extremely low risk of causing MOSS issues, we grouped all together.

                    For each grouping, first apply the full group, and test the config/install you tried with the full set. If no issues occur, snapshot your VM, and move on to the next group. If the group causes an issue, roll back and move through the group 1 by 1 till you locate the setting that causes the issue.  Depending on staff availability, the manner in which certain STIGs are applied, and farm architecture, it can take between 1 to 4 weeks to run through 700 settings in this manner. 

                    Keep in mind, the vast majority of settings will not impact MOSS. In the last project we worked through out of 600+ settings 10 caused issues. You will need to refine your process to find issues like this on an ongoing basis as STIGs are periodically updated and you will find the need to continually test these new restrictions. Although, the updates will come in much smaller chunks than the initial implementation (maybe average 6-20 additional settings every quarter).

                    One other note, reboot at least once after EACH group is applied. Some of these restrictions will not be implemented fully until you reboot. You absolutely need to reboot after each set of restrictions are thrown on the system.

     

    Areas of concern

                    While the implementation of STIGs are to an extent unique to the site. There are some areas of concern that have been encountered clients sites that I can provide a heads up on that may allow you to head off some issues:

    1.       File ACL’s – This is one of the biggest areas of concern. Many of MOSS’ issues here are the same for any ASP.Net based applications. Of particular concern  are the folders used for the ASP.Net JIT compiler and .Net framework assemblies. Many of these are located in c:\windows subfolders and are accessed with user accounts that are NOT in the administrators group. It is HIGHLY recommended you do not just throw all MOSS service accounts into the administrators group. This is in violation of security best practices for MOSS and can lead to other problems. In addition to the c:\windows there is also another important folder at: C:\Program Files\Microsoft Office Servers\12.0. Under this folder, you have the office server web services, on query servers you also have file shares (by default) than contain your search indexes, as well as other resources your farm will need. See references for technet article on MOSS File permissions.

    2.        Component Services/DTS – You will have a hard time finding anyone at MS (even the product team) to tell you any more than “I don’t know” about whether component services and DTS are necessary for MOSS to function. After much debugging and sleepless nights, I can say without it functional, your MOSS farm will not function properly. Whether it is component services or an underlying component,  when this goes down particularly Search begin to malfunction. This also seems to impact the Adobe Ifilters as well. Generally, registry or file ACLs are the culprits for bringing this service down.

    3.       Local Security Policies

    a.       Log in as a service – Some MOSS service accounts (depending on configuration) need to be able to log in as a service (i.e. web app pool accounts, search service accounts, excel services, etc). When these right are revoked, MOSS services will fail to start on reboot.

    b.      FIPS – FIPS is a requirement by many government agencies. However, it is not a throw the setting and life is good with MOSS. You will need to make adjustments to your servers, web apps, and moss configuration to implement it. See the FIPS references to get started on this.

    4.       Registry ACL’s – Registry ACLs can cause issue across the board to just about any component in the operating system or MOSS. You need to carefully inspect any registry ACL’s that are being applied. References for technet article on MOSS registry permissions

     

    Summary

                    Hopefully, this will give you a good start on implementing the DISA STIGs on your environment and help you avoid some pain. Please feel free to post any comments/links, etc from your own experience. This can be a daunting challenge and until we have good guidance from MS on securing the platform for these, any community assistance is greatly appreciated.

    Reference

    1.     MOSS File/Registry permissions: http://technet.microsoft.com/en-us/library/cc721638.aspx

    2.     Firewall rules: http://blogs.msdn.com/joelo/archive/2007/02/13/protocols-ports-and-firewall-rules.aspx

    3.     FIPS References: http://support.microsoft.com/kb/935434, http://support.microsoft.com/kb/811833

    November 16

    Securing MOSS 2007 in a heavily managed environment

                    Recently I have had the opportunity to implement a MOSS farm in an extremely secure and managed environment. This included HIPPA, SOX, and DOD compliance, to an extreme I had never seen before.

                    In such an environment, implementing MOSS can be an extreme challenge to say the least. Using some hindsight looking back at the challenges the project encountered though, I can say a lot of the risks can be mitigated somewhat with some forward planning. I will first give an extremely high level description of the farm, and I will break down the risks into 2 main groups (in no particular order), either of which can severely impact your architecture. There is way too much to go in depth on everything, I will however provide links to help you out if you need to face such a project.

    Farm Layout

                    The farm we created had 4 WFEs, I App/Index/CA server and a clustered SQL 05 (A/P) on the back end. It had only 5 Site collections, 1 of which had to be broken off to its own Web app using a separate SQL instance on the cluster for its content DB (for security/compliance reasons).  It utilized standard NTLM AD auth for the farm with OOB search.  The WFEs were separated out into 2 groups of 2 with each group of 2 having their own load balancer and host header. The entire system was based on 64 bit architecture.

    Networking/Connectivity

                    In our implementation, we had some pretty heavy firewalls around each of the servers. This included firewalls around the network, one for each MOSS server and one for the SQL cluster. Needless to say we had ,to be pretty exhaustive on a list of firewall rules. All told, we have 70+ firewall rules to allow the farm to talk.

                    To this point, you need to consider a few things. First of all, find a tool to independently test each rule to ensure it is open, and to trace what communication MOSS is attempting. I have seen a lot of references on MOSS ports online, NONE of them hit all the ports we needed. No matter how good you are, it is likely you will miss at least a couple. 

    Also, keep in mind that MOSS uses different ports depending on what you are doing.  For example a MOSS farm already configured, running normally will use a smaller set of ports than one that is being actively configured or patched. For example, we found all calls to the DB (UDP 1433, 1434) that only occurred during the config wizard and never any other time.  Also MOSS will utilize different ports depending on if it uses Kerberos or SSL, or LDAP/AD, or depending on how your SQL cluster is configured.

    You would be wise to have a nice, easy to read chart on all the servers and services you need to hit, particularly your non-MOSS servers such as SQL, AD servers, DNS Servers, SMTP, etc. They will likely be firewalled as well.

     IMO, you need to resist the temptation to just grab a huge list from the web and push all the rules it gives you out there. It is a fast track to getting your farm up but in the long run, such a game plan will leave you opening up holes in your firewall that you will never use. As painful as the firewall can be, it is there for a reason and you do not want to open up security holes that can leave you or your client failing an audit.

    You will want to sit down with the networking people and explain everything you are looking for up front. Be extremely anal. Down to the specific host headers (in quotes), ports protocols and be prepared to explain in deep detail why each port is used. Any network engineer in such an environment that is worth his salt will ask for this info. Also, set the expectation with them that while you will try to nail as many rules as possible, you will miss some and you will need to define the process that will allow you to add additional ports. Most network admins will appreciate that you want to open as few as possible at first and then slowly open additional ones as you need them.

    Links:

    http://blogs.msdn.com/joelo/archive/2007/02/13/protocols-ports-and-firewall-rules.aspx

     

    File permissions

                    In our implementation, our server OS’ also had some serious compliance requirements for the DOD restrictions. This meant the compliance software which pushed these rules removed tons of ACLs from the file system and registry, disabled services, blocked ports, reset IE permissions, and a few other fun things. You would be wise to ask for a list of these up front and exclusive obtain exclusive access to the person responsible for maintaining the list of restrictions. Depending on the scope of your restrictions, finding the resolution to this can involve accessing multiple MS product teams or reverse engineering ACL, service, and Group Policy permissions required for MOSS, WSS, IIS, Asp.Net, .Net (all versions), COM+, and even Windows Server 2003 itself. There were rules in there that actually killed the OS.

                    Keep in mind, if you have DOD compliance, you will need to provide detailed justification for each and every exclusion you identify. Usually, it has to be more in depth than “It breaks ASP.Net”. There a few good links I found on technet to at least get you moving on this. However, if you are doing this for the first time for any of the dependencies for MOSS (IIS, Asp.Net, etc), you would be wise to call MS premier support and proactively get them involved in helping you debug this. Also, you will need someone above the front line offshore support, they will not be able to help you in any manner on this. It is simply over their skill set.

                    Aside from MS support, the only other way I have found, is to get a functional system up and compare the permissions as they cause issues. It is a long, painfull process so buy some coffee and stock in whatever company makes it. You will be needing it if you take this route. Also, if at all possible, virtualize the machine you are testing with. Rebuilding a physical machine over and over will cost you weeks.  As with the firewalls, you need to resist the urge to just grant cascading permissions all over the place. You need to just open up what you need and nothing more. Particularly in the c:\windows folder. The DOD restricts these for a reason.

    Links:

    http://technet.microsoft.com/en-us/library/cc721637.aspx

    http://technet.microsoft.com/en-us/library/cc560966.aspx

    http://technet.microsoft.com/en-us/library/cc678863.aspx

    http://technet.microsoft.com/en-us/library/cc721638.aspx

    August 13

    Listing Site Collections with any User - RunWithElevatedPermissions is your friend

    Listing Site Collections with any User - RunWithElevatedPermissions is your friend

     

    So I had a web part I wrote a long time ago that would list sites and subsites on a given SPWeb as deep as you wanted to go and allow a good deal of flexibility on how they are displayed. Recently, I had the opportunity to modify it to scan a list of site collections in a given Web application.

    We had a setup with 80+ site collections for a clients extranet. Each site collection is a separate project for their clients. Some clients had multiple site collections and each of these had the need to explicit security encapsulation. So some users did in fact have access to multiple site collections, most would have 1 or maybe 2.

    The issue with this, the standard scanning code I had could not jump site collections. The only code samples I managed to find, involved using the Microsoft.SharePoint.Administration namespace. The issue there being, it would work fine for a MOSS administrator, however when another user hit the page with the web part, they would be given a generic MOSS Access Denied page.

    The answer I got came from merging a few posts I found while frantically googling some way to do this. It involves first using the RunWithElevatedPermissions directive to run under an account that has privileges to access all the site collections. The second part involves checking the DoesUserHavePermissions function off the root web on the site collections.

    It is not a huge breakthrough but if it helps save someone some time it is worth it. See code below:

     

      SPSecurity.RunWithElevatedPrivileges(delegate()

      {

          SPWebApplication webApplication = SPWebApplication.Lookup(serverUri);

          SPWeb rootweb;

     

          foreach (SPSite siteCollection in webApplication.Sites)

          {

             rootweb = siteCollection.OpenWeb();

             //do not add it is the current user does not have access to the site

             if (rootweb.DoesUserHavePermissions(objOriginalUser.Name,    

                SPBasePermissions.ViewPages) == true)

                {

                 if (rootweb.Url.Contains(startingPointURL + "personal/") == false)

                 {

                     userWebInfo = new clsUserWebInfo();

                     userWebInfo.ID = rootweb.ID;

                     if (rootweb.Name.Trim() == "")

                     {

                         userWebInfo.Name = rootweb.Title;

                      }

                      else

                      {

                         userWebInfo.Name = rootweb.Name;

                      }

                                                            userWebInfo.Description = rootweb.Description;

    userWebInfo.Title = rootweb.Title;

    userWebInfo.URL = rootweb.Url;

    alUserWebs.Add(userWebInfo);

    }

                   }

                 }

              });

    May 30

    Using the MOSS Imaging web service to download images(Imaging.asmx)

    Using the MOSS Imaging web service to download images(Imaging.asmx)

     

                    I recently had the opportunity to utilize the Imaging web service to scan for images, analyze meta-data and download images from a MOSS farm. It was not too bad to do but one of the major issues I had was finding resources with samples. So, I thought I would rectify the lack of information out there with some samples of my own. 

                    First to find the asmx, it is located at “/_vit_bin/Imaging.asmx” from any site within your portal. Many of the functions (such as listing image libs will be done relative to the site you reference the asmx from). Without further ado, here are some samples for tasks I needed to accomplish.

    Note:

    -          Imaging() class is a web reference to imagining.asmx

    -          The Download call natively returns XML so yo uneed to run it through a conversion to byte

    -          If you need to get a reference on the Imagine web service check this on out on MSDN:

    http://msdn.microsoft.com/en-us/library/imaging.imaging.aspx

     

     

    Task 1: Getting a List of Image libs on a given site

    public static XmlNode GetPicLibListingXML(string imagingServiceURL)

     {

                    Imaging wsImaging = new Imaging();

                    wsImaging.UseDefaultCredentials = true;

                    wsImaging.Url = imagingServiceURL;

     

                    XmlNode xnPicLibs = wsImaging.ListPictureLibrary();

     

                    return xnPicLibs;

       }

     

    Sample return XML:


    <Library name="{3C1D52F5-5387-490A-9A2D-A9C99A208C00}" title="Tech Images" guid="3c1d52f5-5387-490a-9a2d-a9c99a208c00" url="Tech Images" xmlns="http://schemas.microsoft.com/sharepoint/soap/ois/" />

     

    Task 2: Listing Images in a given library

    public static XmlNode GetImageFileListing(string imagingServiceURL, string imageFileLibraryName)

     {

          Imaging wsImaging = new Imaging();

          ImageInfo curImageInfo = new ImageInfo();

           wsImaging.UseDefaultCredentials = true;

           wsImaging.Url = imagingServiceURL;

           XmlNode xnListItems = wsImaging.GetListItems(imageFileLibraryName, "");

     

           return xnListItems;

       }

     

    Task 3: Download Image(s)

    private const string ATTR_FILENAME = "name";

    private const string FILENAMESPACEURI = "http://schemas.microsoft.com/sharepoint/soap/ois/";

     

    public static bool DownloadImageFiles(string imagingServiceURL, string imageFileLibraryName, string[] fileNames, string saveToFolder)

     {

            Imaging wsImaging = new Imaging();

            wsImaging.UseDefaultCredentials = true;

            wsImaging.Url = imagingServiceURL;

     

           XmlElement parent = (XmlElement)wsImaging.Download(imageFileLibraryName, string.Empty, fileNames, 0, true);

     

           XmlNodeList files = parent.GetElementsByTagName("File", FILENAMESPACEURI);

     

            foreach (XmlNode file in files)

            {

                 if (Directory.Exists(saveToFolder) == false)

                {

                     Directory.CreateDirectory(saveToFolder);

                  }

               byte[] fileBytes = Convert.FromBase64String(file.InnerText);

                using (FileStream fs = File.OpenWrite(saveToFolder + file.Attributes[ATTR_FILENAME].Value))

                 {

                        BinaryWriter writer = new BinaryWriter(fs);

                        writer.Write(fileBytes);

                        writer.Close();

                    }

              }

     

              return true;

            }

    April 24

    Ajax enabled Web parts Sample

    Ajax enabled Web parts

                    I have run into numerous questions and comments on AJAX enabled web parts and seen way to many partial answers that get folks with sight of a functional web part only to dash their hopes. So I am going to throw my hat in the ring and post some code here from one of my AJAX enabled web parts which has been in production for 7 months now. Like everything else, there are different ways to do it so don’t think mine if the only way. It is just one of the ways that works.  I took some liberties here in assuming your current level of expertise in coding web parts and AJAX.  If further explanation is needed, please feel free to post some comments and I can adjust this post.

                    First, you can only have 1 Script manager object per page. A good way to do this, embed it in your master page.  Sooner or later someone is going to create another AJAX web part and if you allow folks to push script managers into web parts you will end up with a new web part that breaks yours and theirs.  It is easier to just put it in once in the master page and tell your web part developers to refrain from doing so in their code.

                    Next, use the oninit function as the point to set the async triggers and call the EnsureUpdatePanelFixups function I will include further down.

    Creating Update Panel object

                    I created a helper class to set the values on the update panel and other controls. Here is what I did for creating my Update Panel.  This function is called in the onInit class.

                    public static System.Web.UI.UpdatePanel CreateUpdatePanel(string id)

                {

                    System.Web.UI.UpdatePanel updatepanel = new System.Web.UI.UpdatePanel();

                    updatepanel.ID = id;

                    updatepanel.ChildrenAsTriggers = true;

                    updatepanel.UpdateMode = UpdatePanelUpdateMode.Conditional;

     

                    return updatepanel;

                }

     

                    This function is called in the onInit as well. It will override the usual javascript submit functions in MOSS.

     

    private void EnsureUpdatePanelFixups()

            {

                if (this.Page.Form != null)

                {

                    string formOnSubmitAtt = this.Page.Form.Attributes["onsubmit"];

                    if (formOnSubmitAtt == "return _spFormOnSubmitWrapper();")

                    {

                        this.Page.Form.Attributes["onsubmit"] = "_spFormOnSubmitWrapper();";

                    }

                }

                ScriptManager.RegisterStartupScript(this, typeof(ProcessSearch), "UpdatePanelFixup", "_spOriginalFormAction = document.forms[0].action; _spSuppressFormOnSubmitWrapper=true;", true);

            }

                   

    Finally once both the previous code blocks have been called:

    //Search submit button async postbaclk trigger

     AsyncPostBackTrigger UpdatePanel1SearchPostBackTrigger = new AsyncPostBackTrigger();

     UpdatePanel1SearchPostBackTrigger.ControlID = BTNSUBMITSEARCH;

     UpdatePanel1SearchPostBackTrigger.EventName = "Click";

     

     AsyncPostBackTrigger UpdatePanel1WorkflowSelectPostBackTrigger = new AsyncPostBackTrigger();

     UpdatePanel1WorkflowSelectPostBackTrigger.ControlID = DGWORKFLOWLIST;

     UpdatePanel1WorkflowSelectPostBackTrigger.EventName = "ItemCommand";

     

     UpdatePanel1.Triggers.Add(UpdatePanel1SearchPostBackTrigger);

     UpdatePanel1.Triggers.Add(UpdatePanel1WorkflowSelectPostBackTrigger);

     

    This last section will create async post backs triggers for the specified control events within the panel. Keep in mind, I specifically set the ID for the control and the event that will kick off the async postback. In this example it uses the Click event of a button in the update panel and the ItemCommand event from a datagrid that is inserted in the panel. The controls referenced here MUST be in the update panel

    April 02

    Good ways to screw up your WFEs part I

                    I imagine this will be a multiple part posting as off the top of my head, I think I could write an 1,110 page book on this topic. I got a new one the other day which I had never tried, but really, when it was shown to me, it scared the crap out of me.  One of the biggest things to take from these posts, have a solid and TESTED Backup/Recovery process in your organization. Do NOT just give it lip service, write it on paper, file it away (ironically probably in MOSS), and wait till your first major failure to fully test it.

                    I will give you a real life configuration I have worked with which would be majorly impacted. Adjust to your own real world scenario. So, let’s assume you have a 5 server production MOSS farm, 2 A/P clustered SQLs, 1 index, 2 WFEs. You have 3 web apps in your portal. You have a custom membership and role provider.  You have 4 or 5 custom web parts. Maybe a server control or 2 you have thrown in that uses an entry or 2 in your web.config app settings. For fun, let’s throw in a customized CAS file. You also have some SSL settings you had to configure in IIS as well as some Metabase configuration you had to screw with since you are using host headers across SSL. Maybe your did some IPBinding, who knows .

                    WFE1 is being a PITA. You get royally annoyed by the WSS Services misbehaving, or you are getting a freaky error on that box, or you are bored and a masochist so you are playing with your production configuration. For whatever reason, you decide to stop and start the WSS services on that WFE. You click stop. Give it a second, click start. Your memory issue or whatever issue prompted you to restart that service disappears. You are happy, for the moment.  Then your custom web parts start kicking off errors. Probably security errors.  As a matter of fact, you are getting sporadic errors where users cannot even log into your web applications through FBA anymore.  Most likely, you get issues where the server cannot resolve the url to the web apps anymore.  All hell is breaking loose. So what happened?

                    To see what this actually did, grab a virtual with MOSS on and at least 1 MOSS web app. Open ISS, open a windows explorer session to the inetpub folder for your web apps. Now that you’ve got your window open,  go into Central Administration and stop the WSS services. You will see your IIS web apps totally disappear along with the inetpub folder for your web apps in windows explorer. Start the WSS Services. They reappear.  Man, how did MOSS do that?  Geez, it is smart you say. Not really, it pulled the information it needed out of the MOSS farm configuration DB.  When you reactivated WSS Services, it rebuilt the web applications and your inetpub folder(s).

                    Back to your production scenario. You had those SSL settings, the metabase settings for your host header/SSL setup, your web config and Code Access security mods. Then you start to panic as you realize, MOSS rebuilt those web apps using the MOSS configuration DB. Your mods to IIS, web.config, CAS, etc, MOSS knows absolutely nothing about those. You make those settings totally outside of MOSS. In essence you just blew them away. What you have created is an opportunity to discover just how good your backup/recovery process is for the file system of your WFEs.

                    If you restart your WSS services through the central administration site, keep in mind, MOSS will rebuild the web applications on your WFE from the MOSS configuration DB. Any modifications you made outside the realm of MOSS, will be gone.  If you have not taken steps to back up your metabase and your file system mods you will now get to do them again, manually.

                    So, a couple lessons here. First, backing up MOSS involves more than just your DBs. If your DBs are all you are backing up. You are probably missing something. Second, even seemingly miniscule tasks like turning on and off WSS Services can have MAJOR impacts to your farm. Giving an untrained individual access to central admin and throwing a shiny new “Sharepoint Administrator” title on him/her, is akin to handing them a case of nitro glycerin, labeling them a demolitions expert.  Train your staff for the role and once trained they will give you point number 3….ALWAYS test even simple actions like this on a disposable test system BEFORE production. Not just a VPC sandbox, but a fully fledged test system that mimics production. A single server sandbox and a multi-server farm behave differently in many areas. If you have a multi-server production farm and you do all your testing/validation on a single server sandbox, you will have issues. Sooner or later you will create issues on your production environment, and they will be real fun to debug.  You can virtualize the entire test system, but create one and use it.

    Fun with MOSS Resource files

                    How many times have you gone through MOSS and looked at labels, descriptions, grid headers, etc, on the standard MOSS UI and thought, “Man…what I would not give to be able to change that wording!”? 

    After attending an architecture class in Atlanta with Bill English, my mischievous streak got the better of me, and I set out to figure that out myself.  I started with the “Application Management” page in central administration, otherwise known as applications.aspx.  I had assumed that the text was maybe somewhere in the DB. Given the Central admin content DB is actually not that large, I dove in and set out to find the point where this text constant was defined. To my dismay, I only found the text from the navigation/menu bar. This text can be changed through the MOSS site settings UI in central admin, I would not recommend running UPDATE queries directly to the MOSS DB. While I am on that topic, keep in mind this expedition was conducted on a virtual, please do not crack open the DB for fun on a production server.  This may seem like extreme common sense however, we all know common sense is not always that common.  If you crack open the MOSS DB on your production server and screw it up by making what you think is a minor change, well do not expect MS to be too helpful when you call support.

    Back to the hunt, After failing in the DB it occurred to me, that maybe just maybe there was a config/resource file guiding these constants. After all, MOSS is available in a ton of languages, and I could not imagine MS embedded all these text constants into the binaries for MOSS. To my surprise, indeed, MS has embedded a TON of those string literals your find throughout MOSS in a series of resource files which are located in the inetpub\wwwroot\wss\virtualDirectories\<web app dir>\App_GlobalResources folder. The one I was looking for, “Application Management”,was in the SPAdmin.en-US.resx file. So I spent a few minutes having some fun, modified the “Application Management” to “Application Manglement”. Changed a mess of the page descriptions, link text, even removed the “Zones” text and renamed it to “Incoming Vectors”, a term an instructor mentioned in a class which better describes the use of those values.

    So, in practicality, is there any use to this beyond having some fun and getting a little more insight into lower level design in MOSS? Well, I could certainly see some of my clients, particularly those who were more hung up on some of the wording in MOSS taking a good look into this. There are a LOT of resx files, A lot of values to modify. Is this something you would want to do? Maybe, but keep in mind if you did decide to take this and put it to use in a productive way:

    1.        Back up your original resx files.

    2.       You will need to modify these on each WFE.

    3.       You will need to add these modified files to your backup/restore planning.

     One more “little” thing. I would not in the slightest expect MS to be in a position to have tested what would happen to their application when folks modified the resx files. I have not tested this full fledged in a large farm, and I cannot guarantee that there is not some embedded code in the bowels of MOSS, that would go beserk when you modify these values. I would expect that you would be fine, after all this is what resx file usage is for, and embedding resx values (vs keys) into your app sort of defeats the purpose of having one. However, MS still hires humans to do their development, so I cannot guarantee that they always follow best practices. That being said, it is awfully fun to mess with

    March 25

    SharePoint Chaos Theory

                    Despite its bugs, limitations, and quirks, I truly believe MOSS is one of the best solutions out there for building collaborative and application portals. When properly utilized, it can empower a workforce to work together like they have never before. The key term there being “properly utilized”.  When not properly planned, you can end up with the nastiest rat’s nest and corporate money pit you have ever seen. MOSS is not something you just throw up and go with it needs to be planned out, and documented ahead of time, before you even think of installing it on any server?    The following are some of the critical steps/questions you need to sort though in earnest before embarking on an installation.

    1.       What features do you want?

    This seems like it would be an easy enough question. However it is not always. It all depends on WHY you want a portal? Are you looking for a corporate dashboard? A document repository? A system to manage workflows? Collaborative team sites? All of the above? None of it? Many clients I have worked with go out and buy licenses without answers to these questions. The problem is you can end up majorly underestimating or overestimating costs without these questions answered? Standard and Enterprise editions for example, are priced very differently. You cannot possibly know which version you want if you don’t know.

    2.       Hardware

    Hardware is one of the most frustrating components of any MOSS engagement I have been on.  I frequently find myself having to push hard for multiple Web Front ends, application servers, Clustered DBs.  I my opinion, if you are intending your portal to server as your company’s collaborative backbone, document repository, or anything more than just a general curiosity, you cannot afford to cheap out on hardware.  A single server implementation for a production environment is simply unacceptable and if that is all you have a budget for, you really have to accept that you cannot afford a SharePoint  solution. The recovery time for a single server, even with proper backups, can be significant. The smallest production environment I would suggest would be a dual WFE, with a dual SQL 2005 Active/Passive clustered backend.

    The DB clustering is the more critical because MOSS is the DB, All your content, configuration (minus whatever has been customized to the web.configs), everything is in the DB. If you have no DB you have no MOSS. Plus rebuilding a WFE is significantly easier than rebuilding a SQL server and reconfiguring all your permissions.

    Dual WFE’s are critical for multiple reasons, first of all, sooner or later you will need to patch a server. Given MS’ history with patches, having a single machine is a significant risk in this area. If the patch hoses your box, then your entire portal is gone until you repave and rebuild the web front end.  Also, unless you have the simplest of portals, you will need to configure search (crawling and indexing), MySites, and Profile imports among other things. Throw is workflows, custom web parts, branding, etc and you quickly begin to overwhelm your single WFE. Also, once you have multiple WFEs installed with load balancing, adding additional servers, application or WFE, becomes even easier. It is better to take the hit and pony up the cash for dual WFE’s. BTW, I have many clients running MOSS in virtualized environments successfully. The general way to go is virtualize WFEs and app servers, and leave your SQL boxes are dedicated non-virtualized machines.

    3.       Analyze your user base!!

    Like any web application, you need to look long and hard at your user base and how you plan on organizing them. I cannot count how frequently a MOSS engagement ends up getting stalled because of this item. So flash forward…you have your dream portal, it is full of content. How do your users access the content? How are they grouped? Do you want a security free for all with users accessing all content? Do you want to end up with massive inconsistent, individual user permissions assignments, and a nasty security model that nobody can figure out or clean up? You need to plan this out ahead of time, then you need to take a long hard look at your AD/LDAP/SQL  auth  model and verify it supports what you want to do. At least 50% of my MOSS engagements have resulted in a total re-architecture of a clients Active Directory structure.

     

    A quick note on AD re-architecture, if you are going to do this, then you need to do it right. You need to think enterprise when you do it not just fitting your AD structure into a MOSS shaped hole. You can easily get your AD folks into a homicidal rage when you make 200 AD groups that can only be used within MOSS, then follow up 3 months later with 400 more for your exchange groups, and  300 more for an in house web application.  

    4.       Map it out

    Crack open Visio, or something like it, and draw out your portal. Draw out your site collection(s), your  portal structure, your MOSS security groups, how they map to your authentication/role providers.  This is critical to sanity check your implementation. With the portal structure mapped out, the security groups defined, customizations like workflows, web parts, etc all defined (at a high level) in a single document, you can get the full picture of what you are putting together. You can identify critical flaws, pain points, etc. Without this, you are shooting in the dark.

    5.       Define Governance

    Microsoft has some awesome templates for this (http://technet.microsoft.com/en-us/office/sharepointserver/bb507202.aspx ).  You need to know who does what In the implementation. What your support contract is with the user base, who can create sites, who will manage MOSS from central admin, who will serve as site owners. It is also a good idea to put together a MOSS strategy team to help guide where you want to go. The best maintained and utilized MOSS installations I have seen are also the best governed. This is not a coincidence.

    6.       Plan the installation

    Do not just insert a CD and go for it. You will screw it up. You will have a set of service accounts (if you even make the proper service accounts) that expire 3 months down the road and shut down your SSP. Or your service accounts will not have the correct permissions. Trust me, it will get screwed up and 3-6 months down the road you will end up reinstalling and re-configuring everything or limping along with an unreliable and totally unsecured MOSS farm. Secure farms do not happen by accident or chance. They happen because someone took the time to properly plan them. Do your research, buy the MS Press SharePoint Administrators Companion, or hire a Gold Certified consulting partner to assist.

    7.       Know when you are in over your head

    The final guideline, know when you are in over your head. To be clear, I work for a MS gold certified partner. Since MOSS was Beta, I have run through dozens of architecture sessions and installs. My company has spent a lot of money to train me and my co-workers as has any gold certified partner. Your average IT department is not going to have the will or budget to train their folks to that extent,  especially when their staff will likely do 1 or 2 production MOSS installations at most. Creating an enterprise portal is not an insignificant task. It can have a huge positive impact on a company or it can have absolutely no positive impact depending on how it is planned and architected.

     

    February 28

    Adding LDAP Groups to MOSS groups amended

                   Continuing with the previous article I ran on programmatically adding LDAP groups(Domain Groups) to MOSS groups I ran into a fun little bug with MOSS that has the potential to really hose you on this effort. 2 things, make sure your LDAP groups are prefixed with the name of your ROLEPROVIDER name. This will need to match exactly the value you have in your web.config. Now once you do this the bug comes into play.
                  MOSS will do next to no validation on your group and username. It will certainly NOT call your providers to verify the Role name exists. In fact as long as you get the prefix of your custom membership OR custom role provider name in there, it will assume it is valid. So you can enter all sorts of groups and users that don't exist. The important thing here is you are 100% certain the names you enter are valid and that you utilize the ROLEPROVIDER prefix and not the MEMBERSHIPPROVIDER name. Now if you have been bitten by this bug, before you go on a killing spree, I do have some code to assist you in correcting this. Drop me a line and I can post it.
    January 29

    Search Customization: Adding Custom Content Type Meta-Data to Advanced Search (via SSP)

                    As many things in MOSS there are numerous ways to do something, and numerous levels. In this entry I am going through setting up a search scope in my SSP (vs. doing one local on a site collection), mapping  columns from a custom content type, and making a managed property searchable in an advanced search control. This is VERY simplistic but hopefully, you can take the guide given hear and apply it to whatever you need to do.   

                    Please check out the links I reference on the end of this article. These guys have some great and more in depth info on parts of this process.

    1.       Create and deploy Custom Content type

    2.       Create Search Scope(s)

    a.       Central Administration/SSP level

                                                                   i.      Set up the Search scope in the SSP administration

    1.       Click “Search settings “ link

    2.       In the Scopes section click “View Scopes”

    3.       Click “New Scope”

    4.       Enter all valid info and click “OK”

    5.       Click on you new scope and click “new rule”

    6.       Enter your rule and click OK.

    7.       Repeat #7 as many times as you need to complete your set of rules for the current scope.

                                                                 ii.      Once search scope is set up you will need to enable it on your various site collections that will need to utilize it.

    1.       To do this, do into the site collections settings menu (go to your root site, and in the site actions drop down go to all settings).

    2.       Click Search Scopes link (your new scope will be in here marked as “unused”)

    3.       Click on the link that says “Search Dropdown” it is in the search scope listing on one of the section titles.

    4.       Check the box next to your scope, set any appropriate order/default settings and click OK

    3.       Set Managed Properties for Content Type Columns

    a.       Navigate to the SSP Administration site, click “Search Settings”

    b.      Next click on the “Meta data property mappings” link

    c.       Click on “New Managed Property”

    d.      Enter the properties friendly name (no spaces), description, and select the data type

    e.      Then click the “add mapping” button

    f.        Find your property in the listing, select it, and click OK

    g.       Check the search scope checkbox if you want this managed property to be used in defining search scopes.

    h.      Click OK

    4.       Modify Advanced Search Web part to allow search on Managed properties

    a.       Open page that contains  the advanced search web part

    b.      Edit page, on the advanced search web part, modify shared web part

    c.       Under the properties section, find the entry in the Properties text box.

                                                                   i.      T o make your life simple,  copy it into some type of XML editor that will format it for you.

                                                                 ii.      First, in the “propertyDefs”  node, insert and entry for your Managed Property

                                                                iii.      Next, add it to every valid Result type (see the result types node

                                                               iv.      Copy your updated text into the Properties entry for the advanced search web part. You should  now be able to search on your property (make sure you run a full crawl from your SSP site to be safe.

     

     

    References

    1.     http://blah.winsmarts.com/2007-5-MOSS_2007_Trick_-_Search_Scopes_+_Managed_Properties__Tasty_Dish.aspx

    2.    http://blogs.msdn.com/cjohnson/archive/2006/10/19/moss-search-scopes-missing-in-action.aspx

    December 18

    MOSS Load Balanced URL

     

                    One of the fields least understood by someone first coming into MOSS is the Load Balanced URL which if a field required on the “Create Web Application” screen from central administration. It will default to the current server URL and port. If you have worked on a single web front end install, chances are you didn’t even give this field a second look. Chances are of you added another WFE to your farm, you suddenly scratched your head and murmured some type of expletive when you hit your second WFE and found all the links pointed back to your first WFE and no matter what you did you could not fix this.

                    Taken in it’s simplest, most basic explanation what you put in this field, is what all links on MOSS will be prefixed with.  So if you have DocumentA.doc in the Shared Doc lib off of your main site collection. Your URL to it will be http://<Load balanced URL value>/Share documents/DocumentA.doc.

                     So why do we want this? What purpose does it serve? It is all around how NLB works and how to make the most of it. Without this, all links would end up being based on the local server. Any links you shared would jump to the server, not through NLB. In many, if not all you should not even be able to hit the server directly this way. If you could, under the scenario above, if the server you were hitting suddenly became overloaded, you would be stuck there throughout your session. However, since the Load Balanced URL bounces you back to through the NLB URL, this allows you NLB to properly balance the load.

                    There is a lot of info out there to help you out in NLB I have put together a series of links below which hopefully help you out in your search. Happy hunting!

    Link(s):

    -          TechNet Creating a MOSS web application http://technet2.microsoft.com/Office/en-us/library/5ed10d1c-ffdf-4651-b7b6-cac1de6f9ee11033.mspx?mfr=true

    -          Technet MOSS Configure Alternate Access Mapping http://technet2.microsoft.com/Office/en-us/library/be9d31d2-b9cb-4442-bfc6-2adcdbff8fae1033.mspx?mfr=true

    -          What every SharePoint administrator needs to know about Alternate Access Mappings (awesome series) http://blogs.msdn.com/sharepoint/archive/2007/03/06/what-every-sharepoint-administrator-needs-to-know-about-alternate-access-mappings-part-1.aspx

    -          MOSS Farm, Virtual server and NLB http://www.sharepointblogs.com/aaronrh/archive/2007/04/13/sharepoint-farm-virtual-server-and-network-load-balancing.aspx

    December 07

    FBA (Forms Based Auth) has it's catches

        

    I have set up a few farms now with FBA hooked into various sources from SQL to Novell LDAP. I can absolutely say you need to CAREFULLY evaluate your requirements against your need for FBA. A lot of the docs (especially marketing) make this sound like a trivial, just plug and play issue. It is far from the truth. If you are running on a tight timeline and do not have a lot of time to investigate issues you WILL run into you need to stay with AD auth if it is an option.

                    While I would have some serious questions for the MS architect(s) who put together the FBA piece with MOSS, the purpose here is not to flame MS, but to make sure you are completely aware of the implications of your choice to utilize non-integrated auth. There are many cases where FBA is the only option, and plenty of examples where it has been used successfully on MOSS implementations. Just be aware, if you have bought into the marketing,  thinking FBA is a simple issue, and have serious non-flexible requirements on at least 2 items on this list, well it is very likely you have grossly underestimated the time required to get the implementation done.

    Let’s examine some of the common ones I have run into:

    MY Sites

                    The word I have gotten from some at MS is this is impossible to have with FBA. I have however found a few blogs which helped me get this up and running. It involves setting up MySites and your SSP site to use FBA however. This creates some serious usability issues with your SSP site that you need to work though.  

     

    Search

                    At the current time (MS says this may be fixed in SP1), search will not crawl FBA content. Which means even if  you have your sites set up with a second AD enabled zone to allow search to crawl that content, it will not crawl any portion of the  MySites, since they are exclusively set up under FBA.

     

    Office Integration

                    This is a huge deal for many clients. A lot of the compelling office integration features with MOSS disappear or totally screw up when you keep client integration turned on with FBA. If your company figured they would get MOSS and then invest in upgrading their office suite to 2007 to take advantage of all the cool integration features, then they do NOT want to use FBA. If they do, they might as well not upgrade their office if the MOSS integration was a major part of the upgrade.

    Membership and Role Providers

                    MOSS ships with some Membership and Role Providers, the problem is once you get out of MS built products they are of limited value. As an example a recent implementation I worked on with FBA, involved a Novell eDirectory system. A quick search will find numerous folks using this with the default LDAP provider.  However, when you have a Novell eDirectory system with SSL enabled, that does not permit anonymous connections, well get ready to sling some custom membership and role provider code.  The good part is, it is relatively easy to do (once you figure out what you are doing) and you get a tremendous amount of control over the auth, and role system.  In terms of security though, one thing that makes managers nervous, the membership provider contains a method “ValidateUser” which passes the username and password for all users into the roleprovider, a perfect place for a retarded coder to place some bad logging code. 

                    I could easily fill 5-10 pages on this coding process and all the fun little issues you can run into, if you do run into issues, feel free to post a comment here and I can give you more info.

    Profiles

                    This is one of those little items that can sting a bit. MOSS needs to have unique names for your user accounts. So unlike the imports you run for AD, it will append the name of your role and membership provider to the beginning of your users names “MembershipProviderName:Username”. This can be one of those little things that bugs you sponsors a bit, especially when they see it on the dropdowns on the top right of the screen or when you see it in the user profile screen.

    Profile Import

                    Good chance if you have had to go the route of the custom membership and role provider, that the built in profile Import for MOSS won’t work for you either. If this is the case you will be writing your own. Not too bad of a coding effort but by the time you get to coding this you will find you are pretty deep into your timeframe and still have a mess of things to sort through.

     

                    Like I said, FBA can and does work. It is just, depending on your specific requirements, it is not always the simple tasks it seems at the get go. If you are in that boat, hopefully you get some guidance on where you need to start researching.

    November 20

    Programmatically Adding LDAP groups to MOSS groups

     

    I have seen a few posting for this on importing users or AD groups into a MOSS security group. For the most part it is very similar to the AD groups , however there are a couple little special things that can make it interesting and cost you a little time. Rather than annoy you with a lot of chatter, I have included functional code below to help you out. One critical thing, the groups MUST be prefixed with the LDAP provider name. This is the equivalent of the AD domain so when you get over the annoyance and stop to think about it, it makes sense.
    So let’s say your LDAP provider name is: “ACMELDAP” and your group name in the LDAP system is ACME_Managers. You would enter “acmeldap:ACME_Managers” as the LDAPFullRoleName. LDAPRoleName is the field for the friendly name. Anyway, I hope this helps someone out there out. Shoot me a comment/question if you come across a better way to do any of this.

     

    public bool AddLDAPRoleToMossGroup(string sharePointSiteURL, string LDAPFullRoleName, string LDAPRoleName, string MOSSGroupName)

            {

                try

                {

                    SPSite StationWorkspace;

                    SPWeb StationWorkspaceWeb;

                    SPUser LDAPGroupUser;

     

                   

                    StationWorkspace = new SPSite(sharePointSiteURL);

                    StationWorkspaceWeb = StationWorkspace.OpenWeb();

     

                    SPUser spUser = GetSPUser(LDAPFullRoleName,LDAPRoleName, sharePointSiteURL);

     

                    if (spUser == null)

                    {

                        spUser = CreateUser(LDAPFullRoleName, "", LDAPRoleName, "Added by SharePoint utilities", sharePointSiteURL);

                    }

     

                    foreach(SPGroup curGroup in StationWorkspaceWeb.SiteGroups)

                    {

                        if (curGroup.Name.ToUpper() == MOSSGroupName.ToUpper())

                        {

                            //Add and update group with new user

                            curGroup.AddUser(spUser);

                            curGroup.Update();   

                        }

                    }

     

                    return true;

                   

                }

                catch (Exception ex)

                {

                    CommonFunctions.WriteToLogFile(APPNAME, "AdminFunctions:AddLDAPRoleToMossGroup - sharePointSiteURL: " + sharePointSiteURL + " LDAPRoleName: " + LDAPRoleName + " MOSSGroupName: " + MOSSGroupName + " Error:" + ex.ToString());

                    return false;

    }

                }

            }

    private SPUser GetSPUser(string LDAPFullRoleName, string strLoginName, string strSiteURL)

            {

                SPUser spReturn = null;

                SPSite spSite = null;

                SPWeb spWeb = null;

                try

                {

                    //Open the ShrePoint site

                    spSite = new SPSite(strSiteURL);

                    spWeb = spSite.OpenWeb();

     

                    //Check to see if user exists

                    spReturn = spWeb.AllUsers[LDAPFullRoleName];

                  }

                catch (Exception ex)

                {

     CommonFunctions.WriteToLogFile(APPNAME, "AdminFunctions:GetSPUser - Error: " + ex.ToString());

                    return null;

                }

                finally

                {

                    spWeb.Close();

                    spSite.Close();

                }

     

                return spReturn;

            }

     

            //Creates a new user

            private SPUser CreateUser(string strLoginName, string strEMail, string strName, string strNotes, string strSiteURL)

            {

                SPUser spReturn = null;

                SPSite spSite = null;

                SPWeb spWeb = null;

     

                try

                {

                    //Open the SharePoint site

                    spSite = new SPSite(strSiteURL);

                    spWeb = spSite.OpenWeb();

     

                    //Assign role and add user to site

     SPRoleAssignment spRoleAssignment = new SPRoleAssignment(strLoginName, strEMail, strName, strNotes);

     

                    //Using Contribute, might need high access

                    SPRoleDefinition spSPRoleDefinition = spWeb.RoleDefinitions["View Only"];

     

                    spRoleAssignment.RoleDefinitionBindings.Add(spSPRoleDefinition);

                    spWeb.RoleAssignments.Add(spRoleAssignment);

     

                    //Update site

                    spWeb.Update();

                    spReturn = spWeb.AllUsers[strLoginName];

     

                }

                catch (Exception ex)

                {

     CommonFunctions.WriteToLogFile(APPNAME, "AdminFunctions:CreateUser - Error: " + ex.ToString());

                    spReturn = null;

                }

                finally

                {

                    spWeb.Close();

                    spSite.Close();

                }

     

                return spReturn;

    November 15

    Fun MOSS Quirks: Custom Site templates PageLayoutURL

           This particular one will nearly drive you to suicide. So you develop a sweet site and decide, hey, I am going to save this out to a site template (stp) file and deploy it out to my other MOSS farms. You got a nice welcome page that is created with a custom page layout you developed. You include your stp file(s), custom master pages, custom CSS, images, custom page layout(s) and other branding features in a wsp and deploy them to your other MOSS farm and activate the features. Everything is nice, then you create your first site using one of the templates and all hell breaks loose. Your custom welcome page will not load, in your researching you take a look at your page layout and associate content type. It is then, and only then you find...your page layout on your other server farm is piinting back to the original site you developed on. So what the heck is going on?
          What's going on is a field in MOSS called "pageLayoutURL". If is found via the page.ListItem[FieldiD.PageLayout] call. It contains a freaking fully qualified URL to the original site "http://" and all. Apparently, this field passed MS' testing process. It can create some serious issues for your site.
          As part of any feature rollout, I have included some new code to help me out in this. I pulled it off another blog (as soon as I find the URL I will post that fellows URL here so he can get proper credit).
          First you need to create a site using the sp file you pulled over in your feature. Next, you need to be sure publishing is turned on. Next you run the code I will include in this post against that site (it is fugly POC code so if you need to clean it up for your purposes, feel free). Basically, it cycles throught every page in a site and reset this value to the current site URL.  For my feature, I actually created some script that will provision a new site usin gthe template, turn on publishing, execute the fix code, turn off publishing, and save new template from the updated site and blow away the old template.
     
    sample code:
    PublishingPageCollection pages = publishingWeb.GetPublishingPages();
                foreach (PublishingPage page in pages)
                {
                    string pageLayoutUrl = (string)page.ListItem[FieldId.PageLayout];
                    utilities.WriteToLogFile("MOSSFunctions", "FixPages: Now Checking: " + page.Name + " Layout url is at: " + pageLayoutUrl);
                    //if pageLayoutURL is null or empty or page layout url index of "/catalogs" < 0)
                    if (string.IsNullOrEmpty(pageLayoutUrl) || pageLayoutUrl.IndexOf("/_catalogs/") < 0)
                    {
                        utilities.WriteToLogFile("MOSSFunctions", "FixPages: Condition one tripped");
                        continue;
                    }
                    SPFieldUrlValue url = new SPFieldUrlValue(pageLayoutUrl);

                    string temp = publishingWeb.Web.Site.ServerRelativeUrl.TrimEnd('/') +
                                  url.Url.Substring(url.Url.IndexOf("/_catalogs/"));
                    temp = publishingWeb.Web.Site.MakeFullUrl(temp);

                    if (url.Url.ToLowerInvariant() == temp.ToLowerInvariant())
                    {
                        utilities.WriteToLogFile("MOSSFunctions", "FixPages: Condition two tripped");
                        continue;
                    }

    Custom Membership Provider "Server not operational"

    You have coded your own Membership and Role providers to manage the authentication, role validation, user information, etc to a non-AD LDAP data store. You have utilized the standard DirectorySearcher, Directory,Entry, etc classes from the System.DirectoryServices namespace. The installation on central admin, the windows auth site, goes smoothly and you easily verify communication between these sites and LDAP as you add a couple LDAP users to the extended FBA site to make it possible for you to log in to that site and add other users/LDAP groups.

    You attempt to log into the FBA site and despite being a high level user your LDAP authentication fails. You check the logs from your membership provider and find that all attempts to access the LDAP server return a “server not operational” error. As an additional hint, you can look in your SYSTEM event log and find an exception kicked out by “Schannel”, just once. It generally will not repeat until you reboot the server. The text of that exception will be something like:

    “System.Runtime.InteropServices.COMException (0x8007203A): The server is not operational. Also, schannel event 36882 is logged to the event log: "The certificate received from the remote server was issued by an untrusted certificate authority. Because of this, none of the data contained in the certificate can be validated. The SSL connection request has failed. The attached data contains the server certificate.".  

    The code failing is the exact same code that runs fine on the other 2 sites (CA and non-FBA site) and you have installed the certificate for the SSL communication on your server.

    There are a couple circumstances that lead to this exception. The first thing to understand is this, the actual thread that is accessing the membership provider, on the central administration and Windows integration security site, is running under the logged in user, and NOT one of the MOSS service accounts. However, when you set up FBA on the 3rd site, in IIS is turned off integrated security, and enabled anonymous access. As a result, when you attempted to log into the FBA site, there was no windows login to run under. SharePoint will call and run the membership provider validation code under a “GenericIdentity” object user.  

    Now since the LDAP directory requires SSL, there are some certificates that need to be checked. The classes in System.DirectoryServices ( as opposed to System.DirectoryServices.Protocols ) automatically manage the whole certificate validation process for you.  When you are logged in as a known user, it will check the user HIVE for the certificate LDAP is passing to validate, when you are a GenericIdentity, it will check the machine root  for the certificate. This can present a problem since under certain circumstances it appears , that even though the certificate is in the trusted store,  ASP.Net 2.0 would not examine the machine-level certificate store. The result was, the LDAP server’s certificate could not be validated and the machine would NOT make the connection. The first time sChannel logged it in the SYSTEM log (and never again till I rebooted), though in the exception object returned by the catch statement you will only get “server is not operational”

    This is one area where the fix gets ugly. At the current time the only way I found around this issue was to code around it. You need to implement the LDAP connection and queries in the affected methods using the System.DirectoryServices.Protocols namespace level and you need to manually code a function to validate the certificate passed by the LDAP server to the membership/role provider. Keep in mind, the majority of the role provider calls even once you are logged in will be made with a genericidentity or as a user unknown to the server.

    November 07

    Fun SharePoint Quirks: Deleting Master Pages

         This was a little annoying thing I came across recently. I had a custom master page that I pushed in with a feature. I needed to update it and of course rolling the feature back, retracting and deleting the solution did not delete the master page. So I grudgingly went in to delete it manually. Imagine my surprise when I got an error telling me it was being referenced by something and could not be deleted. I went in and checked the file associations through Site Settings --> Manage Content and Structure.
        Navigate to the master pages folder, then check click the "Show related resources" button. You will find the master page is referencing itself. Thus you can NOT delete it...ever. Well not directly. turns out there is a way to remove it.
        First make sure that this is the ONLY thing referencing it, then create a new folder in the master pages folder, move the master page into that folder. You can delete the folder which kills the master page.
    July 31

    Configuring a site collection for multiple authentication providers

                    Here is the scenario. You have a wonderful intranet site going and you’d like to share it with the outside world. You do NOT want this externally facing site using your AD, so you decide to use forms authentication for your external access. Also, you need SSL set up for the external site since you’d rather not broadcast unencrypted content across the web.

    1.       Configure your SQL forms authentication DB

    a.       Determine you access methods for the forms auth DB (Integrated is the suggested way to go here)

    b.      Create your forms Authentication database on your SQL box

                                                                               i.      Just make sure you name it something intelligent

    c.       The preferred way to run the following command is from a machine with Visual Studio. However, if you do not have VS you can find the exe for this call in the C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727  folder.

                                                                               i.      Run the aspnet_regsql.exe command to create the forms auth table structure and SPs

    1.       Run down on all commands for this utility http://msdn2.microsoft.com/en-us/library/x28wfk74.aspx

    2.       Aspnet_regsql.exe  –E  -S <servername> -d <database name> -A all

    d.      Adding the first user(s)

                                                                               i.      Add your first user via SQL Server Management Studio by calling the following SP:

    declare @now datetime      

    set @now= GETDATE()

    exec aspnet_Membership_CreateUser 'appName', 'userid','password','','email@somewhere.com','','',1,@now,@now,0,0,null

                                                                             ii.      http://msdn2.microsoft.com/en-us/library/aa478949.aspx more info on these scripts

    e.      Adding users/role via web app

                                                                               i.      In VS.Net 05 Create a new web app

                                                                             ii.      Add connection strings to your authentication DB within the configuration tabs of your web config

    1.       <system.web>

        <compilation debug=”false”/>

       <authentication = “forms” />

    </system.web>

    <connectionStrings>

     <add name=”MyFormsAuthServer” connectionString=”server=servername;database=authDBName;integratedsecurity=SSPI” />

    </connectionStrings>

     

     

                                                I.            From within Visual Studio à Project Menu à ASP.Net Configuration

                                              II.            On the web form that pops up, use the UI to create your users and roles

                                            III.            Now onto the REAL FUN!

    2.       Extending the forma authentication web app 

                                                I.            Open the MOSS Central Admin console. In Application Management, Create or Extend Web Application à Extend Existing Web Application.

    1.       Make sure the internal web application that you want to expose is selected

    2.        Set the web app name and port approrpriately

    Note: Take some forethought in the name and ports you use. With some planning you can use these to make life easier or make your IIS and MOSS admin consoles a mess.

    3.       Set allow anonymous to “yes”

    4.       Choose the correct Zone (probably extranet or Internet), do not choose default.

    5.       Click “ok”

                                              II.            Now that your site is extended into the new zone, in the MOSS central admin console click the Authentication providers link in the Application security section

    3.       Add the forms authentication provider to web app web config

                                           i.                  Navigate to the web config for your extended site (created in step 2.)

                                         ii.                  Just after the configsections section insert the connection string for your forms authentication provider.

    <connectionStrings>

        <add name="ConnectionString" connectionString="server=serverName ;database=FormsAuthDBName;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />

     </connectionStrings>

                                        iii.                  Now scroll down to the system.web section and add the following sections:

    <membership defaultProvider="AcAspNetSqlMembershipProvider">

          <providers>

     <add name="AcAspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"  connectionStringName="ConnectionString" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed"  maxInvalidPasswordAttempts="5" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/>

          </providers>

        </membership>

        <!-- role provider -->

        <roleManager enabled="true" defaultProvider="AcAspNetSqlRoleProvider">

          <providers>

     <add name="AcAspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ConnectionString" applicationName="/" />

          </providers>

        </roleManager>

                                                                           iv.      Now repeat the same steps on the web config within your MOSS Central administration console, with one exception. The “defaultProvider” will need to be changed in the Cental Admin web config to “AspNetWindowsTokenRoleProvider.” If you do not do this, your central administration will no longer allow you to authenticate.

                                                                             v.      If after doing this, your Central admin site crashes when you try to bring it up.

    1.       Check the make sure your changes are properly formed in the web config.

    2.       Check your connection string and make sure it is valid

    3.       Enable full “ugly” error messages on your central admin web application. This will give you the full text of whatever error was made in the config changes. (9 times out of ten there is something with the connection to the DB).

    4.       Configure forms authentication on your extended site

                                                                           I.               You should see the zone you extended your web app listed, click on it

                                                                         II.               Select your authentication type

                                                                       III.               Check the “enable anonymous access”

                                                                       IV.               Click OK

    5.       Set forms authentication user as secondary site administrator

                                                             i.      From within the SharePoint central administration console à  application management section à click Site Collection administrators.

                                                           ii.      Make sure your site collection is selected. You will note the one on the port that was extended and assigned extranet zone is NOT listed in the listing. It is for lack of a better term, a second window into the one it was extended from. So we are making the administrator settings on the one we extended.

                                                          iii.      Add your forms authentication user to the secondary site administrator column.

    1.       This will allow this user to access the site via forms auth and add in the other forms authentication users.

    2.       If the site cannot resolve the forms auth username in the secondary site administrator then you have not properly modified the Central administration site web.config

    6.       Additional Notes

                                                      I.            When trying to add forms auth users to your form auth site, you will need to log into the forms auth site. The integrated site knows nothing about those users as it does not have the forms auth provider info in its web config.

                                                    II.            When configuring SSL for these sites, you will want to take care to look into alternate access mappings. I have seen this create a serious issue. Basically, if you set this up, then apply SSL, and suddenly your forms auth site tries to resolve to your integrate security site as soon as your users enter their credentials, you will need to work on your alternate access mappings.

                                                  III.            It is likely your folks using Forms auth will have issues saving and checking out documents on your portal. You will fix this with a combination of enabling client integration in the extranet zone for the FBA provider and checking the  “Sign me in automatically” box on the login page.  

    July 20

    MOSS Links and Book Reference

    MOSS Link/Book Directory

                    I am finding frequent cause to share these with lots of folks on a continuous basis. So I thought I would focus this entry on the best links I have around MOSS and some common items I have had to look up.   This is not the full list I have but it is some of the best most common ones I hit. I will continually update this post to try to keep it up to date as I am adding new links and books as I hit them.

    Links

    1.       Developing a MOSS 2007 VPC 

    http://www.pptspaces.com/sharepointreporterblog/Lists/Posts/Post.aspx?List=7537e639%2Db4e5%2D48b6%2D97c0%2Da75e44ee9be3&ID=28

    -          Absolutely the best walkthrough I have ever seen on creating a MOSS VPC.

    2.       Integrating MS AJAX with MOSS

    -          http://sharepoint.microsoft.com/blogs/mike/Lists/Posts/Post.aspx?ID=3

    -          http://weblogs.asp.net/jan/archive/2007/02/26/using-the-ajax-control-toolkit-in-sharepoint.aspx

    -          Excellent walkthrough of some of the issues you will encounter with utilizing AJAX within a MOSS portal. This technology is awesome for getting your web parts to post back separately from the entire site.

    3.       Debugging WSS 3.0 errors

    -          http://blog.thekid.me.uk/archive/2007/02/15/a-solution-to-quot-an-unexpected-error-has-occurred-quot-in-wss-v3.aspx

    -          Unless you are perfect, you will run into the “An unexpected error has occurred” message from WSS 3.0 (especially if you are playing with custom web parts). Since this error usually gets you absolutely nowhere, the solution to get more info is to push in what this guys suggests. Your MOSS/WSS 3.0 site will give you standard Asp.Net error messages (note:  I hope I do not have to remind every how BAD this would be to do on a production server).

    4.       Managing Sites and Site Collections

    -          http://office.microsoft.com/en-us/sharepointserver/HA101577811033.aspx

    -          Good abbreviated walkthrough on planning your site and site collection layouts

    5.       Mapping CMS 2002 API’s to MOSS

    -          http://msdn2.microsoft.com/en-us/library/aa480228.aspx

    -          For those of you who need to run your CMS 2002 into MOSS, this could prove helpful

    6.       MS CMS Assessment tool download

    -          http://www.microsoft.com/downloads/details.aspx?FamilyId=360D0E83-FA70-4C24-BCD6-426CAFBCC627&displaylang=en

    7.       Sharepoint upgrade site

    -          http://www.sharepointupgrade.com/default.aspx

    -          Good landing page for getting information on your upgrade planning and process.

    8.       WSS 2.0 Prescan tool information

    -          http://blogs.msdn.com/joelo/archive/2007/04/13/don-t-be-afraid-of-prescan-part-1.aspx

    -          http://blogs.msdn.com/joelo/archive/2007/05/01/your-friend-prescan-what-it-does-part-2.aspx

    -          I hit this guys site a LOT when first using PreScan.exe. he provides a lot of good detailed info on what it does and how to manage some common issues with it.

    9.       Developing Custom Web parts in MOSS

    -          http://www.datasprings.com/default.aspx?tabid=775

    -          Very simple run through on developing a custom web part gives you a good start.

    10.   MOSS branding – generating a feature

    -          http://www.heathersolomon.com/blog/articles/servermstpageforsitecollect_feature.aspx

    -          Great chunk of info on master page branding and a path for pushing them into a feature to be easily deployed to production MOSS servers.

    11.   Creating BDC Entities

    -          http://msdn2.microsoft.com/en-us/library/bb410048.aspx

    -          From MS’ How-To series. Very cool walkthrough.

    Books

    1.       Microsoft Office SharePoint Portal Server 2007 Administrators Companion

    -          http://www.microsoft.com/MSPress/books/9537.aspx

    -          This is the MOSS bible. I do not go near a client site without this puppy by my side or on my HD. There is just so much info in here around installing, configuring, customizing, etc  MOSS, WSS 3.0. If you are going to implement MOSS this should be part of your library.

    2.       Inside Microsoft Windows SharePoint Services 3.0

    -          http://www.microsoft.com/MSPress/books/9692.aspx

    -          Another must have book,  covers a lot of stuff that your SharePoint developers will absolutely need such as using Workflow foundation, Code Access Security, CAML, and a lot more.

    3.       Professional Web Parts and Custom Controls

    -          http://www.wrox.com/WileyCDA/WroxTitle/productCd-076457860X.html

    -          Great book on web parts and custom controls which can be used in MOSS. Covers using AJAX, and some other common issues you will come across while coding your web parts.

    June 18

    Simple SQL 2005 MOSS 2007 ADF example

    MOSS 2007 ADF (application Definition File)

                    One of the most compelling features of MOSS is the search capability. Of those, the ability to hook into any ODBC compliant data store via the business data catalog (BDC) stands WAY out.  While this is an awesome feature, the issue comes in when you try to implement it.  In order to load an external data source into the BDC, you first need to define how to hook into it, how to retrieve data, how to update data, and various other information about the application. Now here is where it gets real fun.

                    The problem with this is, while I was able to find a HUGE number of links for ADF, trying to find a simple, “ADF for idiots” type of entry was not so easy.  Generally, if you can show me a simple example of something I can pick it up and run with it like an expert in no time. However such a simple example eluded me. 

                    Finally I found a cool ADF generation application. This allowed me to generate a simple ADF file for  extracting data from a single data table in a SQL 2005 database which gave me the insight I needed to now create my own going forward. I will go through this sample ADF file here. To be certain this is a VERY simple example and does not go through everything you can do by any means. Hopefully, if you are new to ADF files, this will give you the boost to get you going.

                    See the document at the end of this post for the sample ADF doc. There really are  2 key sections to focus on.

    1.       LobSystemInstance – this is located in the LobSystemainstances node and contains the definitions for what and how we are going to connect to the external datasource. In this sample we are hooking into a SQL Server data source. We define the server name, database name, SQL user name, and password. This sample uses SQL Auth but this can also be used for integrated or other authentication methods

    2.       Entity – this high level item first defines the name of the table we are accessing “dbo.TableName”. Entity has 2 other sub-sections that are critical

    A.      Identifiers

    -          This section defines that the primary keys is for the table we are accessing.

    B.      Methods

    -          The methods section defines the actions that we can use to perform specific tasks against the data store.  The high level method node defines the name of the task. The Properties and Parameters sub sections further define the information on each task.

    a.       Properties

    -          In the first method for this sample we have 2 properties. One defines the T-SQL text used to pull the data from the data store the other defines the type of command this is (similar to how you set command type in  ADO.Net code.

     

    b.      Parameters

    -          First the individual parameter items define the direction data will be flowing

    -          Within the parameters section we have individual TypeDescriptor subsections which will define the specific data columns and their associated data types.

    The sample I have listed below you can also see the additional method defined for retrieving a specific record from the data table as well as some additional details that are defined.  This sample is specifically for SQL 2005 retrieval using embedded SQL. It is a good start for that purpose.

    Now for the sample ADF I created.

    <LobSystem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog BDCMetadata.XSD" xmlns="http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog" Type="Database" Version="1.0.0.0" Name="CLIENTNAME_APPNAMELOBSystem">

      <Properties>

        <Property Name="WildcardCharacter" Type="System.String">%</Property>

      </Properties>

      <LobSystemInstances>

        <LobSystemInstance Name="CLIENTNAME_APPNAMEInstance"><Properties>

          <Property Name="DatabaseAccessProvider" Type="System.String">SqlServer</Property>

          <Property Name="AuthenticationMode" Type="System.String">PassThrough</Property>

          <Property Name="RdbConnection Data Source" Type="System.String">SQL Server Name</Property>

          <Property Name="RdbConnection Initial Catalog" Type="System.String">SQL Database Name</Property>

          <Property Name="RdbConnection User ID" Type="System.String">sql user name</Property>

          <Property Name="RdbConnection Password" Type="System.String">sql password</Property>

          <Property Name="RdbConnection Integrated Security" Type="System.String" />

          <Property Name="RdbConnection Pooling" Type="System.String">False</Property>

        </Properties>

        </LobSystemInstance>

        </LobSystemInstances>

      <Entities>

        <Entity EstimatedInstanceCount="0" Name="dbo.TableName">

          <Identifiers>

            <Identifier Name="[ID]" TypeName="System.Int32" />

          </Identifiers>

          <Methods>

            <Method Name="Getdbo.[TableName]">

            <Properties>

              <Property Name="RdbCommandText" Type="System.String">Select [ID],[Name],[Description],[APP_Manufacturer],[APPOS],[AppVariant],[AppType],[AppStyle],[ColumnA],[ColumnB],[ColumnC],[ColumnD],[ColumnE],[ColumnF] From dbo.[TableName]</Property>

              <Property Name="RdbCommandType" Type="System.Data.CommandType">Text</Property>

            </Properties>

            <Parameters>

              <Parameter Direction="Return" Name="dbo.[TableName]">

                <TypeDescriptor TypeName="System.Data.IDataReader, System.Data, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Name="dbo.[TableName]DataReader" IsCollection="true">

                  <TypeDescriptors>

                  <TypeDescriptor TypeName="System.Data.IDataRecord, System.Data, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Name="dbo.[TableName]DataRecord">

                    <TypeDescriptors>

                      <TypeDescriptor TypeName="System.Int32" IdentifierName="[ID]" Name="ID" />

                      <TypeDescriptor TypeName="System.String" Name="Name" />

                      <TypeDescriptor TypeName="System.String" Name="Description" />

                      <TypeDescriptor TypeName="System.String" Name="APP_Manufacturer" />

                      <TypeDescriptor TypeName="System.String" Name="APPOS" />

                      <TypeDescriptor TypeName="System.String" Name="AppVariant" />

                      <TypeDescriptor TypeName="System.String" Name="AppType" />

                      <TypeDescriptor TypeName="System.String" Name="AppStyle" />

                      <TypeDescriptor TypeName="System.Int32" Name="ColumnA" />

                      <TypeDescriptor TypeName="System.String" Name="ColumnB" />

                      <TypeDescriptor TypeName="System.String" Name="ColumnC" />

                      <TypeDescriptor TypeName="System.String" Name="ColumnD" />

                      <TypeDescriptor TypeName="System.Boolean" Name="ColumnE" />

                      <TypeDescriptor TypeName="System.Boolean" Name="ColumnF" />

                    </TypeDescriptors>

                  </TypeDescriptor>

                  </TypeDescriptors>

                </TypeDescriptor>

              </Parameter>

            </Parameters>

            <MethodInstances>

              <MethodInstance Name="dbo.[TableName]Finder" Type="Finder" ReturnParameterName="dbo.[TableName]" ReturnTypeDescriptorName="dbo.[TableName]DataReader" ReturnTypeDescriptorLevel="0" /></MethodInstances>

          </Method>

            <Method Name="dbo.[TableName]SpecificFinder">

              <Properties>

                <Property Name="RdbCommandText" Type="System.String">Select [ID],[Name],[Description],[APP_Manufacturer],[APPOS],[AppVariant],[AppType],[AppStyle],[ColumnA],[ColumnB],[ColumnC],[ColumnD],[ColumnE],[ColumnF] From dbo.[TableName] Where ([ID]=@ID)</Property>

                <Property Name="RdbCommandType" Type="System.Data.CommandType">Text</Property>

              </Properties>

              <Parameters>

                <Parameter Direction="In" Name="@ID">

                  <TypeDescriptor TypeName="System.Int32" IdentifierName="[ID]" Name="[ID]" />

                </Parameter>

                <Parameter Direction="Return" Name="dbo.[TableName]">

                  <TypeDescriptor TypeName="System.Data.IDataReader, System.Data, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Name="dbo.[TableName]DataReader" IsCollection="true">

                    <TypeDescriptors>

                    <TypeDescriptor TypeName="System.Data.IDataRecord, System.Data, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Name="dbo.[TableName]DataRecord">

                      <TypeDescriptors>

                        <TypeDescriptor TypeName="System.Int32" IdentifierName="[ID]" Name="ID" />

                        <TypeDescriptor TypeName="System.String" Name="Name" />

                        <TypeDescriptor TypeName="System.String" Name="Description" />

                        <TypeDescriptor TypeName="System.String" Name="APP_Manufacturer" />

                        <TypeDescriptor TypeName="System.String" Name="APPOS" />

                        <TypeDescriptor TypeName="System.String" Name="AppVariant" />

                        <TypeDescriptor TypeName="System.String" Name="AppType" />

                        <TypeDescriptor TypeName="System.String" Name="AppStyle" />

                        <TypeDescriptor TypeName="System.Int32" Name="ColumnA" />

                        <TypeDescriptor TypeName="System.String" Name="ColumnB" />

                        <TypeDescriptor TypeName="System.String" Name="ColumnC" />

                        <TypeDescriptor TypeName="System.String" Name="ColumnD" />

                        <TypeDescriptor TypeName="System.Boolean" Name="ColumnE" />

                        <TypeDescriptor TypeName="System.Boolean" Name="ColumnF" />

                      </TypeDescriptors>

                    </TypeDescriptor>

                    </TypeDescriptors>

                  </TypeDescriptor>

                </Parameter>

              </Parameters>

              <MethodInstances>

                <MethodInstance Name="dbo.[TableName]SpecificFinder" Type="SpecificFinder" ReturnParameterName="dbo.[TableName]" ReturnTypeDescriptorName="dbo.[TableName]DataReader" ReturnTypeDescriptorLevel="0" />

              </MethodInstances>

            </Method>

          </Methods>

        </Entity>

      </Entities>

    </LobSystem>

     

    References:

    Contagious Curiosity Blog - http://blogs.msdn.com/socaldevgal/archive/2007/03/02/use-the-moss-bdc-get-this-adf-generator.aspx

    ADF References - http://msdn2.microsoft.com/en-us/library/ms145931(SQL.90).aspx

    Complete ADF Template - http://msdn2.microsoft.com/en-us/library/ms145313(SQL.90).aspx

    Minimal ADF Template - http://msdn2.microsoft.com/en-us/library/ms145232(SQL.90).aspx

    ADF generation Tool - http://www.bdcmetaman.com/default.aspx