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.