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, February 12, 2009

Synchronizing Quota Settings via STSADM

When working with clients on their SharePoint deployment I often get questions about the quota templates and how they work.  Quotas are kind of odd because of the disconnected nature of the quota template and the quota that is assigned to a site collection.  Often people assume that changing the template settings affects all site collections using that template.  Unfortunately that is not the case.  The issue is made worse by the fact that the “Site Collection Quotas and Locks” page within Central Admin shows the values of the template assigned and not the actual quota assigned to the site – so administrators will change the template settings, then look at a site collection using that template and it “appears” to them that the template change has affected the site.  But if you go to the command line and run the “enumsites” command you can see that the quota values have not in fact been changed.

So how do you get the value applied?  Simply click the “OK” button on the “Site Collection Quotas and Locks” page.  Okay, so that’s all nice and peachy if you have one or two site collections, but what if you have several hundred?  That’s where my new command comes in: gl-syncquotas.  Synchronizing quotas using the object model is actually pretty easy – you just get an instance of the quota template (SPQuotaTemplate), which inherits from SPQuota, and set the Quota property of the SPSite object – that’s it.

Here’s the code showing how I did it:

   1: using System;
   2: using System.Text;
   3: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
   4: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   5: using System.Collections.Specialized;
   6: using Microsoft.SharePoint;
   7: using Microsoft.SharePoint.Administration;
   8:  
   9: namespace Lapointe.SharePoint.STSADM.Commands.Quotas
  10: {
  11:     public class SyncQuotas : SPOperation
  12:     {
  13:         private SPQuotaTemplateCollection m_quotaColl;
  14:         private SPQuotaTemplate m_quota;
  15:         private bool m_setQuota = false;
  16:  
  17:         /// <summary>
  18:         /// Initializes a new instance of the <see cref="SyncQuotas"/> class.
  19:         /// </summary>
  20:         public SyncQuotas()
  21:         {
  22:             SPParamCollection parameters = new SPParamCollection();
  23:             parameters.Add(new SPParam("url", "url", false, null, new SPUrlValidator()));
  24:             parameters.Add(new SPParam("scope", "s", false, "site", new SPRegexValidator("(?i:^Farm$|^WebApplication$|^Site$)")));
  25:             parameters.Add(new SPParam("quota", "q", false, null, new SPNonEmptyValidator()));
  26:             parameters.Add(new SPParam("setquota", "set"));
  27:  
  28:  
  29:             StringBuilder sb = new StringBuilder();
  30:             sb.Append("\r\n\r\nSynchronizes site quota settings with those defined in the quota templates.\r\n\r\nParameters:");
  31:             sb.Append("\r\n\t[-scope <Farm | WebApplication | Site (default)>]");
  32:             sb.Append("\r\n\t[-url <url>]");
  33:             sb.Append("\r\n\t[-quota <quota template name to synchronize (sites using other quotas will not be affected)>]");
  34:             sb.Append("\r\n\t[-setquota (if scope is site or web application and a quota is specified apply the quota to any site that does not currently have a quota assigned)]");
  35:  
  36:             Init(parameters, sb.ToString());
  37:         }
  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:         /// Executes 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:             Verbose = true;
  60:  
  61:             string scope = Params["scope"].Value.ToLowerInvariant();
  62:  
  63:             SPFarm farm = SPFarm.Local;
  64:             SPWebService webService = farm.Services.GetValue<SPWebService>("");
  65:  
  66:             m_quotaColl = webService.QuotaTemplates;
  67:             m_setQuota = Params["setquota"].UserTypedIn;
  68:  
  69:             if (Params["quota"].UserTypedIn)
  70:             {
  71:                 m_quota = m_quotaColl[Params["quota"].Value];
  72:                 if (m_quota == null)
  73:                     throw new ArgumentException("The specified quota template name could not be found.");
  74:             }
  75:  
  76:             SPEnumerator enumerator;
  77:             if (scope == "farm")
  78:             {
  79:                 enumerator = new SPEnumerator(SPFarm.Local);
  80:             }
  81:             else if (scope == "webapplication")
  82:             {
  83:                 enumerator = new SPEnumerator(SPWebApplication.Lookup(new Uri(Params["url"].Value.TrimEnd('/'))));
  84:             }
  85:             else
  86:             {
  87:                 // scope == "site"
  88:                 using (SPSite site = new SPSite(Params["url"].Value.TrimEnd('/')))
  89:                 {
  90:                     Sync(site);
  91:                 }
  92:                 return OUTPUT_SUCCESS;
  93:             }
  94:             
  95:             enumerator.SPSiteEnumerated += enumerator_SPSiteEnumerated;
  96:             enumerator.Enumerate();
  97:  
  98:             return OUTPUT_SUCCESS;
  99:         }
 100:  
 101:         /// <summary>
 102:         /// Validates the specified key values.
 103:         /// </summary>
 104:         /// <param name="keyValues">The key values.</param>
 105:         public override void Validate(StringDictionary keyValues)
 106:         {
 107:             if (Params["scope"].Validate())
 108:             {
 109:                 Params["url"].IsRequired = true;
 110:                 Params["url"].Enabled = true;
 111:                 if (Params["scope"].Value.ToLowerInvariant() == "farm")
 112:                 {
 113:                     Params["url"].IsRequired = false;
 114:                     Params["url"].Enabled = false;
 115:                 }
 116:             }
 117:             if (Params["setquota"].UserTypedIn)
 118:             {
 119:                 Params["quota"].IsRequired = true;
 120:                 if (Params["scope"].Value.ToLowerInvariant() == "farm")
 121:                     throw new SPSyntaxException("Scope of \"farm\" is not valid when \"setquota\" is specified.");
 122:             }
 123:             base.Validate(keyValues);
 124:         }
 125:  
 126:         /// <summary>
 127:         /// Handles the SPSiteEnumerated event of the enumerator control.
 128:         /// </summary>
 129:         /// <param name="sender">The source of the event.</param>
 130:         /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPSiteEventArgs"/> instance containing the event data.</param>
 131:         private void enumerator_SPSiteEnumerated(object sender, SPEnumerator.SPSiteEventArgs e)
 132:         {
 133:             Sync(e.Site);
 134:         }
 135:  
 136:         /// <summary>
 137:         /// Syncs the specified site quota with the quota template.
 138:         /// </summary>
 139:         /// <param name="site">The site.</param>
 140:         private void Sync(SPSite site)
 141:         {
 142:             SPQuota currentQuota = site.Quota;
 143:             SPQuotaTemplate currentTemplate = null;
 144:             foreach (SPQuotaTemplate quota in m_quotaColl)
 145:             {
 146:                 if (currentQuota.QuotaID == quota.QuotaID)
 147:                 {
 148:                     currentTemplate = quota;
 149:                     break;
 150:                 }
 151:             }
 152:             if (currentTemplate == null)
 153:             {
 154:                 if (!m_setQuota)
 155:                 {
 156:                     Log("WARNING: No quota template has been assigned to site {0}.  Use the -setquota parameter to assign a quota.", site.Url);
 157:                     return;
 158:                 }
 159:                 Log("PROGRESS: Synchronizing {0}", site.Url);
 160:                 Log("PROGRESS: No quota template assigned to site.  Assigning template...", site.Url);
 161:                 site.Quota = m_quota;
 162:  
 163:                 Log("PROGRESS: Template \"{0}\" assigned to site.", m_quota.Name);
 164:                 return;
 165:             }
 166:  
 167:             if (m_quota == null || (m_quota != null && currentQuota.QuotaID == m_quota.QuotaID))
 168:             {
 169:                 Log("PROGRESS: Synchronizing {0}", site.Url);
 170:                 Log("PROGRESS: Currently using template \"{0}\".", currentTemplate.Name);
 171:  
 172:                 if (site.Quota.InvitedUserMaximumLevel == currentTemplate.InvitedUserMaximumLevel &&
 173:                     site.Quota.StorageMaximumLevel == currentTemplate.StorageMaximumLevel &&
 174:                     site.Quota.StorageWarningLevel == currentTemplate.StorageWarningLevel)
 175:                 {
 176:                     Log("PROGRESS: No changes necessary, quota already synchronized with template.");
 177:                     return;
 178:                 }
 179:                 site.Quota = currentTemplate;
 180:  
 181:                 Log("PROGRESS: Storage maximum updated from {0}MB to {1}MB", 
 182:                     ((currentQuota.StorageMaximumLevel / 1024) / 1024).ToString(),
 183:                     ((site.Quota.StorageMaximumLevel / 1024) / 1024).ToString());
 184:                 Log("PROGRESS: Storage warning updated from {0}MB to {1}MB",
 185:                     ((currentQuota.StorageWarningLevel / 1024) / 1024).ToString(),
 186:                     ((site.Quota.StorageWarningLevel / 1024) / 1024).ToString());
 187:                 Log("PROGRESS: Invited user maximum updated from {0} to {1}",
 188:                     currentQuota.InvitedUserMaximumLevel.ToString(),
 189:                     site.Quota.InvitedUserMaximumLevel.ToString());
 190:             }
 191:         }
 192:     }
 193: }

The help for the command is shown below:

C:\>stsadm -help gl-syncquotas

stsadm -o gl-syncquotas


Synchronizes site quota settings with those defined in the quota templates.

Parameters:
        [-scope <Farm | WebApplication | Site (default)>]
        [-url <url>]
        [-quota <quota template name to synchronize (sites using other quotas will not be affected)>]
        [-setquota (if scope is site or web application and a quota is specified apply the quota to any site that does not currently have a quota assigned)]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-syncquotas WSS v3, MOSS 2007 Released: 2/12/2009

Parameter Name Short Form Required Description Example Usage
url   Yes if scope is not Farm URL of the web application or site collection. -url http://mysites
scope s No – defaults to site The scope to use.  Valid values are “Farm”, “WebApplication”, and “Site” -scope site

-s site
quota q No The name of the quota template to synchronize.  If not specified then all site collections will be synchronized regardless of the quota assigned to the site. -quota "Personal Site"

-q "Personal Site"
setquota set No If the scope is site or webapplication and a site collection has no quota assigned to it then the quota template specified via the –quota parameter will be assigned to the site. -setquota

-set

The following is an example of how to synchronize all the “My Site” sites and assign the “Personal Site” quota template to any site collections under the http://mysites web application that do not currently have a quota assigned:

stsadm -o gl-syncquotas -scope webapplication -url http://mysites -quota "Personal Site" -setquota

6 comments:

Curt said...

Thanks Gary, this is a really useful command. Ironically we just had the same need for this command at my work recently :)

Gary Lapointe said...

Yeah - I'd been wanting to make this dang command for months now and my discussion with Kevin yesterday prompted me to use my "day on the bench" to just get it done. See - I'm thinking of you guys even when I'm not there :)

Alexaway said...

Great one, I was about to redevelop it for my needs.
Exactly what I was looking for (you just saved me 1 days developing!!)

Thx!

jeremy said...

Trying to run the command to add a quota to a site that currently does not have a quota template assigned or has one and trying to change it does not work.

It says completed successfully but it doesn't actually do it.

Any ideas?

LinuxInductee said...

As always, Gary, top notch work!

Gary Lapointe said...

LinuxInductee - how are you calling the command?