Server Message Block (SMB) is a protocol that's used extensively by Windows for sharing files, printers, serial ports, and communications abstractions such as named pipes and mail slots between computers. Windows 8 and Windows Server 2012 introduced the SMBShare and SMBWitness modules to manage SMB file servers and SMB file shares. Thanks to the module auto-loading feature in Windows PowerShell 3.0 and later, these modules are automatically loaded whenever you refer to any of the contained cmdlets.

If you're running Windows 7, Windows Sever 2008 R2, or Windows Server 2008, the SMBShare and SMBWitness modules aren't available. However, you can use Windows Management Instrumentation (WMI) to work with file shares. Using WMI, you can manage devices and applications in a Windows network environment. In addition to providing information about the status of local or remote computer systems, WMI lets you configure security settings, system properties, permissions, and drive labels.

In the first part of this tutorial, I'll discuss how to use WMI and PowerShell to manage file shares on Windows 7, Server 2008 R2, and Server 2008. In the second part, I'll discuss how to use the new SMBShare and SMBWitness modules on Windows 8 and Server 2012.

Getting Ready to Use WMI

Before using the WMI module, you need to see whether the WMI provider is installed on your computer. Here's the command to do that:

Get-WmiObject -Class __Namespace -Namespace root `
  -Filter "name='WMI'"

It will return the WMI provider if it's present and null if it wasn't found. In the latter case, you need to download and install the Windows Management Framework 3.0. It's available for Windows 7 SP1 and Server 2008 R2 SP1.

Using WMI to Enumerate Shares

The Win32_Share class resides in the root\cimv2 WMI namespace and represents a shared resource on a Windows PC. Examples of shared resources include disk drives, printers, OS events, and other sharable devices.

You can use the Get-WmiObject cmdlet to retrieve all instances of WMI objects on a computer. If you're interested in only a certain class of WMI objects, you can use the -Class parameter to specify that information. So, if you want to retrieve all the instances of the Win32_Share class on the local computer, you'd use the command:

Get-WmiObject -Class Win32_Share

Figure 1 shows sample results.

Figure 1: Retrieving the Shared Resources on the Local Computer

Get-WmiObject works just as well on remote machines. You just need to include the -ComputerName parameter:

Get-WmiObject -Class Win32_Share -ComputerName WH0RCUTEACHER

Figure 2 shows sample results. Note that Get-WmiObject runs under your credentials so you must have sufficient privileges to access the remote system.

Figure 2: Retrieving the Shared Resources on a Remote Computer

Otherwise, you'll get an "Access denied" error. To invoke the command with another account, you need to include the -Credentials parameter along with the account user ID:

Get-WmiObject -Class Win32_Share -ComputerName WH0RCUTEACHER
  -Credential Domain001\rob2112

The system will then pop up a dialog box for you to enter the associated password.

Using WMI to Create a Local Share

Besides enumerating shares, you can create them. The Create method is available to the Win32_Share class object but not to Win32_Share instances. Therefore, you need to include the -List parameter in the Get-WmiObject -Class Win32_Share command so that you're retrieving the actual WMI class, as opposed to just enumerating shares.

Let's use the Create method to create the most popular shared resource: a shared folder. For this example, the shared folder will be named C:\Users\jxg768\Desktop\RobsShare. Before creating a shared folder, it's important to check that the folder already exists and that it's not already shared. To check whether the folder exists, you can use the Test-Path cmdlet. For example, the following command checks whether the RobsShare folder exists:

Test-Path C:\Users\jxg768\Desktop\RobsShare

If it returns False, you need to create the folder by calling New-Item with the -Type parameter:

New-Item C:\Users\jxg768\Desktop\RobsShare -Type Directory

Since you just created the folder, you know that it's not shared. However, suppose that the folder was created previously. You can test for the share's existence by calling Get-WmiObject with the -Filter parameter. The parameter's value needs to be either the folder's name or pathname as a string. If you specify the pathname, you need to escape the backslashes, like this:

Get-WmiObject Win32_Share `
  -Filter "path='C:\\Users\\jxg768\\Desktop\\RobsShare'"

If the command returns the folder, you know that it's a shared folder. If the command doesn't return anything, you know that the folder isn't a shared folder. Since the command didn't return anything, you can go ahead and create the share using the command:

(Get-WmiObject Win32_Share -List).Create
  (
    "C:\Users\jxg768\Desktop\RobsShare", "RobsShare", 0
  )

Before examining the object returned by the Create method, let's take a look at its parameters. The Create method has three required parameters and four optional parameters. The required parameters are Path, Name, and Type. For the Type parameter, you need to use one of seven numeric constants to represent the type of share:

  • 0 = Disk Drive
  • 1 = Print Queue
  • 2 = Device
  • 3 = IPC
  • 2147483648 = Disk Drive Admin
  • 2147483649 = Print Queue Admin
  • 2147483650 = Device Admin
  • 2147483651 = IPC Admin

The optional parameters are:

  • MaximumAllowed. With this parameter, you can specify the maximum number of users allowed to concurrently use the shared resource (e.g., 25 users).
  • Description. You use this parameter to describe the resource being shared (e.g., temp share).
  • Password. Using this parameter, you can set a password for the shared resource on a server that is running with share-level security. If the server is running with user-level security, this parameter is ignored.
  • Access. You use this parameter to specify a Security Descriptor (SD) for user-level permissions. An SD contains information about the permissions, owner, and access capabilities of the resource.

As Figure 3 shows, the Create method returns an object with several attributes, the most important of which is ReturnValue.

Figure 3: Checking the Value of the ReturnValue Attribute

You need to make sure that the ReturnValue attribute's value is 0, which means that the share was successfully created. All other values signify an error condition. Table 1 shows the possible return values and what they mean.

Return Code Description
Table 1: Create Method's Return Values
0 Success
2 Access denied
8 Unknown failure
9 Invalid name
10 Invalid level
21 Invalid parameter
22 Duplicate share
23 Redirected path
24 Unknown device or directory
25 Net name not found

If you want a really easy way to check this value, you can save the Create method's output to a variable and check the ReturnValue attribute's value, like this:

$retObj = (Get-WmiObject Win32_Share -List).create
  (
    "C:\Users\jxg768\Desktop\RobsShare", "RobsShare", 0
  )
$retObj.ReturnValue

In this case, you'd receive the result of 0.

Using WMI to Create a Remote Share

Everything that you just did locally can be done remotely as well. To use Test-Path on a remote machine, you can pass in a Universal Naming Convention (UNC) path. A UNC path describes the actual location of a file or directory, thus eliminating the need for arbitrary Windows drive letter mappings (which tend to change). In this case, the command would be:

Test-Path \\WH0RCUTEACHER\c$\Users\jxg768\Desktop\RobsShare

Notice the use of the c$ Default Share, which points to the C drive on the remote machine. If the file exists, this command returns True.

The Get-WmiObject cmdlet has a -ComputerName parameter that lets you work remotely. You can use it to test for the shared RobsShare folder by name:

Get-WmiObject -Class Win32_Share `
  -ComputerName WH0RCUTEACHER -Filter "Name='RobsShare'"

The -ComputerName parameter also makes creating a remote share a snap:

$share = Get-WmiObject Win32_Share `
  -List -ComputerName "WH0RCUTEACHER"
$share.create("C:\Users\jxg768\Desktop\RobsShare", `
  "RobsShare", 0)

The New-Item cmdlet can also accept a UNC path for creating a remote folder if you need to:

New-Item \\WH0RCUTEACHER\c$\Users\jxg768\Desktop\RobsShare `
  -itemType Directory

Using WMI to Delete a Share

There are two ways to delete a share. The first approach is to simply delete the resource using the Remove-Item cmdlet, like this:

Remove-Item .\foldertodelete -Force -Recurse

The second approach is to use the Win32_Share instance's Delete method, which removes the share without actually deleting the resource. Being an instance method, you have to make sure that the resource in question exists before using the Delete method to remove it. Otherwise, you'll get an error.

To avoid getting an error, you can use an if statement that checks for the share before calling the Delete method:

if ($share = Get-WmiObject -Class Win32_Share `
  -ComputerName WH0RCUTEACHER -Filter "Name='RobsShare'") `
  { $share.delete() }

If the RobsShare folder doesn't exist, the Delete method doesn't execute.

Using WMI to Configure Share Permissions

In Windows, permissions are managed using SDs. They hold access control information that's associated with a share and other objects. When a user tries to access the object, Windows uses the object's SD to determine whether to allow access.

The part of the SD that pertains to access control is the discretionary ACL (DACL). The DACL contains a list of access control entries (ACEs). An ACE has three relevant components:

  • A header that specifies whether the ACE allows or denies access
  • A SID that specifies a particular user or group
  • An access mask that lists the operations that are allowed or denied

In PowerShell, you can manipulate ACLs using the Get-Acl and Set-Acl cmdlets. One way to use the Get-Acl cmdlet is to pipe the shared object to it:

Get-WmiObject -Class Win32_Share `
  -ComputerName WH0RCUTEACHER -Filter "Name='RobsShare'" |
  Get-Acl

However, as you can see in Figure 4, it doesn't provide a whole lot of useful information.

Figure 4: Piping a Shared Object to Get-Acl

For better results, you should pipe the output of Get-Acl to the Format-List cmdlet:

$robsshare = Get-WmiObject -Class Win32_Share `
  -ComputerName WH0RCUTEACHER -Filter "Name='RobsShare'"
$robsshare | Get-Acl | Format-List *

As Figure 5 shows, a lot more information is presented in an easy-to-read format.

Figure 5: Using Format-List When Piping a Shared Object to Get-Acl

Configuring SDs with PowerShell is done with the Set-Acl cmdlet in tandem with Microsoft .NET Framework classes. For instance, access rules are represented by the System.Security.AccessControl.FileSystemAccessRule class. It has a constructor that accepts:

  • An IdentityReference object that contains the user's account information
  • A FileSystemRights value that specifies the type of operation that the access rule pertains to
  • An AccessControlType value that specifies whether to allow or deny the operation

To help clarify, let's look at an example. Here's the procedure for setting access rights on a remote share:

$acl = Get-Acl `
  \\WH0RCUTEACHER\c$\Users\jxg768\Desktop\RobsShare
$permission = "OMEGA\JXL812","FullControl","Allow"
$accessRule = New-Object `
  System.Security.AccessControl.FileSystemAccessRule `
  $permission
$acl.SetAccessRule($accessRule)
$acl |
  Set-Acl \\WH0RCUTEACHER\c$\Users\jxg768\Desktop\RobsShare

Let's review what this code does. The first line retrieves the current ACL for the RobsShare remote share using the Get-Acl cmdlet. The ACL contains a list of ACEs, where each ACE specifies the access rights for a particular user or group.

The next line sets the parameters for the FileSystemRule constructor using a three-argument constructor. It accepts the following parameters:

  • The user account that the rule applied to
  • A collection of predefined right constants such as CreateFiles and ListDirectory
  • An access type that either allows or denies access to an object

You can now re-run the Get-Acl command to confirm that your changes took effect:

Get-Acl \\WH0RCUTEACHER\c$\Users\jxg768\Desktop\RobsShare |
  fl *

Figure 6 shows sample results. The key line is highlighted.

Figure 6: Verifying That the Access Rights Were Changed on the Remote Share

Getting Ready to Use the SMBShare and SMBWitness Modules

Although not strictly required thanks to module auto-loading, you can import the SMBShare and SMBWitness modules in PowerShell using the Import-Module cmdlet:

Import-Module SmbShare
Import-Module SmbWitness

An even easier command to import the modules is:

Import-Module Smb*

With that done, let's perform the same tasks as performed using WMI.

Using the Modules to Enumerate Shares

Generating a list of your local shares couldn't be easier. You just need to call the Get-SmbShare cmdlet without any parameters:

Get-SmbShare

If you want to list the shares on a remote machine, you just add the -Name parameter:

Get-SmbShare -Name LH24CU142

Using the Modules to Create a Local Share

Creating a share is handled by the New-SmbShare cmdlet. To use it, you need to supply, at a minimum, the share's name and path:

New-SmbShare -Name Spring -Path C:\Spring

With said that, it doesn't hurt to include some additional details, such as a description and some access rights. The following command assigns full access to Administrators and read-only access to everyone else:

New-SmbShare -Name Spring -Path C:\Spring `
  -Description 'Shared Folder for Spring Students' `
  -FullAccess Administrator -ReadAccess Everyone

Note that the New-SmbShare command won't create the folder if it doesn't exist.

Using the Modules to Create a Remote Share

As I mentioned previously, you can use the Get-SmbShare cmdlet with the -Name parameter to list the shares on a remote machine. In this case, the -Name parameter is used to specify the name of the remote machine. When you use the New-SmbShare cmdlet to create a share on a remote machine, you also use the -Name parameter. However, in this case, the -Name parameter is used to specify the name of the new remote share. With that in mind, here's an example of how to create a new remote share:

New-SmbShare -Name Spring -Path C:\Spring `
  -CimSession LH25CU143 -FullAccess "OMEGA\admins" `
  -ChangeAccess Everyone `
  -Description "Shared Folder for Spring Students"

This command uses the -CimSession parameter to establish a remote session on another computer that is also running PowerShell 3.0. If you want to be able to refer back to the same session, you can use the New-PSSession cmdlet to establish the remote session, then store it in a variable for later use:

$session=New-PSSession -ComputerName LH25CU143
New-SmbShare -Name Spring -Path C:\Spring `
  -CimSession $session -FullAccess "OMEGA\admins" `
  -ChangeAccess Everyone `
  -Description "Shared Folder for Spring Students"

Using the Modules to Delete a Share

To delete a share, you can use the Remove-SmbShare cmdlet. You simply need to pass in the share's name with the -Name parameter:

Remove-SmbShare -Name ShareName

Like the New-SmbShare cmdlet, the Remove-SmbShare cmdlet also accepts the -CimSession parameter to establish a remote session on another computer:

Remove-SmbShare -CimSession LH25CU143 -Name ShareName

It's important to note that this command will kill the sessions of any users currently connected to your share. If desired, you can check the current connections by issuing the following call to Get-SmbSession:

Get-SmbSession -ClientComputerName LH25CU143

Using the Modules to Configure Share Permissions

One area where the new SmbShare module shines is in granting permissions. Unlike the WMI-based cmdlets, the SmbShare cmdlets feature a greatly simplified mechanism for granting share permissions.

As I mentioned previously, the New-SmbShare cmdlet accepts some parameters for setting access rights, including the -FullAccess, -ChangeAccess, -ReadAccess, and -NoAccess parameters. By combining these parameters, you can assign different rights to several users or groups at once.

For more fine-grained access control, the Grant-SmbShareAccess cmdlet is dedicated to the granting of access permissions. It also uses parameters to do the job, namely -AccountName and -AccessRight. There are only three possible values that can be assigned to the -AccessRight parameter: Full, Change, and Read. For example, the following command assigns Full permissions on the local Spring share to the admins and students groups of the OMEGA domain:

Grant-SmbShareAccess -Name Spring `
  -AccountName OMEGA\admins, OMEGA\students `
  -AccessRight Full

This command will ask you to confirm that you want to perform this action. To suppress the confirmation prompt, you can include the -Force parameter.

Although you can restrict a user's or group's permissions on a share with the Grant-SmbShareAccess cmdlet, you can't revoke the permissions entirely. To do that, you need to use the Revoke-SmbShareAccess cmdlet, like so:

Revoke-SmbShareAccess -Name Spring -AccountName OMEGA\students

Now There's a Choice

With the release of Windows 8 and Server 2012, Windows administrators now have a choice between two mechanisms for managing shares. Windows 8 and Server 2012 offer the SMBShare and SMBWitness modules to manage SMB file servers and SMB file shares. Earlier versions of Windows rely on WMI to accomplish setting up and managing Windows shares.

Having tried both, I'm confident in stating that the new SMB modules offer a number of advantages over the WMI approach. But don't take my word for it. Try them yourself and see what you think.