This management agent (MA) moves information about a group from one Microsoft SharePoint Web site to another SharePoint Web site. It uses the IMAExtensibleFileImport interface to import information from an XML file. This MA is used together with a text file that contains the schema that is associated to this information, in Lightweight Directory Access Protocol (LDAP) Data Interchange Format (LDIF) format, which is written to the directory. The Schema Text File for the SharePoint Management Agent section contains a sample text file. This MA also contains the IMAExtensibleCallExport interface. This interface contains methods that you can use to export the information that is obtained from the first SharePoint Web site to another SharePoint Web site.

You must create a Web service that connects to both SharePoint Web sites that are sharing information.

To create the MA

  1. In Microsoft Visual Studio, create a new Visual C# project named SharePointMA.

  2. Add a reference to the Microsoft.MetadirectoryServices namespace.

  3. In a class named Class1.cs, add the code that appears at the end of this procedure.

  4. In Solution Explorer, right-click Web reference, and then select Add Web Reference.

  5. In the dialog box, type the following URL: http://<sharepoint-servername>/_vti_bin/usergroup.asmx

    Set the local reference name to localhost.

  6. In the Class1.cs file that contains the copied source code, add the following using statement to the top of the class.

      Copy Code
    using SharePointMA.localhost;
    
  7. Add a Web service to this project that will connect to both SharePoint Web sites.

  8. Add the utilities, as described in Adding Utility Functions for this Management Agent.

C#  Copy Code
// Contents of Class1.cs
using System;
using System.Xml;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using Microsoft.MetadirectoryServices;
using System.Collections;
using System.Collections.Specialized;

namespace Microsoft.MetadirectoryServices.SharePoint
{
  public class MAExtensibleSoap : IMAExtensibleCallExport, 
	 IMAExtensibleFileImport
  {
	// WSS user group Web service.
	UserGroup ugs;

	string url		= null;
	string userName	= null;
	string userDomain	= null;
	string userPassword	= null;

	/// <summary>
	/// <tla rid="fim_sync_short"/> call-based export.
	/// </summary>
	public void GenerateImportFile( string filename, 
					string connectTo, 
					string user, 
					string password, 
					ConfigParameterCollection config,
					bool fullImport, 
					TypeDescriptionCollection descriptions, 
					ref string customData )
	{

	// Delta imports from SharePoint Web services are not
	// supported. If a delta import is specified,
	// let the administrator know.
	if( ! fullImport )
		throw new Exception( "Delta imports are not supported by this 
	 management agent.  Change the configuration of this 
	 management agent to a full import." );


	// If the file exists, delete it. If you do not do this,
	// the file will never get deleted because it will 
	// encounter an "object exists" error.
	if( File.Exists( filename ) )
		File.Delete( filename) ;

	string[] sections  = null;
	char[] splitChars  = { '\\' };

	// Break out the username from the domain if specified 
	// in domain\username format.
	sections = user.Split( splitChars );

	// Only the domain\username format is supported.
	 if( sections.Length != 2 )
		throw new Exception( "Invalid username format 
		 specified in management agent.  Specify username 
		 in domain\\username format." );

	// Save some global information.
	userDomain	= sections[ 0 ];
	userName	= sections[ 1 ];
	userPassword  = password;
	url		= connectTo;

	// Bind to the Web service with credentials.
	SpMaUtils.BindToUserGroupWebService( url, userName, 
	userDomain, password, ref ugs );

	// Get the user collection by executing the Web method 
	// on a SharePoint server.
	ArrayList sharePointUsers = SpMaUtils.
	 XmlUserCollectionToArrayList( ugs.GetUserCollectionFromSite() );
	 SpMaUtils.WriteMiisImportFile( filename, sharePointUsers );

}

	/// <summary>
	/// <tla rid="fim_sync_short"/> call-based export initialization.
	/// </summary>
	public void BeginExport(  string connectTo, 
				string user, 
				string password,
				ConfigParameterCollection config,
				TypeDescriptionCollection descriptions )
	{

	string[] sections  = null;
	char[] splitChars  = { '\\' };

	// Break out the username from the domain if 
	// specified in the domain\username format.
	sections = user.Split( splitChars );

	// Only the domain\username format is supported.
	if( sections.Length != 2 )
		throw new Exception( "Invalid username format specified 
		in management agent.  Specify username in 
		domain\\username format." );

	// Save some global information.
	userName	= sections[ 1 ];
	userDomain	= sections[ 0 ];
	userPassword  = password;
	url		= connectTo;

	// Bind to the Web service with credentials.
	SpMaUtils.BindToUserGroupWebService( url, userName, 
	userDomain, password, ref ugs );

}

	/// <summary>
	/// <tla rid="fim_sync_short"/> call-based export.
	/// </summary>
	public void ExportEntry(  ModificationType 
				modificationType, 
				string[] changedAttributes, 
				CSEntry csentry )
	{

	//
	// Validate that export attributes were found.
	//

	string displayName = SpMaUtils.GetCsEntryAttributeValue
		 ( csentry, "displayName" );
	if( displayName.Length == 0 )
		throw new Exception( "No \"displayName\" 
		attribute was found" );

	string email = SpMaUtils.GetCsEntryAttributeValue
			( csentry, "email" );
	if( email.Length == 0 )
		throw new Exception( "No \"email\" 
		 attribute was found" );

	string loginName = SpMaUtils.GetCsEntryAttributeValue
			 ( csentry, "loginName" );
	if( loginName.Length == 0 )
		throw new Exception( "No \"loginName\" 
			 attribute was found" );

	string notes = SpMaUtils.GetCsEntryAttributeValue
			( csentry, "notes" );
	// if( userNotes.Length == 0 )
	// throw new Exception( "No \"notes\" attribute was found" );


	switch( modificationType )
	{

		// New cs entry created.
		case ModificationType.Add:
		throw new Exception("New identities cannot be 
		provisioned to SharePoint" );

		// Exporting a delete.
		case ModificationType.Delete:
		ugs.RemoveUserFromSite( loginName );
		break;

		// cs entry updated.
		case ModificationType.Replace:
		ugs.UpdateUserInfo( loginName, displayName, email, 
				 notes );
		break;

}

}

	/// <summary>
	/// Call-based export complete.
	/// </summary>
	public void EndExport()
	{
}

  }
}

Adding Utility Functions for the Management Agent

The next piece that you add to this project performs miscellaneous tasks, such as creating helper functions and import and export files.

To create the class

  1. In Solution Explorer, right-click the project name, point to Add, and then click Add Class.

  2. Select the Class icon.

  3. In the Name field, type SpMaUtils.cs.

  4. Click Open.

  5. Add the following code to the code pane for this class.

C#  Copy Code
using System;
using System.IO;
using System.Net;
using System.Xml;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Win32;
using System.Web.Mail;

namespace Microsoft.MetadirectoryServices.SharePoint
{

 public class SpMaUtils
 {

 //  -------------------------------------------------------------
 //  Static variables
 //  -------------------------------------------------------------

 //  Import file information.
 internal static string strImportFileFormat = "iD: {0}\r\n" +
 "email: {1}\r\n" +
 "displayName: {2}\r\n" +
 "isDomainGroup: {3}\r\n" +
 "isSiteAdmin: {4}\r\n" +
 "loginName: {5}\r\n" +
 "notes: {6}\r\n" +
 "objectSid: {7}\r\n" +
 "\r\n";

 //  -------------------------------------------------------------
 //  Helper functions
 //  -------------------------------------------------------------

 //  / <summary>
 //  / Convert a SharePoint xml user node from the Web service to a 
 //  string array.
 //  / </summary>
 //  / <param name="xmlUserCollection">XML node of a user collection 
 //  returned by the SharePoint user collection Web method</param>
 //  / <returns>Name value collection for the user</returns>
 internal static NameValueCollection XmlUserToCollection
  (XmlNode xmlUser)
 {
 NameValueCollection user = new NameValueCollection();

 user.Add( "id", xmlUser.Attributes[ "ID" ].Value );
 user.Add( "email", xmlUser.Attributes[ "Email" ].Value );
 user.Add( "displayName", xmlUser.Attributes[ "Name" ].Value );
 user.Add( "isDomainGroup", xmlUser.Attributes
		 [ "IsDomainGroup" ].Value );
 user.Add( "isSiteAdmin", xmlUser.Attributes
		 [ "IsSiteAdmin" ].Value );
 user.Add( "loginName", xmlUser.Attributes[ "LoginName" ].Value );
 user.Add( "notes", xmlUser.Attributes[ "Notes" ].Value );
 user.Add( "objectSid", xmlUser.Attributes[ "Sid" ].Value );

 return user;

 }

 //  <summary>
 //  Convert a SharePoint xml user collection from the Web service  
 //  to an array of name value collections for each user.
 //  </summary>
 //  <param name="xmlUserCollection"></param>
 //  <returns>Array of name value collections for representing
 //  each user</returns>
 internal static ArrayList XmlUserCollectionToArrayList
  ( XmlNode xmlUserCollection )
 {
 ArrayList users = new ArrayList();

 for( int i=0; i<xmlUserCollection.ChildNodes.Item( 0 ).
  ChildNodes.Count; i++ )
 {
 //  Convert each user to a name value collection for properties.
 NameValueCollection user = XmlUserToCollection( xmlUserCollection.
  ChildNodes.Item( 0 ).ChildNodes.Item( i ) );

 //  Add the user property collection to the array.
 users.Add( user );
 }

 return users;
 }

 //  / <summary>
 //  / Gets a string attribute from the connected space and  
 //  / returns a blank string of the attrivute is not present
 //  / </summary>
 //  / <param name="csentry">Connected space entry object</param>
 //  / <param name="attribute">Attribute to retrieve</param>
 //  / <returns>Value of atrribute. Returns an empty string 
 //  / if attribute is not present</returns>
 internal static string GetCsEntryAttributeValue( CSEntry
   csentry, string attribute )
 {
 if( csentry[ attribute ].IsPresent )
 return csentry[ attribute ].StringValue;
 else
 return "";
 }

 //  / <summary>
 //  / Parses out the NETBIOS name of the domain based on a DN
 //  / </summary>
 //  / <param name="dn"></param>
 //  / <returns></returns>
 internal static string GetDomainNetBiosName( string dn )
 {
 //  -TODO-
 //  Need to call into win32 API. This currently assumes
 //  that the netbios name is the same as the fqdn, which
 //  may not be true.

 char[] splitChars = { ',' };
 string[] sections = dn.Split( splitChars );

 foreach( string section in sections )
 {
 //  If section begins with "DC=", then this indicatees the first  
 //  part of the string, which is the netbios name. Everything
 //  after the "=" character must be returned.
 if( section.Substring( 0, 3 ) == "DC=" )
 return section.Substring( 3 );
 }

 //  If the debugger stops here, no "DC=" section was found 
 //  in the DN passed in.
 return "";

 }

 //  / <summary>
 //  / Binds to the user group Web service.
 //  / </summary>
 //  / <param name="connectTo">Server name to bind to</param>
 //  / <param name="ssl">Boolean specifying whether or not SSL is 
 //  / required</param>
 //  / <param name="user">Username to bind as</param>
 //  / <param name="user">Domain to bind as</param>
 //  / <param name="password">Password to bind as</param>
 public static void BindToUserGroupWebService( string url, 
   string username, 
	string domain, string password, ref UserGroup ugs )
 {
 //  Connect to the Web service.
 ugs = new UserGroup( url );

 //  Authenticate based on credentials passed in
 ugs.Credentials = new NetworkCredential( username, password, 
  domain );

 }

 //  -------------------------------------------------------
 //  File manipulation
 //  -------------------------------------------------------

 //  / <summary>
 //  / Write a line to a file.
 //  / </summary>
 //  / <param name="filename">File to append to</param>
 //  / <param name="line">Line to write</param>
 internal static void WriteFile( string filename, string line )
 {
 StreamWriter sw = new StreamWriter( filename, true, System.Text.
   Encoding.Default );
 sw.WriteLine( line );
 sw.Close();
 }

 //  / <summary>
 //  / Writes <tla rid="fim_sync_short"/> import file.
 //  / </summary>
 //  / <param name="filename">File to append to</param>
 //  / <param name="user">Name value collection of 
 //  / user properties</param>
 internal static void WriteMiisImportFile( string filename, 
  NameValueCollection user )
 {

 object[] args = { user[ "id" ], user[ "email" ], user[ "displayName" ], 
   user[ "isDomainGroup" ], user[ "isSiteAdmin" ], user[ "loginName" ], 
   user[ "notes" ], user[ "objectSid" ]
 };

 //  Write the file.
 WriteFile( filename, string.Format(SpMaUtils.strImportFileFormat, 
 args));

 }

 //  / <summary>
 //  / Creates an <tla rid="fim_sync_short"/> import file
 //  / </summary>
 //  / <param name="filename"></param>
 //  / <param name="users"></param>
 internal static void WriteMiisImportFile(string filename, 
   ArrayList users)
 {
 //  Write each user.
 foreach( NameValueCollection user in users )
 WriteMiisImportFile( filename, user );
 }

 }

}

Schema Text File for the SharePoint Management Agent

You use a text file to add the schema that is used for this management agent. Create the text file, name it SharePointMA.txt, and copy the following information to that file.

  Copy Code
iD: iD
email: email
displayName: displayName
isDomainGroup: isDomainGroup
isSiteAdmin: isSiteAdmin
loginName: loginName
notes: notes
objectSid: objectSid

See Also