Bug Alert for April CU and Migrating Users

Just heard about a nasty little bug in the April CU from my friend Syed.  He was using the SPWebApplication.MigrateUsers method to migrate accounts from one claim value to another (i.e. like if you were migrating from Windows claims to SAML claims, or in his case, changing identity claim values).  Turns out after doing the migration the actual claim value in the content databases was not getting updated.  He managed to track this down to a bug in the April CU, but found that it was fixed again in the June CU.  So…if you are doing any kind of claims migrations – including updating an identity claim value – make sure that you are NOT on the April CU, but instead apply one after that.

Thanks Syed for passing this info along.

Converting A Classic Auth Content Database to Claims Auth in SharePoint 2013

In previous blogs for SharePoint 2010 I’ve talked about how to do claims migration – http://blogs.technet.com/b/speschka/archive/2010/07/20/migrating-from-windows-classic-auth-to-windows-claims-auth-in-sharepoint-2010-part-2.aspx and others.  In SharePoint 2013 we have a new cmdlet for doing that called Convert-SPWebApplication, which I may cover at a future time.  As you can tell from the name though, that cmdlet’s scope is the web application.  We believe of course that as you attach content databases to create your new SharePoint 2013 farms, you will want a “lighter” method to migrate databases that still use Windows classic authentication.  In this scenario, I’ll assume that you already have a Windows claims web application running in SharePoint 2013 and you’ve already attached your SharePoint 2010 content database using the Mount-SPContentDatabase cmdlet.  So – assuming you have 1 content database from when you created the SPWebApplication and you just added your 2nd that is Windows Classic mode, here’s how you can convert only that content database to using Windows claims.  Please NOTEsuper important – that you use “$arguments” for the arguments parameter.  I’m not the brightest PowerShell guy in the world (not even close), so I stubbed my toe and banged my head on the desk repeatedly because I called mine “$args”.  After one hour of bloody toe and forehead I realized that it all works if I just called it $arguments.  Somewhere, somehow, I’m sure someone will tell me this is my fault.  🙂

$wa = Get-SPWebApplication http://yourWebAppUrl
$acc = ‘domain\user’
$arguments = New-Object Microsoft.SharePoint.Administration.SPWebApplication+SPMigrateUserParameters

#based on scenario above, content DB 0 is the one that was created with the web app, so 1 is the DB that was added with Mount-SPContentDatabase

$arguments.AddDatabaseToMigrate($wa.ContentDatabases[1])
$wa.MigrateUsersToClaims($acc, $true, $arguments)

After you run this you should be good to go.  I tested on a content DB with a handful of users and a combination of SharePoint groups and unique permissions and it all worked great.  And by the way, yes, that really is a plus sign between SPWebApplication and SPMigrateuserParameters.

Migrating User Accounts from Windows Claims to SAML Claims

In the work I’ve been busy with lately I’ve had a lot of interest from folks that are interested in starting out as Windows claims users, and then at some point switching over and start using SAML claims.  Sounds reasonable enough, but the problem is that we don’t have an out of the box way to migrate accounts from Windows claims to SAML claims.  The good news is the SharePoint product group added in the hooks in the August 2010 CU to let you run your own custom code in the MigrateUsers method.  We have a whole document on the API coming out soon along with a code sample that was a result of some great work by Bryan P. and Raju S., and my sample is based on that.  They’ve done a very good job of documenting the new API (actually an interface – IMigrateUserCallback) so I won’t try and do that in great detail here.  As soon as I have a link to the newly published info on this I will update this posting with it.

So, as usual, I’m going to start out just by pasting in the code of my custom migration class, and then I’ll walk through the parts that I think are interesting.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

using System.Security;

using System.Security.Principal;

//add references to Microsoft.SharePoint and Microsoft.IdentityModel for these

using Microsoft.SharePoint;

using Microsoft.SharePoint.Administration;

using Microsoft.SharePoint.Administration.Claims;

using Microsoft.IdentityModel.Claims;

 

 

namespace MigrateUserSample

{

   public class MigrateTest : IMigrateUserCallback

   {

 

       public string SPTrustedIdentityTokenIssuerName { get; set; }

 

 

       public MigrateTest(string TrustedIdentityTokenIssuerName)

       {

          SPTrustedIdentityTokenIssuerName = TrustedIdentityTokenIssuerName;

       }

 

 

       public string ConvertFromOldUser(string oldUser,

              SPWebApplication.AuthenticationMethod authType, bool isGroup)

       {

          string value = string.Empty;

 

          try

          {

              switch (authType)

              {

                 case SPWebApplication.AuthenticationMethod.Windows:

                     //code for converting from classic Windows would be here

                     Debug.WriteLine(oldUser);

                     break;

                 case SPWebApplication.AuthenticationMethod.Claims:

                     //this is the only scenario this sample will cover

                     //migrating from Windows claims to SAML claims

                     Debug.WriteLine(oldUser);

 

                     //get the claim provider manager

                     SPClaimProviderManager cpm = SPClaimProviderManager.Local;

 

                     //create a claim from the identifier so we can see if the

                     //original issuer came from Windows

                     SPClaim idClaim = cpm.ConvertIdentifierToClaim(oldUser,

                           SPIdentifierTypes.EncodedClaim);

 

                     //this is a Windows claims user, and we are going to

                     //convert to a SAML claims user

                     if (idClaim.OriginalIssuer == “Windows”)

                     {

                        //windows claims users will be in the format domain\user;

                        //windows claims groups will be in the SID format

                        if (idClaim.Value.Contains(“\\”))

                        {

                           //migrating a user

 

                           //you will want to check the identity of the user here

                           //there may be some Windows claims accounts you don’t want to

                           //convert yet, and there will also be service accounts that

                           //are passed in that you may not want to convert either; 

                           //ideally you would just read from a data source to determine

                           //which users you should convert, and then check the identity

                           //here to see if it’s one of the users that should be

                           //converted

 

                           //in this case, I’m only converting one user – darrins

                           if (idClaim.Value == “contoso\\darrins”)

                           {

                               //I’m getting an identity claim here, grabbing the

                               //part after the “domain\”, and appending the email

                               //suffix to it, so it becomes darrins@contoso.com

                               SPClaim migratedUserClaim =

                                         SPClaimProviderManager.CreateUserClaim(

                                         idClaim.Value.Split(‘\\’)[1] + “@contoso.com”,

                                         SPOriginalIssuerType.TrustedProvider,

                                         SPTrustedIdentityTokenIssuerName);

 

                               //get the encoded value of what the new identity

                               //claim will be

                               value = migratedUserClaim.ToEncodedString();

                           }

                        }

                        else

                        {

                           //migrating a group

 

                           //get the plain name of the group

                           SecurityIdentifier sid =

                               new SecurityIdentifier(idClaim.Value);

                           NTAccount groupAccount =

                               (NTAccount)sid.Translate(typeof(NTAccount));

 

                           string groupName = groupAccount.ToString();

 

                           //only interested in migrating the Portal People group

                           if (groupName.ToLower() == “contoso\\portal people”)

                           {

                               //create a new role claim

                               SPClaim migratedGroupClaim =

                                  new SPClaim(http://schemas.microsoft.com/ws/2008/06/identity/claims/role”,

                                  groupName.Split(‘\\’)[1],

                                  Microsoft.IdentityModel.Claims.ClaimValueTypes.String,

                                  SPOriginalIssuers.Format(

                                  SPOriginalIssuerType.TrustedProvider,

                                  SPTrustedIdentityTokenIssuerName));

 

                               //get the encoded value of what the new role claim will be

                               value = migratedGroupClaim.ToEncodedString();

                           }

                        }

                     }

 

                     break;

                 case SPWebApplication.AuthenticationMethod.Forms:

                     //code for converting from Forms would be here

                     Debug.WriteLine(oldUser);

                     break;

              }

          }

          catch (Exception ex)

          {

              Debug.WriteLine(ex.Message);

          }

 

          return value;

       }

   }

}

 

 

The first thing I’m doing is checking the value of the SPWebApplication.AuthenticationMethod parameter that was passed in.  Since I’m only interested in converting claims users (Windows to SAML), that’s the only situation in which I have code to execute.  When the current user is a claims user, I start out by getting a reference to the local SPClaimProviderManager so I can get a claim representation of the user.  I do that so I can determine if the user is a Windows claim user, FBA claims user, or SAML claims user.  In my case I only want to convert users that are Windows claims users. 

 

After I’ve determined I have one of those, the next thing I try to figure out is whether the claim is for a user or group.  Here’s one of the weird things that you may notice.  Even when the current user is a Windows claims group, the isGroup parameter that is passed into the method returns false.  That means that I need to check myself to figure out if the current “entity” is a user or group.  So I just look at the claim value – if it’s a user, it will be in the format domain\user; otherwise it’s a group, which will be in a SID format.

Now that I know which type of entity it is, I can determine which type of claim is needed.  For a user, I need to create an identity claim.  One of the things that requires is for me to know the name of the SPTrustedIdentityTokenIssuer that is being used on the web application.  I could have written code to figure that out, but instead I went the route of “hey this is a sample, sue me for being lazy” and force you to pass in the correct name in the constructor for my class.  So I take the user’s login name (after the domain part) and for my purpose their email address will always be loginname@contoso.com.  If your organization is not that way then you’ll need your own means to determine the correct email address.  I use it with the code above to create an identity claim for that user, and that’s the value that I return – that’s what in this case, the vbtoys\darrins account will be converted to.  (grammar police don’t bug me about ending my sentence in a preposition)

For groups, I take the SID that I’ve been given and I use the NTAccount class to get the friendly name of the group.  I use that to create a new role claim, and then pull the encoded value from that as the value to which the group should be migrated.  (grammar police happy now, yes?!?)

One other thing worth noting there – for both the users and groups, I don’t automatically try and migrate everything.  There are some things you may not want to migrate, like service accounts, built in accounts, etc.; whether you will or not depends on your requirements.  What’s neat about this method of doing the migration though is that you can do it as many times as you want.  If you want migrate just a subset of users, or do it in batches, or over time, or whatever – you can do that.  For example, you might have a database that has all the users you’re supposed to migrate.  You could query that to get the list and then as each user is called in your migration code, you could check to see if it’s in the list of users you got from your database.  Just one example there.

Okay, so I don’t want to get into the SDK documentation that Bryan and Raju worked on too much, but I do feel compelled to at least let you know how your class gets invoked so I don’t leave you hanging here.  What I did was just write a winforms application and add a project reference to my custom assembly I’ve described above.  That made it extremely easy to build and debug together.  The code that I use then to invoke my class and do a migration looks something like this:

 

//get a reference to my web application

SPWebApplication wa = SPWebApplication.Lookup(new Uri(http://foo”));

 

//this is the name of my trusted identity token issuer

string SPTrustedIdentityTokenIssuerName = “ADFSProvider”;

 

//create an instance of the custom migrate user callback class

MigrateUserSample.MigrateTest mt =

new MigrateUserSample.MigrateTest(SPTrustedIdentityTokenIssuerName);

 

//create an interface reference to it

IMigrateUserCallback muc = mt as IMigrateUserCallback;

 

//migrate the users with it

wa.MigrateUsers(muc);

 

That’s it.  There will be many other variations of this needed to cover all scenarios, but this is a pretty good start, and Bryan and Raju will be adding significantly more to this body of work.

Migrating from Windows Classic Auth to Windows Claims Auth in SharePoint 2010 Part 2

NOTE:  UPDATED 1/8/2011

I wanted to follow-up on the previous post about migrating authentiation types in SharePoint 2010 that was done here:  http://blogs.technet.com/b/speschka/archive/2010/06/12/migrating-a-web-application-from-windows-classic-to-windows-claims-in-sharepoint-2010.aspx.  I still recommend taking a look at that posting if you haven’t already because it includes some good background information about the migration process.  Recently we’ve been tweaking with the set ot steps to migrate authentication types and having more success than previously so I wanted to share those steps.  These may or may not become the final steps, but it’s a pretty good start. 

UPDATED INFO:  Folks, one of the things we discovered is that at least one of the issues we were seeing post-migration can be solved if the account that is running the PowerShell commands described below is a) a local administrator on the server where the commands are being run and b) is added to a Full Control web app policy on the web application that is being migrated.  The script below already created a Full Control web app policy so I’ve just updated it to indicate that the user running the script should be used.

So here are the steps:

$WebAppName = “http://yourWebAppUrl

#THIS SHOULD BE THE ACCOUNT THAT IS RUNNING THIS SCRIPT, WHO SHOULD ALSO BE A LOCAL ADMIN
$account = “yourDomain\yourUser”

$wa = get-SPWebApplication $WebAppName

Set-SPwebApplication $wa -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default
# this will prompt about migration, CLICK YES and continue

#This step will set the admin for the site
$wa = get-SPWebApplication $WebAppName
$account = (New-SPClaimsPrincipal -identity $account -identitytype 1).ToEncodedString()

#Once the user is added as admin, we need to set the policy so it can have the right access
$zp = $wa.ZonePolicies(“Default”)
$p = $zp.Add($account,”PSPolicy”)
$fc=$wa.PolicyRoles.GetSpecialRole(“FullControl”)
$p.PolicyRoleBindings.Add($fc)
$wa.Update()

#Final step is to trigger the user migration process
$wa = get-SPWebApplication $WebAppName
$wa.MigrateUsers($true)

There are a couple of things that may still be a problem for you after doing this that you’ll want to take care of or be aware of:

  1. You may find that users cannot log into the site.  You enter your credentials and it says you are domain\user but you don’t have access rights.  If you see that then it’s likely you have configured the portalsuperuseraccount and portalsuperreaderaccount properties of the web application prior to migration.  You need to go back and change them to use the new claims-based account name.  You can get that by looking at the web application policy for the web app after migration and copying from there.  Note that if you are not working with publishing sites then adding these accounts to the web app properties may in fact block you from logging into a site, like a team site.  Yeah I know, this remains fairly odd and confusing.
  2. Existing alerts may not fire.  Right now the only work-around I’ve seen is to delete and recreate them.
  3. Double-check the web application policies and make sure that the search crawl account shows the new converted account name.  If it doesn’t, you will need to manually create a new policy for the crawl account.

If you follow these steps and use these troubleshooting tips you should be good to go in most cases.

 

 

Migrating a Web Application from Windows Classic to Windows Claims in SharePoint 2010

Hey all, this question came up recently about what do I do if I have a web application that is using Windows classic authentication and I want to change it to use Windows claims?  It could be that you started in Windows classic and now want to move to claims, or maybe you had a SharePoint 2007 site that you upgraded.  I did this in a relatively small test case yesterday and wanted to share the the process and things I looked at.

First, an important warning:  ONCE YOU GO TO CLAIMS, YOU CANNOT GO BACK TO CLASSIC!  Make sure you have good backups (I backed up my content database in SQL and my web application in central admin) before you get started.  I strongly recommend that you try this in a lab first before moving into production.

Now with our caveats out of the way, the process itself is fairly straightforward.  Four lines of PowerShell and a little time should get you there.  Here is the PowerShell:

**** UPDATE  ****

See http://blogs.technet.com/b/speschka/archive/2010/07/20/migrating-from-windows-classic-auth-to-windows-claims-auth-in-sharepoint-2010-part-2.aspx for a new set of steps to follow.

$w = Get-SPWebApplication “http://<server>/
$w.UseClaimsAuthentication = “True”
$w.Update()
$w.MigrateUsers(“True”)

MigrateUsers runs in a timer job, so you may need to wait a bit for it to complete.  Once I was done I verified things like:

  • Users could log in – both users that were added individually as well as those that were part of an AD group that had been added to a SharePoint group
  • The My Tasks view of a task list still worked – items that had been assigned to me as a classic auth user are still showing up in My Tasks (i.e. it understands that claims Steve used to be classic Steve)
  • My out of the box approval workflows that were in process still worked – I was able to complete them successfully
  • My custom SharePoint Designer workflows that were in process still worked – I was able to complete them successfully
  • I was able to create new instances of both out of the box and custom SharePoint Designer workflows
  • I was able to successfully crawl the web application
  • I was able to successfully query the contents from that crawl

I have noticed one anomaly so far – when I create a new alert in the site it says it is created successfully (I even get an email telling me it did so), but when I go to manage my alerts it doesn’t show up in the list.  Also changes do not generate an alert email.  If/when I find other anomalies I’ll try and update this post.