Add Script Flexibility with Relative File Paths

Using relative file paths for your PowerShell script’s supporting files ensure it continues to work when the script and files get moved around. Here’s how.

Unpredictable File Paths

My first intensive use of PowerShell was for scripting software installations/configurations in SCCM packages. It seemed like every situation called for something above and beyond a straightforward MSI install.

I would develop and test the SCCM package on my local system with a sandbox VM prior to deploying in SCCM. The SCCM client would then download that package to a cached location on each individual client. From development to deployment, I couldn’t guarantee the absolute file path of my script and any supporting files would be constant. I needed my script to be able to figure out where the supporting files were in relation to itself.

The newer way: $PSScriptRoot

$PSScriptRoot is an Automatic Variable, which are built-in variables that contain information about the PowerShell environment itself. $PSScriptRoot contains the directory path of the script being executed currently. Originally $PSScriptRoot was only applicable to script modules (.psm1), but beginning with PowerShell 3.0, it works for all PowerShell script files.

From the console if I type $PSScriptRoot and press ENTER, it returns nothing. $PSScriptRoot only gets a value when a script is executing, and then is destroyed when the script terminates.

PowerShell

1

2

3

4

5

6

7

PSC:\Users\aaron>$PSScriptRoot

PSC:\Users\aaron>

Let’s say I have a script and config file sitting in C:\TEMP\DemoScript. I would concatenate the value of $PSScriptRoot with a backslash and the config file name:

PowerShell

1

2

3

4

5

6

7

# DemoScript.ps1

$configFilePath=$PSScriptRoot+"\DemoFile.cfg"

Write-Host$configFilePath

If I run DemoScript.ps1, you see $PSScriptRoot gets a value and correctly builds the config file path:

PowerShell

1

2

3

4

5

PSC:\Users\aaron>C:\TEMP\DemoScript\DemoScript.ps1

C:\TEMP\DemoScript\DemoFile.cfg

Now let’s move the DemoScript directory to C:\TEMP\NewRootFolder and run DemoScript.ps1 again. The config file path is built correctly to accommodate the file move.

PowerShell

1

2

3

4

5

PSC:\Users\aaron>C:\TEMP\NewRootFolder\DemoScript\DemoScript.ps1

C:\TEMP\NewRootFolder\DemoScript\DemoFile.cfg

This requires all supporting files be in the same folder as the script or a child folder.

The old way: $MyInvocation.MyCommand.Path

I started on the SCCM packages prior to PowerShell 3.0, when $PSScriptRoot didn’t exist. Instead, we parsed the script path from another Automatic Variable, $MyInvocation.MyCommand.Path, and used it in the same fashion as $PSScriptRoot.

PowerShell

1

2

3

4

5

6

7

# DemoScript.ps1

$scriptPath=split-path-parent$MyInvocation.MyCommand.Path

$configFilePath=$scriptPath+"\DemoFile.cfg"

Write-Host$configFilePath

PowerShell

1

2

3

4

5

PSC:\Users\aaron>C:\TEMP\NewRootFolder\DemoScript\DemoScript.ps1

C:\TEMP\NewRootFolder\DemoScript\DemoFile.cfg

It works, but it requires an extra line of code and isn’t as simple as $PSScriptRoot, but it is an option on any legacy systems you may have that don’t have PowerShell 3.0.

Make your script resilient with $PSScriptRoot

Use $PSScriptRoot to build absolute file paths from relative ones to make your script and supporting files portable and resilient. As part of this, it is a good practice to create a new folder to become that common root when you start developing a new script that will have supporting files.

Comments

Hello Aaron,
First, I like your Beginner series! very informative. I have a question regarding copying security groups and distribution lists between users and security groups? Is there a mechanism to do this all at once? For example, If I want to copy all the group memberships of user A to security group B is that possible? or would I have to do it separately?

Ross – thank you for pointing out that useful cmdlet! There is an example of where I had been using concatenation for so long I didn’t think to look to see if a better option had been implemented. I will be sure to use this going forward!

Hello Thanks for the article,
I would want to know if i write something like this in a script $srvlist = Import-Csv -Path ($PSScriptRoot + “\testvmlistupdated.csv”)
which is placed in c:/user/xxx/Desktop/temp, would that mean powershell will import the the file from the folder where the script being excicuted?