Trouble validating file path in PowerShell script - powershell

I am having some trouble validating a file input in a script. Using the following function
Function pathtest {
[CmdletBinding()]
param (
[ValidateScript({
if (-Not ($_ | Test-Path -PathType Leaf) ) {
throw "The Path argument must be a file. Folder paths are not allowed."
}
return $true
})]
[System.IO.FileInfo]$Path
)
Write-Host ("Output file: {0}" -f $Path.FullName)
}
Then I call the function with these two input files
pathtest -Path c:\temp\test.txt
pathtest -Path c:\temp\test.csv
The first one (test.txt) returns the path, but the second one (test.csv) returns an error:
PS C:\> pathtest c:\it\test.txt
Output file: c:\it\test.txt
PS C:\> pathtest c:\it\test.csv
pathtest : Cannot validate argument on parameter 'Path'. The Path argument must be a file. Folder paths are not
allowed.
At line:1 char:10
+ pathtest c:\it\test.csv
+ ~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [pathtest], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,pathtest
Any idea what's up with this?

Test-Path -Leaf also returns $false when the specified path doesn't exist.
Therefore, you need to test for existence separately, so as to distinguish between an existing path that happens to be a folder and a non-existent path.
Try the following instead:
Function pathtest {
[CmdletBinding()]
param (
[ValidateScript({
$item = Get-Item -ErrorAction Ignore -LiteralPath $_
if (-not $item) {
throw "Path doesn't exist: $_"
}
elseif ($item.PSIsContainer) {
throw "The Path argument must be a file. Folder paths are not allowed."
}
return $true
})]
[System.IO.FileInfo] $Path
)
Write-Host ("Output file: {0}" -f $Path.FullName)
}

Related

How do I pass multiple variables to a powershell function?

Just laying this out there. Here is my code for downloading an application and installing it.
# File Download and Install Function
function FDL($url){
# set to the default download directory; obviously can be wherever one wants
$DL = set-location $env:USERPROFILE\downloads\
# using this to capture just the filename
$FN = $url -split("/")
$FD = $FN[$FN.Length-1]
# Download File
Start-BitsTransfer -source $url -destination $DL\$FD
# Install File
Start-Process -NoNewWindow $DL\$FD -ArgumentList $args
}
PS:> FDL "https://www.kymoto.org/downloads/ISStudio_Latest.exe"
This function works perfectly every time assuming that the URL is correct!
Then I thought, what if I were to have the functionality to place the correct arguments for the installer type. So I came up with this:
# File Download and Install Function
function FDL($url,$p){
# set to the default download directory; obviously can be whereever one wants
$DL = set-location $env:USERPROFILE\downloads\
# using this to capture just the filename
$FN = $url -split("/")
$FD = $FN[$FN.Length-1]
switch ($p){
1 {" /passive /qb /norestart";break}
2 {" /sp- /silent /norestart /SUPPRESSMSGBOXES /CURRENTUSERS /NORESTART /NOCANCEL /FORCECLOSEAPPLICATION /RESTARTAPPLICATIONS";break}
3 {" /SILENT";break}
4 {" /quiet";break}
5 {" /S";break}
6 {" /Q";break}
}
Start-BitsTransfer -source $url -destination $DL\$FD
Start-Process -NoNewWindow $DL\$FD -ArgumentList $p
}
# 2 because this is an InnoSetup installer type
PS:> FDL 'https://www.kymoto.org/downloads/ISStudio_Latest.exe', 2
FAIL
Start-BitsTransfer : The number of items specified in the Source parameter do not match the number of items specified in the Destination parameter. Verify that the same
number of items is specified in the Source and Destination parameters.
At [dir]\FileDownloader Function.ps1:17 char:1
+ Start-BitsTransfer -source $url -destination $DL\$FD
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Start-BitsTransfer], ArgumentException
+ FullyQualifiedErrorId : StartBitsTransferArgumentException,Microsoft.BackgroundIntelligentTransfer.Management.NewBitsTransferCommand
Start-BitsTransfer :
At [dir]\FileDownloader Function.ps1:17 char:1
+ Start-BitsTransfer -source $url -destination $DL\$FD
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Start-BitsTransfer], Exception
+ FullyQualifiedErrorId : System.Exception,Microsoft.BackgroundIntelligentTransfer.Management.NewBitsTransferCommand
Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null or empty. Provide an argument that is not null or empty, and then try the
command again.
At [dir]\FileDownloader Function.ps1:20 char:50
+ Start-Process -NoNewWindow $DL\$FD -ArgumentList ($p)
+ ~~~~
+ CategoryInfo : InvalidData: (:) [Start-Process], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartProcessCommand
No matter how I adjust this code it comes out with the same error. Any suggestions or assistance here will be greatly appreciated!
Might not be exactly what you're looking for but it should give you a hint as to approach the code for a function in PowerShell.
A few pointers, parameters in PowerShell are either Positional or Named, about_Parameters explains both concepts. Most importantly, each argument is separated by a space and not by a comma.
You can parse an URL using the Uri Class, so, for getting the file name from your address, is as simple as:
# Last segment from this Uri (index -1 from the segment array)
([uri] 'https://www.kymoto.org/downloads/ISStudio_Latest.exe').Segments[-1]
-ArgumentList from Start-Process expects string[], you can pass an array of arguments instead of a single string as shown in Example 7.
You're never capturing the output from your switch ($p), which explains the error:
Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null or empty.
A hash table can be used instead of a switch.
Lastly, I have added a -PassThru switch, now if you call the function with the switch activated (DownloadFile -PassThru -Uri ...), the function will output the Process instance representing the started process.
function DownloadFile {
[cmdletbinding()]
param(
[parameter(Mandatory)]
[uri] $Uri,
[parameter()]
[ValidateSet(1,2,3,4,5,6)]
[int] $Arguments,
[parameter()]
[string] $Destination = "$env:USERPROFILE\Downloads",
[parameter()]
[switch] $PassThru
)
$arg = #{
1 = '/passive', '/qb', '/norestart'
2 = #(
'/sp-', '/silent', '/norestart', '/SUPPRESSMSGBOXES'
'/CURRENTUSERS', '/NORESTART', '/NOCANCEL'
'/FORCECLOSEAPPLICATION', '/RESTARTAPPLICATIONS'
)
3 = '/SILENT'
4 = '/quiet'
5 = '/S'
6 = '/Q'
}
$destFile = Join-Path $Destination -ChildPath $Uri.Segments[-1]
Start-BitsTransfer -Source $Uri -Destination $destFile
$param = #{
FilePath = $destFile
ArgumentList = $arg[$Arguments]
NoNewWindow = $true
PassThru = $PassThru.IsPresent
}
Start-Process #param
}
DownloadFile -Uri 'https://www.kymoto.org/downloads/ISStudio_Latest.exe' -Arguments 2

Dynamically created parameter arguments for PowerShell's Get-ChildItem

Long story short, I'm trying to dynamically use a parameter -Directory or -File in PowerShell's Get-ChildItem. Guess what? I'm unable to.
Here's the deal (note: pseudo-code):
Param(
[string]$filter = $(throw "Error: name"),
[string]$type = $(throw "error: file or directory")
)
if( $type -eq "file" ) {
$mode = '-File'
}
elseif( $type -eq "directory" ) {
$mode = '-Directory'
}
Function Find_Plugin_folder {
Write-Host "look for: '$($filter)'"
Invoke-Command -ComputerName (Get-Content servers.txt ) -ScriptBlock {
(Get-ChildItem -Path "z:\www" -Depth 5 $Using:mode -Filter $Using:filter -Recurse ) | %{$_.FullName}
} -ThrottleLimit 80
}
Find_Plugin_folder
$Using:mode is where it throws an error, either:
PS C:\Users\janreilink> v:\test.ps1 vevida-optimizer file
look for: 'vevida-optimizer'
A positional parameter cannot be found that accepts argument '-File'.
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
+ PSComputerName : webserver-01.example.net
Or
PS C:\Users\janreilink> v:\test.ps1 vevida-optimizer directory
look for: 'vevida-optimizer'
A positional parameter cannot be found that accepts argument '-Directory'.
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
+ PSComputerName : webserver-01.example.net
I've been reading about Dynamic Parameter sets all afternoon, but can't wrap my head around it yet. Any points are much (much, much) appreciated.
You'll want to use splatting for this instead. Start by creating a hashtable with some or all of the parameters you want to pass:
$dynamicArgs = #{}
if( $type -eq "file" ) {
$dynamicArgs['File'] = $true
}
elseif( $type -eq "directory" ) {
$dynamicArgs['Directory'] = $true
}
Then, inside Invoke-Command, prefix the variable name with # to indicate that you want to "splat" the arguments:
Get-ChildItem -Path "z:\www" -Depth 5 #Using:dynamicArgs -Filter $Using:filter -Recurse
If the splatting table contains the key File with a value of $true, it's the equivalent of adding -File:$true on the command line, and vice versa for the Directory argument

Powershell ZIP CopyHere counteracting asynchronous behavior

Within Powershell, the CopyHere method for the Shell-Application Namespace is asynchronous. My main goal with this is to convert a KML file to a KMZ file. The process of doing this is to create a ZIP file with the same name, copy the KML into the KMZ (compresses the file) and then rename the ZIP to KMZ. Unfortunately, being asynchronous means the rename function is being called before the CopyHere method is completed. I have found many examples of solving this. The cleanest one I found is below:
$kmlPath = $global:directoryPath + "Test.kml"
$zip = $global:directoryPath + "Test.zip"
New-Item $zip -ItemType file
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zip)
$zipPackage.CopyHere($kmlPath, 16)
while($zipPackage.Items().Item($zip.Name) -Eq $null)
{
start-sleep -seconds 1
write-host "." -nonewline
}
write-host "."
Rename-Item -Path $zip -NewName $([System.IO.Path]::ChangeExtension($zip, ".kmz"))
This responds with the following error:
Exception calling "Item" with "1" argument(s): "Not implemented
(Exception from HRESULT: 0x80004001 (E_NOTIMPL))"
+ while($zipPackage.Items().Item($zip.Name) -Eq $null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
Am I misusing the Item method for this particular package? I am confused why something that "appears" to be neatly done is not working. I have also tried the snippet of code provided Here. It also complains about the .Item method in this particular situation.
The issue i ran into was trying to find away to check on zip status.
So instead i did a trigger for a while that would fire ...If the Zipfile was openable and the File name was inside.
function kml_to_kmz([string]$kmlPath){
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
$kmlInfo = Get-ChildItem -Path $kmlPath
$zipLocation = ($kmlInfo.Directory.FullName + '\' + $kmlInfo.Name.Remove($kmlInfo.Name.LastIndexOf('.')) + '.zip')
New-item $zipLocation
((new-object -com shell.application).NameSpace($zipLocation)).CopyHere($kmlPath, 16)
$trigger = $false
while ($trigger -eq $false){
try{
$zip = [IO.Compression.ZipFile]::OpenRead($zipLocation)
If(($zip.Entries | %{$_.Name}) -contains $kmlInfo.Name){
$zip.Dispose();
$trigger = $true
break;
}
}catch{}
start-sleep -seconds 1
write-host "." -nonewline
}
[IO.Compression.ZipFile]::OpenRead($zipLocation).Dispose()
Rename-Item -Path $zipLocation -NewName $([System.IO.Path]::ChangeExtension($zipLocation, '.kmz'))
}
kml_to_kmz -kmlPath "C:\Users\Default\Desktop\Test.kml"

Need help in writing EICAR content in a utf8 file using PowerShell

Basically I have file in my Windows machine of UTF8 code. Similar to this path directory and file "G:\这是一\个令人沮". And I am trying to write EICAR string (X5O!P%#AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*) to the file using PowerShell.
For regular file path it is being done like following:
#Base64 of Eicar string
[string] $EncodedEicar = 'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo='
if (!(Test-Path -Path $FilePath)) {
try {
[byte[]] $EicarBytes = [System.Convert]::FromBase64String($EncodedEicar)
[string] $Eicar = [System.Text.Encoding]::UTF8.GetString($EicarBytes)
Set-Content -Value $Eicar -Encoding ascii -Path $FilePath -Force
}
Tried the following suggestions didnt help :(
Executed powershell.exe "& {Import-Module C:\PROGRA~1\WindowsPowershell\Modules\new_eicar.psm1 -Force -DisableNameChecking ;New-Eicar -Path 'N:\这是一\个令人沮' -FileName 'eicar.com'; if ($?) {exit 0} else {exit 1}}". Result: {'status': 1, 'stderr': '', 'stdout': 'New-Eicar : Cannot validate argument on parameter \'Path\'. The "Test-Path $_ \r\n-PathType \'Container\'" validation script for the argument with value \r\n"N:\\\x8a\xa8T\x91~_\x84,?\\\x84,\xa6\x84\xaf\x0f\x84\xa7\xa7\x91\xfdr" did not return a result of True. Determine why the \r\nvalidation script failed, and then try the command again.\r\nAt line:1 char:116\r\n+ ... ew-Eicar -Path \'N:\\\x8a\xa8T\x91~_\x84,?\\\x84,\xa6\x84\xaf\x0f\x84\xa7\xa7\x91\xfdr\' -FileName \'eicar.com\'; if \r\n($?) {exit ...\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n + CategoryInfo : InvalidData: (:) [New-Eicar], ParameterBindingVa \r\n lidationException\r\n + FullyQualifiedErrorId : ParameterArgumentValidationError,New-Eicar\r\n \r\n'}
2017-08-30 23:31:12 DEBUG ssh.py:228 (TB) 10.5.22.4>> 'powershell.exe "& {Import-Module C:\PROGRA~1\WindowsPowershell\Modules\new_eicar.psm1 -Force -DisableNameChecking ;New-Eicar -Path 'N:\这是一\个令人沮' -FileName 'eicar.com'; if ($?) {exit 0} else {exit 1}}"', timeout: 300
2017-08-30 23:31:13 DEBUG ssh.py:306 (TB) 10.5.22.4<< '{
status: 1,
stderr: ,
stdout: New-Eicar : Cannot validate argument on parameter 'Path'. The "Test-Path $_
-PathType 'Container'" validation script for the argument with value
"N:\T~_,?\,r" did not return a result of True. Determine why the
validation script failed, and then try the command again.
At line:1 char:116
+ ... ew-Eicar -Path 'N:\T~_,?\,r' -FileName 'eicar.com'; if
($?) {exit ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [New-Eicar], ParameterBindingVa
lidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,New-Eicar
}'

Test-Path returns exception when testing if location exists

I am trying to check if a location exists before copying it, using Test-Path. As I understand it this should return true or false, depending on if the location exists.
My code
if (Test-Path $updatedsource) {
Copy-Item $updatedsource $record.Target -Recurse -Container -Force
}
Instead of true or false, this line is throwing an exception
Test-Path : An object at the specified path
\\wnasdce03\SECENV_PROD-01\Users\Results\thorn-01.00rc3\ICEVol_BC_20160715
does not exist
At \\VFIPPFIL005\EMT_Projects\Vimmi\SE\CoypHistoryData.ps1:31 char 22
+ if (Test-Path <<<< $updatedsource) {
+ CategoryInfo: ObjectNotFound: (\\wnasdce03\SEC...tC_20160716:String) [Test-Path1. IOException
+ FullyQualifiedErrorId : ItemDoesNotExist.Microsoft.Powershell.Commands.TestPathCommand
Why am I getting an exception that the location doesn't exist? Surely this should just return false?
You want to change your erroraction for this command as below:
if (Test-Path $updatedsource -ErrorAction SilentlyContinue)
{
Copy-Item $updatedsource $record.Target -Recurse -Container -Force
}