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.

Thursday, April 30, 2009

Creating Default Site Groups After Creating Site Collections Using STSADM

I got an email from Jennifer Davis today asking why, when she ran my gl-createsiteindb command, did the default site groups not show up in the site collection, specifically the “<site name> Members”, “<site name> Owners”, and “<site name> Visitors” groups.  Upon digging further she realized that this behavior was not limited to my command as the out-of-the-box createsite and createsiteinnewdb commands exhibited the same behavior.

Basically what’s happening is that if you create the site collection via the browser an additional method call gets made on the root web site of the site collection: SPWeb.CreateDefaultAssociatedGroups.  For whatever reason this method call is not made when using STSADM and as my gl-createsiteindb command just mimics the createsite command I too did not make the necessary method call.  Well, I agree with Jennifer that this is just wrong so I decided to go ahead and modify my code so that those default site groups would get created.  Fortunately it was a really simple change - here’s the code that I added to the command:

   1: if (!string.IsNullOrEmpty(webTemplate))
   2: {
   3:     using (SPWeb web = site.RootWeb)
   4:     {
   5:         web.CreateDefaultAssociatedGroups(ownerLogin, secondaryContactLogin, string.Empty);
   6:     }
   7: }

If you’re using any of the existing out of the box commands you can easily achieve the same end result with a couple lines of PowerShell, as the following demonstrates (requires my custom cmdlets):

$url = "http://<site url>"
$primaryOwner = "domain\user"
$secondaryOwner = "domain\user"
$site = Get-SPSite $url
$site.SPBase.RootWeb.CreateDefaultAssociatedGroups($primaryOwner, $secondaryOwner, "")
$site.SPBase.Dispose()

Sunday, April 26, 2009

Getting an SPWebApplication object using PowerShell

A SharePoint deployment isn’t much of a deployment if there are no web applications.  A web application in SharePoint contains one or more content databases, each of which can contain one or more site collections, etc., etc.  The SPWebApplication class has tons of methods and properties for directly or indirectly manipulating all things related to web applications - you can do backups, add content databases and site collections, set alert settings, manipulate the web.config file, etc.

There are a couple of different ways in which we can work with the SPWebApplication using PowerShell.  The first is to get a specific object using the static Lookup method and the second, which is useful for looping through all web applications, is to use the SPFarm object’s Service property.  The first approach is shown below:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$webapp = [Microsoft.SharePoint.Administration.SPWebApplication]::Lookup("http://portal")
If you need to loop through all web applications you would write something like the following:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$websvcs = $farm.Services | where -FilterScript {$_.GetType() -eq [Microsoft.SharePoint.Administration.SPWebService]}
$webapps = @()
foreach ($websvc in $websvcs) {
    foreach ($webapp in $websvc.WebApplications) {
        $webapps = $webapps + $webapp
    }
}

The code above isn’t the most intuitive but could be easily wrapped into a function to abstract out the complexity (note that there may be a better way to do the above - I’m still figuring this whole PowerShell thing out:) ).  Personally I think the above sucks - it’s not intuitive and it’s difficult to maintain and forces me to do additional filtering if I want a subset of the items returned - thus the motivation behind my Get-SPWebApplication cmdlet.  Here’s a couple examples that do the same thing as the above two examples but using my cmdlet instead:

$webapp = Get-SPWebApplication http://portal
$webapps = Get-SPWebApplication *

On the first line I’m retrieving a specific web application and on the second line I’m using a wildcard character to retrieve all web applications in the farm.  You could easily utilize the wildcard capabilities to reduce the set of web applications that are returned to those matching a specific pattern.   You can also pass in multiple, comma separated URLs such as in the following example:

$webapps = Get-SPWebApplication http://portal,http://mysites

The code for the cmdlet is reasonably simple - most of the work is in dealing with the fact that wildcards are allowed thus making it necessary to loop through all the web applications and the corresponding alternate URL mappings in order to identify the web applications to return:

   1: using System;
   2: using System.Management.Automation;
   3: using Lapointe.SharePoint.PowerShell.Commands.OperationHelpers;
   4: using Lapointe.SharePoint.PowerShell.Commands.Validators;
   5: using Microsoft.SharePoint;
   6: using Microsoft.SharePoint.Administration;
   7:  
   8: namespace Lapointe.SharePoint.PowerShell.Commands.WebApplications
   9: {
  10:     [Cmdlet(VerbsCommon.Get, "SPWebApplication", SupportsShouldProcess=true, DefaultParameterSetName = "Url")]
  11:     public class GetSPWebApplicationCommand : PSCmdletBase
  12:     {
  13:         /// <summary>
  14:         /// Gets or sets the URL.
  15:         /// </summary>
  16:         /// <value>The URL.</value>
  17:         [Parameter(
  18:             ParameterSetName = "Url",
  19:             Mandatory = true,
  20:             Position = 0,
  21:             ValueFromPipeline = true,
  22:             ValueFromPipelineByPropertyName = true,
  23:             HelpMessage = "The URL of the web application to return.  Supports wildcards.")]
  24:         [ValidateNotNullOrEmpty]
  25:         [ValidateUrl(true)]
  26:         public string[] Url { get; set; }
  27:  
  28:  
  29:         /// <summary>
  30:         /// Processes the record.
  31:         /// </summary>
  32:         protected override void ProcessRecordEx()
  33:         {
  34:             foreach (string url in Url)
  35:             {
  36:                 if (!WildcardPattern.ContainsWildcardCharacters(url))
  37:                 {
  38:                     string webApp = url.TrimEnd('/');
  39:                     WriteObject(SPWebApplication.Lookup(new Uri(webApp)));
  40:                 }
  41:                 else
  42:                 {
  43:                     WildcardPattern wildCard = new WildcardPattern(url.TrimEnd('/'), WildcardOptions.IgnoreCase);
  44:                     if (SPFarm.Local == null)
  45:                         throw new SPException(
  46:                             "The SPFarm object is null.  Make sure you are running as a Farm Administrator.");
  47:  
  48:                     foreach (SPService svc in SPFarm.Local.Services)
  49:                     {
  50:                         if (!(svc is SPWebService))
  51:                             continue;
  52:  
  53:                         foreach (SPWebApplication webApp in ((SPWebService) svc).WebApplications)
  54:                         {
  55:                             foreach (SPAlternateUrl altUrl in webApp.AlternateUrls)
  56:                             {
  57:                                 if (wildCard.IsMatch(altUrl.Uri.AbsolutePath.TrimEnd('/')))
  58:                                 {
  59:                                     WriteObject(webApp);
  60:                                     break;
  61:                                 }
  62:                             }
  63:                         }
  64:                     }
  65:                 }
  66:             }
  67:         }
  68:     }
  69: }
Here’s the full help for the cmdlet:
NAME
    Get-SPWebApplication
    
SYNOPSIS
    Gets one or more SPWebApplication objects representing a SharePoint 2007 Web Application.
    
SYNTAX
    Get-SPWebApplication [-Url] <String[]> [-WhatIf] [-Confirm] [<CommonParameters>]
    
    
DETAILED DESCRIPTION
    Pass in a comma separated list of URLs or a string array of URLs to obtain a collection of SPWebAppl
    ication objects.
    
    Copyright 2008 Gary Lapointe
      > For more information on these PowerShell cmdlets:
      > http://stsadm.blogspot.com/
      > Use of these cmdlets is at your own risk.
      > Gary Lapointe assumes no liability.
    

PARAMETERS
    -Url <String[]>
        Specifies the URL of the web application(s) to retrieve. Wildcards are permitted. If you specify
         multiple URLs, use commas to separate the URLs.
        
        Required?                    true
        Position?                    1
        Default value                
        Accept pipeline input?       true (ByValue, ByPropertyName)
        Accept wildcard characters?  false
        
    -WhatIf
        
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -Confirm
        
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    <CommonParameters>
        This cmdlet supports the common parameters: -Verbose, -Debug,
        -ErrorAction, -ErrorVariable, and -OutVariable. For more information,
        type, "get-help about_commonparameters".
    
INPUT TYPE
    String
     
    
RETURN TYPE
    Collection of SPWebApplication objects.
     
    
NOTES
    
    
        For more information, type "Get-Help Get-SPWebApplication -detailed". For technical information,
         type "Get-Help Get-SPWebApplication -full".
    
    --------------  EXAMPLE 1 --------------
    
    C:\PS>$webapp = get-spwebapplication -url http://portal
    
    
    This example returns back a single SPWebApplication object.
    
    
    
    
    
    
RELATED LINKS
    http://stsadm.blogspot.com

Saturday, April 25, 2009

Getting an SPFarm object using PowerShell

There are several core SharePoint objects that PowerShell programmers may need to work with in order to manipulate SharePoint via PowerShell scripts.  Getting these objects is pretty simple but not all that intuitive to users who are still trying to learn PowerShell and the SharePoint API.

The SPFarm object is the top level object for working with SharePoint and it provides access to all the global settings for all servers, services, and solutions that are installed in the farm.  To get an SPFarm object and see the public properties of that object you can either load up the Microsoft.SharePoint assembly and call the static “Local” property of the SPFarm class or use my new cmdlet, Get-SPFarm.  The first approach is shown below (note that you could of course easily move this into a function in a script and achieve the same thing as my cmdlet):

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$farm

The problem I have with the above code is that it’s just ugly so I created a real simple cmdlet that allows getting the SPFarm object with one line of code (which, again, could be achieved using a function in a script):

$farm = Get-SPFarm
$farm

Isn’t that much cleaner and easier to read:)?  The results of running the above code are shown below:

AlternateUrlCollections       : {Central Administration, SharePoint My Sites (80), SharePoint Portal (80), SharePoint Shared Services Admin (80)}
Servers                       : {SHAREPOINT1, sharepoint1.spdev.com, spsql1}
Services                      : {, , WSS_Administration, ...}
TimerService                  : SPTimerService Name=SPTimerV3 Parent=SPFarm Name=SharePoint_ConfigDB
Solutions                     : {lapointe.sharepoint.stsadm.commands.wsp}
TypeName                      : Farm
PairConnectionString          : 
IsPaired                      : False
CanMigrate                    : False
PersistedFileChunkSize        : 4194304
ErrorReportingAutomaticUpload : False
FeatureDefinitions            : {FeatureDefinition/001f4bd7-746d-403b-aa09-a6cc43de7942, FeatureDefinition/00bfea71-1c5e-4a24-b310-ba51c3eb7a57, FeatureDefinition/00bfea71-1e1d-4562-b56a-f05371bb0115, FeatureDefinition/00bfea71-2062-426c-90bf-714c59600103...}
BuildVersion                  : 12.0.0.6318
ErrorReportingEnabled         : True
DownloadErrorReportingUpdates : False
CEIPEnabled                   : False
TraceSessionGuid              : 89c8c935-99ff-48ce-9376-31daaaf32b85
ExternalBinaryStoreClassId    : 00000000-0000-0000-0000-000000000000
DiskSizeRequired              : 0
CanSelectForBackup            : True
CanRenameOnRestore            : False
CanSelectForRestore           : True
CanUpgrade                    : True
NeedsUpgradeIncludeChildren   : False
NeedsUpgrade                  : False
UpgradeContext                : Microsoft.SharePoint.Upgrade.SPUpgradeContext
Name                          : SharePoint_ConfigDB
DisplayName                   : SharePoint_ConfigDB
Id                            : 1e94781b-07f0-4f79-831e-235e0b17518c
Status                        : Online
Parent                        : SPFarm Name=SharePoint_ConfigDB
Version                       : 2279
Properties                    : {}
Farm                          : SPFarm Name=SharePoint_ConfigDB
UpgradedPersistedProperties   : {}

Obviously this is a pretty simple cmdlet and there’s not a whole of lot of advantages to doing this as a cmdlet instead of a function in a script.  The reason I went the cmdlet route for this (and the many others that I will be documenting) versus just a function is because I wanted to be able to access all my building block “stuff” in a consistent way and I wanted features such as parameter sets (which don’t make sense here but do in a lot of the others that I have).

Here’s an example script that displays all the services running on each server in the farm:

$farm = Get-SPFarm
foreach ($svr in $farm.Servers) {
    Write-Host($svr.DisplayName)
    Write-Host("-----------------------------")
    foreach ($svc in $svr.ServiceInstances) {
        Write-Host($svc.TypeName)
    }
    Write-Host("");
}

The code above produces output similar to the following:

SHAREPOINT1
-----------------------------
Session State
Windows SharePoint Services Search
Information Management Policy Configuration Service
Office SharePoint Server Search
Shared Services Timer
Office SharePoint Server Search Admin Web Service
Excel Calculation Services
Single Sign-on Service
SSP Job Control Service
Portal Service
Business Data Catalog
Office SharePoint Server Search
Document Conversions Launcher Service
Document Conversions Load Balancer Service
Windows SharePoint Services Web Application
Central Administration
Windows SharePoint Services Incoming E-Mail
Windows SharePoint Services Administration
Windows SharePoint Services Search
Windows SharePoint Services Timer
Office SharePoint Usage Analytics Service

sharepoint1.spdev.com
-----------------------------
Windows SharePoint Services Outgoing E-Mail

spsql1
-----------------------------
Windows SharePoint Services Database

The code for the cmdlet is extremely simple.  It takes no parameters and simply writes out the SPFarm.Local property:

   1: using System.Management.Automation;
   2: using Lapointe.SharePoint.PowerShell.Commands.OperationHelpers;
   3: using Microsoft.SharePoint.Administration;
   4:  
   5: namespace Lapointe.SharePoint.PowerShell.Commands.Farm
   6: {
   7:     [Cmdlet(VerbsCommon.Get, "SPFarm", SupportsShouldProcess=true)]
   8:     public class GetSPFarmCommand : PSCmdletBase
   9:     {
  10:  
  11:         /// <summary>
  12:         /// Processes the record.
  13:         /// </summary>
  14:         protected override void ProcessRecordEx()
  15:         {
  16:             WriteObject(SPFarm.Local);
  17:         }
  18:     }
  19: }

You can see the help for the Get-SPFarm cmdlet by typing “get-help get-spfarm -full”:

NAME
    Get-SPFarm
    
SYNOPSIS
    Gets an SPFarm object representing a SharePoint 2007 server farm.
    
    
SYNTAX
    Get-SPFarm [-WhatIf] [-Confirm] [<CommonParameters>]
    
    
DETAILED DESCRIPTION
    Copyright 2008 Gary Lapointe
      > For more information on these PowerShell cmdlets:
      > http://stsadm.blogspot.com/
      > Use of these cmdlets is at your own risk.
      > Gary Lapointe assumes no liability.
    

RELATED LINKS
    http://stsadm.blogspot.com

REMARKS
    For more information, type: "get-help Get-SPFarm -detailed".
    For technical information, type: "get-help Get-SPFarm -full".




NAME
    Get-SPFarm
    
SYNOPSIS
    Gets an SPFarm object representing a SharePoint 2007 server farm.
    
SYNTAX
    Get-SPFarm [-WhatIf] [-Confirm] [<CommonParameters>]
    
    
DETAILED DESCRIPTION
    Copyright 2008 Gary Lapointe
      > For more information on these PowerShell cmdlets:
      > http://stsadm.blogspot.com/
      > Use of these cmdlets is at your own risk.
      > Gary Lapointe assumes no liability.
    

PARAMETERS
    -WhatIf
        
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -Confirm
        
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    <CommonParameters>
        This cmdlet supports the common parameters: -Verbose, -Debug,
        -ErrorAction, -ErrorVariable, and -OutVariable. For more information,
        type, "get-help about_commonparameters".
    
INPUT TYPE
    
     
    
RETURN TYPE
    SPFarm
     
    
NOTES
    
    
        For more information, type "Get-Help Get-SPFarm -detailed". For technical information, 
        type "Get-Help Get-SPFarm -full".
    
    
RELATED LINKS
    http://stsadm.blogspot.com

PowerShell CmdLet Name Changes

Ever since I released my PowerShell CmdLets I’ve been unhappy about my choice to use the -gl in the name of the cmdlet.  I felt it would be useful for numerous reasons but I didn’t like that it “broke the rules” of cmdlet naming conventions.  And then Microsoft announced, via the PowerShell team blog, that some code will be added to V2 to enforce the prescribed naming conventions.  You can find the post here: http://blogs.msdn.com/powershell/archive/2009/04/16/increasing-visibility-of-cmdlet-design-guidelines.aspx.  The specific piece of concern is that a warning will be generated if more than one hyphen is present in the cmdlet name.

As a result of this enforcement of the guidance and in anticipation of eventually having a V2 version of my cmdlets I decided to go ahead and pull the -gl suffix from my cmdlet names.  I thought about keeping an alias in place so that existing scripts would not break but in the end decided that there was probably a pretty small user base actually using my them and that it would be better to just get people to make the move.  I will try and update my existing PowerShell posts to account for the removal of the name (guess it’s a good thing I’m behind on documenting most of them :) ).

Friday, April 24, 2009

Importing Audiences using STSADM

I recently posted about exporting audiences using my gl-exportaudiences STSADM command.  Of course an export wouldn’t be of much use if you didn’t also have an import so I give you gl-importaudiences.

Developing this was really easy as I already had code that created an audience and its associated rules.  All I had to do was read in the source XML file, do a little refactoring of the audience creation code and then call the rules creation code.  One cool thing I added was the ability to output a mapping file which provides the search and replace XML necessary to use my gl-replacefieldvalues command so that you can replace the old GUIDs used in audience targeting with the new GUID of the new audience.  The code can be seen below:

   1: #if MOSS
   2: using System;
   3: using System.Collections;
   4: using System.Collections.Specialized;
   5: using System.Collections.Generic;
   6: using System.IO;
   7: using System.Text;
   8: using System.Xml;
   9: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
  10: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
  11: using Microsoft.Office.Server;
  12: using Microsoft.Office.Server.Audience;
  13:  
  14: namespace Lapointe.SharePoint.STSADM.Commands.Audiences
  15: {
  16:     public class ImportAudiences : SPOperation
  17:     {
  18:         /// <summary>
  19:         /// Initializes a new instance of the <see cref="ImportAudiences"/> class.
  20:         /// </summary>
  21:         public ImportAudiences()
  22:         {
  23:             SPParamCollection parameters = new SPParamCollection();
  24:             parameters.Add(new SPParam("ssp", "ssp", false, null, new SPNonEmptyValidator()));
  25:             parameters.Add(new SPParam("deleteexisting", "delete"));
  26:             parameters.Add(new SPParam("inputfile", "input", false, null, new SPFileExistsValidator()));
  27:             parameters.Add(new SPParam("compile", "c"));
  28:             parameters.Add(new SPParam("mapfile", "map", false, null, new SPDirectoryExistsAndValidFileNameValidator()));
  29:  
  30:             StringBuilder sb = new StringBuilder();
  31:             sb.Append("\r\n\r\nImports all audiences given the provided input file.\r\n\r\nParameters:");
  32:             sb.Append("\r\n\t-inputfile <file to input results from>");
  33:             sb.Append("\r\n\t[-deleteexisting <delete existing audiences>]");
  34:             sb.Append("\r\n\t[-ssp <SSP name>]");
  35:             sb.Append("\r\n\t[-compile]");
  36:             sb.Append("\r\n\t[-mapfile <generate a map file to use for search and replace of Audience IDs>]");
  37:             Init(parameters, sb.ToString());
  38:         }
  39:  
  40:         /// <summary>
  41:         /// Gets the help message.
  42:         /// </summary>
  43:         /// <param name="command">The command.</param>
  44:         /// <returns></returns>
  45:         public override string GetHelpMessage(string command)
  46:         {
  47:             return HelpMessage;
  48:         }
  49:  
  50:         /// <summary>
  51:         /// Executes the specified command.
  52:         /// </summary>
  53:         /// <param name="command">The command.</param>
  54:         /// <param name="keyValues">The key values.</param>
  55:         /// <param name="output">The output.</param>
  56:         /// <returns></returns>
  57:         public override int Execute(string command, StringDictionary keyValues, out string output)
  58:         {
  59:             output = string.Empty;
  60:  
  61:             string inputFile = Params["inputfile"].Value;
  62:             bool deleteExisting = Params["deleteexisting"].UserTypedIn;
  63:             bool compile = Params["compile"].UserTypedIn;
  64:             string mapFile = default(string);
  65:             if (Params["mapfile"].UserTypedIn)
  66:                 mapFile = Params["mapfile"].Value;
  67:  
  68:             string xml = File.ReadAllText(inputFile);
  69:  
  70:             Import(xml, Params["ssp"].Value, deleteExisting, compile, mapFile);
  71:  
  72:             return OUTPUT_SUCCESS;
  73:         }
  74:  
  75:         /// <summary>
  76:         /// Imports the specified XML.
  77:         /// </summary>
  78:         /// <param name="xml">The XML.</param>
  79:         /// <param name="sspName">Name of the SSP.</param>
  80:         /// <param name="deleteExisting">if set to <c>true</c> [delete existing].</param>
  81:         /// <param name="compile">if set to <c>true</c> [compile].</param>
  82:         /// <param name="mapFile">The map file.</param>
  83:         private void Import(string xml, string sspName, bool deleteExisting, bool compile, string mapFile)
  84:         {
  85:             ServerContext context;
  86:             if (string.IsNullOrEmpty(sspName))
  87:                 context = ServerContext.Default;
  88:             else
  89:                 context = ServerContext.GetContext(sspName);
  90:  
  91:             AudienceManager manager = new AudienceManager(context);
  92:  
  93:             XmlDocument audiencesDoc = new XmlDocument();
  94:             audiencesDoc.LoadXml(xml);
  95:  
  96:             XmlNodeList audienceElements = audiencesDoc.SelectNodes("//Audience");
  97:             if (audienceElements == null)
  98:                 throw new ArgumentException("The input file does not contain any audience elements.");
  99:  
 100:             StringBuilder sb = new StringBuilder();
 101:             XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
 102:             xmlWriter.Formatting = Formatting.Indented;
 103:             xmlWriter.WriteStartElement("Replacements");
 104:  
 105:             Dictionary<Guid, string> ids = new Dictionary<Guid, string>();
 106:             if (deleteExisting)
 107:             {
 108:                 Log("Progrss: Deleting existing audiences.");
 109:                 foreach (Audience au in manager.Audiences)
 110:                     ids.Add(au.AudienceID, au.AudienceName);
 111:  
 112:                 foreach (Guid id in ids.Keys)
 113:                 {
 114:                     if (id == Guid.Empty)
 115:                         continue;
 116:  
 117:                     string name = manager.Audiences[id].AudienceName;
 118:                     manager.Audiences.Remove(id);
 119:                 }
 120:             }
 121:  
 122:             foreach (XmlElement audienceElement in audienceElements)
 123:             {
 124:                 string audienceName = audienceElement.GetAttribute("AudienceName");
 125:                 string audienceDesc = audienceElement.GetAttribute("AudienceDescription");
 126:  
 127:                 Audience audience;
 128:                 bool updatedAudience = false;
 129:                 if (manager.Audiences.AudienceExist(audienceName))
 130:                 {
 131:                     Log("Progress: Updating audience {0}.", audienceName);
 132:                     audience = manager.Audiences[audienceName];
 133:                     audience.AudienceDescription = audienceDesc ?? "";
 134:                     updatedAudience = true;
 135:                 }
 136:                 else
 137:                 {
 138:                     // IMPORTANT: the create method does not do a null check but the methods that load the resultant collection assume not null.
 139:                     Log("Progress: Creating audience {0}.", audienceName);
 140:                     audience = manager.Audiences.Create(audienceName, audienceDesc ?? "");
 141:                 }
 142:  
 143:                 audience.GroupOperation = (AudienceGroupOperation)Enum.Parse(typeof (AudienceGroupOperation),
 144:                                                      audienceElement.GetAttribute("GroupOperation"));
 145:  
 146:                 audience.OwnerAccountName = audienceElement.GetAttribute("OwnerAccountName");
 147:  
 148:                 audience.Commit();
 149:  
 150:                 if (updatedAudience && audience.AudienceID != Guid.Empty)
 151:                 {
 152:                     // We've updated an existing audience.
 153:                     xmlWriter.WriteStartElement("Replacement");
 154:                     xmlWriter.WriteElementString("SearchString", (new Guid(audienceElement.GetAttribute("AudienceID")).ToString().ToUpper()));
 155:                     xmlWriter.WriteElementString("ReplaceString", string.Format("(?i:{0})", audience.AudienceID.ToString().ToUpper()));
 156:                     xmlWriter.WriteEndElement(); // Replacement
 157:                 }
 158:                 else if (!updatedAudience && audience.AudienceID != Guid.Empty && ids.ContainsValue(audience.AudienceName))
 159:                 {
 160:                     // We've added a new audience which we just previously deleted.
 161:                     xmlWriter.WriteStartElement("Replacement");
 162:                     foreach (Guid id in ids.Keys)
 163:                     {
 164:                         if (ids[id] == audience.AudienceName)
 165:                         {
 166:                             xmlWriter.WriteElementString("SearchString", id.ToString().ToUpper());
 167:                             break;
 168:                         }
 169:                     }
 170:                     xmlWriter.WriteElementString("ReplaceString", string.Format("(?i:{0})", audience.AudienceID.ToString().ToUpper()));
 171:                     xmlWriter.WriteEndElement(); // Replacement
 172:                 }
 173:  
 174:                 XmlElement rulesElement = (XmlElement)audienceElement.SelectSingleNode("rules");
 175:                 if (rulesElement == null || rulesElement.ChildNodes.Count == 0)
 176:                 {
 177:                     audience.AudienceRules = new ArrayList();
 178:                     audience.Commit();
 179:                     continue;
 180:                 }
 181:  
 182:  
 183:                 string rules = rulesElement.OuterXml;
 184:                 Log("Progress: Adding rules to audience {0}.", audienceName);
 185:                 AddAudienceRule.AddRules(sspName, audienceName, rules, true, compile, false, AddAudienceRule.AppendOp.AND);
 186:             }
 187:  
 188:             xmlWriter.WriteEndElement(); // Replacements
 189:  
 190:             if (!string.IsNullOrEmpty(mapFile))
 191:             {
 192:                 xmlWriter.Flush();
 193:                 File.WriteAllText(mapFile, sb.ToString());
 194:             }
 195:         }
 196:  
 197:     }
 198: }
 199: #endif

The help for the command is shown below:

C:\>stsadm -help gl-importaudiences

stsadm -o gl-importaudiences


Imports all audiences given the provided input file.

Parameters:
        -inputfile <file to input results from>
        [-deleteexisting <delete existing audiences>]
        [-ssp <SSP name>]
        [-compile]
        [-mapfile <generate a map file to use for search and replace of Audience IDs>]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-importaudiences MOSS 2007 Released: 4/24/2009

Parameter Name Short Form Required Description Example Usage
inputfile input Yes The path to the input file obtained via the gl-exportaudiences command. -inputfile c:\audiences.xml

-input c:\audiences.xml
deleteexisting delete No If specified then all existing audiences will be deleted prior to importing the audiences.  Note that the “All site users” audience will not be deleted or updated. -deleteexisting

-delete
ssp   No The name of the SSP to import the audiences into.  If omitted then the default SSP will be used. -ssp SSP1
compile c No If specified then compile the audience after creation/update. -compile

-c
mapfile map No If specified then an XML file will be generated providing the search and replace strings to use in order to update the GUIDs in your site collections. -mapfile c:\map.xml

-map c:\map.xml

The following is an example of how to import all audiences contained in the audiences.xml file:

stsadm -o gl-importaudiences -inputfile c:\audiences.xml -ssp SSP1 -mapfile c:\map.xml -compile

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

<Replacements>
  <Replacement>
    <SearchString>98E29BEF-8B1E-4113-BB15-6FAF1E6FB8D0</SearchString>
    <ReplaceString>(?i:1CA1F37E-A50A-4F84-BDCD-8C1279BADB3E)</ReplaceString>
  </Replacement>
</Replacements>