Thursday, May 7, 2009

Script to Create Profile Properties in SharePoint Profile Store

Recently I was working on a large enterprise project that made extensive use of the user profile store in SharePoint. To meet the business requirements we needed a lot of new properties to be created in the profile store. While that may initially look easy to just open up the profile section in the SSP and start creating properties by hand - you need to take into account different developer VPCs and the dev/test/stage/prod environments (and suddenly its not so easy). There was also a need to map these properties to either AD or the BDC connection. That is where the data was being pulled from.

So here is a handy little script I wrote to create all the profile properties you need (its configurable via the application file) and also to map these properties to the appropriate connector (if you are pulling data from AD or BDC).

I wrote this in a hurry and this is not production quality code, so take it as it is :).



using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Office.Server;
using Microsoft.Office.Server.Administration;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using System.Web;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using Microsoft.SharePoint.Portal;
using Microsoft.SharePoint.Portal.Topology;
using Microsoft.SharePoint.Administration;


namespace company.Mt.Utilities.ProfileProperties
{
class Program
{


static void Main(string[] args)
{
if (args.Length != 1)
{
Usage();
return;
}

switch (args[0])
{
case "CreateProfileAttributes":
CreateProfileAttributes();
MapExistingAttributes();
break;

case "MapExistingAttributes":
//MapExistingAttributes();
break;

case "DeleteOtherAttributes":
DeleteOtherAttributes();
break;

//case "DeleteUserProfiles":
// ProfileInstanceManagement.DeleteUserProfiles();
// break;

default:
Usage();
break;
}
}


///
/// Display user information
///

private static void Usage()
{
Console.WriteLine("Usage: Program [choice] where choice = CreateProfileAttributes or MapExistingAttributes or DeleteOtherAttributes");
Console.WriteLine("Example --> Program CreateProfileAttributes");
}


///
/// Delete other attributes
///

private static void DeleteOtherAttributes()
{
using (SPSite site = new SPSite(ConfigurationManager.AppSettings["server"])) //("http://localhost"))
{
ServerContext context = ServerContext.GetContext(site);
UserProfileManager profileManager = new UserProfileManager(context);
UserProfileConfigManager mgr = new UserProfileConfigManager(context);


//Get the properties
PropertyCollection pc = profileManager.PropertiesWithSection;

//Property companySection = pc.GetPropertyByName(ConfigurationManager.AppSettings["CustomPropertySectionName"]);
//if (companySection == null)
//{
// companySection = pc.Create(true);
// companySection.Name = ConfigurationManager.AppSettings["CustomPropertySectionName"];
// companySection.DisplayName = ConfigurationManager.AppSettings["CustomPropertySectionValue"];
//}

int order = 1;
NameValueCollection coll = (NameValueCollection)ConfigurationManager.GetSection("company/NewProfileProperties");

string keyValue = null;

foreach (String key in coll.AllKeys)
{
try
{
pc.RemovePropertyByName(key);
}
catch (Exception ex)
{
string s = ex.Message;
}
}
}


using (SPSite site = new SPSite(ConfigurationManager.AppSettings["server"])) //("http://localhost"))
{
ServerContext context = ServerContext.GetContext(site);
UserProfileManager profileManager = new UserProfileManager(context);
UserProfileConfigManager mgr = new UserProfileConfigManager(context);

PropertyCollection pc = profileManager.Properties;

NameValueCollection coll = (NameValueCollection)ConfigurationManager.GetSection("company/ExistingProfilePropertyUpdates");

string keyValue = null;

foreach (String key in coll.AllKeys)
{
try
{


keyValue = coll[key];
String[] details = keyValue.Split(new char[] { ',' });


Property property = pc.GetPropertyByName(key);
property.DisplayName = details[0];

DataSource ds = mgr.GetDataSource();
PropertyMapCollection pmc = ds.PropertyMapping;

string tomap = details[1].Trim();
string connnection = details[2].Trim();

try
{
pmc.Remove(key);
}
catch (Exception exe) { }

ArrayList invmap = pmc.VerifyMapping(false);

property.Commit();
}
catch (System.Exception e2)
{
Console.WriteLine(e2.Message + " - " + key);
}
}
}

}

///
/// Change mappings for existing attributes
///

private static void MapExistingAttributes()
{
using (SPSite site = new SPSite(ConfigurationManager.AppSettings["server"])) //("http://localhost"))
{
ServerContext context = ServerContext.GetContext(site);
UserProfileManager profileManager = new UserProfileManager(context);
UserProfileConfigManager mgr = new UserProfileConfigManager(context);

PropertyCollection pc = profileManager.Properties;

NameValueCollection coll = (NameValueCollection)ConfigurationManager.GetSection("company/ExistingProfilePropertyUpdates");

string keyValue = null;

foreach (String key in coll.AllKeys)
{
try
{
keyValue = coll[key];
String[] details = keyValue.Split(new char[] { ',' });


Property property = pc.GetPropertyByName(key);
property.DisplayName = details[0];

DataSource ds = mgr.GetDataSource();
PropertyMapCollection pmc = ds.PropertyMapping;

string tomap = details[1].Trim();
string connnection = details[2].Trim();

try
{
pmc.Remove(key);
}
catch (Exception exe) { }

if ((!string.IsNullOrEmpty(tomap)) && (!(string.IsNullOrEmpty(connnection))))
pmc.Add(key, tomap, connnection);

//ArrayList invmap = pmc.VerifyMapping(false);

property.Commit();
}
catch (System.Exception e2)
{
Console.WriteLine(e2.Message + " - " + key);
}
}
}

}

///
/// Create the profile attributes
///

private static void CreateProfileAttributes()
{

//
using (SPSite site = new SPSite(ConfigurationManager.AppSettings["server"])) //("http://localhost"))
{
ServerContext context = ServerContext.GetContext(site);
UserProfileManager profileManager = new UserProfileManager(context);
UserProfileConfigManager mgr = new UserProfileConfigManager(context);


//Get the properties
PropertyCollection pc = profileManager.PropertiesWithSection;

//Property companySection = pc.GetPropertyByName(ConfigurationManager.AppSettings["CustomPropertySectionName"]);
//if (companySection == null)
//{
// companySection = pc.Create(true);
// companySection.Name = ConfigurationManager.AppSettings["CustomPropertySectionName"];
// companySection.DisplayName = ConfigurationManager.AppSettings["CustomPropertySectionValue"];
//}

int order = 1;
NameValueCollection coll = (NameValueCollection)ConfigurationManager.GetSection("company/NewProfileProperties");

string keyValue = null;

foreach (String key in coll.AllKeys)
{
try
{
keyValue = coll[key];
String[] details = keyValue.Split(new char[] { ',' });

Property p = pc.Create(false);
p.Name = key;
p.DisplayName = details[0].Trim();
//p.Type = details[1].Trim().ToLower();

//sample to get a property type "URL"
PropertyDataTypeCollection pdtc = mgr.GetPropertyDataTypes();
PropertyDataType ptype = null;
IEnumerator enumType = pdtc.GetEnumerator();
while (enumType.MoveNext())
{
ptype = (PropertyDataType)enumType.Current;
if (ptype.Name.ToLower().Equals(details[1].Trim().ToLower())) break;
}

p.Type = ptype.Name;

if (details[1].Trim().ToLower() == "string")
p.Length = Convert.ToInt32(details[2].Trim());

p.PrivacyPolicy = PrivacyPolicy.OptIn;
p.DefaultPrivacy = Privacy.Public;
if (details[3].Trim() == "true")
p.IsSearchable = true;
else
p.IsSearchable = false;

p.IsVisibleOnEditor = false;
p.IsVisibleOnViewer = true;
pc.Add(p);
pc.SetDisplayOrderByPropertyName(p.Name, order++);
pc.CommitDisplayOrder();


DataSource ds = mgr.GetDataSource();
PropertyMapCollection pmc = ds.PropertyMapping;


string tomap = details[4].Trim();
string connnection = details[5].Trim();

if ((!string.IsNullOrEmpty(tomap)) && (!(string.IsNullOrEmpty(connnection))))
pmc.Add(key, tomap, connnection);

//ArrayList invmap = pmc.VerifyMapping(false);
}
catch (DuplicateEntryException e)
{
Console.WriteLine(e.Message + " - " + key);
}
catch (System.Exception e2)
{
Console.WriteLine(e2.Message + " - " + key);
}
}


//get arraylist of invalid mappings
//ArrayList invmap = pmc.VerifyMapping(false);
//remove current mapping if invalid
//foreach (PropertyMap pm in invmap)
//{
// if (pm.PropName == p.Name)
// {
// pmc.Remove(pm.PropName);
// }
//}

}
}
}
}

Here are the corresponding entries from the web.config.


<configuration>
<configsections>
<sectiongroup name="Company">
<section name="NewProfileProperties" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="ExistingProfilePropertyUpdates" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
</section></section></sectiongroup>
</configsections>
<company>
<newprofileproperties>
<!-- key=name, value="Display name, type, length, true if indexed, mapping to AD/BDC"/> -->

<add key="NamePrefix" value="Name Prefix,String,20,false,NamePrefix,BDC_Connection_Name"></add>
<add key="NameTitle" value="Name Title,String,40,false,NameTitle,BDC_Connection_Name"></add>
<add key="CompanyEmail" value="Company Email,Email,,true,userPrincipalName,ad_Connection_name"></add>

</newprofileproperties></company></configuration>
......
......

1 comment:

365Days said...

Hi Faraz,
Nice post. Just couple of points.
If you put this code in a feature and try and activate the feature from command prompt then it will never work.
The reason being at that point it will have no context awareness. So no web config.
The way around for me was to have the settings in a xml file in the feature .

Hope it helps.

Cheers---Jag