none
Powershell crash with Set-Acl and XAML form

    Question

  • I made a graphical interface with XAML and PowerShell to set some ACL and everytime I run the command Set-Acl it crashes.My XAML have a few textbox and the one I'm using here is serviceTB (it's the department for which I wan to create a root folder) and folderTB being the folder name. The function getGPath is a custom function to retrieve the UNC path of the department.Here is the function I am calling with the last line causing the issue (PowerShell does not respond and crash ISE+console), do you have an idea why?

    function createRGroup{
        # Création du groupe AD
        New-ADGroup -name "CONTOSO\$($WPFserviceTB.Text)ACL$($WPFfolderTB.Text)R" -path $ouData -GroupScope Global -ErrorAction Stop
        # Modification de la description
        $description = "L:\$($WPFfolderTB.Text) W access"
        Set-ADGroup -identity "CONTOSO\$($WPFserviceTB.Text)ACL$($WPFfolderTB.Text)R" -Description $description
       
        sleep(10)
        # Set des ACL
        $path = getGPath $WPFserviceTB.Text
        $Acl = Get-ACL -Path $path
        $ADObject = New-Object System.Security.Principal.NTAccount("CONTOSO\$($WPFserviceTB.Text)ACL$($WPFfolderTB.Text)R")
        $permissions = 'ReadAndExecute, Synchronize'
        $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule($ADObject, $permissions, $InheritanceDefaultFlag, $PropagationFlag, $objType)
        $Acl.AddAccessRule($objACE)
        ################# Crash the script
        Set-Acl -path $path -AclObject $Acl
    }

    Global variables:

    $InheritanceDefaultFlag = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
    $PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None
    $objType =[System.Security.AccessControl.AccessControlType]::Allow


    Wednesday, January 9, 2019 1:49 PM

Answers

  • Well that's more helpful than nothing.  You are running the WPF form in a runspace?  How is the code being called? 

    It sounds like you are getting an error but not detecting it because there is no error detection in the script you posted.

    Without an example that fails there is no way to guess at the cause of the issue.  If you had programming experience you would know this.  I also suggest that you need to add tracing capability to test the variables being used.

    Start with the following and change the call parameters to real strings and test at a prompt to be sure the call works correctly.

    function Create-RGroup{
        param (
            [Parameter(Mandatory = $true)]
            $GroupName,
            [Parameter(Mandatory = $true)]
            $Domain,
            [Parameter(Mandatory = $true)]
            $FolderPath,
            [Parameter(Mandatory = $true)]
            $Description,
            [Parameter(Mandatory = $true)]
            $OUPath
        )
        
        $ErrorActionPreference = 'Stop'
        
        Try{
            
            New-ADGroup -name $GroupName -Path $OUPath -GroupScope Global -Description $description
            
            $acl = Get-Acl -Path $FolderPath
            
            $ntAccount = New-Object System.Security.Principal.NTAccount("$Domain\$GroupName")
            $ace = New-Object System.Security.AccessControl.FileSystemAccessRule($ntAccount, 'ReadAndExecute,Synchronize', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
            $acl.AddAccessRule($ace)
            
            Set-Acl -path $FolderPath -AclObject $ace
        }
        Catch {
            Throw $_
        }
        
    }
    
    Create-RGroup -GroupName "$($WPFserviceTB.Text)ACL$($WPFfolderTB.Text)R" -Domain Contoso -Path "L:\$($WPFfolderTB.Text) W access" -Description "L:\$($WPFfolderTB.Text) W access"

    When you are sure this works with no errors or issues then try to call it from your form.

    One bad idea is copying dissimilar bits of code that you do  not fully understand and trying to paste them together.  This can lead to some very unusual outcomes.


    \_(ツ)_/


    • Edited by jrv Wednesday, January 9, 2019 4:52 PM
    • Marked as answer by alex-df Thursday, January 10, 2019 9:43 AM
    Wednesday, January 9, 2019 4:52 PM

All replies

  • Not enough information.

    You have failed to show the error message.


    \_(ツ)_/

    Wednesday, January 9, 2019 2:18 PM
  • Not enough information.

    You have failed to show the error message.


    \_(ツ)_/

    It's difficult to show an error message when there isn't one.

    Nothing in event viewer, it's just the application crashing.


    Wednesday, January 9, 2019 3:20 PM
  • If there is no error  then how do you know it is "crashing".  It sounds like it is just exiting. 

    There is absolutely no way to guess at what you are doing.  I also highly recommend that you do not use XAML/WPF if you do not have good programming experience.


    \_(ツ)_/

    Wednesday, January 9, 2019 3:25 PM
  • If there is no error  then how do you know it is "crashing".  It sounds like it is just exiting. 

    There is absolutely no way to guess at what you are doing.  I also highly recommend that you do not use XAML/WPF if you do not have good programming experience.


    \_(ツ)_/

    You look like you're assuming I'm a total ignorant by default.

    I perfectly know it's not just ending because PowerShell just crash with (Program do not respond). It's a WPF form, it won't end automaticaly if I Don't explicitly write an exit code.

    If I comment the line set-acl the program just keep running perfectly, so I know by fact that it is this command that crash my instance.

    Wednesday, January 9, 2019 4:24 PM
  • Well that's more helpful than nothing.  You are running the WPF form in a runspace?  How is the code being called? 

    It sounds like you are getting an error but not detecting it because there is no error detection in the script you posted.

    Without an example that fails there is no way to guess at the cause of the issue.  If you had programming experience you would know this.  I also suggest that you need to add tracing capability to test the variables being used.

    Start with the following and change the call parameters to real strings and test at a prompt to be sure the call works correctly.

    function Create-RGroup{
        param (
            [Parameter(Mandatory = $true)]
            $GroupName,
            [Parameter(Mandatory = $true)]
            $Domain,
            [Parameter(Mandatory = $true)]
            $FolderPath,
            [Parameter(Mandatory = $true)]
            $Description,
            [Parameter(Mandatory = $true)]
            $OUPath
        )
        
        $ErrorActionPreference = 'Stop'
        
        Try{
            
            New-ADGroup -name $GroupName -Path $OUPath -GroupScope Global -Description $description
            
            $acl = Get-Acl -Path $FolderPath
            
            $ntAccount = New-Object System.Security.Principal.NTAccount("$Domain\$GroupName")
            $ace = New-Object System.Security.AccessControl.FileSystemAccessRule($ntAccount, 'ReadAndExecute,Synchronize', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
            $acl.AddAccessRule($ace)
            
            Set-Acl -path $FolderPath -AclObject $ace
        }
        Catch {
            Throw $_
        }
        
    }
    
    Create-RGroup -GroupName "$($WPFserviceTB.Text)ACL$($WPFfolderTB.Text)R" -Domain Contoso -Path "L:\$($WPFfolderTB.Text) W access" -Description "L:\$($WPFfolderTB.Text) W access"

    When you are sure this works with no errors or issues then try to call it from your form.

    One bad idea is copying dissimilar bits of code that you do  not fully understand and trying to paste them together.  This can lead to some very unusual outcomes.


    \_(ツ)_/


    • Edited by jrv Wednesday, January 9, 2019 4:52 PM
    • Marked as answer by alex-df Thursday, January 10, 2019 9:43 AM
    Wednesday, January 9, 2019 4:52 PM
  • Well that's more helpful than nothing.  You are running the WPF form in a runspace?  How is the code being called? 

    It sounds like you are getting an error but not detecting it because there is no error detection in the script you posted.

    Without an example that fails there is no way to guess at the cause of the issue.  If you had programming experience you would know this.  I also suggest that you need to add tracing capability to test the variables being used.

    Start with the following and change the call parameters to real strings and test at a prompt to be sure the call works correctly.

    function Create-RGroup{
        param (
            [Parameter(Mandatory = $true)]
            $GroupName,
            [Parameter(Mandatory = $true)]
            $Domain,
            [Parameter(Mandatory = $true)]
            $FolderPath,
            [Parameter(Mandatory = $true)]
            $Description,
            [Parameter(Mandatory = $true)]
            $OUPath
        )
        
        $ErrorActionPreference = 'Stop'
        
        Try{
            
            New-ADGroup -name $GroupName -Path $OUPath -GroupScope Global -Description $description
            
            $acl = Get-Acl -Path $FolderPath
            
            $ntAccount = New-Object System.Security.Principal.NTAccount("$Domain\$GroupName")
            $ace = New-Object System.Security.AccessControl.FileSystemAccessRule($ntAccount, 'ReadAndExecute,Synchronize', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
            $acl.AddAccessRule($ace)
            
            Set-Acl -path $FolderPath -AclObject $ace
        }
        Catch {
            Throw $_
        }
        
    }
    
    Create-RGroup -GroupName "$($WPFserviceTB.Text)ACL$($WPFfolderTB.Text)R" -Domain Contoso -Path "L:\$($WPFfolderTB.Text) W access" -Description "L:\$($WPFfolderTB.Text) W access"

    When you are sure this works with no errors or issues then try to call it from your form.

    One bad idea is copying dissimilar bits of code that you do  not fully understand and trying to paste them together.  This can lead to some very unusual outcomes.


    \_(ツ)_/


    Thanks for your valuable time.

    I didn't copy any code, I did the whole script by myself, I'm experienced with PowerShell since years but not in an academical way, I just self learned what I needed. I make a variety of scripts, using try catch as well but I probably don't have enough high level debugging experience.

    This is what I'm using to run the form:

    $async = $Form.Dispatcher.InvokeAsync({
        $Form.ShowDialog() | out-null
    })
    $async.Wait() | Out-Null

    So I made a set of test from your function and I finally found the issue. It was with the $path variable that was the root path and not the folder path when I tried the code from the console I didn't call the GetGPath function and therefore didn't realise my mistake, as simple as that. Anyway I like your function that is more industry standard than mine and I now use it for both R and RW groups .

    Thanks again for your time and quick solution.

    Here is my non-academic code just for info, I just replaced my domain and path by default values:

    https://github.com/alex-df/Temp/blob/master/script01.ps1

    Working with a label as a log was probably a dumb choice but I didn't have the time to work and understand a more advanced log window.





    • Edited by alex-df Thursday, January 10, 2019 10:01 AM
    Thursday, January 10, 2019 9:23 AM
  • Don't use InvokeAsync.  It will cause many issues with the code.  Also check the error stack for any exceptions that will have been thrown.

    PowerShell is not capable of correctly running top level forms.  This causes the use of "Show() and "InvokeAsync()" to fail in unusual ways.

    If you just use ShowDialog() all errors will be available in the main PowerShell.

    Here is a safe way to run an independent form that will return errors when it closes.

    Add-Type -AssemblyName PresentationCore, PresentationFramework
    $syncHash = [hashtable]::Synchronized(@{})
    $newRunspace =[runspacefactory]::CreateRunspace()
    $newRunspace.ApartmentState = "STA"
    $newRunspace.ThreadOptions = "ReuseThread"         
    $newRunspace.Open()
    $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)          
    $psCmd = [PowerShell]::Create().AddScript({   
        [xml]$xaml = @"
        <Window
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            x:Name="Window" Title="Test Text" WindowStartupLocation = "CenterScreen"
            Width = "500" Height = "160" ShowInTaskbar = "True">
    
                <Grid>
     <TextBlock x:Name ="textblock" TextWrapping = "Wrap" VerticalAlignment="Top" HorizontalAlignment="Center" Height="20"  Margin="0,10,0,0">Test Text</TextBlock>
    <TextBox x:Name = "textbox" IsReadOnly = "true" VerticalAlignment="Top" HorizontalAlignment="Center" Height="20"  Margin="0,30,0,0"/>
    <ProgressBar Minimum="0" Maximum="100" Name="pbStatus" IsIndeterminate="True" Width = "450" Height = "20" VerticalAlignment="Bottom" Margin="0,0,0,40"/>
        </Grid>
    
        </Window>
    "@
    
        $reader=(New-Object System.Xml.XmlNodeReader $xaml)
        $syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )
        $syncHash.TextBox = $syncHash.window.FindName("textbox")
        $syncHash.Window.ShowDialog() | Out-Null
        $syncHash.Error = $Error
    })
    
    $psCmd.Runspace = $newRunspace
    $data = $psCmd.BeginInvoke()

    Here is how to set values on forms controls or on the form.

    # this writes data to the form
    $action= [action]{
    	$syncHash.Window.Title = $title
    	$syncHash.TextBox.Text = 'Hello World'
    }
    $syncHash.textbox.Dispatcher.invoke($action,'Normal')

    This is how to call methods on the form or form controls.

    #this calls a method on the form
    $action = [action]{$syncHash.Window.Close()}
    $syncHash.textbox.Dispatcher.invoke($action, 'Normal')

    To check errors in the form after it has closed we can do this:

    $syncHash.Error


    \_(ツ)_/


    • Edited by jrv Thursday, January 10, 2019 10:37 AM
    Thursday, January 10, 2019 10:33 AM
  • Looks like a better way to load a form thanks, I'll keep this template for my next project.

    About the InvokAsync I used this workaround because I had the exact same issue as describe in this post:

    https://gist.github.com/altrive/6227237

    But I didn't know another way of working with XAML forms.

    Thursday, January 10, 2019 12:05 PM
  • Yeah.  It is a bad workaround.  The Window has been destroyed and the runspace is not re-initialized.  It may work at times but will not be stable.

    XAML Windows are not reusable because of the ShowDialog().  To re-display wrap the whole thing in a function and call the function each time.

    WinForms are reusable and do not have many of the ugly behaviors of WPF forms when used in PowerShell.  They are also much easier to build and use though not as pretty.


    \_(ツ)_/

    Thursday, January 10, 2019 12:22 PM