Configure Share Permissions with WMI and PowerShell

Sometimes in IT, the hardest part of a task is figuring out the right tool for the job. And even then, you may run into situations where you can’t use your desired tool. Here’s an example pulled from a recent PowerShell forum post. In this article, we’ll learn how to configure share permissions with WMI and PowerShell.

The problem at hand is how to remove the everyone group from share permissions, presumably across multiple servers or desktops. Life would be certainly easier if the shares were created accordingly in the first place. With that said, circumstances change and the need to modify share permissions isn’t that extraordinary. Fortunately, with PowerShell and the commands in the SMBShare module, this is very easy. I’ll use a share on my Windows 8.1 desktop running PowerShell 4.0, but it’s just as easy to use the cmdlets with remote computers.
It’s very easy to view permissions using Get-SMBShareAccess:

Listing share permissions. (Image Credit: Jeff Hicks)
Listing share permissions. (Image Credit: Jeff Hicks)

And just as easy to modify them.
Modifying share permissions (Image Credit: Jeff Hicks)
Modifying share permissions (Image Credit: Jeff Hicks)

Unfortunately, your servers or desktops need to be running a version of Windows and PowerShell that supports the CIM classes for this to work remotely. For older servers and desktops, this might be a large obstacle. In the case of the IT pro asking the original question, they wanted to try and accomplish this using WMI.
Managing Windows permissions is without a doubt one of the most cumbersome and complex tasks out there, even with tools like PowerShell. And trying to do this with WMI adds an extra layer of madness. Still, it isn’t impossible, so let me guide you through the process. For the sake of my demonstration, I’ve gone back and added the everyone group back to the share.
If you’re just starting out, then you might first try to get the share with WMI and PowerShell. I’m going to use the Get-WMIObject cmdlet. You could use Get-CimInstance, I’d like to think you could use the SMBShare module if that’s the case. So we’ll stick with WMI.

​
I'm testing locally, but this should all work remotely. If you look at all the object properties, you won't see anything for permissions or access.
Listing WMI share properties (Image Credit: Jeff Hicks)
Listing WMI share properties (Image Credit: Jeff Hicks)
No. The class you need is actually called Win32_LogicalShareSecuritySetting. Let's get it for the test share.
​
What did we get?
Share security setting properties (Image Credit: Jeff Hicks)
Share security setting properties (Image Credit: Jeff Hicks)
That doesn't look much better. This is where we need to turn to Get-Member and check some methods.
Logical share security setting methods (Image Credit: Jeff Hicks)
Logical share security setting methods (Image Credit: Jeff Hicks)
This looks promising. Let's try and get the security descriptor.
Getting the security descriptor (Image Credit: Jeff Hicks)
Getting the security descriptor (Image Credit: Jeff Hicks)
Hmmm. Looks like the descriptor property is a nested object. Let's dig deeper.
Expanding the security descriptor (Image Credit: Jeff Hicks)
Expanding the security descriptor (Image Credit: Jeff Hicks)
Looking at the DACL, there appear to be two entries. Still digging.
Listing DACL entries (Image Credit: Jeff Hicks)
Listing DACL entries (Image Credit: Jeff Hicks)
I'll come back to the AccessMask. The trustee appears to be yet one more object. See how far down the rabbit hole we're going?
Expanding the trustee (Image Credit: Jeff Hicks)
Expanding the trustee (Image Credit: Jeff Hicks)
Finally, we reach something meaningful. Now that I know the property hierarchy, I can easily list the access control entries.
Listing access control entries (Image Credit: Jeff Hicks)
Listing access control entries (Image Credit: Jeff Hicks)
Before I show you how to remove the everyone group, let's take a slight detour and look at the AccessMask. This is a bitwise value that indicates what type of access is allowed. You can decode this value by performing a series of bitwise operations. Here's a function I use to make this much easier.
Function ConvertFrom-AccessMask {
[cmdletbinding()]
Param (
[Parameter(Position=0,Mandatory,HelpMessage="Enter an AccessMask",
ValueFromPipeline,ValueFromPipelineByPropertyName)]
[ValidateNotNullorEmpty()]
[uint32]$AccessMask,
[switch]$AsString
)
Begin {
    Write-Verbose "Starting $($MyInvocation.Mycommand)"
} #begin
Process {
    Write-Verbose "Decoding $Accessmask"
    $AccessMaskDecode=@()
    If ($AccessMask -bAnd 1048576) {$AccessMaskDecode+= "Synchronize"}
    If ($AccessMask -bAnd 524288)  {$AccessMaskDecode+= "WriteOwner"}
    If ($AccessMask -bAnd 262144)  {$AccessMaskDecode+= "WriteACL"}
    If ($AccessMask -bAnd 131072)  {$AccessMaskDecode+= "ReadSecurity"}
    If ($AccessMask -bAnd 65536)   {$AccessMaskDecode+= "Delete"}
    If ($AccessMask -bAnd 256)     {$AccessMaskDecode+= "WriteAttrib"}
    If ($AccessMask -bAnd 128)     {$AccessMaskDecode+= "ReadAttrib"}
    If ($AccessMask -bAnd 64)      {$AccessMaskDecode+= "DeleteDir"}
    If ($AccessMask -bAnd 32)      {$AccessMaskDecode+= "Execute"}
    If ($AccessMask -bAnd 16)      {$AccessMaskDecode+= "WriteExtAttrib"}
    If ($AccessMask -bAnd 8)       {$AccessMaskDecode+= "ReadExtAttrib"}
    If ($AccessMask -bAnd 4)       {$AccessMaskDecode+= "Append"}
    If ($AccessMask -bAnd 2)       {$AccessMaskDecode+= "Write"}
    If ($AccessMask -bAnd 1)       {$AccessMaskDecode+= "Read"}
    If ($AsString) {
        #join the result as a comma separated string
        Write-Verbose "Writing result as a string"
        $AccessMaskDecode -join ","
    }
    else {
        #write the result to the pipeline
        Write-Verbose "Writing result as an array"
        $AccessMaskDecode
    }
} #process
End {
    Write-Verbose "Ending $($MyInvocation.Mycommand)"
} #end
} #close Get-AccessMask

The default behavior is to display an array of access rights, or you can display it as a string. Here’s a revised version of my previous command.

Displaying a decoded access mask (Image Credit: Jeff Hicks)
Displaying a decoded access mask (Image Credit: Jeff Hicks)
Let's get back to the problem at hand. If you recall when we looked at methods for the security setting object, there was one for setting. The method needs a parameter value for the new security descriptor object. So let's create one by essentially getting all the current entries except for the one that matches the everyone group.
Revised DACL with a single entry (Image Credit: Jeff Hicks)
Revised DACL with a single entry (Image Credit: Jeff Hicks)
I'll get the current security descriptor.
​
Assign a new value to the DACL to reflect my revised changes.
​
Finally, I can apply the new security descriptor.
Applying a new security descriptor (Image Credit: Jeff Hicks)
Applying a new security descriptor (Image Credit: Jeff Hicks)
A ReturnValue of 0 indicates the operation was successful. Anything else and you'd have to check the MSDN documentation for the SetSecurityDescriptorSetting(). The change is immediate which I can verify with Get-SMBShareAccess. Verifying the new share permissions (Image Credit: Jeff Hicks)Verifying the new share permissions (Image Credit: Jeff Hicks) If you wanted, you could probably distill everything down to a scriptblock like this:
​

Note that when you invoke methods like this, you don't have the ability to take advantage of parameters like WhatIf and Confirm. You could rewrite my examples using Invoke-WMIMethod, but know that it will take several steps. Don't try to cram all of this into a single pipelined expression. It should go without saying that you will need to build your own tool for the task at hand and test thoroughly in a non-production environment.