With Windows Server 2012 we got the SmbShare Module. But since it's a CDXML module that just defines the mapping between PowerShell cmdlets and CIM class operations or methods, it's bound to the OS having the relevant CIM Classes. So it's not portable to older operating systems.
For Windows Server 2012 and above, there's no problem using the native New-SmbShare and Grant-SmbShareAccess cmdlets.
But for Windows Server 2008, I needed something else.
I ended up creating two functions that would replace the New-SmbShare and Grant-SmbShareAccess.
For creating a new share with specific permissions:
function New-Share {
param(
[string] $ComputerName = $env:COMPUTERNAME,
[string] $Path = 'C:Temp',
[string] $ShareName = 'Temp',
[string] $AccountName = 'Domain Users',
[ValidateSet('FullControl', 'Change','Read')] $AccessPermissions = 'Read',
[string] $ShareDescription
)
# Convert the AccessPermissions
$accessFlags = @{
FullControl = 2032127
Change = 1245631
Read = 1179817
}; $access = $accessFlags[$AccessPermissions]
# Extract Domain and User account
$Domain, $Identity = if($AccountName -match '\') {
$AccountName -split '\'
} else {
$env:USERDOMAIN, $AccountName
}
# Build the Security Descriptor and Trustee objects
$sd = ([wmiclass] "\$ComputerNamerootcimv2:Win32_SecurityDescriptor").CreateInstance()
$trustee = ([wmiclass] "\$ComputerNamerootcimv2:Win32_Trustee").CreateInstance()
$trustee.Name = $Identity
$trustee.Domain = $Domain
# Build the Access Control Entry object
$ace = ([wmiclass] "\$ComputerNamerootcimv2:Win32_ACE").CreateInstance()
$ace.AccessMask = $access
$ace.AceFlags = 3
$ace.AceType = 0 # 0 Allow, 1 = Deny
$ace.Trustee = $trustee
$sd.DACL = $ace.psObject.BaseObject
# Create the share with the required permissions
$mc = [wmiclass]"\$ComputerNamerootcimv2:Win32_Share"
$inParams = $mc.psbase.GetMethodParameters('Create')
$inParams.Access = $sd
$inParams.Description = $ShareDescription
$inParams.MaximumAllowed = $null
$inParams.Name = $ShareName
$inParams.Password = $null
$inParams.Path = $Path
$inParams.Type = [uint32]0
$ret = $mc.psbase.InvokeMethod('Create',$inParams, $null)
# Determine the return value from the WMI method
Switch ($ret.ReturnValue){
0 { Write-Verbose 'Share created successfully'; break }
2 { Write-Error 'Access denied (2)'; break }
8 { Write-Error 'Unknown failure (8)'; break }
9 { Write-Error 'Invalid name (9)'; break }
10 { Write-Error 'Invalid level (10)'; break }
21 { Write-Error 'Invalid parameter (21)'; break }
22 { Write-Error 'Duplicate share (22)'; break }
23 { Write-Error 'Redirected path (23)'; break }
24 { Write-Error 'Unknown device or directory (24)'; break }
25 { Write-Error 'Net name not found (25)'; break }
default { Write-Error 'Other Error (26–4294967295)' }
}
}
Then, to create a new share:
$params = @{
ComputerName = 'myWebServer'
Path = 'C:inetpublogsLogFiles'
ShareName = 'IISLogFiles'
AccountName = 'CONTOSOWebOperators'
AccessPermissions = 'Read'
ShareDescription = 'IIS Websites Logs'
}
New-Share @params
For adding permissions on an existing share:
function Add-ShareAccess {
param(
[string] $ComputerName = $env:COMPUTERNAME,
[string] $ShareName = 'Temp',
[string] $AccountName = 'Domain Users',
[ValidateSet('FullControl', 'Change','Read')] $AccessPermissions = 'Read'
)
# Convert the AccessPermissions
$accessFlags = @{
FullControl = 2032127
Change = 1245631
Read = 1179817
}; $access = $accessFlags[$AccessPermissions]
# Extract Domain and User account
$Domain, $Identity = if($AccountName -match '\') {
$AccountName -split '\'
} else {
$env:USERDOMAIN, $AccountName
}
# Build the Security Descriptor and Trustee objects
$sd = ([wmiclass] "\$ComputerNamerootcimv2:Win32_SecurityDescriptor").CreateInstance()
$trustee = ([wmiclass] "\$ComputerNamerootcimv2:Win32_Trustee").CreateInstance()
$trustee.Name = $Identity
$trustee.Domain = $Domain
# Build the Access Control Entry object
$ace = ([wmiclass] "\$ComputerNamerootcimv2:Win32_ACE").CreateInstance()
$ace.AccessMask = $access
$ace.AceFlags = 3
$ace.AceType = 0 # 0 Allow, 1 = Deny
$ace.Trustee = $trustee
# Get the current permissions on the share
$current = Get-WmiObject -ComputerName $computerName -Class Win32_LogicalShareSecuritySetting -Filter "Name='$ShareName'"
[System.Management.ManagementBaseObject[]]$newACE = $current.GetSecurityDescriptor().Descriptor.DACL
# Add the access control entry to the list
[array]::Resize([ref]$newACE, $newACE.Count + 1)
$newACE[$newACE.Count-1] = $ace
$sd.DACL = $newACE
# Set the permissions on the share
$mc = Get-WmiObject -ComputerName $computerName -Class Win32_Share -Filter "Name='$ShareName'"
$ret = $mc.SetShareInfo($null, $mc.Description, $sd)
# Determine the return value from the WMI method
Switch ($ret.ReturnValue){
0 { Write-Verbose 'Share updated successfully'; break }
2 { Write-Error 'Access denied (2)'; break }
8 { Write-Error 'Unknown failure (8)'; break }
9 { Write-Error 'Invalid name (9)'; break }
10 { Write-Error 'Invalid level (10)'; break }
21 { Write-Error 'Invalid parameter (21)'; break }
22 { Write-Error 'Duplicate share (22)'; break }
23 { Write-Error 'Redirected path (23)'; break }
24 { Write-Error 'Unknown device or directory (24)'; break }
25 { Write-Error 'Net name not found (25)'; break }
default { Write-Error 'Other Error (26–4294967295)' }
}
}
Then, to update a share's permissions:
$params = @{
ComputerName = 'myWebServer'
ShareName = 'IISLogFiles'
AccountName = 'CONTOSOWebAdmins'
AccessPermissions = 'FullControl'
}
Add-ShareAccess @params
For further reading, see:
Create method of the Win32_Share class: https://msdn.microsoft.com/en-us/library/aa389393
SetShareInfo method of the Win32_Share class: https://msdn.microsoft.com/en-us/library/aa393598
Win32_SecurityDescriptor class: https://msdn.microsoft.com/en-us/library/aa394402
Win32_LogicalShareSecuritySetting class: https://msdn.microsoft.com/en-us/library/aa394188
HTH,
Martin