Winsmarts.com

Microsoft MVP

MVP Logo

Awarded the Microsoft MVP Award.

Hosted By

blah!bLaH!BLOG!!

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

Download code at - http://www.codeplex.com/MOSSProfileReplicate/

Posted on 1/15/2007 @ 5:52 PM in #Sharepoint | 11 comments | 11404 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/


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?

Please post your comments:


Your feedback will be submitted for moderation, and will appear after it is approved.

Name:  
Email (optional): Your email address will not be posted.
URL (optional):
Comments: HTML will be ignored, URLs will be converted to hyperlinks  
Enter the text you see in the box:
 

Site designed and maintained by Sahil Malik | All Rights Reserved. ©2007 WinSmarts.com.