Wednesday, 25 August 2010

Feature upgrade (part 5) – using PowerShell to upgrade Features

In this article series:

  1. Feature upgrade (part 1) – fundamentals
  2. Feature upgrade (part 2) – a sample to play with
  3. Feature upgrade (part 3) – introducing SPFeatureUpgrade kit
  4. Feature upgrade (part 4) – advanced scenarios
  5. Feature upgrade (part 5) – using PowerShell to upgrade Features (this article)

OK, so I lied in article 4 about this series being finished – I’ve now added PowerShell support to my SPFeatureUpgrade kit, a set of tools designed to help manage the process of upgrading Features in SharePoint 2010. In this article I’ll talk about the PowerShell cmdlet I’m providing, but also some interesting things I learnt about PowerShell along the way – turns out writing a custom cmdlet is a great way to learn some aspects of PowerShell! These might be of interest if, like me, you haven’t done tons of PS to date.

Introduction

There are several reasons why you might prefer to use PowerShell to upgrade Features, rather than the administration pages also in the kit:

  • You have a large farm, and upgrading hundreds of site/web-scoped Features could time out in a web process
  • You’re scripting lots of other things
  • You want to use some advanced logic to upgrade some Feature instances and not others
  • You don’t want to write a C# console app (or similar) to do this, because that would be well, “a bit 2003” :)

As I mentioned in the earlier posts, SharePoint 2010 does not provide any handy cmdlets (or UI) out-of-the-box for upgrading Features, so this is the gap I’m hoping to fill somewhat.

How to upgrade using PowerShell

Install the supplied WSP to get the admin pages (see earlier articles) and the PowerShell cmdlet. Once installed, the command is:

Upgrade-SPFeatures -Scope <SPFeatureScope> [-WebApplication [<SPWebApplicationPipeBind>]] [-Site [<SPSitePipeBind>]] [<CommonParameters>]

Let’s consider some scenarios – the following table shows the command to use to depending on the scope of Features you are upgrading:

Scope

Command

Notes

Farm Upgrade-SPFeatures –Scope Farm All farm-scoped Features requiring upgrade are upgraded.
Web application Upgrade-SPFeatures –Scope WebApplication All web application-scoped Features requiring upgrade are upgraded.
Site

Upgrade-SPFeatures –Scope Site
-WebApplication “http://cob.collab.dev”
---------------------------------- OR -----------------------------------Get-SPWebapplication “http://cob.collab.dev” | Upgrade-SPFeatures –Scope Site

All site-scoped Features requiring upgrade within the specified web application are upgraded.
Web Upgrade-SPFeatures –Scope Web –Site “http://cob.collab.dev/sites/site1”
---------------------------------- OR -----------------------------------Get-SPSite “http://cob.collab.dev/sites/site1” | Upgrade-SPFeatures –Scope Web
All web-scoped Features requiring upgrade within the specified site collection are upgraded.

When the command is executed, if any Feature instances fail to upgrade you will see in the result:

PS_MixedSuccessAndFailure

In terms of the breakdown of commands/parameters, effectively this is the same model my admin pages use – farm and web app Features are upgraded without specifying a filter, but for site or web Features you must specify the parent object. As you can see from the alternate usages for these scopes, the –WebApplication and –Site and parameters can be specified inline or piped-in. Being able to pipe in is useful as it allows you to filter the parent object in order to, say, only upgrade the Features in certain web applications like so:

Get-SPWebApplication | Where-Object {$_.Url -like "http://cob*"} | Upgrade-SPFeatures -Scope Site

Thus this gives you more control over the parent object and therefore what gets upgraded. But what happens if what you’re doing just doesn’t match how my commands are broken down?

Using other logic to selectively upgrade Features

Examples of this might be “upgrade  all web-scoped Features where the web is in this list [‘foo’, ‘bar’]”, or perhaps “upgrade all site-scoped Features where the site has other Feature ‘foo’ activated” and so on. For now, my cmdlet won’t really help you too much in this case so you will need to write some PowerShell which goes straight to the API. For upgrading web-scoped Features, this might look something like:

Function shouldUpgrade([Microsoft.SharePoint.SPFeature]$aFeature)
{
    # add whatever testing logic here..
    if ($aFeature.Definition.Name -eq 'SomeName')
    {
        return $true
    }
    return $false
}
 
$site = Get-SPSite "http://cob.collab.dev/sites/featuretesting" 
$featuresRequiringUpgrade = $site.QueryFeatures([Microsoft.SharePoint.SPFeatureScope]::Web, $true)
 
foreach ($feature in $featuresRequiringUpgrade)
{
    if (shouldUpgrade($feature) -eq $true)
    {
        try
        {
            $feature.Upgrade($false)
            Write-Host ("Upgrade of Feature '" + $feature.DefinitionId + "' with parent '" + $site.Url + "' succeeded")
        }
        catch 
        {
            Write-Host ("Upgrade of Feature '" + $feature.DefinitionId + "' with parent '" + $site.Url + "' failed")
        }
    }
    else
    {
        Write-Host ("Not upgrading this Feature")
    }
}

Obviously this example would need amending for other scopes, proper error reporting etc. Clearly, unless you’re very comfortable cutting PowerShell, having a cmdlet which contains most of the logic does make things easier. So at some point I’ll extend my cmdlet to somehow allow you to filter on which Feature instances to upgrade rather than upgrading all of them - perhaps by specifying a list in an XML file or similar. I could easily get PowerShell to ask you to confirm for each item in the loop, but obviously that doesn’t scale when large numbers of Feature instances (e.g. web-scoped Features) are involved and is no good for unattended use, so that doesn’t feel like the way to go.

Using –WhatIf to see what *would* be upgraded

My cmdlet fully supports the PowerShell –WhatIf switch – this can be very useful as it allows you to see what Feature instances would be upgraded without actually doing anything. In the image below I can see all of the webs in my site collection where my Feature would be upgraded:

 Upgrade-SPFeatures_WhatIf

Future enhancements

There are a couple of shortcomings which spring to mind in the current implementation. Actually an experienced PowerShell eye would probably find lots (feedback welcome, help me learn!), but I do know of these:

  • No ability to filter which Feature instances are upgraded (as discussed earlier)
  • Doesn’t return an appropriate object to the pipeline for downstream processing
    • Actually I only gained a real understanding of this after I’d uploaded to Codeplex. I’m happy to leave it for now, but will improve this in the next version.

The code

The cmdlet and the admin pages I blogged about previously can be downloaded from http://SPFeatureUpgrade.codeplex.com. Here’s what my current code for the cmdlet looks like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.PowerShell;
 
using COB.SharePoint.Utilities.FeatureUpgradeKit.Core;
using COB.SharePoint.Utilities.FeatureUpgradeKit.Core.Exceptions;
using COB.SharePoint.Utilities.FeatureUpgradeKit.Entities;
 
namespace COB.SharePoint.Utilities.FeatureUpgradeKit.PowerShell
{
    /// <summary>
    /// Provides a PowerShell cmdlet for upgrading Features in SharePoint.
    /// </summary>
    /// <created by="Chris O'Brien" date="20 August 2010" />
    [Cmdlet("Upgrade", "SPFeatures", SupportsShouldProcess=true)]
    [SPCmdlet(RequireLocalFarmExist=true, RequireUserFarmAdmin=true)]
    public class SPCmdletUpgradeFeatures : SPCmdlet
    {
        [Parameter(
           Mandatory = true,
           HelpMessage = "Scope of the Features to upgrade.")]
        public SPFeatureScope Scope
        {
            get;
            set;
        }
 
        [Parameter(
           ValueFromPipeline=true,     
           HelpMessage = "Value of the SPWebApplication whose Site-scoped Features should be upgraded.")]
        public SPWebApplicationPipeBind WebApplication
        {
            get;
            set;
        }
        
        [Parameter(
           ValueFromPipeline = true,
           HelpMessage = "Value of the SPSite whose Web-scoped Features should be upgraded.")]
        public SPSitePipeBind Site
        {
            get;
            set;
        }
 
        protected override void InternalValidate()
        {
            if (((Scope == SPFeatureScope.Farm) || (Scope == SPFeatureScope.WebApplication)) && (Site != null || WebApplication != null))
            {
                ThrowTerminatingError(new InvalidFeatureUpgradeDetailsException("When the -Scope parameter is Farm or WebApplication, no parent object (e.g. Farm, Web application, Site or Web) should " +
                    "be passed. Alternatively, specify Site or Web for the -Scope parameter and pass the appropriate parent object."), ErrorCategory.InvalidArgument, null);
            }
            if (Scope == SPFeatureScope.Site && WebApplication == null)
            {
                ThrowTerminatingError(new InvalidFeatureUpgradeDetailsException("To upgrade Site-scoped Features, the parent web application must be piped in or specified using the -WebApplication parameter."), ErrorCategory.InvalidArgument, null);
            }
            if (Scope == SPFeatureScope.Web && Site == null)
            {
                ThrowTerminatingError(new InvalidFeatureUpgradeDetailsException("To upgrade Web-scoped Features, the parent site collection must be piped in or specified using the -Site parameter."), ErrorCategory.InvalidArgument, null);
            }
            base.InternalValidate();
        }
 
        protected override void InternalProcessRecord()
        {   
            SPFeatureQueryResultCollection featuresForUpgrade = null;
            List<SPFeature> featuresForUpgradeList = null;
            SPWebApplication webApp = null;
            SPSite site = null;
 
            switch(Scope)
            {
                case SPFeatureScope.Farm:
                    featuresForUpgrade = SPWebService.AdministrationService.QueryFeatures(Scope, true);
                    featuresForUpgradeList = featuresForUpgrade.ToList<SPFeature>();
                    if (featuresForUpgradeList == null || featuresForUpgradeList.Count() == 0)
                    {
                        WriteResult(string.Format("No Features found requiring upgrade at scope '{0}'.", Scope));
                    }
                    break;
                case SPFeatureScope.WebApplication:
                    featuresForUpgrade = SPWebService.QueryFeaturesInAllWebServices(Scope, true);
                    featuresForUpgradeList = featuresForUpgrade.ToList<SPFeature>();
                    if (featuresForUpgradeList == null || featuresForUpgradeList.Count() == 0)
                    {
                        WriteResult(string.Format("No Features found requiring upgrade at scope '{0}'.", Scope));
                    }
                    break;
                case SPFeatureScope.Site:
                    webApp = WebApplication.Read();
                    
                    featuresForUpgrade = webApp.QueryFeatures(Scope, true);
                    featuresForUpgradeList = featuresForUpgrade.ToList<SPFeature>();
 
                    if (featuresForUpgradeList == null || featuresForUpgradeList.Count() == 0)
                    {
                        WriteResult(string.Format("No Features found requiring upgrade at scope '{0}' with parent web application '{1}'.", 
                            Scope, webApp.GetResponseUri(SPUrlZone.Default)));
                    }
                    break;
                case SPFeatureScope.Web:
                    site = Site.Read();
                    featuresForUpgrade = site.QueryFeatures(Scope, true);
                    featuresForUpgradeList = featuresForUpgrade.ToList<SPFeature>();
 
                    if (featuresForUpgradeList == null || featuresForUpgradeList.Count() == 0)
                    {
                        WriteResult(string.Format("No Features found requiring upgrade at scope '{0}' with parent site collection '{1}'.", 
                            Scope, site.Url));
                    }
                    break;
            }
 
            if ((featuresForUpgradeList != null) && (featuresForUpgradeList.Count() > 0))
            {
                List<FeatureDetails> featureDetailsList = getFeatureDetails(featuresForUpgradeList);
 
                if (ShouldProcess(string.Format("Scope = {0}", Scope)))
                {
                    Dictionary<FeatureDetails, IEnumerable<Exception>> upgradeResults = FeatureManager.UpgradeFeatures(featureDetailsList);
                    presentResultMessages(upgradeResults);
                }
                else
                {
                    string msg = string.Format("Would upgrade the following Features:{0}{0}", Environment.NewLine);
                    foreach(FeatureDetails details in featureDetailsList)
                    {
                        msg += string.Format("Feature Name='{0}', Parent='{1}', ID='{2}'.{3}",
                            details.FeatureName, details.ParentString, details.FeatureID, Environment.NewLine);
                    }
                  
                    WriteResult(msg);
                }
            }
            
            base.InternalProcessRecord();
        }
 
        private void presentResultMessages(Dictionary<FeatureDetails, IEnumerable<Exception>> upgradeResults)
        {
            int errorCount = 0;
            StringBuilder sbSuccessResults = new StringBuilder();
            StringBuilder sbFailedResults = new StringBuilder();
 
            Dictionary<FeatureDetails, IEnumerable<string>> resultOutput = new Dictionary<FeatureDetails, IEnumerable<string>>();
 
            foreach (KeyValuePair<FeatureDetails, IEnumerable<Exception>> kvp in upgradeResults)
            {
                List<Exception> exceptions = null;
                List<string> exceptionMessages = new List<string>();
 
                if (kvp.Value != null)
                {
                    exceptions = kvp.Value.ToList();
                }
 
                if (exceptions != null && exceptions.Count > 0)
                {
                    sbFailedResults.AppendFormat("{2}ERROR: Feature '{0}' with parent '{1}' failed to upgrade with the following errors:{2}{2}",
                        kvp.Key.FeatureName, kvp.Key.ParentString, Environment.NewLine);
                    foreach (Exception exception in exceptions)
                    {
                        sbFailedResults.AppendFormat("  - {0}{1}", exception.ToString(), Environment.NewLine);
                    }
                }
                else
                {
                    sbSuccessResults.AppendFormat("{2}SUCCESS: Feature '{0}' with parent '{1}' upgraded successfully.{2}",
                        kvp.Key.FeatureName, kvp.Key.ParentString, Environment.NewLine);
                }
            }
 
            string failures = sbFailedResults.ToString();
            string successes = sbSuccessResults.ToString();
 
            if (!string.IsNullOrEmpty(failures))
            {
                WriteResult(failures);
            }
            if (!string.IsNullOrEmpty(successes))
            {
                WriteResult(successes);
            }
        }
 
        private List<FeatureDetails> getFeatureDetails(List<SPFeature> featuresForUpgradeList)
        {
            List<FeatureDetails> featureDetails = new List<FeatureDetails>();
            foreach (var featureForUpgrade in featuresForUpgradeList)
            {
                featureDetails.Add(featureForUpgrade.ConvertToFeatureDetails());
            }
 
            return featureDetails;
        }
    }
}

Appendix - some interesting things I learnt about PowerShell

  • The pipeline concept – much like piping elsewhere (UNIX, jQuery spring to mind).
    • This basically allows multiple commands to be chained together into very powerful one-liners. Made possible by the fact that each cmdlet returns an object to the pipeline for the parent script or downstream cmdlets to process.
  • Passing parameters to PS functions – the syntax doesn’t separate parameters with a comma, which no doubt always fools newbies!
    • So an example from a script I wrote yesterday: 
      SetPropertyOnWebApp $webApplication "portalsuperuseraccount" $objectCachePortalSuperUser
  • The SharePoint PowerShell framework comes with custom ‘PipeBind’ objects for many SharePoint objects – using these for your parameters allows the SharePoint object to be identified in different ways (e.g. SPSite by URL or GUID)
  • Implementing Should-Process/-WhatIf in a cmdlet – thanks to Shay Levy (PowerShell MVP) for helping me with this :)
    • If your cmdlet has a property of ‘SupportsShouldProcess=true’ on the Cmdlet attribute, callers can use the –WhatIf switch. You can test for this by calling the ShouldProcess method on the base Cmdlet class – it will be false if –WhatIf was used.
    • A related approach is used when your cmdlet should ask for confirmation (-Confirm) before making a change/writing data
  • Parameter sets in a cmdlet
    • So long as the caller specifies at least one unique optional parameter, you can identify which ‘set’ of parameters was used and do conditional logic on this. This is akin to method overloading in C#.

A great source of learning for SharePoint/PowerShell development is of course, Gary Lapointe – the source code for all his cmdlets is published on his blog.

Monday, 2 August 2010

Feature upgrade (part 4) – advanced scenarios

In this article series:

  1. Feature upgrade (part 1) – fundamentals
  2. Feature upgrade (part 2) – a sample to play with
  3. Feature upgrade (part 3) – introducing SPFeatureUpgrade kit
  4. Feature upgrade (part 4) – advanced scenarios (this article)
  5. Feature upgrade (part 5) – using PowerShell to upgrade Features

If you’ve been following this series you should hopefully be familiar with SP2010 Feature upgrade by now, conceptually at least. As you start to use this capability for real, it will quickly become clear that there are some interesting scenarios where it’s difficult to know for sure what would happen. I’ll document some examples that I’ve become aware of already in this post, but will also come back and add further cases as I come across them – kinda like a ‘Feature upgrade FAQ’ of sorts. I don’t have too many of these at the moment, but I wanted to share what I know now rather than wait for a longer list to materialize – if you have a scenario in mind that you’d like checked out, by all means leave a comment and I’ll look into it.

Here’s what I have so far:

Question: What happens if multiple VersionRange elements match my Feature instance version?

Consider the case where we have a Feature instance (N.B. this means a particular instance of a Feature on a particular web, site or web app - depending on the scope in the Feature definition) at version 1.0.0.0, and we apply the following upgrade instructions to it:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/" Version="3.0.0.0">
  <UpgradeActions>
    <VersionRange BeginVersion="1.0.0.0" EndVersion="1.9.9.9">
        ...
    </VersionRange>
    <VersionRange BeginVersion="1.0.0.0" EndVersion="2.9.9.9">
        ...
    </VersionRange>
  </UpgradeActions>
</Feature>

Answer: Since both of the VersionRange elements match, what happens? The answer is that both VersionRange elements will be processed against the Feature, in the order they are defined in the file. Any instructions within the VersionRange elements will be processed, be they declarative or imperative via CustomUpgradeAction. This is probably the behavior you’d expect, but it’s good to know. [P.S. Special thanks to Gary Yeoman (Combined Knowledge instructor) for winning the bet and bottoming this one out before I managed to get to it :)]

Impact: What this means is that if you have ‘overlapping’ VersionRange elements like this, you might need to plan for the fact that both sets of upgrade actions will execute for Feature instances caught by the ranges. This could be in contrast to other Feature instances across the farm which are at say, 2.0.0.0, and therefore would only be caught by the second VersionRange. Would both sets of Feature instances be in the right ‘state’ to upgrade successfully? Are we removing anything in the first VersionRange (e.g. a content type) which is used in the second VersionRange? Clearly this depends on what you’re doing, but is worth considering if you do overlap.


Question: What happens if a Feature which has been upgraded several times is activated for the first time on a site/web?

Consider the case where you have a web-scoped Feature which has been upgraded a few times to (for example) version 3.0.0.0 across all of the webs which currently exist. What happens if you create a brand new web at this point? Are all the upgrade steps executed when the Feature is activated there?

Answer: No – since activation and upgrade are different actions. However, note that some declarative provisioning will happen, since Visual Studio adds element manifest links in two places in the Feature manifest.xml file. Specifically, if you add a new element manifest during a Feature upgrade (e.g. to provision some new fields/content types/modules etc.), Visual Studio adds this into the UpgradeActions/VersionRange/ApplyElementManifests section but also into the good old-fashioned ElementManifests section too – you can see this by looking at the preview of the generated file. Of course, the latter section is what SharePoint Features have always used in Feature activation, meaning that any artifacts in the referenced elements file will be provisioned on both initial activation and subsequent upgrade. Note this only applies to elements files - other declarative steps such as AddContentTypeField and MapFile are specific only to upgrade, and the same applies to imperative steps linked with CustomUpgradeAction.

Impact: Features must be designed carefully to deal with the fact that Feature instances can have varying lifecycles. Since a new Feature is given a version number of 0.0.0.0, one option may be to use overlapping VersionRange elements (as described above) which always start at 0.0.0.0, and then immediately upgrade the Feature(s) when a new site/web is created in our scenario. Otherwise, if you are upgrading Features using AddContentTypeField or CustomUpgradeAction, it may be necessary to ensure the same steps happen on activation also – for example by ensuring the same code is called in FeatureUpgrading and FeatureActivated. This is a good illustration of some of the complexities around Feature lifecycles.
   

Question:  Do Feature activation dependencies affect the order in which Feature instances are upgraded?

Answer: Yes – any depended on Features will be upgraded before dependent Features.

Impact: It should be possible to reference ‘parent’ artifacts which are provisioned in the same Feature upgrade cycle in dependent Features. For example, if a Feature upgrade provisions a field, dependent Features would be able to reference that same field because it would exist by the time their upgrade executes.

Summary

This post outlines some considerations around Feature upgrade and ALM in SharePoint, and (probably!) concludes my series on this topic. If you’re interested in hearing more on this I have a chapter in Real World SharePoint 2010 (“the SharePoint MVP book”), where I discuss this and related topics - coverage there includes things like versioning assemblies in SharePoint 2010 development and recommendations for assembly/versioning strategies.

P.S. I’d love to hear any interesting Feature upgrade scenarios for me to test out to add to the list!

Sunday, 25 July 2010

Feature upgrade (part 3) – introducing SPFeatureUpgrade kit

In this article series:

  1. Feature upgrade (part 1) – fundamentals
  2. Feature upgrade (part 2) – a sample to play with
  3. Feature upgrade (part 3) – introducing SPFeatureUpgrade kit (this article)
  4. Feature upgrade (part 4) – advanced scenarios
  5. Feature upgrade (part 5) – using PowerShell to upgrade Features

This is the third article in my Feature upgrade series. I wanted to post this earlier but (typical developer) I got caught up in doing some more work on the tool I’m introducing here. Back in the ‘Feature upgrade fundamentals’ post, I mentioned that although we now have this great framework for versioning and upgrading Features (thus providing a great vehicle to roll out new/modified functionality to our SharePoint sites), there are no tools out-of-the-box to help manage this process. This often confuses people when they start to look at Feature upgrade – no amount of redeploying/upgrading solutions or deactivating/reactivating Features will trigger the upgrade event (because reactivation is *not* upgrade, by design!), and SharePoint 2010 does not ship with any user interface, STSADM command or PowerShell cmdlet to do this. In other words, a situation also known as a “partner opportunity”! What you do get is a series of methods in the API – although it doesn’t take too much to throw together a console app which will fill most of this gap, there are a few permutations and there is some complexity around handling failed upgrades. And anyway, in my world it’s always nice to have the option of doing things visually.

So, just like the Content Deployment Wizard plugged the gap of SharePoint’s content deployment framework not having a rich UI, my SPFeatureUpgrade kit on Codeplex might be useful if you start to version/upgrade Features in your environment. The kit installs as a WSP and all source code is published on the Codeplex site.

The following is a slightly expanded version of the Codeplex blurb – any of the images can be clicked on to enlarge.

What's in the kit

The kit is comprised of 3 things:
  • An application page in Central Admin - administrators can use this to upgrade Features of any scope across the entire farm
  • An OPTIONAL page in each site collection's Site Settings area - this can be used to devolve responsibility for upgrading Features (and therefore updating functionality provided by developers) to local site administrators. A Feature must be enabled on each site to enable this.
  • [Coming soon] - a PowerShell cmdlet which allows Feature upgrade to be run from script. (This is the final piece and will be ready shortly, since I’ve got the core logic implemented already.)

One useful thing that the application pages allow you to do is selectively upgrade Features – it could be that you don’t want all sites or webs to receive the new functionality in the upgraded Feature. These pages display checkboxes next to each Feature instance so you can control what gets upgraded where. This is also useful if you plan to ‘phase’ the rollout of new functionality deployed using Feature upgrade.

[Sidenote – Feature upgrade is typically a developer-led thing, since it’s developers who build Features. If developers do not have permissions to to use the admin pages (or PowerShell) then clearly some communication will be required with the administrators who do. Note also that, as I discussed in Upgrading sandboxed solutions, the Feature upgrade concept does not apply in the sandbox since all Features instances there are automatically upgraded when a sandboxed solution is updated.]

Usage

Central Admin page

After deploying updates to a Feature, this page can be used to find and upgrade instances of the Feature across the farm. The tool is accessed from the ‘System Settings’ area with Central Admin:

CentralAdmin_FeatureUpgradeLink

Farm administrators can then select the scope (Farm, WebApplication, Site or Web) and then find Feature instances which require upgrade (this uses a set of methods named QueryFeatures() in the SharePoint API). In the image below I've selected 'WebApplication' as the scope and clicked 'Search' to display the results – I get a table of Features in different web applications which need upgrade (i.e. an updated version has been deployed but not yet upgraded):

(click any image to see larger/clearer version)
CentralAdmin_UpgradeWebAppFeatures 
I can use the checkboxes to selectively upgrade some instances and not others (by default all will be upgraded, but this can be controlled by deselecting checkboxes). In the image below I've upgraded 1 of the 2 web application Features found previously - the success/failure result of all feature instances are shown on the page:

CentralAdmin_CompletedUpgradeWebAppFeatures

In the example above, the selected Feature has now been upgraded.

To help the experience when used in larger farms, when the Central Admin page is used to upgrade Site or Web scoped Features, additional filters appear to allow you to restrict the scope of the query. [Sidenote - this uses some gratuitous jQuery to slide the filter controls in – I can practically hear a couple of server admins (or Spence :)) complaining about things sliding around, but hey I’ve got to have somewhere to practice!]. So for upgrading Site-scoped Features, a selector appears allowing to select the parent web application:

CentralAdmin_UpgradeSiteFeatures

Similarly for Web-scoped Features, you filter on parent site collection:

CentralAdmin_UpgradeWebFeaturesFiltering

Usage

Site Settings page
The Site Settings page works in a very similar way, except only Site and Web-scoped Features can be upgraded (in the current site collection). The ‘COB.SharePoint.Utilities.SiteSettingFeatureUpgrade’ Feature must be activated against each site collection which should show a link to this page. When enabled, the tool is accessed from the Site Settings area for the site collection:

SiteSettings_FeatureUpgradeLink

Site collection administrators can then select the scope (e.g. Site or Web) and then find the Features which require upgrade. In the image below I've selected 'Web' as the scope and clicked 'Search' to display the results:

CentralAdmin_UpgradeWebFeatures

As before, I can use the checkboxes to selectively upgrade some instances and not others (by default all will be upgraded, but this can be controlled by deselecting checkboxes). In the image below I've upgraded 2 of the 4 web Features found previously - the success/failure result of all feature instances are shown on the page:

FeatureUpgradeResult

Summary

SharePoint 2010 introduces the ability to version and upgrade Features, but doesn’t provide any high-level tools to manage this process. I’m providing tools for farm administrators and (optionally) site collection administrators to do this. At the time of writing this article, a final piece in the form of a PowerShell cmdlet is still in progress but should be available soon.

The kit can be downloaded from http://spfeatureupgrade.codeplex.com/

Thursday, 8 July 2010

Renewed again as SharePoint MVP

Received the mail from Microsoft last week to say I’m a SharePoint MVP for another year (my third). Looking back over the last 12 months, it’s been fun being an MVP in the run-up and aftermath of a new release and I definitely feel privileged to have been in that position. I had some opportunities to do interesting things such as help write the 2010 exams, produce MSDN content and co-author a book, which was great. Ultimately I had to decline several of them due to the combination of full-time job, existing community commitments and the arrival of our twins, but then you can’t have everything :)

This list is mainly for me rather than you, but the main things I did were:

One thing that does nag at me is that I know that some blog comments slipped through the net during the super-busy periods. I feel bad about this, but sometimes they just come in too thick and fast and I don’t seem to have a good system for finding unanswered ones! Apologies to those affected – by all means try again and I’ll do my best to answer.

Many thanks to those who left feedback, it’s always appreciated :)

Tuesday, 6 July 2010

Feature upgrade (part 2) – a sample to play with

In this article series:

  1. Feature upgrade (part 1) – fundamentals
  2. Feature upgrade (part 2) – a sample to play with (this article)
  3. Feature upgrade (part 3) – introducing SPFeatureUpgrade kit
  4. Feature upgrade (part 4) – advanced scenarios
  5. Feature upgrade (part 5) – using PowerShell to upgrade Features

In my last post I covered some fundamental aspects of SharePoint 2010’s Feature upgrade framework, and covered concepts such as upgrading Features using pure XML (ApplyElementManifests, AddContentTypeField) or shelling out to code (CustomUpgradeAction) to perform the necessary steps. For some reason it feels like Feature upgrade is one of those areas which is slightly harder to get up and running with, possibly because some XML will need to be cranked for some steps, or maybe because coming up with life-like upgrade scenarios with realistic artifacts takes some effort in itself. So, I thought it might be useful if I made available a sample I’ve been using (e.g. in my ALM talks and book chapter) for folks to download and use, in the hope that developers interested in seeing this stuff for themselves might have less work to do.

The sample

In addition to providing the source/XML files, I spent some time documenting the exact steps to go through when using the files to ‘execute’ my Feature upgrade scenarios. There’s a 3 page Word document which walks you through the upgrade steps, meaning the sample is effectively a “lab” of the type you might do on a training course:

 FeatureUpgradeSample_Doc

Similarly, the files provided cover all the different “states” in the process so you can just copy and paste the bits as you go:

FeatureUpgradeSample_Files

The upgrade examples

I came up with the following upgrade scenarios for the sample:

  • Upgrade iteration 1 - using ApplyElementManifests and AddContentTypeField to add new fields to a content type and push down to lists using it.
  • Upgrade iteration 2 – using CustomUpgradeAction/FeatureUpgrading to pseudo-upgrade a Text field to a Note field, by provisioning a new Note field/running code to iterate list items/copy data from old field to new/mark old field as hidden. (This scenario illustrates that many upgrades require code.)

The link

Download the files from http://sites.google.com/site/sharepointchris/sp2010_featureupgradesample

Summary

Feature upgrade is a slightly complex area, and I’m providing a sample that might help if this is an area of SharePoint 2010 you want to look more closely at. I’ve tested the documentation/files, but if anyone uses the sample and hits an issue, feel free to leave a comment here and I’ll do my best to help. The book chapter I wrote contains a rendition of this sample (along with much wider Application Lifecycle Management coverage, as you’d expect), but I thought it would be good to publish it to a wider audience.