Download Script: [download id=”12″]

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. https://blog.ctglobalservices.com
# // 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!