Powershell – WMI: Working with Shares – Part 1: Creating a Share with Custom Permissions

Download Script: NewShareWithPermissions-0.0.1.zip 2.3 KB
Downloaded 105 times

Intro:

Last week, i was teaching a Powershell course (MOC6434), when a student asked me, how to create a share with custom permissions via WMI.

I tried to find an answer for him, but could not really find any examples, cmdlets or functions for it.

I found some examples and help in vbscript, so i decided to make some powershell functions of my own.

I Created these functions that uses the Win32_Share class of WMI. They support remote creation and multiple permissions.

New-Share is for creating a share.

New-ACE is for creating one or more ACEs for use with New-Share

New-SecurityDescriptor is used by New-Share to combine the ACE’s into a SecurityDescriptor for the WMI Method.

Functions:

New-Share

SYNOPSIS
Creates a new Share on local or remote PC. Support for Custom Permissions

SYNTAX
New-Share [-FolderPath] <string> [-ShareName] <string> [-ACEs <Win32_ACE Object>] [-Description <string>] [-Computer] <string>

DETAILED DESCRIPTION
Create new share, either on local or remote PC.

Shares can be created without the use of –ACEs parameter, this will create a share with standard permissions. If the ACEs parameter if used, custom permissions can be added.

PARAMETERS

FolderPath <string>
Specifies the local path for folder that is to be shared.

Required? true
Position? 1
Default value
Accept pipeline input? false
Accept wildcard characters? false

ShareName <string>
Specifies the name of the share.

Please notice that duplicate share names are not supported in windows.

Required? true
Position? 2
Default value
Accept pipeline input? false
Accept wildcard characters? false

ACEs <(Array of) Win32_ACE Objects>
Specifies the custom permissions.
Create the Win32_ACE object by using New-ACE function.
This parameter either support a single object or an array of objects

Required? false
Position? 3
Default value
Accept pipeline input? false
Accept wildcard characters? false

Description <string>
Description for the share

Required? false
Position? 4
Default value
Accept pipeline input? false
Accept wildcard characters? false

Computer <string>
Specify computername for the target computer

Required? false
Position? 4
Default value . (localhost)
Accept pipeline input? false
Accept wildcard characters? false

INPUT TYPE
No Pipe input in version 0.0.1

RETURN TYPE
System.Object
Properties: ReturnCode, Message

————————– EXAMPLE 1 ————————–

$ACE = New-ACE -Name “Domain Users” -Domain “CORETECH” -Permission “Read” -Group
New-Share -FolderPath “C:\Temp” -ShareName “Temp4″  -ACEs $ACE `
–Description “Test Description” -Computer “localhost”

This examples creates one Win32_ACE object that specifies “CORETECH\Domain Users”, to have read permission and use it to share C:\Temp on the local machine.

————————– EXAMPLE 2 ————————–

$ACE = New-ACE -Name “Domain Users” -Domain “CORETECH” -Permission “Read” -Group
$ACE2 = New-ACE -Name “CCO” -Domain “CORETECH” -Permission “Full”
New-Share -FolderPath “C:\Temp” -ShareName “Temp4″  -ACEs $ACE,$ACE2 `
-Description “Test Description” -Computer “localhost”

This examples create both a users ACE and a group ACE, and send them both with the New-Share function

————————– EXAMPLE 3 ————————–

New-Share “C:\Temp” “Temp4″

This is the shortest way to run the function. Using default permissions (which is decided from whom, who run the function). and using position parameters, ignoring all optional parameters

New-ACE

SYNOPSIS
Creates a Win32_ACE object contains permissions for use with New-share (or another function that need Win32_ACE objects)

SYNTAX
New-ACE [-Name] <string> [-Domain] <string> [-Permission] <string> [-Computer <string>] [-Group]

NOTICE

Due to the limitations of the WMI_Account class, it is only possible to get local accounts when connecting to WMI via a remote machine (as far as i know, please contact me if you have input).

This means that usually you should not use the –ComputerName paramter, only use it if you are connecting to a workgroup computer, and using local user/groups for the permissions

PARAMETERS

Name <string>
Specifies the name of the user or group.

Required? true
Position? 1
Default value
Accept pipeline input? false
Accept wildcard characters? false

Domain <string>
Specifies the domain for the user or group.

Required? true
Position? 2
Default value
Accept pipeline input? false
Accept wildcard characters? false

Permission <string>
Specifies the permission granted for the user/group.

Possible values are “Read”, “Change”, “Full”

Required? true
Position? 3
Default value
Accept pipeline input? false
Accept wildcard characters? false

Computername <string>
Specify computername for the target computer to get the user info from.
NB! Please read the Notice above this.

Required? false
Position? 4
Default value . (localhost)
Accept pipeline input? false
Accept wildcard characters? false

Group <switch>
This is a switch parameter, add it if you are creating an ACE for a group instead of a user.

Required? false
Position? 5
Default value
Accept pipeline input? false
Accept wildcard characters? false

INPUT TYPE
No Pipe input in version 0.0.1

RETURN TYPE
Win32_ACE Object

————————– EXAMPLE 1 ————————–

New-ACE -Name “Domain Users” -Domain “CORETECH” -Permission “Read” -Group

This examples creates one Win32_ACE object that specifies “CORETECH\Domain Users”.

————————– EXAMPLE 2 ————————–

$ACE2 = New-ACE -Name “CCO” -Domain “CORETECH” -Permission “Full”

This examples creates a user ACE

New-SecurityDescriptor

SYNOPSIS
Creates a Win32_SecurityDescriptor object from on or more Win32_ACEs, for use by New-Share.

SYNTAX
New-SecurityDescriptor [-ACEs] <[array of] Win32_ACE Object(s)> [-ComputerName <string>]

NOTICE

Due to the limitations of the WMI_Account class, it is only possible to get local accounts when connecting to WMI via a remote machine (as far as i know, please contact me if you have input).

This means that usually you should not use the –ComputerName paramter, only use it if you are connecting to a workgroup computer, and using local user/groups for the permissions

PARAMETERS

ACEs <[Array of] Win32_ACE object(s)>
Specifies one or more Win32ACE object that should be added to the SecurityDescriptor

Required? true
Position? 1
Default value
Accept pipeline input? false
Accept wildcard characters? false

ComputerName <string>
Specify computername for the target computer to get the user info from.
NB! Please read the Notice above this.

Required? false
Position? 2
Default value . (localhost)
Accept pipeline input? false
Accept wildcard characters? false

INPUT TYPE
No Pipe input in version 0.0.1

RETURN TYPE
Win32_SecurityDescriptor Object

————————– EXAMPLE 1 ————————–

$ACE = New-ACE -Name “Domain Users” -Domain “CORETECH” -Permission “Read” –Group
New-SecurityDescriptor $ACE

This examples creates a Win32_SecurityDescriptor from one Win32_ACE object

————————– EXAMPLE 2 ————————–

$ACE = New-ACE -Name “Domain Users” -Domain “CORETECH” -Permission “Read” –Group
$ACE2 = New-ACE -Name “CCO” -Domain “CORETECH” -Permission “Full”
New-SecurityDescriptor $ACE,$ACE2

This examples creates a Win32_SecurityDescriptor from more than one Win32_ACE object

Source:

# //************************************************************************************************************
# // ***** Script Header *****
# //
# // Solution:  Coretech Share Functions
# // File:      NewShareWithPermission.ps1
# // Author:	Jakob Gottlieb Svendsen, Coretech A/S. http://blog.coretech.dk
# // Purpose:
# // New-Share: Creates new Share on local or remote PC, with custom permissions.
# // Required Parameters: FolderPath, ShareName
# //
# // New-ACE: Creates ACE Objects, for use when running New-Share.
# // Required Parameters: Name, Domain
# //
# // New-SecurityDescriptor: used by New-Share to prepare the permissions.
# // Required Parameters: ACEs
#//
# // Usage Examples:
# // New-Share -FolderPath "C:\Temp" -ShareName "Temp" -ACEs $ACE,$ACE2  -Description "Test Description" -Computer "localhost"
# // Sharing of folder C:\Temp, with the Name "Temp". ACE's (Permissions) are sent via the -ACEs parameter.
# // Create them with New-ACE and send one  or more, seperated by comma (or create and array and use that)
# //
# // This is the first in a couple of share-administration scripts i am planning to make and release on the blog.
# //
# // Please comment the blog post, if you have any suggestions, questions or feedback.
# // Contact me if you need us to make a custom script (or cause not for free ;-)  )
# //
# // CORETECH A/S History:
# // 0.0.1     JGS 30/06/2009  Created initial version.
# //
# // Customer History:
# //
# // ***** End Header *****
# //**************************************************************************************************************
#//----------------------------------------------------------------------------
#//  Procedures
#//----------------------------------------------------------------------------

Function New-SecurityDescriptor (
$ACEs = (throw "Missing one or more Trustees"),
[string] $ComputerName = ".")
{
	#Create SeCDesc object
	$SecDesc = ([WMIClass] "\\$ComputerName\root\cimv2:Win32_SecurityDescriptor").CreateInstance()
	#Check if input is an array or not.
	if ($ACEs -is [System.Array])
	{
		#Add Each ACE from the ACE array
		foreach ($ACE in $ACEs )
		{
			$SecDesc.DACL += $ACE.psobject.baseobject
		}
	}
	else
	{
		#Add the ACE
		$SecDesc.DACL =  $ACEs
	}
	#Return the security Descriptor
	return $SecDesc
}

Function New-ACE (
	[string] $Name = (throw "Please provide user/group name for trustee"),
	[string] $Domain = (throw "Please provide Domain name for trustee"),
	[string] $Permission = "Read",
	[string] $ComputerName = ".",
	[switch] $Group = $false)
{
	#Create the Trusteee Object
	$Trustee = ([WMIClass] "\\$ComputerName\root\cimv2:Win32_Trustee").CreateInstance()
	#Search for the user or group, depending on the -Group switch
	if (!$group)
	{ $account = [WMI] "\\$ComputerName\root\cimv2:Win32_Account.Name='$Name',Domain='$Domain'" }
	else
	{ $account = [WMI] "\\$ComputerName\root\cimv2:Win32_Group.Name='$Name',Domain='$Domain'" }
	#Get the SID for the found account.
	$accountSID = [WMI] "\\$ComputerName\root\cimv2:Win32_SID.SID='$($account.sid)'"
	#Setup Trusteee object
	$Trustee.Domain = $Domain
	$Trustee.Name = $Name
	$Trustee.SID = $accountSID.BinaryRepresentation
	#Create ACE (Access Control List) object.
	$ACE = ([WMIClass] "\\$ComputerName\root\cimv2:Win32_ACE").CreateInstance()
	#Select the AccessMask depending on the -Permission parameter
	switch ($Permission)
	{
		"Read" 		 { $ACE.AccessMask = 1179817 }
		"Change"  {	$ACE.AccessMask = 1245631 }
		"Full"		   { $ACE.AccessMask = 2032127 }
		default { throw "$Permission is not a supported permission value. Possible values are 'Read','Change','Full'" }
	}
	#Setup the rest of the ACE.
	$ACE.AceFlags = 3
	$ACE.AceType = 0
	$ACE.Trustee = $Trustee
	#Return the ACE
	return $ACE
}

Function New-Share (
	[string] $FolderPath = (throw "Please provide the share folder path (FolderPath)"),
	[string] $ShareName = (throw "Please provide the Share Name"),
	$ACEs,
	[string] $Description = "",
	[string] $ComputerName=".")
{
	#Start the Text for the message.
	$text = "$ShareName ($FolderPath): "
	#Package the SecurityDescriptor via the New-SecurityDescriptor Function.
	$SecDesc = New-SecurityDescriptor $ACEs
	#Create the share via WMI, get the return code and create the return message.
	$Share = [WMICLASS] "\\$ComputerName\Root\Cimv2:Win32_Share"
	$result = $Share.Create($FolderPath, $ShareName, 0, $false , $Description, $false  , $SecDesc)
	switch ($result.ReturnValue)
	{
		0 {$text += "has been success fully created" }
		2 {$text += "Error 2: Access Denied" }
		8 {$text += "Error 8: Unknown Failure" }
		9 {$text += "Error 9: Invalid Name"}
		10 {$text += "Error 10: Invalid Level" }
		21 {$text += "Error 21: Invalid Parameter" }
		22 {$text += "Error 22 : Duplicate Share"}
		23 {$text += "Error 23: Redirected Path" }
		24 {$text += "Error 24: Unknown Device or Directory" }
		25 {$text += "Error 25: Net Name Not Found" }
	}
	#Create Custom return object and Add results
	$return = New-Object System.Object
	$return | Add-Member -type NoteProperty -name ReturnCode -value $result.ReturnValue
	$return | Add-Member -type NoteProperty -name Message -value $text
	#Return result object
	$return
}

#//----------------------------------------------------------------------------
#//  Main routines
#//----------------------------------------------------------------------------

#Create ACE's for the securitydescriptor for the share:
#a group ACE, containing Group info, please notice the -Group switch
$ACE = New-ACE -Name "Domain Users" -Domain "CORETECH" -Permission "Read" -Group
#a user ACE.
$ACE2 = New-ACE -Name "CCO" -Domain "CORETECH" -Permission "Full"

#Create the share on the local machine
$result = New-Share -FolderPath "C:\Temp" -ShareName "Temp4"  -ACEs $ACE,$ACE2 -Description "Test Description" -Computer "localhost" 

#Output result message from new-share
Write-Output $result.Message

#Check if the share was succesfully created
If ($result.ReturnCode -eq 0)
{
	#Creation was succesfull, put your next code here.
}

#//----------------------------------------------------------------------------
#//  End Script
#//----------------------------------------------------------------------------

Last Words:

Script is version 0.0.1. Tested on Windows Vista and Windows XP local and remote, in my enviroment.

There might be a lot that i have not thought of yet, please comment this post if you have suggestions/bugs/

Next Part:

Next time i will try to look into changing existing shares, adding and remove permissions to the list.

Replacing the permissions seems easy, but editing the existing ones might be a challenge.

I will look at it as soon as i have some spare time :)

As usual, please contact me/us if you need any help on a custom scripting project (at standard price rate of cause).

I hope you all have a nice summer!

6 Comments

  1. James:

    Nice script. I had to modify it slightly for my env though.

    This line:
    $result = $Share.Create($FolderPath, $ShareName, 0, $false , $Description, $false, $SecDesc)

    Changed to this:
    $result = $Share.Create($FolderPath, $ShareName, 0, $null , $Description, $null, $SecDesc)

    The $null’s work better for the optional arguments. Especially argument 4. Maximum users. I found $false was setting it to 0. So nobody could connect to the share.

    James

  2. Jakob Gottlieb Svendsen

    Jakob:

    Great to hear that someone is using my script ;-)

    and thanks alot for the bug fix! :)

  3. Tom:

    Hello!

    Thanks!!! Your script rescued my goal to create a script for sharing folders remote.

    But I’ve two problems.
    - How can I change the “user limit” in the creating share from now 1 user to “maximum allowed”?
    - How can I grant access to the created share to the “Authenticated Users”?

    Maybe you have an idea….

    thanks a lot
    Tom

  4. Jonathan Warnken:

    Nice work!!
    I added support for setting permissions for Everyone and Authenticated users. Also Updated the New-Share function to address the bug James found.

    # //************************************************************************************************************
    # // ***** Script Header *****
    # //
    # // Solution: Coretech Share Functions
    # // File: NewShareWithPermission.ps1
    # // Author: Jakob Gottlieb Svendsen, Coretech A/S. http://blog.coretech.dk
    # // Purpose:
    # // New-Share: Creates new Share on local or remote PC, with custom permissions.
    # // Required Parameters: FolderPath, ShareName
    # //
    # // New-ACE: Creates ACE Objects, for use when running New-Share.
    # // Required Parameters: Name, Domain
    # //
    # // New-SecurityDescriptor: used by New-Share to prepare the permissions.
    # // Required Parameters: ACEs
    #//
    # // Usage Examples:
    # // New-Share -FolderPath “C:\Temp” -ShareName “Temp” -ACEs $ACE,$ACE2 -Description “Test Description” -Computer “localhost”
    # // Sharing of folder C:\Temp, with the Name “Temp”. ACE’s (Permissions) are sent via the -ACEs parameter.
    # // Create them with New-ACE and send one or more, seperated by comma (or create and array and use that)
    # //
    # // This is the first in a couple of share-administration scripts i am planning to make and release on the blog.
    # //
    # // Please comment the blog post, if you have any suggestions, questions or feedback.
    # // Contact me if you need us to make a custom script (or cause not for free )
    # //
    # // CORETECH A/S History:
    # // 0.0.1 JGS 30/06/2009 Created initial version.
    # //
    # // Customer History:
    # // 0.0.2 – Jonathan Warnken(jon.warnken@gmail.com) 16/12/2009
    # // – Added logic to all permissions to be set using Everyone and Authenticated Users. These use well known SIDs and are easier to hard code then lookup.
    # // – Usage Examples:
    # // – (Everyone) $ACE = New-ACE -Name “Everyone” -Domain “.” -Permission “Read” -Group
    # // – (Authenticated Users) $ACE = New-ACE -Name “Authenticated Users” -Domain “NT AUTHORITY” -Permission “Read” -Group
    # // –
    # // – Updated New-Share so that -MaxUsers and -Password are optional parameters. The default values are $null.
    # // – Updated the Main Code examples to use an Array for the Access Control objects
    # //
    # // ***** End Header *****
    # //**************************************************************************************************************
    #//—————————————————————————-
    #// Procedures
    #//—————————————————————————-

    Function New-SecurityDescriptor (
    $ACEs = (throw “Missing one or more Trustees”),
    [string] $ComputerName = “.”)
    {
    #Create SeCDesc object
    $SecDesc = ([WMIClass] “\\$ComputerName\root\cimv2:Win32_SecurityDescriptor”).CreateInstance()
    #Check if input is an array or not.
    if ($ACEs -is [System.Array])
    {
    #Add Each ACE from the ACE array
    foreach ($ACE in $ACEs )
    {
    $SecDesc.DACL += $ACE.psobject.baseobject
    }
    }
    else
    {
    #Add the ACE
    $SecDesc.DACL = $ACEs
    }
    #Return the security Descriptor
    return $SecDesc
    }

    Function New-ACE (
    [string] $Name = (throw “Please provide user/group name for trustee”),
    [string] $Domain = (throw “Please provide Domain name for trustee”),
    [string] $Permission = “Read”,
    [string] $ComputerName = “.”,
    [switch] $Group = $false)
    {
    #Create the Trusteee Object
    $Trustee = ([WMIClass] “\\$ComputerName\root\cimv2:Win32_Trustee”).CreateInstance()
    #Check for Special cases Everyone and Authenticated Users)
    switch ($Name.ToUpper()) {
    “EVERYONE” {
    $Trustee.Domain = $Null
    $Trustee.Name = “EVERYONE”
    $Trustee.SID = @(1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0)
    }
    “AUTHENTICATED USERS” {
    $Trustee.Domain = “NT AUTHORITY”
    $Trustee.Name = “Authenticated Users”
    $Trustee.SID = @(1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0)
    }
    default {
    #Search for the user or group, depending on the -Group switch
    if (!$group)
    { $account = [WMI] “\\$ComputerName\root\cimv2:Win32_Account.Name=’$Name’,Domain=’$Domain’” }
    else
    { $account = [WMI] “\\$ComputerName\root\cimv2:Win32_Group.Name=’$Name’,Domain=’$Domain’” }
    #Get the SID for the found account.
    $accountSID = [WMI] “\\$ComputerName\root\cimv2:Win32_SID.SID=’$($account.sid)’”
    #Setup Trusteee object
    $Trustee.Domain = $Domain
    $Trustee.Name = $Name
    $Trustee.SID = $accountSID.BinaryRepresentation
    }
    }
    #Create ACE (Access Control List) object.
    $ACE = ([WMIClass] “\\$ComputerName\root\cimv2:Win32_ACE”).CreateInstance()
    #Select the AccessMask depending on the -Permission parameter
    switch ($Permission)
    {
    “Read” { $ACE.AccessMask = 1179817 }
    “Change” { $ACE.AccessMask = 1245631 }
    “Full” { $ACE.AccessMask = 2032127 }
    default { throw “$Permission is not a supported permission value. Possible values are ‘Read’,'Change’,'Full’” }
    }
    #Setup the rest of the ACE.
    $ACE.AceFlags = 3
    $ACE.AceType = 0
    $ACE.Trustee = $Trustee
    #Return the ACE
    return $ACE
    }

    Function New-Share (
    [string] $FolderPath = (throw “Please provide the share folder path (FolderPath)”),
    [string] $ShareName = (throw “Please provide the Share Name”),
    $ACEs,
    [string] $Description = “”,
    [string] $ComputerName = “.”,
    $MaxUsers = $null,
    $Password = $null)
    {
    #Start the Text for the message.
    $text = “$ShareName ($FolderPath): ”
    #Package the SecurityDescriptor via the New-SecurityDescriptor Function.
    $SecDesc = New-SecurityDescriptor $ACEs
    #Create the share via WMI, get the return code and create the return message.
    $Share = [WMICLASS] “\\$ComputerName\Root\Cimv2:Win32_Share”
    $result = $Share.Create($FolderPath, $ShareName, 0, $MaxUsers, $Description, $Password, $SecDesc)
    switch ($result.ReturnValue)
    {
    0 {$text += “has been success fully created” }
    2 {$text += “Error 2: Access Denied” }
    8 {$text += “Error 8: Unknown Failure” }
    9 {$text += “Error 9: Invalid Name”}
    10 {$text += “Error 10: Invalid Level” }
    21 {$text += “Error 21: Invalid Parameter” }
    22 {$text += “Error 22 : Duplicate Share”}
    23 {$text += “Error 23: Redirected Path” }
    24 {$text += “Error 24: Unknown Device or Directory” }
    25 {$text += “Error 25: Net Name Not Found” }
    }
    #Create Custom return object and Add results
    $return = New-Object System.Object
    $return | Add-Member -type NoteProperty -name ReturnCode -value $result.ReturnValue
    $return | Add-Member -type NoteProperty -name Message -value $text
    #Return result object
    $return
    }

    #//—————————————————————————-
    #// Main routines
    #//—————————————————————————-

    #Create ACE’s for the securitydescriptor for the share:
    #a group ACE, containing Group info, please notice the -Group switch
    $ACE = @(New-ACE -Name “Domain Users” -Domain “CORETECH” -Permission “Read” -Group)
    $ACE += New-ACE -Name “Authenticated Users” -Domain “NT AUTHORITY” -Permission “Read” -Group
    $ACE += New-ACE -Name “everyone” -Domain “.” -Permission “Read” -Group
    #a user ACE.
    $ACE += New-ACE -Name “CCO” -Domain “CORETECH” -Permission “Full”

    #Create the share on the local machine
    $result = New-Share -FolderPath “C:\Temp” -ShareName “Temp4″ -ACEs $ACE -Description “Test Description” -Computer “localhost”

    #Output result message from new-share
    Write-Output $result.Message

    #Check if the share was succesfully created
    If ($result.ReturnCode -eq 0)
    {
    #Creation was succesfull, put your next code here.
    }

    #//—————————————————————————-
    #// End Script
    #//—————————————————————————-

  5. Jakob Gottlieb Svendsen

    Jakob:

    thnx alot!

    i havent had time myself to do it, its great to see other helping out!

    merry christmas to you all.

  6. Joe:

    Thank you so much for this script. It saved me a lot of time.

Leave a comment