Using CIM to create a shared folder

Hi,

I’m trying to create a shared folder in PowerShell with the Everyone group set to Full Control. I’ve previously done this in the past using WMI, but I would like to try and do this using CIM

This is what i have so far and it works to create the shared folder

$FolderName = "Test" $Path = "C:\Temp" $ShareName = 'Test$' $Description = "Test Share Folder"

if (-not(Test-Path -Path “$Path$FolderName”)){
New-Item -Path $Path -Name $FolderName -Type Directory -Verbose
}

$DiskDrive = [uint32]0

Invoke-CimMethod -ClassName Win32_Share -MethodName Create -Arguments @{Path=“$Path$FolderName”; Name=“$FolderName”; Type=$DiskDrive; Description=“$Description”}

The issue that i’m having is figuring how to use the Access Argument.

I tried this

New-CimInstance -ClassName Win32_Trustee -Property @{Name="Everyone";} New-CimInstance -ClassName Win32_ACE -Property @{AccesMask=[UInt32]2032127; AceFlags=[UInt32]3; AceType=[UInt32]0; Trustee=$Trustee}

But i get these errors

PS H:> New-CimInstance -ClassName Win32_Trustee -Property @{Name=“Everyone”;}
New-CimInstance : Attempt to put an instance with no defined key.
At line:1 char:1

  • New-CimInstance -ClassName Win32_Trustee -Property @{Name=“Everyone”;}
  •   + CategoryInfo          : NotSpecified: (Win32_Trustee:CimInstance) [New-CimInstance], CimException
      + FullyQualifiedErrorId : HRESULT 0x80041089,Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand
    
    

PS H:>
PS H:> New-CimInstance -ClassName Win32_ACE -Property @{AccesMask=[UInt32]2032127; AceFlags=[UInt32]3; AceType=[UInt32]
0; Trustee=$Trustee}
New-CimInstance : Could not infer CimType from the provided .NET object.
At line:1 char:1

  • New-CimInstance -ClassName Win32_ACE -Property @{AccesMask=[UInt32]2032127; AceF …
  •   + CategoryInfo          : InvalidOperation: (Microsoft.Manag...InstanceCommand:NewCimInstanceCommand) [New-CimInst
     ance], ArgumentException
      + FullyQualifiedErrorId : New-CimInstance,Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand</code>
    
    

any suggestions. I tried Google but i haven’t found any example of using CIM to create a share while specifying access.

Thanks

Unfortunately, this is a non-trivial task. Its something I’ve been meaning to look at for a while so if you can give me a bit of time I’ll see if I can come up with an answer.

Sure. It would be much appreciated.

btw do you know if there are any books on powershell and cim?

There’s my PowerShell and WMI (PowerShell and WMI)
It was written in the early days of PowerShell v3 and has chapters specifically on the CIM cmdlets and CDXML. The bulk of the book use Get-WmiObject and the other WMI cmdlets. The download code supplies versions using WMI & CIM cmdlets.

I’ve got this function working to add a standard share permission (read, change or full control) to a share

#requires -Version 3.0
function Add-SharePermission {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]$sharename,

[string]$domain = $env:COMPUTERNAME,

[Parameter(Mandatory=$true)]
[string]$trusteeName,

[Parameter(Mandatory=$true)]
[ValidateSet(“Read”, “Change”, “FullControl”)]
[string]$permission = “Read”,

[string]$computername = $env:COMPUTERNAME
)

switch ($permission) {
‘Read’ {$accessmask = 1179817}
‘Change’ {$accessmask = 1245631}
‘FullControl’ {$accessmask = 2032127}
}

$tclass = [wmiclass]“\$computername\root\cimv2:Win32_Trustee”
$trustee = $tclass.CreateInstance()
$trustee.Domain = $domain
$trustee.Name = $trusteeName

$aclass = [wmiclass]“\$computername\root\cimv2:Win32_ACE”
$ace = $aclass.CreateInstance()
$ace.AccessMask = $accessmask
$ace.AceFlags = 0
$ace.AceType = 0
$ace.Trustee = $trustee

$shss = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter “Name=‘$sharename’” -ComputerName $computername
$sd = Invoke-WmiMethod -InputObject $shss -Name GetSecurityDescriptor |
select -ExpandProperty Descriptor

$sclass = [wmiclass]“\$computername\root\cimv2:Win32_SecurityDescriptor”
$newsd = $sclass.CreateInstance()
$newsd.ControlFlags = $sd.ControlFlags

foreach ($oace in $sd.DACL){$newsd.DACL += $oace}
$newsd.DACL += $ace

$share = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter “Name=‘$sharename’”
$share.SetSecurityDescriptor($newsd)

} # end function

I needed to drop back to the WMI cmdlets to make it work. The reason you can’t create a Win32_ACE instance with New-CimInstance is that the class doesn’t appear to have a key which the cmdlet expects.

Have a look at the function. Its late where I am. I’ll put up a blog post explaining the function tomorrow and provide a link.
Hope this helps

Thanks Richard. It’s too bad that it wasn’t doable with CIM.

I ended up using some of the info in your example and some for an example i found on the Microsoft script gallery and came up with this.

<# .Synopsis To create a shared a folder on local or remote Computer. .DESCRIPTION To share a folder on local or remote Computer using WMI. Access Type is Read by default. Different Access can be set with the -Access Parameter. The share is created with maximum number of connections by default, but can be changed with the -MaxConnection parameter. If the Folder Path doesn't exist, this script will attempt to create it.

.PARAMETER ComputerName
Specifies the name of the local or remote computer(s) where you want to share a folder.
The default is the local Computer

.PARAMETER FolderPath
Specifies the path to the location of the folder to share. The path must be fully qualified; relative paths or paths that contain wildcard characters are not permitted.
This is a required parameter

.PARAMETER ShareName
Specifies a name for the new share.
This is a required parameter

.PARAMETER Description
Specifies an optional description of the new share.
The default value no description, or an empty description

.PARAMETER AccessType
The type of access that you wish to grant to for the user to the share.
Can be set to “Allow”, “Deny”.
Default is “Allow”

.PARAMETER Access
The share permision. Can be one of the set “None”,“Read”,“Modify”,“Full”.
Default is “Read”

.PARAMETER Users
Specifies which accounts are granted permission to the share
If adding a domain user make sure that the user is in the following format DOMAIN\Username
This is a required parameter

.PARAMETER MaxConnections
Specifies the maximum number of concurrently connected users that the new share may accommodate.
If this parameter is set to zero (0), then the number of users is unlimited.
The default value is zero (0).

.EXAMPLE
New-Share -FolderPath ‘C:\Temp’ -ShareName ‘Temp’ -Description ‘Test shared folder’ -AccessType Allow -Access Full -Users ‘Everyone’

Create a share named Temp for the C:\Temp folder with Allow Full control for the Everyone group

.EXAMPLE
New-Share -FolderPath ‘C:\Temp’ -ShareName ‘Temp’ -Description ‘Test shared folder’ -AccessType Deny -Access Full -Users ‘Everyone’

Create a share named Temp for the C:\Temp folder with Deny Full control for the Everyone group

.EXAMPLE
New-Share -ComputerName ‘MyServer’ -FolderPath ‘C:\Temp’ -ShareName ‘Temp’ -Description ‘Test shared folder’ -AccessType Deny -Access Full -Users ‘MyDomain\TestUser’

Create a share named Temp for the C:\Temp folder on a remote computer named MyServer, with Allow Full control for the The user named TestUser on the MyDomain domain.

.NOTES
This Function was written based on the example Set-LHSFileShare.ps1 found at http://gallery.technet.microsoft.com/scriptcenter/To-share-a-folder-or-ff92b3fb
and example provided by Richard Siddaway https://powershell.org/forums/topic/using-cim-to-create-a-shared-folder/

AUTHOR: John-Rock Bilodeau
LASTEDIT: 16/05/2014
KEYWORDS: Share, Foldershare  

.INPUTS
System.String, you can pipe ComputerNames to this Function

.OUTPUTS
System.Boolean, True when the given folder could be shared.

.LINK
Create method of the Win32_Share class
http://msdn.microsoft.com/en-us/library/aa389393(v=vs.85).aspx

Win32_ACE class
http://msdn.microsoft.com/en-us/library/aa394063(v=vs.85).aspx

#>
function New-Share
{
[CmdletBinding()]
[OutputType([bool])]
Param
(
[Parameter(Position=0,Mandatory=$False,ValueFromPipeline=$True,
HelpMessage=‘An array of computer names. The default is the local computer.’)]

    [string[]]$ComputerName = $Env:COMPUTERNAME,

    [Parameter(Position=1, Mandatory=$false,HelpMessage="No folder path specified")]
    [string]$FolderPath,
   
    [Parameter(Position=2, Mandatory=$true, 
        HelpMessage="No share name specified")]
    [string]$ShareName,

    [Parameter(Position=3,HelpMessage="No description specified")]
    [string]$Description,

    [Parameter(Position=4)]
    [ValidateSet('Allow','Deny','Audit')]
    [String]$AccessType = "Allow",

    [Parameter(Position=5)]
    [ValidateSet('None','Read','Modify','Full')]
    [String]$Access = "Read",

    [Parameter(Position=6, Mandatory=$True)]
    [String[]]$Users,

    [Parameter(Position=7)]
    [UInt32]$MaxConnections = 0

)

Begin
{

    # Set the AccessMask
    [UInt32]$AccessMask = Switch($Access) {

        'None'   {'1'}
        'Read'   {'1179817'}
        'Modify' {'1245631'}
        'Full'   {'2032127'}

    }

    # Set the AceType
    [UInt32]$AceTypeAccess = Switch($AceType) {

        'Allow' {'0'}
        'Deny'  {'1'}
        #'Audit' {'2'} possibly for future use

    }


    # Define the Ace Flag values
    [UInt32]$ACEFLAG_OBJECT_INHERIT_ACE = 1
    [UInt32]$ACEFLAG_CONTAINER_INHERIT_ACE = 2
    [UInt32]$ACEFLAG_NO_PROPAGATE_INHERIT_ACE = 4
    [UInt32]$ACEFLAG_INHERIT_ONLY_ACE = 8
    [UInt32]$ACEFLAG_INHERITED_ACE = 16
    [UInt32]$ACEFLAG_VALID_INHERIT_FLAGS = 31
    [UInt32]$ACEFLAG_SUCCESSFUL_ACCESS_ACE_FLAG = 64
    [UInt32]$ACEFLAG_FAILED_ACCESS_ACE_FLAG = 128

    # Should almost always be 3. Really. don't change it.
    [UInt32]$AceFlag = $ACEFLAG_OBJECT_INHERIT_ACE + $ACEFLAG_CONTAINER_INHERIT_ACE

    # Set the ShareType
    [String]$Type = 'Disk Drive'

    # I simply listed all the share type to give me the flexibility to expand the function easily in the future.
    [UInt32]$ShareType = switch($Type){

        'Disk Drive' {'0'}
        'Print Queue' {'1'}
        'Device' {'2'}
        'IPC' {'3'}
        'Disk Drive Admin' {'2147483648'}
        'Print Queue Admin' {'2147483649'}
        'Device Admin' {'2147483650'}
        'IPC Admin' {'2147483651'}

    }
    
}
Process
{
    # Loop through each Computer name specified
    foreach($Computer in $ComputerName){

        # Loop through each user
        foreach($user in $Users){
        
            # If username is specified in Domain\username format split it
            if($User.Contains("\")){
                $Domain = ($User -split "\\")[0]
                $Username = ($User -split "\\")[1]
            } else {
                $Domain = $null
                $Username = $User
            }

            # Create the Trustee on the specified computer
            $Trustee = ([WMIClass] "\\$Computer\root\cimv2:Win32_Trustee").CreateInstance()
            $Trustee.Name = $Username
            $Trustee.Domain = $Domain

            # Create the ACE on the specified computer
            $Ace = ([WMIClass] "\\$Computer\root\cimv2:Win32_ACE").CreateInstance() 
            $Ace.AccessMask = $AccessMask
            $Ace.AceFlags = $AceFlag
            $Ace.AceType = $AceTypeAccess
            $Ace.Trustee = $Trustee

            # Create the Security Descriptor on the specified computer
            $SD = ([WMIClass] "\\$Computer\root\cimv2:Win32_SecurityDescriptor").CreateInstance()
            $SD.DACL = @()
            $SD.DACL += $Ace.psObject.baseobject

            # Check if the share exists on the specified computer
            if (!(Get-WmiObject -Class Win32_Share -ComputerName $Computer -Filter "Name='$ShareName'"))
            {   

                # Check if the directory exist at the specified path.
                # If not create it    
                [String]$UncPath = "\\$Computer\$($FolderPath.Replace(':','$'))"
                if(!(Test-Path -Path "$UncPath")){
                    New-Item -Path $UncPath -ItemType Directory | Out-Host $null
                }

                # Create the Share
                $mc = [WmiClass]"\\$Computer\root\cimv2:Win32_share"
                $InParams = $mc.psbase.GetMethodParameters("Create")
                $InParams.Access = $SD
                $InParams.Description = $Description
                $InParams.MaximumAllowed = $MaxConnections
                $InParams.Name = $ShareName
                $InParams.Password = $null
                $InParams.Path = $FolderPath
                $InParams.Type = $ShareType
                
                $Return = $Null
                $Return = $mc.PSBase.InvokeMethod("Create", $InParams, $Null)

                # Check if it was successfull
                $rvalue = Switch ($Return.returnvalue) {
                    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"}
                    Default {"Unknown Error"}
                }
     
                if ($Return.returnvalue -ne 0) 
                {
                    Write-Error ("Failed to create share {0} for {1} on {2}. Error: {3}" -f $ShareName,$FolderPath,$Computer,$rvalue) 
                    Write-Output $False
                }
                else 
                {
                    Write-Host "Successfully shared folder [$FolderPath] on [$Computer] as [$ShareName] ."
                    Write-Output $True
                }

            } else {

                Write-Error "The share $ShareName already exists on $Computer"

	        }

        }

    }

    
}
End
{
}

}

I came across this same error while trying to work with CIM, and I wanted to share the solution I came up with. The trick is using a CimSession (even if it’s to the local computer):

# If you already have a CimSession that you used to get the security descriptor, you can leave this line
# out and use the existing one:
$CimSession = New-CimSession localhost

# Create the Win32_Trustee instance
$Trustee = New-Object ciminstance $CimSession.GetClass("root/cimv2", "Win32_Trustee")
$Trustee.Name = "Everyone"

# Create the Win32_ACE instance
$Ace = New-Object ciminstance $CimSession.GetClass("root/cimv2", "Win32_ACE")
$Ace.AceType = [uint32] [System.Security.AccessControl.AceType]::AccessAllowed
$Ace.AccessMask = [uint32] 2032127
$Ace.AceFlags = [uint32] [System.Security.AccessControl.AceFlags]::None
$Ace.Trustee = $Trustee

# Do something with the $Ace here

# Cleanup
$CimSession | Remove-CimSession