Function Execute-Process in 3.2.0 Beta

Topics: Archive - General
Aug 25, 2014 at 4:51 AM
Hi

Found out another potential bug in this function, getting this error,
[25-08-2014 05:26:46] [Uninstallation] Execution failed 
The given path's format is not supported.
Which is caused by this part,
# If the file is in the Files subdirectory of the App Deploy Toolkit, set the full path to the file
If (Test-Path (Join-Path $dirFiles $FilePath -ErrorAction Stop) -ErrorAction Stop) {
        $FilePath = (Join-Path $dirFiles $FilePath -ErrorAction Stop)
}
Shouldn't this be like the below, since "Test-Path" suppose to check whether it's a valid path or not, if yes, then only "Join-Path"?
If (Test-Path (Join-Path $dirFiles $FilePath -ErrorAction Stop)) {
        $FilePath = (Join-Path $dirFiles $FilePath -ErrorAction Stop)
}
Actually i'm not too sure whether i shoud post this in "ISSUES" or in "DISCUSSIONS".

/Andy
Sep 1, 2014 at 7:51 AM
Hi Sean

Are you going to include this fix as well in the release ? It's going to always stop the script if $dirFiles $FilePath is invalid path, whereby it shouldn't because there is already "Test-Path"?

/Andy
Developer
Sep 1, 2014 at 12:55 PM
The -ErrorAction Stop is for error trapping. It will only stop the script if the Test-path cmdlet produces an error when it tries to check the path. If the path doesn't exist, it will not produce an error, it will return $false.
Sep 1, 2014 at 1:38 PM
yes, it is error trapping, so when an invalid path is provided then it is stopping the script entirely, "Execution failed The given path's format is not supported", so it won't return $false and continue the script.
Sep 1, 2014 at 2:11 PM
Edited Sep 2, 2014 at 1:34 AM
Ok, now I know why you don't get this problem, it only happens on PowerShell 2.0 Win7, you can run only this simple line to test,
Execute-Process -FilePath "$envWinDir\System32\cmd.exe" -Arguments "/c" -WindowStyle Hidden
Looks like many codes are not running very well with PowerShell 2.0.
Sep 1, 2014 at 2:21 PM
Edited Sep 1, 2014 at 2:22 PM
Suggestion, since the minimum requirement is PowerShell 2.0, shouldn't we always test with PowerShell 2.0, I'm not good with PowerShell, but I like the ISE in PowerShell 3.0 though. :)
Coordinator
Sep 1, 2014 at 8:32 PM
Hi Andy,

Actually the toolkit was built on PS2.0, we maintain backwards compatibility so don't add any cmdlets that are specific to PS 3.0. I wasn't able to reproduce the error you mentioned on either PS 2.0 or 3.0 but I've included the suggested fix in the 3.2 release.

Thanks,
Sean
Sep 2, 2014 at 1:40 AM
Thanks Sean, but I'm still wondering why you (and mmashwani) don't get the error, I tested again today by downloaded the stable version of 3.2.0.

Then changed the following in AppDeployToolkitMain.ps1
If (Test-Path (Join-Path $dirFiles $FilePath -ErrorAction Stop)) {
To,
If (Test-Path (Join-Path $dirFiles $FilePath -ErrorAction Stop) -ErrorAction Stop) {
Added just one line in Deploy-Application.ps1
Execute-Process -FilePath "$envWinDir\System32\cmd.exe" -Arguments "/c" -WindowStyle Hidden
Then got this in log,
[02-09-2014 03:33:20] [Initialization] Deployment type is [Installation]
[02-09-2014 03:33:20] [Initialization] Script [C:\Temp\3.2.0\AppDeployToolkit\AppDeployToolkitMain.ps1] dot-source invoked by [C:\Temp\3.2.0\Deploy-Application.ps1]
[02-09-2014 03:33:21] [Initialization] No User is logged on
[02-09-2014 03:33:21] [Initialization] Session 0 not detected.
[02-09-2014 03:33:21] [Initialization] Installation is running in [Interactive] mode.
[02-09-2014 03:33:22] [Pre-Installation] Spinning up Progress Dialog in a separate thread with message: [Installation in progress. Please wait...]
[02-09-2014 03:33:23] [Installation] Execution failed 
The given path's format is not supported.
[02-09-2014 03:33:23] [Installation] test_test_1.0_EN_01 Installation completed with exit code [999].
[02-09-2014 03:33:23] [Installation] ----------------------------------------------------------------------------------------------------------
It just worrying if we get different results.

/Andy
Developer
Sep 2, 2014 at 4:11 AM
Hi Andy,

I think we should certainly explore this issue further as well. The error you are seeing is because the file path is somehow malformed by the time Test-Path sees it. I want to make sure that this is not because of something in the TookKit itself. The fact that we took -ErrorAction Stop out of the line simply means that we are choosing to not see the problem. Since it is not a terminating error, as most errors produced by PowerShell cmdlets are not terminating errors, the script continues. However, the error still takes place. We simply are not telling the script to stop when the error happens and thus the catch block does not come into play.

If copying/pasting lines, please make sure there are no malformed lines of text getting in there as some weird encoding issues could potentially cause this issue. Also, can you try the same path but not use the $envWindir variable? Do you still get the error if you do this?
Execute-Process -FilePath "C:\Windows\System32\cmd.exe" -Arguments "/c" -WindowStyle Hidden
Sep 2, 2014 at 5:09 AM
Edited Sep 2, 2014 at 5:11 AM
Hi mmashwani,

Same result, below is the full log and I also Write-Log extra two entries just before the If-Else statement to show what were the values passed to Test-Path.
[02-09-2014 07:04:21] [Installation] dirFiles: C:\Temp\3.2.0\Files
[02-09-2014 07:04:21] [Installation] FilePath: C:\Windows\System32\cmd.exe
[02-09-2014 07:04:18] [Initialization] test_test_1.0_EN_01 setup started.
[02-09-2014 07:04:19] [Initialization] Script [C:\Temp\3.2.0\AppDeployToolkit\AppDeployToolkitExtensions.ps1] dot-source invoked by [C:\Temp\3.2.0\AppDeployToolkit\AppDeployToolkitMain.ps1]
[02-09-2014 07:04:19] [Initialization] test_test_1.0_EN_01 script version is [1.0.0]
[02-09-2014 07:04:19] [Initialization] Deploy Application script version is [3.2.0]
[02-09-2014 07:04:19] [Initialization] App Deploy Toolkit Main script version is [3.2.0]
[02-09-2014 07:04:19] [Initialization] App Deploy Toolkit Extensions version is [1.0.0]
[02-09-2014 07:04:19] [Initialization] PowerShell version is [2.0 x64]
[02-09-2014 07:04:19] [Initialization] PowerShell host is [ConsoleHost version 2.0]
[02-09-2014 07:04:19] [Initialization] OS version is [Microsoft Windows 7 Enterprise  64-bit 6.1.7601]
[02-09-2014 07:04:19] [Initialization] Hardware platform is [Virtual:VMWare]
[02-09-2014 07:04:19] [Initialization] Computer name is [XXXX]
[02-09-2014 07:04:19] [Initialization] Current user is [XXXX\XXXX$]
[02-09-2014 07:04:19] [Initialization] Current Culture is [en-US] and UI language is [EN]
[02-09-2014 07:04:19] [Initialization] Deployment type is [Installation]
[02-09-2014 07:04:19] [Initialization] Script [C:\Temp\3.2.0\AppDeployToolkit\AppDeployToolkitMain.ps1] dot-source invoked by [C:\Temp\3.2.0\Deploy-Application.ps1]
[02-09-2014 07:04:19] [Initialization] No User is logged on
[02-09-2014 07:04:19] [Initialization] Session 0 not detected.
[02-09-2014 07:04:19] [Initialization] Installation is running in [Interactive] mode.
[02-09-2014 07:04:20] [Pre-Installation] Spinning up Progress Dialog in a separate thread with message: [Installation in progress. Please wait...]
[02-09-2014 07:04:21] [Installation] dirFiles: C:\Temp\3.2.0\Files
[02-09-2014 07:04:21] [Installation] FilePath: C:\Windows\System32\cmd.exe
[02-09-2014 07:04:21] [Installation] Execution failed 
The given path's format is not supported.
[02-09-2014 07:04:22] [Installation] test_test_1.0_EN_01 Installation completed with exit code [999].
[02-09-2014 07:04:22] [Installation] ----------------------------------------------------------------------------------------------------------
I don't get this error on PowerShell 3.0 Win7 though.

/Andy
Sep 2, 2014 at 5:19 AM
Edited Sep 2, 2014 at 6:14 AM
Playing around by adding these lines,
Write-Log "dirFiles: $dirFiles"
Write-Log "FilePath: $FilePath"
        
$testResult1 = Test-Path (Join-Path $dirFiles $FilePath -ErrorAction Stop)
Write-Log "testResult1: $testResult1"
$testResult2 = Test-Path (Join-Path $dirFiles $FilePath -ErrorAction Stop) -ErrorAction Stop
Write-Log "testResult2: $testResult2"
Shows that the -ErrorAction Stop for Test-Path won't returns $false but quits the script entirely which is not intended since Test-Path is suppose to check whether the path is valid or not and then continue.
[02-09-2014 07:15:05] [Installation] dirFiles: C:\Temp\3.2.0\Files
[02-09-2014 07:15:05] [Installation] FilePath: C:\Windows\System32\cmd.exe
[02-09-2014 07:15:05] [Installation] testResult1: False
[02-09-2014 07:15:05] [Installation] Execution failed 
The given path's format is not supported.
But really you guys should get the same result as me. :)
Developer
Sep 2, 2014 at 12:50 PM
Edited Sep 2, 2014 at 6:10 PM
So I just woke up and for some strange reason, the answer instantly popped into my head. I guess my brain was still thinking about this while I was sleeping. Strange how that happens sometimes :).

I wasn't getting the error because I was not testing with the whole toolkit. I was selectively copying functions and lines of code to test the problem and thus not doing a proper test. Sorry :).

Anyways, the problem goes away when we do not do error trapping with the -ErrorAction Stop for the Test-Path cmdlet. The reason it goes away is that we simply choose to ignore the problem and rely on the fact that it is not a terminating error to continue forward in the script. However, path validation does not take place when we simply continue forward in such cases. The problem is that the inner parentheses does a join on $dirFiles and $FilePath. If this happens when $FilePath is already a fully qualified path, Test-Path sees a malformed path and thus errors out.

Please change the code as below and see if this issue goes away:
If (Test-Path -Path $FilePath -ErrorAction Stop)
{
    Write-Log "`$FilePath [$FilePath] is a valid path, continue"
}
ElseIf (Test-Path -Path $dirFiles -ErrorAction Stop)
{
    Write-Log "`$dirFiles [$dirFiles] is a valid path"
    $TestJoin = Join-Path -Path $dirFiles -ChildPath $FilePath -ErrorAction Stop
    Write-Log "Joining paths `$dirFiles [$dirFiles] and `$FilePath [$FilePath] produced [$TestJoin]"
    If (Test-Path -Path $TestJoin -ErrorAction Stop)
    {
        $FilePath = $TestJoin
        Write-Log "`$FilePath set to [$TestJoin]"
    }
    Else
    {
        # There is a problem at this part of the script now because if user simply passes
        # the name of a file to $FilePath, such as cmd.exe or msiexec.exe, which resides in a
        # system directory, path validation does not take place. If user passes the name of an
        # exe which does not exist in any of the paths in the $env:Path environment variables,
        # script will continue and error out below when we try to start the process. It will,
        # however, succeed if the exe actualy does exist in one of the $env:Path directories.
        # The only way to get around this would be to go through each directory in the
        # $env:Path environment variable directory to look for the exe to see if it exists
        # there. If it does not, then throw terminating error
    }
}
Else
{
    Write-Log "`$dirFiles [$dirFiles] is not a valid path"
}
Also, please try passing the full path to a file which does not exist and see what happens. I think that for certain paths which do not actually exist, this funciton will not do path validation. For example, pass this path to the function: C:\Windows\System32\nonsense.exe
Developer
Sep 2, 2014 at 5:19 PM
Edited Sep 2, 2014 at 6:18 PM
I wrote some code to take care of the concerns I had in the above post where certain paths passed to the $FilePath variable were not being validated before attempting execution. The below code will expand file names to their fully qualified path if they exist in one of the folders in the Path environment variable. The code can also handle instances where a user may not specify the file extension for the file. In that instance, we will search for the file using the file extension in the PathExt environment variable (which is what Windows does as well).

Please use the below code and I think this should take care of this reported issue:
# Validate and find the fully qualified path for the $FilePath variable.
# If the file is in the Files subdirectory of the script directory, set the full path to the Files subdirectory.
If (Test-Path -Path $FilePath -ErrorAction Stop)
{
    Write-Log "`$FilePath [$FilePath] is a valid path, continue"
}
Else
{
    # The first directory to search will be the 'Files' subdirectory of the script directory
    [array]$PathFolders     = $dirFiles
    # Add the current location of the console (Windows always searches this location first)
    [array]$PathFolders     = $PathFolders + (Get-Location).Path
    # Then search all of the paths in the Path environment variable; make sure we only have unique folder locations
    [array]$PathFolders    += $($env:PATH).Trim(';')    | %{$_.Split(';')} | %{ $_.Trim() } | %{ $_.Trim('"') } | Select-Object -Unique
    # File extensions, in the order that Windows uses to search for files, for when the file extension is not specified
    [array]$PathExtensions  = $($env:PATHEXT).Trim(';') | %{$_.Split(';')} | %{ $_.Trim() } | %{ $_.Trim('"') } | %{ $_.ToLower()} | Select-Object -Unique
    # Initialize variable
    [string]$FullyQualifiedPath
    
    # If the $FilePath variable contains a file extension
    If ($FilePath.Contains('.'))
    {
        ForEach ($Folder in $PathFolders)
        {
            If (-not [string]::IsNullOrEmpty($Folder))
            {
                If (Test-Path -Path (Join-Path -Path $Folder -ChildPath $FilePath -ErrorAction Stop) -ErrorAction Stop)
                {
                    $FullyQualifiedPath = Join-Path -Path $Folder -ChildPath $FilePath -ErrorAction Stop
                    Break
                }
            }
        }
    }
    # If the $FilePath variable does not contain a file extension
    Else
    {
        ForEach ($Folder in $PathFolders)
        {
            If (-not [string]::IsNullOrEmpty($Folder))
            {
                ForEach ($Extension in $PathExtensions)
                {
                    If (-not [string]::IsNullOrEmpty($Extension))
                    {
                        If (Test-Path -Path (Join-Path -Path $Folder -ChildPath $($FilePath + $Extension) -ErrorAction Stop) -ErrorAction Stop)
                        {
                            $FullyQualifiedPath = Join-Path -Path $Folder -ChildPath $($FilePath + $Extension) -ErrorAction Stop
                            Break
                        }
                    }
                }
            }
        }
    }
    
    If (-not [string]::IsNullOrEmpty($FullyQualifiedPath))
    {
        Write-Log "`$FilePath [$FilePath] successfully resolved to fully qualified path [$FullyQualifiedPath]"
        $FilePath = $FullyQualifiedPath
    }
    Else
    {
        Write-Log "`$FilePath [$FilePath] contains an invalid path"
        Throw "`$FilePath [$FilePath] contains an invalid path"
    }
}
Sep 4, 2014 at 12:43 AM
Edited Sep 4, 2014 at 12:44 AM
Hi mmashwani

The codes become longer now just for this small part. :)

Tested the above code with an invalid executable,
Execute-Process -FilePath "C:\Windows\System32\nonsense.exe" -Arguments "/c" -WindowStyle Hidden
Error being trapped in this If-Else statement,
If (Test-Path -Path (Join-Path -Path $Folder -ChildPath $FilePath -ErrorAction Stop) -ErrorAction Stop)
{
    $FullyQualifiedPath = Join-Path -Path $Folder -ChildPath $FilePath -ErrorAction Stop
    Break
}
Script terminated immediately with same error,
[04-09-2014 02:34:02] [Installation] Execution failed 
The given path's format is not supported.
Hmm, "Test-Path" with "-ErrorAction Stop" always doesn't seem like able to return $true or $false with PS 2.0. :)

/Andy
Developer
Sep 4, 2014 at 4:44 PM
Edited Sep 4, 2014 at 4:45 PM
Yup, got the same result as you when using that path. Same issue as before when we were actually passing a valid path. It is trying to Test-Path after joining two fully qualified paths so the path is malformed. I agree, the code has gotten quite long, but it has some solid error trapping and expands files to the fully qualified path if they are in a directory from the Path environment variable (this will also ensure that we have a WorkingDirectory when we execute such executables).

Also, I don't think that "Test-Path" with -ErrorAction Stop is the problem. The fact that we pass a path to the cmdlet which has two fully qualified paths makes it fail. For example, we are passing a path such as this to it when it fails: "C:\scriptFolder\Files\C:\Windows\System32\nonsense.exe".

Anyways, I think the below code should now resolve all of the various scenarios you've been breaking this function with :).
        ## Validate and find the fully qualified path for the $FilePath variable.
        ## If the file is in the Files subdirectory of the script directory, set the full path to the Files subdirectory.
        If (Test-Path -Path $FilePath -ErrorAction 'Stop')
        {
            Write-Log "`$FilePath contains a valid path [$FilePath], continue"
        }
        Else
        {
            # The first directory to search will be the 'Files' subdirectory of the script directory
            [array]$PathFolders     = $dirFiles
            # Add the current location of the console (Windows always searches this location first)
            [array]$PathFolders     = $PathFolders + (Get-Location).Path
            # Then search all of the paths in the Path environment variable; make sure we only have unique folder locations
            [array]$PathFolders    += $($env:PATH).Trim(';')           |
                                      ForEach-Object { $_.Split(';') } |
                                      ForEach-Object { $_.Trim() }     |
                                      ForEach-Object { $_.Trim('"') }  |
                                      Select-Object -Unique
            # File extensions, in the order that Windows uses to search for files, for when the file extension is not specified
            [array]$PathExtensions  = $($env:PATHEXT).Trim(';')        |
                                      ForEach-Object { $_.Split(';') } |
                                      ForEach-Object { $_.Trim() }     |
                                      ForEach-Object { $_.Trim('"') }  |
                                      ForEach-Object { $_.ToLower() }  |
                                      Select-Object -Unique
            # Initialize variable
            [string]$FullyQualifiedPath
            
            # If the $FilePath variable contains a file extension
            If ($FilePath.Contains('.') -and (-not $FilePath.Contains(':')))
            {
                ForEach ($Folder in $PathFolders)
                {
                    If (-not [string]::IsNullOrEmpty($Folder))
                    {
                        If (Test-Path -Path (Join-Path -Path $Folder -ChildPath $FilePath -ErrorAction 'Stop') -ErrorAction 'Stop')
                        {
                            $FullyQualifiedPath = Join-Path -Path $Folder -ChildPath $FilePath -ErrorAction 'Stop'
                            Break
                        }
                    }
                }
            }
            # If the $FilePath variable does not contain a file extension
            ElseIf (-not $FilePath.Contains(':'))
            {
                ForEach ($Folder in $PathFolders)
                {
                    If (-not [string]::IsNullOrEmpty($Folder))
                    {
                        ForEach ($Extension in $PathExtensions)
                        {
                            If (-not [string]::IsNullOrEmpty($Extension))
                            {
                                If (Test-Path -Path (Join-Path -Path $Folder -ChildPath $($FilePath + $Extension) -ErrorAction 'Stop') -ErrorAction 'Stop')
                                {
                                    $FullyQualifiedPath = Join-Path -Path $Folder -ChildPath $($FilePath + $Extension) -ErrorAction 'Stop'
                                    Break
                                }
                            }
                        }
                    }
                }
            }
            
            If (-not [string]::IsNullOrEmpty($FullyQualifiedPath))
            {
                Write-Log "`$FilePath with value [$FilePath] successfully resolved to fully qualified path [$FullyQualifiedPath]"
                $FilePath = $FullyQualifiedPath
            }
            Else
            {
                Write-Log "`$FilePath contains an invalid path [$FilePath]"
                Throw "`$FilePath contains an invalid path [$FilePath]"
            }
        }
Oct 6, 2014 at 11:16 AM
I'm seeing the same error when actually using test-path:

if(test-path -Path "C:\Program Files\QGIS Dufour\Uninstall-QGIS.exe")
{
#write-host "yay"
Execute-Process -FilePath "C:\Program Files\QGIS Dufour\Uninstall-QGIS.exe" -Arguments "/S" -ContinueOnError $true
this doesn't work with psappdeploytoolkit but works if I move it to an empty script.
Developer
Oct 7, 2014 at 1:33 PM
Does the error go away if you use the updated code I posted right about your post?
Developer
Nov 4, 2014 at 12:14 AM
The fix for this issue has been integrated into the toolkit with the latest 3.5.0 release.