6

I have File Service cluster where one of the file server resources is hosting ~50,000 user home directories. The home directories do have a quota template assigned through the FSRM.

When trying to add a new share using the Failover Cluster Manager's "Add File Share" wizard, it starts by retrieving all quotas on all shared folders over all defined file server cluster resources. Which is taking ~10 minutes in this environment.

How could I either

  • speed up the process of quota enumeration
  • limit the quota enumeration process to just a single file server cluster resource
  • disable the quota enumeration completely for the New Share Wizard

?

the-wabbit
  • 40,319
  • 13
  • 105
  • 169

1 Answers1

3

I finally worked around the problem by lifting the requirement to use the GUI for file share creation entirely. Instead, I documented the use of New-SmbShare as the share creation procedure. Adding shares in this way bypasses all pre-configuration checks the GUI wizard is performing, including the quota enumeration.

New-SmbShare -Name <ShareName> -ScopeName "<CAPName>" -Path "<LocalDirectoryToBeShared>" -FullAccess "Everyone" -Description "<Comment>"

The New-SmbShare cmdlet has been introduced with Server 2012 R2 / Windows 8.1. For previous (2012, 2008R2, 2008) version File Server clusters, you can borrow the NativeMethods class with which is importing the NetShareAdd function from Netapi32.dll from a script published along with an MSDN blog post about shares in Fileover Cluster scopes.

My significantly shortened version looks like this:

#Using Win32 API NetShareAdd via p/invoke to be able to specify the scope in SHARE_INFO_503
$signature = @"

    using System;
    using System.Runtime.InteropServices;
    using System.Collections;

    public class NativeMethods
    {
        [DllImport("Netapi32.dll")]
        public static extern uint NetShareAdd([MarshalAs(UnmanagedType.LPWStr)] string strServer, Int32 dwLevel, ref SHARE_INFO_503 buf, out uint parm_err);

        [StructLayoutAttribute(LayoutKind.Sequential)]
        struct SECURITY_DESCRIPTOR {
            public byte revision;
            public byte size;
            public short control;
            public IntPtr owner;
            public IntPtr group;
            public IntPtr sacl;
            public IntPtr dacl;
        }

        public enum SHARE_TYPE : uint
        {
            STYPE_DISKTREE = 0,
            STYPE_PRINTQ = 1,
            STYPE_DEVICE = 2,
            STYPE_IPC = 3,
            STYPE_SPECIAL = 0x80000000
        };

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct SHARE_INFO_503
        {
            public string shi503_netname;
            [MarshalAs(UnmanagedType.U4)]
            public SHARE_TYPE shi503_type;
            public string shi503_remark;
            [MarshalAs(UnmanagedType.U4)]
            public int shi503_permissions;
            [MarshalAs(UnmanagedType.U4)]
            public int shi503_max_uses;
            [MarshalAs(UnmanagedType.U4)]
            public int shi503_current_uses;
            public string shi503_path;
            public string shi503_passwd;
            public string shi503_servername;
            [MarshalAs(UnmanagedType.U4)]
            public int shi503_reserved;
            public IntPtr shi503_security_descriptor;
        }


        public static uint ShareFolder(string servername, string sharename, string path, string remark)
        {
            SHARE_INFO_503 shInfo = new SHARE_INFO_503();
            shInfo.shi503_netname = sharename;
            shInfo.shi503_type = SHARE_TYPE.STYPE_DISKTREE;
            shInfo.shi503_remark = remark;
            shInfo.shi503_permissions = 0;
            shInfo.shi503_max_uses = -1;
            shInfo.shi503_current_uses = 0;
            shInfo.shi503_path = path;
            shInfo.shi503_passwd = null;
            shInfo.shi503_servername = servername;
            shInfo.shi503_reserved = 0;
            shInfo.shi503_security_descriptor = IntPtr.Zero;

            uint nRetValue = 0;
            uint param_err = 0;

            nRetValue = NetShareAdd(servername, 503, ref shInfo, out param_err);

            //Console.WriteLine("Sharing " + path + " on " + servername + " as " + sharename + " returned " + nRetValue + " (" + param_err+ ")");

            return nRetValue;
        }
    }
"@


#Import the FailoverClusters PowerShell module if it is not already imported
Import-Module FailoverClusters

#Add the function type that will be used to share the folder in the defined scope
Add-Type -TypeDefinition $signature

Usage is trivial:

[NativeMethods]::ShareFolder("<CAPname>", "<ShareName>", "<LocalDirectoryToBeShared>", "<Comment>")

The ShareFolder function returns 0 upon successful execution and finishes instantly, even with quotas enabled. To be run on one of the cluster nodes hosting the CAP / file server resource. You might have to fix share ACLs afterwards, as the default share creation ACL is just Everyone:Read and cannot be specified with this method.

the-wabbit
  • 40,319
  • 13
  • 105
  • 169