MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Monday, April 20, 2009

Exporting Audiences using STSADM

I’d been wanting to build an export and import command for Audiences for quite some time but just haven’t gotten around to it.  I’m currently looking for a good sample command to build during a possible talk at the next Best Practices conference so I decided to give this one a whack considering that I already had a good chunk of the code written and just needed to repurpose it.  I don’t know if I’ll use this command for the presentation so if anyone has any good ideas of things they’d like to see please let me know and I’ll look into it.

One of the things I was hoping to achieve through this was the ability to do an import and preserve the ID of the Audience so that content targeting Audiences could be migrated between environments – unfortunately I quickly discovered that the ID is set via stored procedures during the creation of the Audience so there was no way for me to intercept the creation process and change the ID without going to the database directly which is something I was not willing to do.  I decided that the ability to migrate Audiences between environments, even without being able to set the ID, was still valuable so I went ahead with the creation of the commands.

To do the export I get an instance of the AudienceManager class and then loop through the collection of Audience objects using the Audiences property.  I then use an XmlTextWriter to write out all the properties of the Audience and then I loop through the AudienceRuleComponent objects which you can get via the AudienceRules property of the Audience object, once again writing out all the properties using the XmlTextWriter.  There’s really not much to it as you can see in the code below:

   1: #if MOSS
   2: using System;
   3: using System.Collections;
   4: using System.Collections.Specialized;
   5: using System.IO;
   6: using System.Text;
   7: using System.Xml;
   8: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
   9: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
  10: using Microsoft.Office.Server;
  11: using Microsoft.Office.Server.Audience;
  12: using Microsoft.SharePoint;
  13:  
  14: namespace Lapointe.SharePoint.STSADM.Commands.Audiences
  15: {
  16:     public class ExportAudiences : SPOperation
  17:     {
  18:         /// <summary>
  19:         /// Initializes a new instance of the <see cref="ExportAudiences"/> class.
  20:         /// </summary>
  21:         public ExportAudiences()
  22:         {
  23:             SPParamCollection parameters = new SPParamCollection();
  24:             parameters.Add(new SPParam("name", "n", false, null, new SPNonEmptyValidator()));
  25:             parameters.Add(new SPParam("ssp", "ssp", false, null, new SPNonEmptyValidator()));
  26:             parameters.Add(new SPParam("explicit", "ex"));
  27:             parameters.Add(new SPParam("outputfile", "output", false, null, new SPDirectoryExistsAndValidFileNameValidator()));
  28:  
  29:             StringBuilder sb = new StringBuilder();
  30:             sb.Append("\r\n\r\nExports all audiences or a specific audience if a name is provided.\r\n\r\nParameters:");
  31:             sb.Append("\r\n\t-outputfile <file to output results to>");
  32:             sb.Append("\r\n\t[-name <audience name>]");
  33:             sb.Append("\r\n\t[-ssp <SSP name>]");
  34:             sb.Append("\r\n\t[-explicit (shows field and value attributes for every rule)]");
  35:             Init(parameters, sb.ToString());
  36:         }
  37:  
  38:         /// <summary>
  39:         /// Gets the help message.
  40:         /// </summary>
  41:         /// <param name="command">The command.</param>
  42:         /// <returns></returns>
  43:         public override string GetHelpMessage(string command)
  44:         {
  45:             return HelpMessage;
  46:         }
  47:  
  48:         /// <summary>
  49:         /// Executes the specified command.
  50:         /// </summary>
  51:         /// <param name="command">The command.</param>
  52:         /// <param name="keyValues">The key values.</param>
  53:         /// <param name="output">The output.</param>
  54:         /// <returns></returns>
  55:         public override int Execute(string command, StringDictionary keyValues, out string output)
  56:         {
  57:             output = string.Empty;
  58:  
  59:             string outputFile = Params["outputfile"].Value;
  60:             string xml = Export(Params["ssp"].Value, Params["name"].Value, Params["explicit"].UserTypedIn);
  61:  
  62:             File.WriteAllText(outputFile, xml);
  63:  
  64:             return OUTPUT_SUCCESS;
  65:         }
  66:  
  67:         /// <summary>
  68:         /// Returns an XML structure containing all the audience details.
  69:         /// </summary>
  70:         /// <param name="sspName">Name of the SSP.</param>
  71:         /// <param name="audienceName">Name of the audience.</param>
  72:         /// <param name="includeAllAttributes">if set to <c>true</c> [include all attributes].</param>
  73:         /// <returns></returns>
  74:         private string Export(string sspName, string audienceName, bool includeAllAttributes)
  75:         {
  76:             ServerContext context;
  77:             if (string.IsNullOrEmpty(sspName))
  78:                 context = ServerContext.Default;
  79:             else
  80:                 context = ServerContext.GetContext(sspName);
  81:  
  82:             AudienceManager manager = new AudienceManager(context);
  83:  
  84:             if (!string.IsNullOrEmpty(audienceName) && !manager.Audiences.AudienceExist(audienceName))
  85:             {
  86:                 throw new SPException("Audience name does not exist");
  87:             }
  88:  
  89:             StringBuilder sb = new StringBuilder();
  90:             XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
  91:             xmlWriter.Formatting = Formatting.Indented;
  92:  
  93:             xmlWriter.WriteStartElement("Audiences");
  94:  
  95:             if (!string.IsNullOrEmpty(audienceName))
  96:             {
  97:                 Audience audience = manager.Audiences[audienceName];
  98:                 ExportAudience(xmlWriter, audience, includeAllAttributes);
  99:             }
 100:             else
 101:             {
 102:                 foreach (Audience audience in manager.Audiences)
 103:                     ExportAudience(xmlWriter, audience, includeAllAttributes);
 104:             }
 105:  
 106:             xmlWriter.WriteEndElement(); // Audiences
 107:             xmlWriter.Flush();
 108:             return sb.ToString();
 109:         }
 110:  
 111:         /// <summary>
 112:         /// Exports the audience.
 113:         /// </summary>
 114:         /// <param name="xmlWriter">The XML writer.</param>
 115:         /// <param name="audience">The audience.</param>
 116:         /// <param name="includeAllAttributes">if set to <c>true</c> [include all attributes].</param>
 117:         private static void ExportAudience(XmlWriter xmlWriter, Audience audience, bool includeAllAttributes)
 118:         {
 119:             xmlWriter.WriteStartElement("Audience");
 120:             xmlWriter.WriteAttributeString("AudienceDescription", audience.AudienceDescription);
 121:             xmlWriter.WriteAttributeString("AudienceID", audience.AudienceID.ToString());
 122:             xmlWriter.WriteAttributeString("AudienceName", audience.AudienceName);
 123:             xmlWriter.WriteAttributeString("AudienceSite", audience.AudienceSite);
 124:             xmlWriter.WriteAttributeString("CreateTime", audience.CreateTime.ToString());
 125:             xmlWriter.WriteAttributeString("GroupOperation", audience.GroupOperation.ToString());
 126:             xmlWriter.WriteAttributeString("LastCompilation", audience.LastCompilation.ToString());
 127:             xmlWriter.WriteAttributeString("LastError", audience.LastError);
 128:             xmlWriter.WriteAttributeString("LastPropertyUpdate", audience.LastPropertyUpdate.ToString());
 129:             xmlWriter.WriteAttributeString("LastRuleUpdate", audience.LastRuleUpdate.ToString());
 130:             xmlWriter.WriteAttributeString("MemberShipCount", audience.MemberShipCount.ToString());
 131:             xmlWriter.WriteAttributeString("OwnerAccountName", audience.OwnerAccountName);
 132:  
 133:  
 134:             ArrayList audienceRules = audience.AudienceRules;
 135:             xmlWriter.WriteStartElement("rules");
 136:             if (audienceRules != null && audienceRules.Count > 0)
 137:             {
 138:                 foreach (AudienceRuleComponent rule in audienceRules)
 139:                 {
 140:                     xmlWriter.WriteStartElement("rule");
 141:                     if (includeAllAttributes)
 142:                     {
 143:                         xmlWriter.WriteAttributeString("field", rule.LeftContent);
 144:                         xmlWriter.WriteAttributeString("op", rule.Operator);
 145:                         xmlWriter.WriteAttributeString("value", rule.RightContent);
 146:                     }
 147:                     else
 148:                     {
 149:                         switch (rule.Operator.ToLowerInvariant())
 150:                         {
 151:                             case "=":
 152:                             case ">":
 153:                             case ">=":
 154:                             case "<":
 155:                             case "<=":
 156:                             case "contains":
 157:                             case "<>":
 158:                             case "not contains":
 159:                                 xmlWriter.WriteAttributeString("field", rule.LeftContent);
 160:                                 xmlWriter.WriteAttributeString("op", rule.Operator);
 161:                                 xmlWriter.WriteAttributeString("value", rule.RightContent);
 162:                                 break;
 163:                             case "reports under":
 164:                             case "member of":
 165:                                 xmlWriter.WriteAttributeString("op", rule.Operator);
 166:                                 xmlWriter.WriteAttributeString("value", rule.RightContent);
 167:                                 break;
 168:                             case "and":
 169:                             case "or":
 170:                             case "(":
 171:                             case ")":
 172:                                 xmlWriter.WriteAttributeString("op", rule.Operator);
 173:                                 break;
 174:                         }
 175:                     }
 176:                     xmlWriter.WriteEndElement(); // rule
 177:                 }
 178:             }
 179:             xmlWriter.WriteEndElement(); // rules
 180:             xmlWriter.WriteEndElement(); // Audience
 181:         }
 182:     }
 183: }
 184: #endif

The help for the command is shown below:

C:\>stsadm -help gl-exportaudiences

stsadm -o gl-exportaudiences


Exports all audiences or a specific audience if a name is provided.

Parameters:
        -outputfile <file to output results to>
        [-name <audience name>]
        [-ssp <SSP name>]
        [-explicit (shows field and value attributes for every rule)]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-exportaudiences MOSS 2007 Released: 4/17/2009

Parameter Name Short Form Required Description Example Usage
outputfile output Yes The path to the output file to save the exported audience details. -outputfile c:\audiences.xml

-output c:\audiences.xml
name n No The name of a specific audience if only one audience is to be exported. -name HR

-n HR
ssp   No The name of the SSP containing the audiences to export.  If omitted then the default SSP will be used. -ssp SSP1
explicit ex No If specified then all field and value properties for the rules will be populated (in many cases these properties are redundant and do not add any information and are not necessary for import). -explicit

The following is an example of how to export all audiences belonging to SSP1:

stsadm -o gl-exportaudiences -outputfile c:\audiences.xml -ssp SSP1

The following shows an example output of running the above command:

<Audiences>
  <Audience AudienceDescription="All users who can access the site" AudienceID="00000000-0000-0000-0000-000000000000" AudienceName="All site users" AudienceSite="http://sspadmin/ssp/admin" CreateTime="1/1/0001 12:00:00 AM" GroupOperation="AUDIENCE_NOGROUP_OPERATION" LastCompilation="1/1/0001 12:00:00 AM" LastError="" LastPropertyUpdate="1/1/0001 12:00:00 AM" LastRuleUpdate="1/1/0001 12:00:00 AM" MemberShipCount="0" OwnerAccountName="">
    <rules />
  </Audience>
  <Audience AudienceDescription="" AudienceID="a2e62d72-fa52-4772-a5d0-9b950ec7d220" AudienceName="HR" AudienceSite="http://sspadmin/ssp/admin" CreateTime="4/20/2009 8:23:48 PM" GroupOperation="AUDIENCE_OR_OPERATION" LastCompilation="4/20/2009 8:26:22 PM" LastError="" LastPropertyUpdate="4/20/2009 8:26:07 PM" LastRuleUpdate="4/20/2009 8:26:07 PM" MemberShipCount="6" OwnerAccountName="spdev\spadmin">
    <rules>
      <rule op="Member of" value="CN=Human Resources Dept,OU=Users,DC=CompanyName,DC=com" />
      <rule op="OR" />
      <rule field="Department" op="=" value="Human Resources" />
    </rules>
  </Audience>
</Audiences>

2 comments:

John Palmer said...

Hey Gary,

As always, great stuff! There are two tasks that the default STSADM and your extensions do not do:

- Export a list of groups and users that are granted site permissions (i.e., what is listed on the Site Permissions page).
- Export a list of users that are members of a specified group.

If I'm mistaken, please correct me...

Gary Lapointe said...

You are correct - I've not gotten to those yet (exporting list security is as close as I get - I'm sure the need will pop up eventually though :))