Create Folder If Not Exists in PowerShell

📜Part of the PowerShell Complete Guide, every PowerShell post on this site, grouped by the job you are doing.

Creating folders programmatically can streamline tasks and ensure that required directory structures are in place.

In this guide, we explore a simple yet effective PowerShell script to check if a folder exists and create it if it doesn’t. By leveraging the Test-Path and New-Item commands, you can automate this task for any directory you need.

Key Topics Covered:
> Using Test-Path to Check for Folder Existence
> Creating a Folder with New-Item
> Customizing the Script for Different Use Cases

1. Using Test-Path to Check if a Folder Exists

The first step is to determine if the folder already exists using the Test-Path command. This command returns a boolean value: True if the path exists, and False otherwise.

Here’s the basic syntax to check for a folder:

# Define the folder path
$path = "C:\temp\"

# Check if folder exists
If (!(Test-Path $path)) {
    Write-Host "Folder does not exist."
}

$path:
Stores the folder path to check.

!(Test-Path $path):
The ! operator negates the result. The condition evaluates to True only if the folder does not exist.

Write-Host:
Outputs a message to the console for feedback.

2. Creating a Folder with New-Item

If the folder does not exist, you can create it using New-Item. This command allows you to specify the type of item (in this case, a directory) and the location for creation.

Here’s the full script to check for and create a folder:

# Define the folder path
$path = "C:\temp\"

# Check if folder exists
If (!(Test-Path $path)) {
    Write-Host "Folder does not exist."
}

3. Customizing the Script for Different Folders

You can easily adapt this script to check and create different folders by modifying the $path variable.

For example, to check for and create the folder C:\myfolder\:

# Define the custom folder path
$path = "C:\myfolder\"

# Check and create folder if not exists
If (!(Test-Path $path)) {
    New-Item -ItemType Directory -Force -Path $path
    Write-Host "Folder created at $path."
} Else {
    Write-Host "Folder already exists."
}

By modifying the $path variable, you can use this script to check for and create any folder you need.

PowerShell Create Folder if Not Exist

Additional Tips:

Dynamic Paths:
You can use environment variables or parameters to dynamically define folder paths.$userProfilePath = "$env:USERPROFILE\Documents\NewFolder"

Error Handling:
Use Try-Catch blocks to handle unexpected errors when creating folders.

Automation:
Integrate the script into larger automation tasks for efficient file management.

If SQL Server is part of your day job, my current work lives over at sqldba.blog: production DBA scripts, an error library and the SSMS guide.

The One Thing This Guard Does Not Protect You From

The Test-Path check looks like it covers you. There is one case where it does the opposite, and it is the case most likely to bite a deployment script.

If a file already exists at that path, Test-Path returns True. Not “True, but it is a file”. Just True. So the guard decides the folder is already there, skips creation, and every later write into $path\something fails with “Could not find a part of the path”. The error surfaces somewhere else entirely, which is what makes it expensive to find.

It gets worse if you remove the guard and lean on -Force. Running New-Item -ItemType Directory -Force against a path where a file already exists does not error. Tested on PowerShell 7.6: it returned the existing FileInfo object, left the file exactly as it was, and created no directory at all. The script reports success and nothing was deployed.

One parameter fixes the guard:

if (-not (Test-Path -LiteralPath $path -PathType Container)) {
    $null = New-Item -ItemType Directory -Path $path -Force
}

-PathType Container makes the test mean “is there a directory here”, which is the question you were actually asking. -LiteralPath and the $null = are explained below, and both matter more than they look.

Square Brackets in the Path Will Lie to You

-Path treats [ and ] as wildcard characters, so a folder whose name genuinely contains brackets is not tested literally. This is not exotic: OneDrive and browsers produce names like report [1].pdf all the time.

Tested on 7.6 with a real directory named t[1]: Test-Path "...\t[1]" returned False even though the directory was sitting right there. Then, once a directory called t1 was created beside it, the same command returned True, because [1] is a wildcard set matching the single character 1. It was answering about the wrong directory entirely.

-LiteralPath told the truth in both cases. Use it whenever the path came from anywhere you do not control.

Two Things That Bite Straight After This Works

The returned object leaks into your function’s output. New-Item emits the DirectoryInfo it created onto the pipeline. Inside a function, that object silently becomes part of the return value, so a function that was supposed to return a count or a status hands back a directory object as well. Assigning to $null, as above, is the usual fix. This is the single most common way a working script starts returning something the caller does not expect.

Do not carry -Force over to files. Having learned that -Force makes “already exists” errors go away on directories, the natural next step is to use it on files. It does something completely different there. Tested on 7.6: New-Item -ItemType File -Force against an existing 15 byte file truncated it to 0 bytes, with no warning and no confirmation. On a directory -Force is harmless and returns the existing folder with its contents intact. On a file it is destructive.

Do You Even Need the Check?

Mostly, no, and it is worth knowing why rather than just deleting it.

  • New-Item -ItemType Directory -Force is already idempotent on directories. Run it against a folder that exists and it returns that folder, contents untouched.
  • It already creates missing parents. New-Item -ItemType Directory C:\x\a\b\c builds the whole chain. Tested on both 7.6 and 5.1.
  • Check-then-create has a race. Two parallel jobs can both pass the test and both call New-Item. Harmless here, but it is the same shape as bugs that are not harmless elsewhere.

So the guard buys you very little, except in the one case people assume it covers: a file sitting where the folder should be. Keep it if you write it as -PathType Container. Drop it otherwise.

A Note on $env:USERPROFILE\Documents

If you are building a path under Documents, that literal is not reliable. With OneDrive Known Folder Move, which consumer Windows 10 and 11 setups prompt for by default, the real Documents folder is %USERPROFILE%\OneDrive\Documents. Building the path by hand creates a folder outside the synced location, which looks correct locally and quietly is not.

$docs = [Environment]::GetFolderPath('MyDocuments')
$path = Join-Path $docs 'NewFolder'

That asks Windows where Documents actually is, rather than assuming.

One Version Difference Worth Knowing

If $path is empty or unset, the two PowerShell versions disagree. Windows PowerShell 5.1 throws “Cannot bind argument to parameter ‘Path’ because it is an empty string”, which at least stops you. PowerShell 7.6 quietly returns False, so the script sails past the guard and fails at New-Item instead, one step further from the actual cause. Both behaviours tested this week. If the path comes from a parameter or a config file, validate it before you get here.


Looking for SQL Server?

This site started as a SQL Server blog in 2017 and the archive is still here. The new SQL Server writing, and all the scripts, moved to a site of their own.

sqldba.blogScripts, error library, wait types, SSMS
Browse by topic