- Context
- Valid and invalid syntax
- A usefull validation tool
- Now let’s validate
- Limiting to local drives
- Advanced file validation
- More about
Context 
It’s a known best practice to validate all parameters of a function or a script.
So let’s say you want to create a function which will create a file (for example a report) and the user has to provide the path and the name of the file.
How do you validate this parameter?
Valid and invalid syntax 
Valid syntax 
Before I start the list, it is good to know that, technically spoken, both, files and folders, can have an extension and both, files and folders, can be without extension.
Also, if a filesystem object contains multiple periods, the last one is considered as being the extension.
Now, here are the valid patterns for a file:
- Simple names
FileName
- Relative paths
.\FileName
and all the combinations with double periods like
..\..\FileName
- simple folder names
FolderName\Filename
and all the combinations with one or multiple
subfolders likeFolderName\FolderName\FileName
- Drive letters
DriveLetter:\\FileName
and all the combinations with one or multiple
subfolders likeDriveLetter:\\FolderName\FileName
- NetBIOS computer names
\\NetBIOSComputerName\ShareName\FileName
and all the
combinations with one or multiples subfolders like\\NetBIOSComputerName\ShareName\FolderName\FileName
- Fully Qualified Domain Names
\\FQDNComputerName\ShareName\FileName
and all the
combinations with one or multiples subfolders like\\FQDNComputerName\ShareName\FolderName\FileName
- IP adresses
\\IPAddress\ShareName\FileName
and all the combinations with one or
multiples subfolders like\\IPAddress\ShareName\FolderName\FileName
Invalid syntax 
The full list of requirements can be found in an official Microsoft document named Naming Files, Paths, and Namespaces.
Here are some of these requirements:
- Not using reserved characters like:
- <
- >
- :
- “
- |
- ?
- *
- The maximum path length depends on your computer’s settings
- Not using reserved names like
- CON
- PRN
- AUX
- NUL
- COM1
- LPT1
- Not ending the name with a space or a period
A usefull validation tool 
At first glance, this seems to be a huge task to validate all these cases…
But fortunately, we have:
- Native validation attributes
- the
Test-Path
cmdlet
We will come back to the validation attributes later.
Let’s first have a look at the Test-Path
cmdlet.
It validates the syntax of a folder or a file even when this file or folder does not yet exist.
For this purpose, you have to provide the -IsValid parameter.
Now let’s try some validations:
PS C:\> #Relative path: only the file name which means it is in the current folder Test-Path -IsValid -Path MyFile.MyExtension True PS C:\> #Same as above: current folder but explicit Test-Path -IsValid -Path .\MyFile.MyExtension True PS C:\> #Another relative path with the parent folder Test-Path -IsValid -Path ..\MyFile.MyExtension True PS C:\> #Drive letter Test-Path -IsValid -Path C:\SomeSubFolder\MyFile.MyExtension True PS C:\> #NetBIOS name Test-Path -IsValid -Path \\ComputerName\ShareName\SomeSubFolder\MyFile.MyExtension True PS C:\> #FQDN Test-Path -IsValid -Path \\Fully.Qualified.Domain.\ShareName\SomeSubFolder\MyFile.MyExtension True PS C:\> #IPv4 address Test-Path -IsValid -Path \\10.11.12.13\ShareName\SomeSubFolder\MyFile.MyExtension True PS C:\> #Path containing a pipe character Test-Path -IsValid -Path 'C:\SomeSubFolder\My|File.MyExtension' False PS C:\> #Path containing an asterisk Test-Path -IsValid -Path 'C:\SomeSubFolder\My*File.MyExtension' False PS C:\>
So far, so good!
Now let’s try some other stuff…
PS C:\> #Name ending with a period Test-Path -IsValid -Path 'C:\SomeSubFolder\MyFile.' True PS C:\> #Name ending with a space Test-Path -IsValid -Path 'C:\SomeSubFolder\MyFile ' True PS C:\> #IPv6 address Test-Path -IsValid -Path \\2001:db8:85a3:0:0:8a2e:370:7334\ShareName\SomeSubFolder\MyFile.MyExtension False PS C:\> #NetBIOS names are limited to 15 characters Test-Path -IsValid -Path \\TooLongNetBIOSComputerName\ShareName\SomeSubFolder\MyFile.MyExtension True PS C:\>
As you can notice, the Test-Path cmdlet is not bulletproof.
- Names ending with a period or a space are validated
- IPv6 addresses are not accepted
- Too long NetBIOS names are not detected as being invalid
Valid but unwanted syntax 
The Test-Path
also legitimately validates as true other providers than the filesystem.
For example, it also validates paths to objects of the registry and the certificate store.
PS C:\> #Registry path Test-Path -IsValid -Path HKLM:\HiveNAme\KeyName True PS C:\> #Certificate store Test-Path -IsValid -Path Cert:\LocalMachine\My True PS C:\> #Certificate store with invalid subfolder Test-Path -IsValid -Path Cert:\LocalMachine\My False PS C:\>
However, in our case, we only want to validate a file.
Making the difference between a file and a folder 
There is also another quirk with the Test-Path
cmdlet: despite you can use the IsValid and the PathType parameters together, the Test-Path
cmdlet simply ignores the PathType switch and only validates the syntactic path without validating simultaneously the path type.
I have opend a Github issue, and a breaking change is planned in order to put the two parameters in a different parameter set. As a consequence it will not possible to use them together anymore, which will avoid confusion. And meanwhile, I have updated the official documentation under the PathType parameter entry too, in oder to hightlight this specific case.
Anyway, what would differentiate them?
Because like I said in the introduction, both files and folders can be with or without extension, only a possible final backslash can make the difference.
This is a valid folder name but also a valid file name:
C:\Something
This is not a valid filename and can only be a folder name:
C:\Something\
Now let’s validate 
While the Test-Path
is doing most of the work, depending on your own requirements, you have to add some additional validations.
Here are a few examples:
Basic file validation 
In this example, only the basic syntax validation is performed.
We will use the ValidateScript attribute combined with the Test-Path
cmdlet.
function New-ReportFile { Param( [Parameter(Mandatory)] #Basic Syntax validation [ValidateScript( {Test-Path -Path $PSItem -IsValid})] [ValidateNotNullOrEmpty()] [string]$FilePath ) #Doing some stuff before creating the export }
Limiting to local drives 
In our next example, we will also use the ValidatePattern attribute associated with a regular expression.
And here is what we will validate:
- Basic syntactic validation
- The parent folder must exist
- Only drive letters are authorized (no UNC path)
function New-ReportFile { Param( [Parameter(Mandatory)] #Checking if the path is starting with a drive letter [ValidatePattern('^\w:\\\w+')] [ValidateScript( { #Checking if the parent folder exists if (-not(Test-Path -Path (Split-Path -Path $PSItem -Parent))) { Throw 'The parent path does not exist.' } else { $true } } )] #Syntax validation [ValidateScript({Test-Path -Path $PSItem -IsValid})] [ValidateNotNullOrEmpty()] [string]$FilePath ) #Doing some stuff before creating the export }
Note 1 
Concerning the ValidateScript attribute, PowerShell generates an error if the validation script returns anything else than $true.
Note 2 
All validation attributes are processed in the reverse order.
For example, in the following scenario, the String casting is processed first, then the ValidateNotNullOrEmpty attribute, and finally the ValidateScript attribute.
[ValidateScript( {Test-Path -Path $PSItem -IsValid})] [ValidateNotNullOrEmpty()] [string]$FilePath
Advanced file validation 
In the following example you will validate followings:
- Basic syntactic validation
- The parent folder must exist
- Drive letters are authorized but no other providers like registry or certificate store
- UNC paths
- Relative paths
- The path does not end with a backslash (because we expect a file and not a folder), nor a period, nor a space
function New-ReportFile { Param( [Parameter(Mandatory)] #Three regular expressions separated by a pipe meaning: #Local file | UNC path | relative path [ValidatePattern('^\w:\\\w+|^\\\\\w+\\\w+|^\.\.?\\\w+')] #Checking if the parent folder exists [ValidateScript( { if (-not(Test-Path -Path (Split-Path -Path $PSItem -Parent))) { Throw 'The file cannot be created in the path you provided because the folder does not exist.' } else { $true } } )] [ValidateScript( { #Checking the last character of the file path switch ($PSItem[-1]) { '.' {Throw 'A valid filepath cannot end with a period.'} '\' {Throw 'A valid filepath cannot end with a backslash.'} {$PSItem -match '\s'} {Throw 'A valid filepath cannot end with a blank character.'} Default {$true} } } )] #Syntax validation [ValidateScript( {Test-Path -Path $PSItem -IsValid})] [ValidateNotNullOrEmpty()] [string]$FilePath ) #Doing some stuff before creating the export }
Happy file validation!
More about 
Parameter validation with Parameters Attributes (Microsoft Docs)
Oldies but goodies:
Simplify Your PowerShell Script with Parameter Validation (Microsoft Technet blog)
Thank you so much for taking the time to do this write up. It helped me immensely with a project I was working on. I’d consider myself slightly above average when it comes to PowerShell and knowing my way around it for common tasks, but when it came time to work on a script that required stringent input validation I stumbled across this write up and it was SUCH a life saver. With slight modifications here and there to fit my needs it saved me countless hours I would’ve spent stumbling through it. I wouldn’t have known where to start, so having someone else’s code to trace and work backwards allowed me to learn a lot and I wanted to take a moment to thank you.
LikeLiked by 1 person
Thank you for the article! :)
LikeLike