Using CIM to create a shared folder

This topic contains 7 replies, has 3 voices, and was last updated by Profile photo of Rohn Edwards Rohn Edwards 2 years, 6 months ago.

  • Author
    Posts
  • #15264
    Profile photo of JohnRock Bilodeau
    JohnRock Bilodeau
    Participant

    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

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

    Thanks

  • #15288
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    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.

  • #15290
    Profile photo of JohnRock Bilodeau
    JohnRock Bilodeau
    Participant

    Sure. It would be much appreciated.

  • #15292
    Profile photo of JohnRock Bilodeau
    JohnRock Bilodeau
    Participant

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

  • #15296
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    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

  • #19308
    Profile photo of Rohn Edwards
    Rohn Edwards
    Participant

    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
    
  • #15293
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    There's my PowerShell and WMI (http://manning.com/siddaway2)
    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.

  • #15300
    Profile photo of JohnRock Bilodeau
    JohnRock Bilodeau
    Participant

    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
    {
    }
    }

You must be logged in to reply to this topic.