Setting share permissions with WMI


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] "\\$ComputerName\root\cimv2:Win32_SecurityDescriptor").CreateInstance()
    $trustee = ([wmiclass] "\\$ComputerName\root\cimv2:Win32_Trustee").CreateInstance()
    $trustee.Name = $Identity
    $trustee.Domain = $Domain

        # Build the Access Control Entry object
    $ace = ([wmiclass] "\\$ComputerName\root\cimv2: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]"\\$ComputerName\root\cimv2: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:\inetpub\logs\LogFiles' 
    ShareName         = 'IISLogFiles'
    AccountName       = 'CONTOSO\WebOperators'
    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] "\\$ComputerName\root\cimv2:Win32_SecurityDescriptor").CreateInstance()
    $trustee = ([wmiclass] "\\$ComputerName\root\cimv2:Win32_Trustee").CreateInstance()
    $trustee.Name = $Identity
    $trustee.Domain = $Domain

    # Build the Access Control Entry object
    $ace = ([wmiclass] "\\$ComputerName\root\cimv2: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       = 'CONTOSO\WebAdmins'
    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

Comments (0)

Skip to main content