SharePoint 2007 Utility #1 - ProfilePropertyMgr - Utility to Import/Export profile properties

Posted on 1/15/2007 @ 5:52 PM in #SharePoint by | Feedback | 22776 views

Recommended Reading/Basics before you dive into this blog post: User Profiles and Audience Targeting.

What is this utility?

This is a command line utility that will allow you to manage profile properties setup on a SharePoint web's SharedService provider. It allows you to export properties from an SSP to XML, and vice versa.

How to use this utility?

The utility comes with online help. Just type in "ProfilePropertyMgr -help" at commandline to get the help screen. The help screen reproduced below for your pleasure -

=================================================
Profile Property Manager - Help
=================================================

Usage: ProfilePropertyMgr -url <UrlToWeb> -filename <FileName> -import
ProfilePropertyMgr -url <UrlToWeb> -filename <FileName> -export

or

ProfilePropertyMgr -help

*********** Used For ***********
Export Profile properties from SharePoint to an XML file.
Import Profile properties from an XML file to SharePoint.

*********** Required Parameters are: ***********
-url <UrlToWeb>
-file <FileName.xml>

*********** XML File structure: ***********
<Properties>
<Property>
<PropertyInfo Name=" Type=" Data="/>
<PropertyInfo ..>
...
</Property>
<Property>...</Property>
...
</Properties>

*********** Sample usage: ***********
ProfilePropertyMgr -url
http://moss2007 -filename output.xml -export
ProfilePropertyMgr -url
http://moss2007 -filename input.xml -import

Code Explanation

The code accepts input as command line parameters, and passes them to a business object called ProgramInputs as shown below:

static void Main(string[] args)
{
    ProgramInputs inputs = new ProgramInputs(args);

The ProgramInputs business object has 3 properties - Boolean "IsValid", String "URL", and "CurrentOperation". CurrentOperation is a custom enum with 3 possible valies - Unspecified, Import and Export. ProgramInputs encapsulates the functionality to validate inputs, and tell the end user if the inputs are accepted, or what was missing.

Once we know that the inputs are valid, we can act upon them. This is done using the following code

if (inputs.IsValid)
{
  ...
}
else
{
    Trace.WriteLine("Invalid inputs specified", true);
    Trace.PrintHelp();
}

And the actual code looks like this -

using (SPSite site = new SPSite(inputs.Url))
{
    ServerContext context = ServerContext.GetContext(site);
    UserProfileManager profileManager = new UserProfileManager(context);

    switch (inputs.CurrentOperation)
    {
        case Operation.Unspecified:
            throw new InvalidOperationException("Invalid input operation specified, or input operation omitted, cannot continue");
        case Operation.Import:
            WorkerBee.ImportProfiles(inputs, profileManager);
            break;
        case Operation.Export:
            WorkerBee.ExportProfiles(inputs, profileManager);
            break;
        default: // This will never happen, so safely ignored.
            break;
    }
}

As you would note from the above, the work is delegated to a class called WorkerBee. It has two methods, one to export, and one to import. I first get a hold of all the SharePoint properties using ProfileManager.Properties, and then I use reflection to sniff out which properties are exportable. Basically, anything that can be succesfully imported on the other end, is exportable on this end (duh!). So what is importable/exportable? Anything that is Read/Write, is value-type (or string), and doesn't start with "SSP-" (reserved for SharePoint), or isn't the reserved UserProfile_GUID property column. This in C#, is described as below -

private static bool ShouldExport(PropertyInfo typeProperty, Property profileProperty, bool entireProperty)
{
    bool toReturn = false;

    toReturn = typeProperty.CanRead & typeProperty.CanWrite &
                (
                    typeProperty.PropertyType.IsValueType |
                    typeProperty.PropertyType.FullName.Equals("System.String")
                );

    // Proceed only if toReturn is still true ;
    // Also, the following applies to only entire properties
    if (toReturn & entireProperty)
    {
        object reflectedValue = typeProperty.GetValue(profileProperty, null);
        string strReflectedValue = (reflectedValue == null) ? String.Empty : reflectedValue.ToString();
        toReturn &= !strReflectedValue.Equals("UserProfile_GUID");
        toReturn &= !strReflectedValue.StartsWith("SPS-");
    }
    return toReturn;
}

The full Export Profiles method looks like this -

internal static void ExportProfiles(ProgramInputs inputs, UserProfileManager profileManager)
{
    // Variable Naming convention: <objectinConcern><Property/Properties>

    PropertyCollection profileProperties = profileManager.Properties;

    XmlDocument exportProperties = new XmlDocument();
    XmlNode nodeProperties = exportProperties.CreateNode(XmlNodeType.Element, "Properties", "");
    exportProperties.AppendChild(nodeProperties);

    PropertyInfo[] allProps = typeof(Property).GetProperties();
    Dictionary<string, PropertyInfo> typeProperties = new Dictionary<string, PropertyInfo>();
    foreach (PropertyInfo typeProperty in allProps)
    {
        typeProperties.Add(typeProperty.Name, typeProperty);
    }

    foreach (Property profileProperty in profileProperties)
    {
        // Should I be even exporting this property?
        bool isExportable = ShouldExport(typeProperties["Name"], profileProperty, true);

        if (isExportable)
        {
            XmlNode nodeProperty = exportProperties.CreateNode(XmlNodeType.Element, "Property", "");
            nodeProperties.AppendChild(nodeProperty);

            foreach (PropertyInfo typeProperty in typeProperties.Values)
            {
                if (ShouldExport(typeProperty, profileProperty, false))
                {
                    XmlNode nodePropertyInfo = exportProperties.CreateNode(XmlNodeType.Element, "PropertyInfo", "");

                    XmlAttribute attribProfilePropInfo;
                    object reflectedValue;

                    //Name
                    attribProfilePropInfo = exportProperties.CreateNode(XmlNodeType.Attribute, "Name", "") as XmlAttribute;
                    attribProfilePropInfo.Value = typeProperty.Name;
                    nodePropertyInfo.Attributes.Append(attribProfilePropInfo);

                    // Data Type
                    attribProfilePropInfo = exportProperties.CreateNode(XmlNodeType.Attribute, "Type", "") as XmlAttribute;
                    attribProfilePropInfo.Value = typeProperty.PropertyType.AssemblyQualifiedName;
                    nodePropertyInfo.Attributes.Append(attribProfilePropInfo);

                    // Value
                    attribProfilePropInfo = exportProperties.CreateNode(XmlNodeType.Attribute, "Data", "") as XmlAttribute;
                    reflectedValue = typeProperty.GetValue(profileProperty, null);
                    attribProfilePropInfo.Value = (reflectedValue == null) ? String.Empty : reflectedValue.ToString();
                    nodePropertyInfo.Attributes.Append(attribProfilePropInfo);

                    nodeProperty.AppendChild(nodePropertyInfo);
                }
            }
        }
    }

    exportProperties.Save(inputs.FileName);
}

The ImportProfiles method is the exact anti-thesis of the above, so I am not going to bother copy pasting that here.

You can find the full code here -

http://www.codeplex.com/MOSSProfileReplicate/

Sound off but keep it civil:

Older comments..


On 3/26/2007 1:46:17 PM Katherine Woolverton said ..
Hi. I'm not a coder so I can't tell if this will help. I am looking for something that will help us resolve import of information from multiple AD forests/domains into MOSS. We're experiencing what we believe are collision type conflicts resulting from users having multiple accounts residing in different domains. This causes odd behavior in alerts, etc. Can't resolve the AD side of things immediately, so we're looking for an interim solution. Will this help?


On 3/26/2007 3:20:18 PM Sahil Malik said ..
Hey Katherine,

That is a very typical problem in most organizations. Luckily it is a solvable problem.

You may not be able to use the above directly if you are uncomfortable with code. But the solution to the above problem is to first sit and chalk out the various rules you would like to have in place, if in case collisions occur. Say for instance, Phone # in this forest will override phone # in this forest, if a collision occurs.

Once you have such policies sorted out on paper, then all of this can be put into MOSS. Depending upon the version of MOSS you are running, the exact solution may differ, but it's do-able.

Also, in the long run, once you have clean data in MOSS, you can create a web service to update the AD.

SM


On 4/13/2007 2:06:55 PM Dan Matthews said ..
I have now updated the tool to support multi-choice list values, available at the original URL.


On 4/13/2007 2:21:22 PM Sahil Malik said ..
SWEET! Thanks Dan!


On 7/12/2007 8:49:05 AM Fred Morrison said ..
Can I modify your tool to remove these bogus/experimental/not-even-real-people users from MOSS?


One of our developers decided to find out how much of a "load" Active Directory could handle, in terms of total users, not realizing that some of us have development MOSS envrioments that run scheduled (once a night in my case) incremental user imports from AD via Central Administration. He wrote a C# program to programattically populate AD with over 300,000 users, just to see if it could handle "the load".

My every-night-at-2200 incremental import of users from AD choked on those 300,000 users.


Worse, now I have over 300,000 of these bogus not-even-real-people userids in MOSS and the only way I can get rid of them is to (painfully, 50 at a time) bring them up via Central Admin's View Users page, use the "select all" icon at the top-left, and click the Delete button. Not the ideal way to spend 6 weeks of my time.

The users all have a common pattern to their names, so I could probably use either a simple StartsWith operation on the userid to find all of them. What I need is the equivalent of SPUser.Delete (I think) to get rid of them.


On 11/21/2007 2:40:36 AM vasudevan said ..
Hi Malik,

The above article was really helpful in importing and exporting the custom properties to the Sharepoint.


I have one question can we modify the inbuilt properties(Sharepoint properties) and the custom properties using Xml File through API.

ex:- By default for Work Phone


Name : WorkPhone


Display Name: Work Phone

Now i want to modify the display name like given below

Name : WorkPhone


Display Name: Primary Phone

Can i do this...............

Thanks in advance


Vasudevan


On 2/1/2008 12:28:16 PM Alex said ..
Hello,

this tool seems to be exactly what I need but I can't get the functionality that I want. I want to export the stored profile information in the ssp into an xml file. My Problem is that I don't know what URL is exactly needed. What is the http://moss2007 url? Is this the link to the SSP Administration Page? In my case this would be http://moss:22003. But I don't see any profile Information in the output xml file. The file is 78KB and has no Information like Name: XXX Phone: 000.

It contains only lines like:


<Property>


<PropertyInfo Name="Name" Type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Data="CellPhone" />


<PropertyInfo Name="Type" Type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Data="string" />

How do I export the properties?

Thank you very much!


On 2/7/2008 5:19:05 AM akshatkt said ..
hi

i was using PofilePropertyManager tool for exporting and importing the properties from SSP in one Central Adminsite to SSP in another central adminsite.

But results are not coming according to expectations.

What ever the properties i am exporting is not importing properly and thus Properties of another SSP remains unchanged.

because of this every time i need to change thm manually.

can any one pleasehelp me on this.


On 2/11/2008 8:24:07 AM Van de Walle Kurt said ..
A stupid question about the tool .... where do I have to extract the files to make it work???

Kurt


On 2/11/2008 1:09:59 PM Sahil Malik said ..
Extract them on a machine with WSS on it.


On 3/3/2008 2:20:34 PM Bruno Correa said ..
Did anyone changed this code to be able to import/export the properties with Localized strings for DisplayName/Description for the fields?


On 5/26/2008 1:49:18 AM Mohamed said ..
It works great to export/import user profile properties. But how to export the user profile data (like responsibilities, skills, past projects etc..) from one ssp ina farm to another ssp in another farm. These responsibilities, skills, past projects are not part of my AD.


On 6/2/2008 11:38:26 AM tenille bennett said ..
Thanks for the very informative post. My scenario involves using an LDAP query to exclude a certain user group from the import. But this does not seem to work. When i run a full import or even an incremental import, sharepoint still imports the same number of profiles, even thought the group is specifically excluded holds 43 profiles. Could this be because the import will not update the user accounts or groups it matches with when importing? How do i remove the group when importing all other user profiles? The reason I am trying to exclude it is because there is a group which I do not want to appear in user's mysite memberships webparts. Any help is very much appreciated.


On 9/2/2008 10:00:24 AM Mike said ..
Anyone knows where I can download this utility? It is no longer available on codeplex


On 9/2/2008 10:01:16 AM Mike said ..
Anyone knows where I can download this utility? It is no longer available on codeplex


On 2/10/2009 9:11:51 PM baner said ..
I am not a coder too(Put me in a very hard situation in my work). I am using a tool called Sharepoint AD Sync now. right now


On 2/13/2009 9:44:33 AM dav Singh said ..
This is whack...

how the hell are you supposed to use this ??

I have compiled it - got the dll from the source placed it into my assembly folder (c:\windows\assembly) but it doesnt recognise the commands in command line.

C:\>ProfilePropertyMgr -help


'ProfilePropertyMgr' is not reco


operable program or batch file.


On 3/8/2010 12:34:06 AM Rajavi said ..
Hi,


When I am trying to execute the ProfilePropertyMgr command it gives error as 'ProfilePropertyMgr' is not recognized as an internal or external command,


operable program or batch file.

Please tell me how to solve this problem to run the command.


On 6/21/2010 1:16:22 PM Carmen said ..
Hi, I am looking to download this utility and I do not seem to be bale to find it. Can you please help me find it?


Thanks in advance,


Carmen


On 6/21/2010 11:59:01 PM Sahil Malik said ..
Carmen,

Only source code is available - which will contain a compiled EXE.


It is right here - http://mossprofilereplicate.codeplex.com/SourceControl/list/changesets

No support :)

S


On 12/1/2010 3:20:10 PM Sree Vankayala said ..
Is there a plan to create one for MOSS (SharePoint) 2010?

Thanks


On 12/1/2010 3:20:43 PM Sree Vankayala said ..
Is there a plan to create one for MOSS (SharePoint) 2010?

Thanks


On 4/14/2011 3:01:16 AM vikas said ..
Hey sahil, i downloaded the code and compiled and found it working for only utility 2. and not utility 1. actually i am confused. the command line arguments for ProfilePropertyMgr -url <UrlToWeb> -filename <FileName> -export are not getting recognized. :-/ :-/ :-/


On 6/7/2011 5:57:56 PM Brittney said ..
Hey Sahil - how would you change the code to allow importing of HTML data type like with "About Me" profile property? Not sure how to change the xml for HTML.