How to validate a new file parameter in PowerShell

  1. Context
  2. Valid and invalid syntax
  3. A usefull validation tool
  4. Now let’s validate
  5. Limiting to local drives
  6. Advanced file validation
  7. 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 like

    FolderName\FolderName\FileName
  • Drive letters
    DriveLetter:\\FileName

    and all the combinations with one or multiple
    subfolders like

    DriveLetter:\\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)

Naming Files, Paths, and Namespaces (Microsoft Docs)

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s