SharePoint Automation Gary Lapointe – Founding Partner, Aptillon, Inc.

14May/1016

Announcing My SharePoint 2010 PowerShell Cmdlets & STSADM Commands Now Available for Download

I’ve been wanting to release the SharePoint 2010 version of my STSADM extensions for quite some time but honestly just haven’t had the time to migrate as many as I would have liked. With over 145 STSADM extensions for SharePoint 2007 it was a challenge determining which ones I should focus on initially for the migration.

But today I’m happy to announce my initial release which contains 46 PowerShell cmdlets and 56 STSADM commands specific to SharePoint 2010. Yup, you read right, I’ve decided to maintain support for my STSADM commands and have been migrating them over as I create the equivalent replacement PowerShell cmdlet (though I recommend you don’t use them and suck it up and get used to PowerShell). You should note that there are more STSADM commands than PowerShell cmdlets – that’s because some of the things I was doing with STSADM can now easily be done with out of the box PowerShell cmdlets (I also have new PowerShell cmdlets that do not have an STSADM equivalent – everything new I create will be a cmdlet and I’ll create no new STSADM commands).

It’s going to take me a while to create all the posts needed to explain each cmdlet (assuming I create one at all) so for now I’ve created this simple table which lists all the STSADM commands and PowerShell cmdlets that are available in this initial release (I’ll eventually update my command index page but for now let this serve as the main reference for what is available as of 5/14/2010):

STSADM Commands

PowerShell Cmdlets

Notes

gl-activatefeature

Enable-SPFeature2

There’s an OOTB Enable-SPFeature cmdlet, this one simple adds some capabilities which were present in my existing STSADM command.

gl-addaudiencerule

New-SPAudienceRule

 

gl-addavailablesitetemplate

  I’ll eventually create a cmdlet for this.

gl-adduser2

  Use the OOTB New-SPUser cmdlet.

gl-adduserpolicyforwebapp

Add-SPWebApplicationUserPolicy

 

gl-applytheme

  This can be done pretty easily using Get-SPWeb and the ApplyTheme() method of the SPWeb object.

gl-backup

  Use the OOTB Backup-SPFarm and Backup-SPSite cmdlets.

gl-backupsites

Backup-SPSite2

Extends Backup-SPSite by including IIS settings.

gl-convertsubsitetositecollection

ConvertTo-SPSite

 

gl-copycontenttypes

Copy-SPContentType

 

gl-copylist

Copy-SPList

 

gl-copylistsecurity

Copy-SPListSecurity

 

gl-createaudience

New-SPAudience

 

gl-createcontentdb

  Use the OOTB New-SPContentDatabase cmdlet.

gl-createpublishingpage

New-SPPublishingPage

 

gl-createquotatemplate

New-SPQuotaTemplate

 

gl-createwebapp

  Use the OOTB New-SPWebApplication cmdlet

gl-deactivatefeature

Disable-SPFeature2

Extends the OOTB Disable-SPFeature cmdlet.

gl-deleteallusers

  I probably won’t replicate this as it is easily done using the OOTB Remove-SPUser cmdlet.

gl-deleteaudience

Remove-SPAudience

 

gl-deletelist

Remove-SPList

 

gl-deletewebapp

  Use the OOTB Remove-SPWebApplication cmdlet.

gl-disableuserpermissionforwebapp

  This is fairly easy to do OOTB so I may not create a cmdlet for it.

gl-editquotatemplate

Set-SPQuotaTemplate

 

gl-enableuserpermissionforwebapp

  This is fairly easy to do OOTB so I may not create a cmdlet for it.

gl-enumaudiencerules

Export-SPAudienceRules

 

gl-enumavailablepagelayouts

Get-SPPublishingPageLayout

 

gl-enumavailablesitetemplates

Get-SPAvailableWebTemplates

 

gl-enumeffectivebaseperms

  This is fairly easy to do OOTB so I may not create a cmdlet for it.

gl-enumfeatures

  Use the OOTB Get-SPFeature cmdlet.

gl-enuminstalledsitetemplates

  Use the OOTB Get-SPWebTemplate cmdlet.

gl-enumpagewebparts

Get-SPWebPartList

 

gl-enumunghostedfiles

Get-SPCustomizedPages

 

gl-execadmsvcjobs

Start-SPAdminJob2

I honestly need to research this a bit more as I’m not sure it’s necessary anymore but I’ve replicated the functionality in case someone finds it useful.

gl-exportaudiences

Export-SPAudiences

 

gl-exportcontenttypes

Export-SPContentType

 

gl-exportlist

Export-SPWeb2

I’ve just extended the Export-SPWeb2 cmdlet to add additional parameters.

gl-exportlistsecurity

Export-SPListSecurity

 

gl-extendwebapp

  Use the OOTB New-SPWebApplicationExtension cmdlet.

gl-fixpublishingpagespagelayouturl

Repair-SPPageLayoutUrl

 

gl-importaudiences

Import-SPAudiences

 

gl-importlist

Import-SPWeb2

I’ve just extended the Import-SPWeb2 cmdlet to add additional parameters.

gl-importlistsecurity

Import-SPListSecurity

 

gl-listaudiencetargeting

Set-SPListAudienceTargeting

 

gl-managecontentdbsettings

  Use the OOTB Set-SPContentDatabase cmdlet.

gl-propagatecontenttype

Propagate-SPContentType

 

gl-publishitems

Publish-SPListItems

 

gl-reghostfile

Reset-SPCustomizedPages

 

gl-removeavailablesitetemplate

  I’ll eventually create a cmdlet for this (maybe).

gl-repairsitecollectionimportedfromsubsite

Repair-SPSite

 

gl-replacewebpartcontent

Replace-SPWebPartContent

 

gl-setbackconnectionhostnames

Set-SPBackConnectionHostNames

 

gl-setselfservicesitecreation

  Not sure if I’ll migrate this or not.

gl-syncquotas

Set-SPQuota

 

gl-tracelog

  Use the OOTB Set-SPDiagnosticConfig cmdlet.

gl-unextendwebapp

  Use the OOTB Remove-SPWebApplication cmdlet.
 

Get-SPAudience

 
 

Get-SPAudienceManager

 
 

Get-SPContentType

 
 

Get-SPFile

 
 

Get-SPLimitedWebPartManager

 
 

Get-SPList

 
 

Get-SPPublishingPage

 
 

Get-SPQuotaTemplate

 
 

Set-SPAudience

 

For those that know a thing or two about cmdlet development you might be interested in knowing that I am dynamically generating the help XML file for the cmdlets. If you download the source you’ll find a class which uses reflection to interrogate the assembly and dynamically build the help file just prior to building the WSP package. This saved me literally days of hand editing XML.

You can download the source and WSP files here or from the Downloads page:

After you deploy the package you can type “help <cmdlet name>” to get detailed help about each cmdlet, including parameter descriptions and example usage. If you want to see the list of cmdlets installed type the following:

gcm | where {$_.DLL –like "*lapointe*"}

As always, your use of these cmdlets/stsadm commands is at your own risk – I do as much testing as I can but every environment is different and there’s simply not enough time in a day. If you have any suggestions or feedback please don’t hesitate to leave a comment – I appreciate all of them!

20Sep/099

Setting Back Connection Host Names for SharePoint 2007 Using STSADM

Not too long ago Microsoft introduced a security fix which addresses a possible attack vector in which malicious software tries to impersonate a local request, thereby bypassing certain constraints.  The problem with this fix is that it introduces some issues for SharePoint servers, effectively resulting in 401.1 Access Denied errors.  Spence Harbar does a great write-up of the fix and the options available to get your SharePoint environment working again so I won't re-hash all that here: http://www.harbar.net/archive/2009/07/02/disableloopbackcheck-amp-sharepoint-what-every-admin-and-developer-should-know.aspx.

As Spence points out, the preferred way to fix this is to add the host names to the BackConnectionHostNames registry key and to not set the DisableLoopbackCheck registry key.  You can of course do this using Group Policy but for those not managing their servers using GPO I decided to implement a custom STSADM command that would make setting the BackConnectionHostNames registry key really simple.  I called this new command, oddly enough, gl-setbackconnectionhostnames.

The command has two ways to run it, you can run it without any parameters which will cause it to update only the server in which the command is executed on, or you can pass in an -updatefarm parameter along with a username and password which will cause it to update every server in the farm.  There's no need to pass in the host header names as the code will dynamically determine them by inspecting each web application and their alternate access mappings (alternate URLs) and perform some logic to determine whether the host header is pointing to a local IP address or to a specific SharePoint server (I do this to exclude Central Admin which is usually accessed using a server name and non-standard port).

I accomplish the farm update by using a custom Timer Job which executes on each server.  Unfortunately the timer service account does not have access to write to the registry (unless you've given it rights, which you shouldn't) so it was necessary to pass in a username and password and then use impersonation to update the registry.  The custom timer job code is shown below, notice that all the core work is being done via the SetBackConnectionHostNames class which is shown below the timer job code:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Runtime.InteropServices;
   4: using System.Security.Principal;
   5: using System.Text;
   6: using Microsoft.SharePoint;
   7: using Microsoft.SharePoint.Administration;
   8:  
   9: namespace Lapointe.SharePoint.STSADM.Commands.WebApplications
  10: {
  11:     public class SetBackConnectionHostNamesTimerJob : SPJobDefinition
  12:     {
  13:         public const int LOGON32_LOGON_INTERACTIVE = 2;
  14:         public const int LOGON32_LOGON_SERVICE = 3;
  15:         public const int LOGON32_PROVIDER_DEFAULT = 0;
  16:  
  17:         [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
  18:         public static extern bool LogonUser(
  19:           String lpszUserName,
  20:           String lpszDomain,
  21:           String lpszPassword,
  22:           int dwLogonType,
  23:           int dwLogonProvider,
  24:           ref IntPtr phToken
  25:         );
  26:  
  27:         [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
  28:         public extern static bool CloseHandle(IntPtr handle);
  29:  
  30:         private const string JOB_NAME = "job-set-back-connection-host-names-";
  31:         private const string KEY_USER = "userName";
  32:         private const string KEY_PWD = "password";
  33:  
  34:         private static readonly string jobId = Guid.NewGuid().ToString();
  35:  
  36:         public SetBackConnectionHostNamesTimerJob() : base() { }
  37:  
  38:         /// <summary>
  39:         /// Initializes a new instance of the <see cref="SetBackConnectionHostNamesTimerJob"/> class.
  40:         /// </summary>
  41:         public SetBackConnectionHostNamesTimerJob(SPService service)
  42:             : base(JOB_NAME + jobId, service, null, SPJobLockType.None)
  43:         {
  44:             Title = "Set BackConnectionHostNames Registry Key";
  45:         }
  46:  
  47:         /// <summary>
  48:         /// Executes the job definition.
  49:         /// </summary>
  50:         /// <param name="targetInstanceId">For target types of <see cref="T:Microsoft.SharePoint.Administration.SPContentDatabase"></see> this is the database ID of the content database being processed by the running job. This value is Guid.Empty for all other target types.</param>
  51:         public override void Execute(Guid targetInstanceId)
  52:         {
  53:             string user = Properties[KEY_USER] as string;
  54:             string password = Properties[KEY_PWD] as string;
  55:  
  56:             if (string.IsNullOrEmpty(user) || password == null)
  57:                 throw new ArgumentNullException("Username and password is required.");
  58:  
  59:             if (user.IndexOf('\\') < 0)
  60:                 throw new ArgumentException("Username must be in the form \"DOMAIN\\USER\"");
  61:  
  62:             IntPtr userHandle = new IntPtr(0);
  63:             WindowsImpersonationContext impersonatedUser = null;
  64:  
  65:             bool returnValue = LogonUser(
  66:               user.Split('\\')[1],
  67:               user.Split('\\')[0],
  68:               password,
  69:               LOGON32_LOGON_INTERACTIVE,
  70:               LOGON32_PROVIDER_DEFAULT,
  71:               ref userHandle
  72:               );
  73:  
  74:             if (!returnValue)
  75:             {
  76:                 throw new Exception("Invalid Username");
  77:             }
  78:             WindowsIdentity newId = new WindowsIdentity(userHandle);
  79:             impersonatedUser = newId.Impersonate();
  80:  
  81:             SetBackConnectionHostNames.SetBackConnectionRegKey(SetBackConnectionHostNames.GetUrls());
  82:  
  83:             impersonatedUser.Undo();
  84:             CloseHandle(userHandle);
  85:         }
  86:  
  87:         /// <summary>
  88:         /// Submits the job.
  89:         /// </summary>
  90:         public void SubmitJob(string user, string password)
  91:         {
  92:             Properties[KEY_USER] = user;
  93:             Properties[KEY_PWD] = password;
  94:             Schedule = new SPOneTimeSchedule(DateTime.Now);
  95:             Update();
  96:         }
  97:     }
  98: }

The following is the code for the SetBackConnectionHostNames class:

   1: using System;
   2: using System.Collections;
   3: using System.Collections.Generic;
   4: using System.Collections.Specialized;
   5: using System.Net;
   6: using System.Text;
   7: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   8: using Microsoft.SharePoint;
   9: using Microsoft.SharePoint.Administration;
  10: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
  11: using Microsoft.Win32;
  12:  
  13: namespace Lapointe.SharePoint.STSADM.Commands.WebApplications
  14: {
  15:     public class SetBackConnectionHostNames : SPOperation
  16:     {
  17:         /// <summary>
  18:         /// Initializes a new instance of the <see cref="SetBackConnectionHostNames"/> class.
  19:         /// </summary>
  20:         public SetBackConnectionHostNames()
  21:         {
  22:             
  23:             SPParamCollection parameters = new SPParamCollection();
  24:             parameters.Add(new SPParam("updatefarm", "uf"));
  25:             parameters.Add(new SPParam("username", "user", false, null, new SPNonEmptyValidator()));
  26:             parameters.Add(new SPParam("password", "pwd", false, null, new SPNullOrNonEmptyValidator()));
  27:  
  28:             StringBuilder sb = new StringBuilder();
  29:             sb.Append("\r\n\r\nSets the BackConnectionHostNames registry key with the URLs associated with each web application.\r\n\r\nParameters:");
  30:             sb.Append("\r\n\t[-updatefarm (update all servers in the farm)]");
  31:             sb.Append("\r\n\t[-username <DOMAIN\\user (must have rights to update the registry on each server)>]");
  32:             sb.Append("\r\n\t[-password <password>]");
  33:  
  34:             Init(parameters, sb.ToString());
  35:         }
  36:  
  37:         #region ISPStsadmCommand Members
  38:  
  39:         /// <summary>
  40:         /// Gets the help message.
  41:         /// </summary>
  42:         /// <param name="command">The command.</param>
  43:         /// <returns></returns>
  44:         public override string GetHelpMessage(string command)
  45:         {
  46:             return HelpMessage;
  47:         }
  48:  
  49:         /// <summary>
  50:         /// Runs the specified command.
  51:         /// </summary>
  52:         /// <param name="command">The command.</param>
  53:         /// <param name="keyValues">The key values.</param>
  54:         /// <param name="output">The output.</param>
  55:         /// <returns></returns>
  56:         public override int Execute(string command, StringDictionary keyValues, out string output)
  57:         {
  58:             output = string.Empty;
  59:  
  60:             if (!Params["updatefarm"].UserTypedIn)
  61:                 SetBackConnectionRegKey(GetUrls());
  62:             else
  63:             {
  64:                 SPTimerService timerService = SPFarm.Local.TimerService;
  65:                 if (null == timerService)
  66:                 {
  67:                     throw new SPException("The Farms timer service cannot be found.");
  68:                 }
  69:                 SetBackConnectionHostNamesTimerJob job = new SetBackConnectionHostNamesTimerJob(timerService);
  70:  
  71:                 string user = Params["username"].Value;
  72:                 if (user.IndexOf('\\') < 0)
  73:                     user = Environment.UserDomainName + "\\" + user;
  74:                 job.SubmitJob(user, Params["password"].Value + "");
  75:  
  76:                 output += "Timer job successfully created.";
  77:             }
  78:  
  79:             return OUTPUT_SUCCESS;
  80:         }
  81:  
  82:         public override void Validate(StringDictionary keyValues)
  83:         {
  84:             base.Validate(keyValues);
  85:  
  86:             if (Params["updatefarm"].UserTypedIn)
  87:             {
  88:                 if (!Params["username"].UserTypedIn)
  89:                     throw new SPSyntaxException("A valid username with rights to edit the registry is required.");
  90:             }
  91:         }
  92:  
  93:         #endregion
  94:  
  95:         public static List<string> GetUrls()
  96:         {
  97:             List<string> urls = new List<string>();
  98:             foreach (SPService svc in SPFarm.Local.Services)
  99:             {
 100:                 if (!(svc is SPWebService))
 101:                     continue;
 102:  
 103:                 foreach (SPWebApplication webApp in ((SPWebService)svc).WebApplications)
 104:                 {
 105:                     
 106:                     foreach (SPAlternateUrl url in webApp.AlternateUrls)
 107:                     {
 108:                         string host = url.Uri.Host.ToLower();
 109:                         if (!urls.Contains(host) && // Don't add if we already have it
 110:                             !url.Uri.IsLoopback && // Quick check to short circuit the more elaborate checks
 111:                             host != Environment.MachineName.ToLower() && // Quick check to short circuit the more elaborate checks
 112:                             IsLocalIpAddress(host) && // If the host name points locally then we need to add it
 113:                             !IsSharePointServer(host)) // Don't add if it matches an SP server name (handles central admin)
 114:                         {
 115:                             urls.Add(host);
 116:                         }
 117:                     }
 118:                 }
 119:             }
 120:             return urls;
 121:         }
 122:  
 123:         private static bool IsSharePointServer(string host)
 124:         {
 125:             foreach (SPServer server in SPFarm.Local.Servers)
 126:             {
 127:                 if (server.Address.ToLower() == host)
 128:                     return true;
 129:             }
 130:             return false;
 131:         }
 132:  
 133:         private static bool IsLocalIpAddress(string host)
 134:         {
 135:             try
 136:             { 
 137:                 IPAddress[] hostIPs = Dns.GetHostAddresses(host);
 138:                 IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
 139:  
 140:                 // test if any host IP equals to any local IP or to localhost
 141:                 foreach (IPAddress hostIP in hostIPs)
 142:                 {
 143:                     // is localhost
 144:                     if (IPAddress.IsLoopback(hostIP)) return true;
 145:                     // is local address
 146:                     foreach (IPAddress localIP in localIPs)
 147:                     {
 148:                         if (hostIP.Equals(localIP)) return true;
 149:                     }
 150:                 }
 151:             }
 152:             catch { }
 153:             return false;
 154:         }
 155:  
 156:         public static void SetBackConnectionRegKey(List<string> urls)
 157:         {
 158:             const string KEY_NAME = "SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0";
 159:             const string KEY_VAL_NAME = "BackConnectionHostNames";
 160:  
 161:             RegistryKey reg = Registry.LocalMachine.OpenSubKey(KEY_NAME, true);
 162:             if (reg != null)
 163:             {
 164:                 string[] existing = (string[])reg.GetValue(KEY_VAL_NAME);
 165:                 if (existing != null)
 166:                 {
 167:                     foreach (string val in existing)
 168:                     {
 169:                         if (!urls.Contains(val.ToLower()))
 170:                             urls.Add(val.ToLower());
 171:                     }
 172:                 }
 173:                 string[] multiVal = new string[urls.Count];
 174:                 urls.CopyTo(multiVal);
 175:                 
 176:                 reg.SetValue(KEY_VAL_NAME, multiVal, RegistryValueKind.MultiString);
 177:             }
 178:             else
 179:             {
 180:                 throw new SPException("Unable to open registry key.");
 181:             }
 182:         }
 183:  
 184:     }
 185: }

There's two core methods, GetUrls and SetBackConnectionRegKey.  The SetBackConnectionRegKey method started out from a bit of sample code that my friend Ben Robb sent me - there's no much of his original code but it saved me some time in trying to remember how to manipulate the registry using C#.  Essentially all this method does is get the current list of host names, add any missing items to the passed in list, and then reset the list (thus avoiding duplicate entries).  The GetUrls method is the more interesting piece - I'm looping through all the Farm's Web Applications and their corresponding Alternate URLs and then building a list of URLs that meet some basic inclusion criteria:

  • Don't add duplicates - you can get duplicates when both HTTP and HTTPS are used so we make sure that we exclude them
  • Don't add loopback URLs - this shouldn't come up but if the URL is localhost or 127.0.0.1 it will be flagged as a loopback URL so we exclude them
  • Don't add URLs that match the server name - if the host name matches the server name then exclude it (this is essentially just a short circuit for the next check which is a bit more thorough)
  • Exclude host names that map to the local IP address - this is the most crucial bit (the previous steps were just short circuits for this step to avoid the additional querying necessary); I use the System.Net.Dns class's static GetHostAddresses method to check the local addresses against those associated with the provided host name
  • Exclude host names that map to SharePoint servers - this step is necessary to address host names such as those belonging to Central Administration

The help for the command is shown below:

C:\>stsadm -help gl-setbackconnectionhostnames

stsadm -o gl-setbackconnectionhostnames


Sets the BackConnectionHostNames registry key with the URLs associated with each web application.
Parameters:
        [-updatefarm (update all servers in the farm)]
        [-username <DOMAIN\user (must have rights to update the registry on each server)>]
        [-password <password>]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-setbackconnectionhostnames WSS v3, MOSS 2007 Released: 9/20/2009

Parameter Name Short Form Required Description Example Usage
updatefarm uf No If provided then update the BackConnectionHostNames registry key on all servers in the farm. -updatefarm

-uf
username user Yes if updatefarm is provided The username with sufficient rights to update the registry.  If no domain part is specified then the current users domain is used. -username domain\spadmin

-user spadmin
password pwd No If the users password is blank then this parameter is not required (please change your password if this is the case!); otherwise, this parameter is required if the updatefarm parameter is provided. -password pa$$w0rd

-pwd pa$$w0rd

The following is an example of how to update the BackConnectionHostNames registry key on the current server only:

stsadm -o gl-setbackconnectionhostnames

The following is an example of how to update the BackConnectionHostNames registry key on all servers in the farm:

stsadm -o gl-setbackconnectionhostnames -updatefarm -username domain\spadmin -password pa$$w0rd

8Jul/090

Download My Custom Extensions Source From CodePlex

I've been putting this off for a long time but I decided that it was time to push my source code for my custom STSADM commands and PowerShell CmdLets to CodePlex.  You can find the project here: http://stsadm.codeplex.com/.

Note that if you want to download the latest tested release you should still do so from my downloads page on this blog but if you want to see the version history or get some checked in changes that are not yet released (think beta) then feel free to download from the CodePlex project.  Ultimately I think this will make it easier for people to see the specific changes I've made from one build to another and thus make it easier to decide whether they should re-deploy the latest version.

6Jul/092

Setting List Properties using STSADM

Recently I had a reader of my blog send me a modified version of my gl-addlist command in which he added some additional properties to set a couple of the SPList properties (specifically the versioning settings).  In thinking about this I decided that it might be helpful to have a command specifically for setting most of the SPList properties rather than try to incorporate them into a more general command like the gl-addlist command so I decided to create a new command that I called gl-setlistproperties.

Now ideally you would just use PowerShell to set list properties and you can do so pretty easily using my custom CmdLet: Get-SPList.  Here’s an example of how you could do this using my CmdLet:

$list = Get-SPList http://portal/documents
$list.EnableVersioning = $true
$list.EnableMinorVersions = $true
$list.EnableModeration = $true
$list.Update()

For a full list of the properties that you can set see the SPList documentation in the SDK. But if you’re not a PowerShell guy and prefer batch files then you can use my gl-setlistproperties command instead. The code, as you can imagine, is really simple:

   1: using System;
   2: using System.Collections.Specialized;
   3: using System.Text;
   4: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
   5: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   6: using Microsoft.SharePoint;
   7:  
   8: namespace Lapointe.SharePoint.STSADM.Commands.Lists
   9: {
  10:     public class SetListProperties : SPOperation
  11:     {
  12:         internal enum VersionSettings {None, Major, MajorMinor}
  13:         /// <summary>
  14:         /// Initializes a new instance of the <see cref="SetListProperties"/> class.
  15:         /// </summary>
  16:         public SetListProperties()
  17:         {
  18:             SPParamCollection parameters = new SPParamCollection();
  19:             parameters.Add(new SPParam("url", "url", true, null, new SPUrlValidator(), "Please specify URL to the list."));
  20:             SPEnumValidator versionValidator = new SPEnumValidator(typeof(VersionSettings));
  21:             parameters.Add(new SPParam("versioning", "version", false, null, versionValidator, "Please specify the version settings."));
  22:             SPEnumValidator draftVisibilityValidator = new SPEnumValidator(typeof(DraftVisibilityType));
  23:             parameters.Add(new SPParam("draftvisibility", "dvv", false, null, draftVisibilityValidator));
  24:             parameters.Add(new SPParam("majorversionlimit", "mvl", false, "0", new SPIntRangeValidator(0, 50000)));
  25:             parameters.Add(new SPParam("majorwithminorversionlimit", "mmvl", false, "0", new SPIntRangeValidator(0, 50000)));
  26:             parameters.Add(new SPParam("forcecheckout", "fc", false, null, new SPTrueFalseValidator()));
  27:             parameters.Add(new SPParam("enablemoderation", "mod", false, null, new SPTrueFalseValidator()));
  28:             parameters.Add(new SPParam("enablecontenttypes", "ect", false, null, new SPTrueFalseValidator()));
  29:             parameters.Add(new SPParam("enablefoldercreation", "efc", false, null, new SPTrueFalseValidator()));
  30:             parameters.Add(new SPParam("allowdeletion", "del", false, null, new SPTrueFalseValidator()));
  31:             parameters.Add(new SPParam("alloweveryoneviewitems", "aevi", false, null, new SPTrueFalseValidator()));
  32:             parameters.Add(new SPParam("enablesyndication", "syn", false, null, new SPTrueFalseValidator()));
  33:             parameters.Add(new SPParam("hidden", "hidden", false, null, new SPTrueFalseValidator()));
  34:             parameters.Add(new SPParam("onquicklaunch", "oql", false, null, new SPTrueFalseValidator()));
  35:             SPEnumValidator itemOpenValidator = new SPEnumValidator(typeof(DefaultItemOpen));
  36:             parameters.Add(new SPParam("defaultitemopen", "open", false, null, itemOpenValidator));
  37:             parameters.Add(new SPParam("ordered", "ordered", false, null, new SPTrueFalseValidator()));
  38:             parameters.Add(new SPParam("title", "title", false, null, new SPNullOrNonEmptyValidator()));
  39:             parameters.Add(new SPParam("description", "desc", false, null, new SPNullOrNonEmptyValidator()));
  40:             parameters.Add(new SPParam("nocrawl", "nocrawl", false, null, new SPTrueFalseValidator()));
  41:             parameters.Add(new SPParam("enableattachments", "att", false, null, new SPTrueFalseValidator()));
  42:  
  43:             StringBuilder sb = new StringBuilder();
  44:             sb.Append("\r\n\r\nSets various properties of a list.\r\n\r\nParameters:");
  45:             sb.Append("\r\n\t-url <URL to the list to update>");
  46:             sb.Append("\r\n\t[-title <title of the list>]");
  47:             sb.Append("\r\n\t[-description <title of the list>]");
  48:             sb.AppendFormat("\r\n\t[-versioning <{0}>", versionValidator.DisplayValue);
  49:             sb.AppendFormat("\r\n\t[-draftvisibility <{0}>", draftVisibilityValidator.DisplayValue);
  50:             sb.Append("\r\n\t[-majorversionlimit <0 - 50000>]");
  51:             sb.Append("\r\n\t[-majorwithminorversionlimit <0 - 50000>]");
  52:             sb.Append("\r\n\t[-forcecheckout <true | false>]");
  53:             sb.Append("\r\n\t[-enablemoderation <true | false>]");
  54:             sb.Append("\r\n\t[-enablecontenttypes <true | false>]");
  55:             sb.Append("\r\n\t[-enablefoldercreation <true | false>]");
  56:             sb.Append("\r\n\t[-allowdeletion <true | false>]");
  57:             sb.Append("\r\n\t[-alloweveryoneviewitems <true | false>]");
  58:             sb.Append("\r\n\t[-enablesyndication <true | false>]");
  59:             sb.Append("\r\n\t[-hidden <true | false>]");
  60:             sb.Append("\r\n\t[-onquicklaunch <true | false>]");
  61:             sb.AppendFormat("\r\n\t[-defaultitemopen <{0}>", itemOpenValidator.DisplayValue);
  62:             sb.Append("\r\n\t[-ordered <true | false>]");
  63:             sb.Append("\r\n\t[-nocrawl <true | false>]");
  64:             sb.Append("\r\n\t[-enableattachments <true | false>]");
  65:             Init(parameters, sb.ToString());
  66:         }
  67:  
  68:        
  69:         #region ISPStsadmCommand Members
  70:  
  71:         /// <summary>
  72:         /// Gets the help message.
  73:         /// </summary>
  74:         /// <param name="command">The command.</param>
  75:         /// <returns></returns>
  76:         public override string GetHelpMessage(string command)
  77:         {
  78:             return HelpMessage;
  79:         }
  80:  
  81:         /// <summary>
  82:         /// Runs the specified command.
  83:         /// </summary>
  84:         /// <param name="command">The command.</param>
  85:         /// <param name="keyValues">The key values.</param>
  86:         /// <param name="output">The output.</param>
  87:         /// <returns></returns>
  88:         public override int Execute(string command, StringDictionary keyValues, out string output)
  89:         {
  90:             output = string.Empty;
  91:             Verbose = true;
  92:  
  93:             string url = Params["url"].Value.TrimEnd('/');
  94:  
  95:             using (SPSite site = new SPSite(url))
  96:             using (SPWeb web = site.OpenWeb())
  97:             {
  98:                 SPList list = Utilities.GetListFromViewUrl(web, url);
  99:  
 100:                 if (list == null)
 101:                     throw new SPException("List was not found.");
 102:  
 103:  
 104:                 if (Params["versioning"].UserTypedIn)
 105:                 {
 106:                     VersionSettings versioning =
 107:                         (VersionSettings) Enum.Parse(typeof (VersionSettings), Params["versioning"].Value, true);
 108:                     SetVersioning(list, versioning);
 109:                 }
 110:  
 111:  
 112:                 SetProperties(list,
 113:                     GetValue(list.Title, "title"),
 114:                     GetValue(list.Title, "description"),
 115:                     GetValue(list.ContentTypesEnabled, "enablecontenttypes"),
 116:                     GetValue(list.DraftVersionVisibility, "draftvisibility"),
 117:                     GetValue(list.MajorVersionLimit, "majorversionlimit"),
 118:                     GetValue(list.MajorWithMinorVersionsLimit, "majorwithminorversionlimit"),
 119:                     GetValue(list.ForceCheckout, "forcecheckout"),
 120:                     GetValue(list.EnableModeration, "enablemoderation"),
 121:                     GetValue(list.EnableFolderCreation, "enablefoldercreation"),
 122:                     GetValue(list.AllowDeletion, "allowdeletion"),
 123:                     GetValue(list.AllowEveryoneViewItems, "alloweveryoneviewitems"),
 124:                     GetValue(list.EnableSyndication, "enablesyndication"),
 125:                     GetValue(list.DefaultItemOpen, "defaultitemopen"),
 126:                     GetValue(list.Hidden, "hidden"),
 127:                     GetValue(list.OnQuickLaunch, "onquicklaunch"),
 128:                     GetValue(list.Ordered, "ordered"),
 129:                     GetValue(list.NoCrawl, "nocrawl"),
 130:                     GetValue(list.EnableAttachments, "enableattachments"));
 131:  
 132:                 list.Update();
 133:             }
 134:  
 135:             return OUTPUT_SUCCESS;
 136:         }
 137:  
 138:         #endregion
 139:  
 140:         /// <summary>
 141:         /// Gets the value.
 142:         /// </summary>
 143:         /// <typeparam name="T"></typeparam>
 144:         /// <param name="defaultValue">The default value.</param>
 145:         /// <param name="paramName">Name of the param.</param>
 146:         /// <returns></returns>
 147:         private T GetValue<T>(T defaultValue, string paramName)
 148:         {
 149:             if (!Params[paramName].UserTypedIn)
 150:                 return defaultValue;
 151:  
 152:             string val = Params[paramName].Value;
 153:             if (typeof(T).IsEnum)
 154:                 return (T) Enum.Parse(typeof (T), val, true);
 155:  
 156:             return (T)Convert.ChangeType(val, typeof (T));
 157:  
 158:         }
 159:  
 160:         /// <summary>
 161:         /// Sets the properties.
 162:         /// </summary>
 163:         /// <param name="list">The list.</param>
 164:         /// <param name="title">The title.</param>
 165:         /// <param name="description">The description.</param>
 166:         /// <param name="contentTypesEnabled">if set to <c>true</c> [content types enabled].</param>
 167:         /// <param name="draftVisibility">The draft visibility.</param>
 168:         /// <param name="majorVersionLimit">The major version limit.</param>
 169:         /// <param name="majorWithMinorVersionLimit">The major with minor version limit.</param>
 170:         /// <param name="forceCheckout">if set to <c>true</c> [force checkout].</param>
 171:         /// <param name="enableModeration">if set to <c>true</c> [enable moderation].</param>
 172:         /// <param name="enableFolderCreation">if set to <c>true</c> [enable folder creation].</param>
 173:         /// <param name="allowDeletion">if set to <c>true</c> [allow deletion].</param>
 174:         /// <param name="allowEveryoneViewItems">if set to <c>true</c> [allow everyone view items].</param>
 175:         /// <param name="enableSyndication">if set to <c>true</c> [enable syndication].</param>
 176:         /// <param name="itemOpen">The item open.</param>
 177:         /// <param name="hidden">if set to <c>true</c> [hidden].</param>
 178:         /// <param name="onQuickLaunch">if set to <c>true</c> [on quick launch].</param>
 179:         /// <param name="ordered">if set to <c>true</c> [ordered].</param>
 180:         /// <param name="noCrawl">if set to <c>true</c> [no crawl].</param>
 181:         /// <param name="enableAttachments">if set to <c>true</c> [enable attachments].</param>
 182:         private void SetProperties(SPList list,
 183:             string title,
 184:             string description,
 185:             bool contentTypesEnabled,
 186:             DraftVisibilityType draftVisibility,
 187:             int majorVersionLimit,
 188:             int majorWithMinorVersionLimit,
 189:             bool forceCheckout,
 190:             bool enableModeration,
 191:             bool enableFolderCreation,
 192:             bool allowDeletion,
 193:             bool allowEveryoneViewItems,
 194:             bool enableSyndication,
 195:             DefaultItemOpen itemOpen,
 196:             bool hidden,
 197:             bool onQuickLaunch,
 198:             bool ordered,
 199:             bool noCrawl,
 200:             bool enableAttachments)
 201:         {
 202:             list.Title = title;
 203:             list.Description = description;
 204:  
 205:             if (list.AllowContentTypes || !contentTypesEnabled)
 206:                 list.ContentTypesEnabled = contentTypesEnabled;
 207:  
 208:             list.DraftVersionVisibility = draftVisibility;
 209:  
 210:             if (enableModeration && list.BaseTemplate == SPListTemplateType.PictureLibrary)
 211:                 Log("WARNING: Cannot set moderation on a picture library.");
 212:             else
 213:                 list.EnableModeration = enableModeration;
 214:  
 215:             if (list.EnableVersioning)
 216:                 list.MajorVersionLimit = majorVersionLimit;
 217:  
 218:             if (list.EnableMinorVersions && list.EnableModeration)
 219:                 list.MajorWithMinorVersionsLimit = majorWithMinorVersionLimit;
 220:  
 221:  
 222:             if (list.BaseTemplate == SPListTemplateType.DocumentLibrary)
 223:                 list.ForceCheckout = forceCheckout;
 224:             else if (forceCheckout)
 225:                 Log("WARNING: Force checkout can only be set on document libraries.");
 226:  
 227:             list.EnableFolderCreation = enableFolderCreation;
 228:             list.AllowDeletion = allowDeletion;
 229:             list.AllowEveryoneViewItems = allowEveryoneViewItems;
 230:             list.EnableSyndication = enableSyndication;
 231:             list.DefaultItemOpen = itemOpen;
 232:             list.Hidden = hidden;
 233:             list.OnQuickLaunch = onQuickLaunch;
 234:             list.NoCrawl = noCrawl;
 235:             
 236:             if (list.BaseTemplate == SPListTemplateType.GenericList)
 237:                 list.Ordered = ordered;
 238:             else if (ordered)
 239:                 Log("WARNING: The Ordered property can only be set for generic lists.");
 240:  
 241:             if (!enableAttachments)
 242:                 list.EnableAttachments = false;
 243:             else
 244:             {
 245:                 if (!(list.BaseType == SPBaseType.DocumentLibrary || list.BaseType == SPBaseType.Survey))
 246:                     list.EnableVersioning = true;
 247:                 else
 248:                     Log("WARNING: Attachments are only allowed on document libraries and surveys.");
 249:             }
 250:  
 251:  
 252:         }
 253:  
 254:         /// <summary>
 255:         /// Sets the versioning.
 256:         /// </summary>
 257:         /// <param name="list">The list.</param>
 258:         /// <param name="versioning">The versioning.</param>
 259:         private void SetVersioning(SPList list, VersionSettings versioning)
 260:         {
 261:             switch (versioning)
 262:             {
 263:                 case VersionSettings.None:
 264:                     list.EnableVersioning = false;
 265:                     list.EnableMinorVersions = false;
 266:                     break;
 267:                 case VersionSettings.Major:
 268:                     list.EnableVersioning = true;
 269:                     list.EnableMinorVersions = false;
 270:                     break;
 271:                 case VersionSettings.MajorMinor:
 272:                     list.EnableVersioning = true;
 273:                     list.EnableMinorVersions = true;
 274:                     break;
 275:             }
 276:         }
 277:     }
 278: }

The help for the command is shown below:

C:\>stsadm -help gl-setlistproperties

stsadm -o gl-setlistproperties


Sets various properties of a list.

Parameters:
        -url <URL to the list to update>
        [-title <title of the list>]
        [-description <title of the list>]
        [-versioning <none | major | majorminor>
        [-draftvisibility <reader | author | approver>
        [-majorversionlimit <0 - 50000>]
        [-majorwithminorversionlimit <0 - 50000>]
        [-forcecheckout <true | false>]
        [-enablemoderation <true | false>]
        [-enablecontenttypes <true | false>]
        [-enablefoldercreation <true | false>]
        [-allowdeletion <true | false>]
        [-alloweveryoneviewitems <true | false>]
        [-enablesyndication <true | false>]
        [-hidden <true | false>]
        [-onquicklaunch <true | false>]
        [-defaultitemopen <preferclient | browser>
        [-ordered <true | false>]
        [-nocrawl <true | false>]
        [-enableattachments <true | false>]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-setlistproperties WSS 3.0
MOSS 2007
Released: 7/6/2009

Parameter Name Short Form Required Description Example Usage
url   Yes The URL of the list or library to update. -url http://portal/documents
title   No String representing the title of the list.  Corresponds to the SPList.Title property. -title Documents
description desc No String representing the description of the list.  Corresponds to the SPList.Description property. -description "Shared Documents"

-desc "Shared Documents"
versioning version No Sets whether to enable versioning and if minor versions should be created or not (sets the SPList.EnableVersioning and the SPList.EnableMinorVersions properties).  Valid values are “none”, “major”, and “majorminor”. -versioning majorminor

-version majorminor
draftvisibility dvv No Sets a value that determines the type of user who can view minor versions of document drafts within the list.  Corresponds to the SPList.DraftVersionVisibility property.  Valid values are “reader”, “author”, and “approver”. -draftvisibility approver

-dvv approver
majorversionlimit mvl No

Sets the maximum number of major versions allowed for an item in a document library that uses version control with major versions only.  Corresponds to the SPList.MajorVersionLimit property.

-majorversionlimit 3

-mvl 3
majorwithminorversionlimit mmvl No Sets the maximum number of major versions that are allowed for an item in a document library that uses version control with both major and minor versions.  Corresponds to the SPList.MajorWithMinorVersionsLimit property. -majorwithminorversionlimit 3

-mmvl 3
forcecheckout fc No Sets whether forced checkout is enabled for the document library.  Valid values are “true” and “false”.  Corresponds to the SPList.ForceCheckout property. -forcecheckout true

-fc true
enablemoderation mod No Sets whether Content Approval is enabled for the list.  Valid values are “true” and “false”.  Corresponds to the SPList.EnableModeration property. -enablemoderation true

-mod true
enablecontenttypes ect No Sets whether content types are enabled for the list.  Valid values are “true” and “false”.  Corresponds to the SPList.ContentTypesEnabled property. -enablecontenttypes true

-ect true
enablefoldercreation efc No Sets whether folders can be created for the list.  Valid values are “true” and “false”.  Corresponds to the SPList.EnableFolderCreation property. -enablefoldercreation true

-efc true
allowdeletion del No Sets whether the list can be deleted.  Valid values are “true” and “false”.  Corresponds to the SPList.AllowDeletion property. -allowdeletion true

-del true
alloweveryoneviewitems aevi No Sets whether everyone can view documents in the document library or attachments to items in the list.  Valid values are “true” and “false”.  Corresponds to the SPList.AllowEveryoneViewItems property. -alloweveryoneviewitems true

-aevi true
enablesyndication syn No Sets whether RSS syndication is enabled for the list.  Valid values are “true” and “false”.  Corresponds to the SPList.EnableSyndication property. -enablesyndication true

-syn true
hidden   No Sets whether the list is hidden.  Valid values are “true” and “false”.  Corresponds to the SPList.Hidden property. -hidden false
onquicklaunch oql No Sets whether the list appears on the Quick Launch of the home page.  Valid values are “true” and “false”.  Corresponds to the SPList.OnQuickLaunch property. -onquicklaunch true

-oql true
defaultitemopen open No Sets whether to open list items in a client application or in the browser.  Valid values are “preferclient” and “browser”.  Corresponds to the SPList.DefaultItemOpen property. -defaultitemopen preferclient

-open preferclient
ordered   No Sets whether the option to allow users to reorder items in the list is available on the Edit View page for the list.  Valid values are “true” and “false”.  Corresponds to the SPList.Ordered property. -ordered true
nocrawl   No Sets whether crawling is enabled for the list.  Valid values are “true” and “false”.  Corresponds to the SPList.NoCrawl property. -nocrawl false
enableattachments att No Sets whether attachments can be added to items in the list.  Valid values are “true” and “false”.  Corresponds to the SPList.EnableAttachments property. -enableattachments false

-att false

The following is an example of how to set a few properties on a list:

stsadm -o gl-setlistproperties -url http://portal/documents -versioning majorminor -enablemoderation true -draftvisibility approver

Note that I'm not exposing every property on the SPList class but I believe I've hit the more widely used ones.

6Jul/095

Recent Custom STSADM Extensions Bug Fixes

For those that follow me on twitter (http://twitter.com/glapointe) you probably already know about these fixes but I’ll try to provide more details here.  While I was waiting to start my new job (see my previous post) I decided to take a look at a few of the STSADM commands that I built and fix some things that’s been nagging me for a while.  The one I spent the most time on was the gl-convertsubsitetositecollection command and more specifically its partner the gl-repairsitecollectionimportedfromsubsite command.

This command is undoubtedly my most popular command – it’s corresponding blog post is consistently the top requested post on my blog every week.  The problem is that I had some direct access database calls in the code which never really set well with me – it violated everything I always tell people – never hit the database directly.  The reason for the code was due to two issues: the ContentType field of the master page gallery was getting incorrectly set to a Text type instead of a Choice type and there was no way via the API to change this; discussion lists were getting flattened and there was no way via the API to change this without recreating each item (copy/move operation) which messes up historical/audit information.

There are two things that enabled me to get rid of this DB access code:  I’m smarter than I was 2 years ago when I originally wrote the code and SP2 came out.  Using code that I wrote for copying lists I was able to simply export the original source master page gallery and import it on top of the target gallery thus forcing the content type field to be fixed.  I also found that, in every case I tested, activating the PublishingResources feature now fixes the list (I believe this is the result of SP2 but I’ve not had time to confirm specifically).  Also, I removed some code to deal with an earlier bug that was resulting in items being put in arbitrary locations (under the wrong folder) so now discussion list items are correctly imported and not flattened.

So, what does all this mean – if you are not running SP2 then most everything with these commands should work just fine for you but I can’t guarantee everything I’m accounting for will be fixed (particularly discussion lists) – so if you plan on using these commands I strongly recommend you deploy SP2.  I also added some additional logging and removed the unnecessary site template related parameters.

Another command that I “fixed” was the gl-createsiteindb command.  This one never really sat well with me because I was using reflection to call an internal method in order to pass the target database in – it just seemed like there had to be a way to do this without using reflection.  Turns out there was but for whatever reason I just missed it (maybe it’s new with an update or something or maybe I just brain farted).  Once you get the SPContentDatabase object you can simply use the Sites property which returns an SPSiteCollection object and then call it’s Add method to add the site to the database – seems so obvious now.  Anyways, the code  has been updated to use this approach thus avoiding the use of reflection.

The following remaining commands also had some minor fixes done:

  1. gl-copylistsecurity: Fixed issues for list items (document libraries were not affected) where the target items was being located via the file name property (SPListItem.Name) which was not necessarily set for a generic list.  I now first try the FileLeafRef field and if that fails then I use the index of the item in the list.
  2. gl-importaudiences: The XML file that I was outputting was reversing the value of the SearchString and ReplaceString attributes.
  3. gl-copycontenttypes: If the document template pointed to an existing document outside the current web the code now correctly copies the value of the document template property without attempting to copy the document itself (previously it would ignore the property if it couldn’t access the file).
  4. gl-replacefieldvalues: I was previously checking the document out, making the change, and checking the document back in.  For various reasons I’ve decided to not attempt to check the document out and instead simply call SystemUpdate to save the changes.  I went back and forth on this and would be curious to get peoples thoughts – is it better to have a separate version for each change or leave it as I’ve got it now where no version history is generated?

I’ve not yet updated the documentation for all of these commands but I will attempt do so this week.

Tagged as: 5 Comments
30Apr/091

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()

24Apr/092

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>
20Apr/092

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>

20Apr/092

Configuring SSO via STSADM

I can’t actually take credit for this particular command – in fact I’ve never configured SSO so I personally don’t know much about it.  I got the code from Stef van Hooijdonk who graciously provided the code he produced.

I made a few minor changes to Stef’s code just to bring it in line with the rest of my code but otherwise it’s as was provided to me – as such I’m not really prepared to support this command as I didn’t personally create it but it’s really straightforward.  One word of caution – the code uses a class which has been flagged as being meant for internal use only and not for use in custom code – specifically the Microsoft.SharePoint.Portal.SignleSignon.Configuration class.  Here’s the code:

   1: #if MOSS
   2: using System;
   3: using System.Collections.Specialized;
   4: using System.Text;
   5: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
   6: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   7: using Microsoft.SharePoint.Portal.SingleSignon;
   8: using System.Diagnostics;
   9:  
  10: namespace Lapointe.SharePoint.STSADM.Commands.Security
  11: {
  12:     public class ConfigureSso : SPOperation
  13:     {
  14:         /// <summary>
  15:         /// Initializes a new instance of the <see cref="ConfigureSso"/> class.
  16:         /// </summary>
  17:         public ConfigureSso()
  18:         {
  19:             SPParamCollection parameters = new SPParamCollection();
  20:             parameters.Add(new SPParam("adminaccount", "admin", true, string.Empty, new SPNonEmptyValidator()));
  21:             parameters.Add(new SPParam("enterpriseapplicationaccount", "eaa", true, string.Empty, new SPNonEmptyValidator()));
  22:             parameters.Add(new SPParam("sqlserver", "sql", true, string.Empty, new SPNonEmptyValidator()));
  23:             parameters.Add(new SPParam("database", "db", true, string.Empty, new SPNonEmptyValidator()));
  24:             parameters.Add(new SPParam("tickettimeout", "tick", false, "2", new SPIntRangeValidator(1, 60), "Please specify minutes between 1 and 60"));
  25:             parameters.Add(new SPParam("auditlogs", "logs", false, "10", new SPIntRangeValidator(1, 90), "Please specify days between 1 and 90."));
  26:  
  27:             StringBuilder sb = new StringBuilder();
  28:             sb.Append("\r\n\r\nConfigure the SSO Service in the Farm. \r\n\r\nParameters:");
  29:             sb.Append("\r\n\t-admin <admin account or group that will admininster the SSO Service>");
  30:             sb.Append("\r\n\t-eaa <admin account or group that will administer the enterprise applications>");
  31:             sb.Append("\r\n\t-sql <sql server for the SSO Database>");
  32:             sb.Append("\r\n\t-db <database name for the SSO Storage>");
  33:             sb.Append("\r\n\t[-tick] <minutes a sso ticket is valid, defaults to 2>");
  34:             sb.Append("\r\n\t[-logs] <days to keep logs, defaults to 10>");
  35:  
  36:             Init(parameters, sb.ToString());
  37:         }
  38:  
  39:         #region ISPStsadmCommand Members
  40:  
  41:         /// <summary>
  42:         /// Gets the help message.
  43:         /// </summary>
  44:         /// <param name="command">The command.</param>
  45:         /// <returns></returns>
  46:         public override string GetHelpMessage(string command)
  47:         {
  48:             return HelpMessage;
  49:         }
  50:  
  51:         /// <summary>
  52:         /// Runs the specified command.
  53:         /// </summary>
  54:         /// <param name="command">The command.</param>
  55:         /// <param name="keyValues">The key values.</param>
  56:         /// <param name="output">The output.</param>
  57:         /// <returns></returns>
  58:         public override int Execute(string command, StringDictionary keyValues, out string output)
  59:         {
  60:             output = string.Empty;
  61:  
  62:             string adminAccount = Params["adminaccount"].Value;
  63:             string eaDefAccount = Params["eaa"].Value;
  64:             string sqlServer = Params["sqlserver"].Value;
  65:             string database = Params["database"].Value;
  66:  
  67:             uint ticketTimeout = 2;
  68:             if (Params["tickettimeout"].UserTypedIn)
  69:                 ticketTimeout = uint.Parse(Params["tickettimeout"].Value);
  70:  
  71:             uint auditLogs = 10;
  72:             if (Params["auditlogs"].UserTypedIn)
  73:                 auditLogs = uint.Parse(Params["auditlogs"].Value);
  74:  
  75:             Configure(adminAccount, eaDefAccount, sqlServer, database, ticketTimeout, auditLogs);
  76:  
  77:             return OUTPUT_SUCCESS;
  78:         }
  79:  
  80:         #endregion
  81:  
  82:         /// <summary>
  83:         /// Configures the SSO settings.
  84:         /// </summary>
  85:         /// <param name="adminAccount">The admin account.</param>
  86:         /// <param name="eaDefAccount">The ea def account.</param>
  87:         /// <param name="sqlServer">The SQL server.</param>
  88:         /// <param name="database">The database.</param>
  89:         /// <param name="ticketTimeout">The ticket timeout.</param>
  90:         /// <param name="auditLogs">The audit logs.</param>
  91:         private static void Configure(string adminAccount, string eaDefAccount, string sqlServer, string database, uint ticketTimeout, uint auditLogs)
  92:         {
  93:             try
  94:             {
  95:                 Log("Connecting to {0}", sqlServer);
  96:                 Configuration.ConfigureSecretServer(
  97:                     adminAccount,
  98:                     eaDefAccount,
  99:                     sqlServer,
 100:                     database,
 101:                     ticketTimeout,
 102:                     auditLogs);
 103:                 Log("SSO Configured succesfull.");
 104:             }
 105:             catch (Exception exc)
 106:             {
 107:                 if (exc.Message.Contains("-2147023143"))
 108:                     Log("Error occured in setting the SSO Server settings. Is the SSOService configured to run as a domain account?", EventLogEntryType.Error);
 109:                 throw;
 110:             }
 111:  
 112:             Log("Generating MasterSecret..");
 113:             Configuration.GenerateMasterSecret(false);
 114:             Log("Master Secret Key set.");
 115:         }
 116:  
 117:     }
 118: }
 119: #endif

The help for the command is shown below:

C:\>stsadm -help gl-configuresso

stsadm -o gl-configuresso


Configure the SSO Service in the Farm.

Parameters:
        -admin <admin account or group that will administer the SSO Service>
        -eaa <admin account or group that will administer the enterprise applications>
        -sql <sql server for the SSO Database>
        -db <database name for the SSO Storage>
        [-tick] <minutes a sso ticket is valid, defaults to 2>
        [-logs] <days to keep logs, defaults to 10>

The following table summarizes the command and its various parameters:

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

Parameter Name Short Form Required Description Example Usage
adminaccount admin Yes Admin account or group that will administer the SSO service. -adminaccount domain\spadmin

-admin domain\spadmin
enterpriseapplicationaccount eaa Yes Admin account or group that will administer the enterprise application. -enterpriseapplicationaccount domain\spadmin

-eaa domain\spadmin
sqlserver sql Yes SQL Server name for the SSO database. -sqlserver sql1

-sql sql1
database db Yes Database name for the SSO storage. -database SSODB

-db SSODB
tickettimeout tick No Minutes an SSO ticket is valid, defaults to 2. -tickettimeout 2

-tick 2
auditlogs logs No Day to keep logs, defaults to 10. -auditlogs 10

-logs 10

The following is an example of how to configure SSO:

stsadm -o gl-configuresso –adminaccount domain\spadmin –enterpriseapplicationaccount domain\spadmin –sqlserver sql1 –database ssodb –tickettimeout 2 –auditlogs 10

22Mar/090

New Issue with STSADM MergeContentDB

I just saw this post on the SharePoint forums: http://social.msdn.microsoft.com/Forums/en-US/sharepointgeneral/thread/e9cd9836-5a50-42b3-bf2f-02338a3168f3 – it appears that there’s a possibility for data corruption when using the STSADM mergecontentdb command.  The post details a few things you can do to reduce the possibility of corruption when using the command but it would appear that the hidden message is to use Backup/Restore until a fix is released.

Tagged as: , No Comments