This article will teach you how to upload files to SharePoint using PowerShell.
Prerequisites
Here are the things youโll need.
- A computer with PowerShell 7.4 or later installed. This article uses PowerShell 7 on Windows 11.
- Access to a SharePoint Online document library.
Install the PnP PowerShell Module
You must install the PnP PowerShell module on your computer first. This module provides cmdlets that allow you to authenticate and interact with your SharePoint Online tenant.
- Open PowerShell and run this command:
Install-Module 'Pnp.PowerShell'
- Verify that the module is installed:
Get-Module 'Pnp.PowerShell' -ListAvailable

- Connect to SharePoint Online with Interactive Authentication. Depending on your organization’s config, you may need a Microsoft Entra ID app registration and Client ID to authenticate with PnP PowerShell.
$ClientId = "your-app-client-id"
Connect-PnPOnline `
-Url $spoSite `
-ClientId $ClientId `
-InteractiveAn interactive login window pops up. Log in with your credentials.
Note that the examples in this article use interactive authentication. For unattended automation cases, you should consider certificate-based authentication.
Certificate-Based Authentication
Certificate-based authentication can be used for unattended automation (such as scheduled tasks, Azure Automation runbooks, CI/CD pipelines, and other cases where interactive sign-in is not possible). For a simple interactive example, you can securely enter the certificate password with Read-Host:
$CertificatePassword = Read-Host "Enter certificate password" -AsSecureString
Connect-PnPOnline `
-Url $SiteUrl `
-ClientId $ClientId `
-Tenant "contoso.onmicrosoft.com" `
-CertificatePath "C:\Certificates\PnP.pfx" `
-CertificatePassword $CertificatePassword
Note that you should avoid hardcoding certificate passwords in scripts. Store secrets securely using Azure Key Vault, SecretManagement, or automation platform secret stores.
Before we try to upload files to SharePoint using PowerShell, letโs prepare the details.
- What is the target SharePoint site URL for the upload?
- In this example, the target SharePoint site URL is:
https://.sharepoint.com/sites/SalesandMarketing.
- What is the target document library relative to the site URL?
- The target document library (folder) is DummyDocs.

- What is the location of the files to upload?
- Suppose you have one or more files that you want to upload to a SharePoint document library. For example, I have ten files under the ./temp folder.

Upload Files to SharePoint Using PowerShell
Now that you have all the details letโs dive into the upload process.
Open PowerShell and store the following variables.
# What is the target SharePoint site URL for the upload? $spoSite = 'https://<tenant>.sharepoint.com/sites/SalesandMarketing/' # What is the target document library relative to the site URL? $spoDocLibrary = 'DummyDocs' # What is the location of the files to upload? $localFolder = "./temp"
Now, connect to the SharePoint Site URL by running this command. This command authenticates to the SPO site you specified in the $spoSite variable. Enter your username and password when prompted.
Connect-PnPOnline `
-Url $spoSite `
-ClientId $ClientId `
-Interactive
Run this command to ensure youโre connected to the correct SPO site.
Get-PnPSite

Next, confirm that the target folder you specified in the $spoDocLibrary variable exists in the SPO site.
Resolve-PnPFolder -SiteRelativePath $spoDocLibrary
The result below confirms that the target folder exists.

Letโs now gather the files to upload.
$files = Get-ChildItem $localFolder -File
Once the files are loaded into the $files variable, letโs use iteration to upload each file. Weโll use the foreach loop in this example to upload each file.
What ultimately uploads the files to the target is the Add-PnPFile cmdlet.
foreach ($item in $files) {
Add-PnPFile `
-Path ($item.FullName.ToString()) `
-Folder $spoDocLibrary `
-Values @{"Title" = $($item.Name) }
} Each item appears on the screen as they get uploaded to the document library.

Finally, open the document library in your browser and see the new files you uploaded.

Recursively Upload Files to SharePoint using PowerShell
In the previous section, we demonstrated uploading files under a folder. What if you need to upload files recursively and want to keep the folder structure?
For example, the below screenshot shows that the ./temp folder has a child folder called subdir1. Both the parent and child folders contain ten files each.

The goal is to upload these folders and files to the SharePoint document library, retaining the sub-directory structure.
Letโs use the same information as we did in the previous section. Run the below code in PowerShell, but ensure to replace the values with yours.
# What is the target SharePoint site URL for the upload? $spoSite = 'https://<tenant>.sharepoint.com/sites/SalesandMarketing/' # What is the target document library relative to the site URL? $spoDocLibrary = 'DummyDocs' # What is the location of the files to upload? $localFolder = "./temp"
Connect to the SharePoint Online Site:
Connect-PnPOnline `
-Url $spoSite `
-ClientId $ClientId `
-Interactive
Once connected, letโs first upload the files on the parent level. The below command is the same one we ran in the previous section to upload the files from one folder.
$files = Get-ChildItem $localFolder -File
foreach ($item in $files) {
Add-PnPFile `
-Path ($item.FullName.ToString()) `
-Folder $spoDocLibrary `
-Values @{"Title" = $($item.Name) }
} 
And that takes care of the parent folder upload.

But whereโs the subfolder? Thatโs what weโll upload next.
Run this following command to get all child folders under the $localFolder location.
# Get all child folders. $childFolders = Get-ChildItem -Path $localFolder -Directory -Recurse
Now, run this command to upload the files recursively. You do not have to pre-create the subfolders in the document library; the Add-PnPFile cmdlet already does that for you.
# Loop through each child folder
foreach ($childFolder in $childFolders) {
# Form the target subfolder name (i.e. "DummyDocs/subdir1")
$TargetSubFolderName = `
"$($spoDocLibrary)$(($childFolder.FullName
).Replace((Resolve-Path $localFolder).Path,'')
.Replace('\','/'))"
# Get all files in the child folder
$files = Get-ChildItem ($childFolder.FullName) -File
# Loop through each file under the child folder.
foreach ($item in $files) {
Add-PnPFile `
-Path ($item.FullName.ToString()) `
-Folder $TargetSubFolderName `
-Values @{"Title" = $($item.Name) }
}
} Youโll see a similar display as the screenshot below during the file upload.

Once the upload is complete, you can verify that the subfolder was created in the document library.

And the files were also uploaded to the subfolder, as shown below.

Putting It All Together in a Script
So far, weโve demonstrated the steps for uploading files and folders so that you can grasp how the process works (hopefully). Whatโs even better is turning this whole process into a script.
First, save the code below as Start-FolderUpload.ps1 on your computer.
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]
$SiteUrl,
[Parameter(Mandatory)]
[string]
$ClientId,
[Parameter(Mandatory)]
[string]
$LocalFolderPath,
[Parameter(Mandatory)]
[string]
$TargetFolderName,
[Parameter()]
[Switch]
$Recursive
)
# Ensure that the LocalFolderPath exists. Exit if not.
if (!$(Test-Path $LocalFolderPath)) {
"The LocalFolderPath does not exist." | Out-Default
return $null
}
else {
$LocalFolderPath = (Resolve-Path $LocalFolderPath).Path
}
# Connect to the SPO site. Exit if failed.
try {
Connect-PnPOnline `
-Url $SiteUrl `
-ClientId $ClientId `
-Interactive
}
catch {
$_.Exception.Message | Out-Default
return $null
}
# Ensure that the Document Library exists. Exit if not.
try {
$null = Resolve-PnPFolder -SiteRelativePath $TargetFolderName -ErrorAction Stop
}
catch {
$_.Exception.Message | Out-Default
return $null
}
# Store upload results
$UploadResults = @()
# Upload the top-level folder files only.
$Files = Get-ChildItem -Path $LocalFolderPath -File
foreach ($File in $Files) {
try {
Add-PnPFile `
-Path ($File.FullName.ToString()) `
-Folder $TargetFolderName `
-Values @{"Title" = $($File.Name) } `
-ErrorAction Stop | Out-Null
$UploadResults += [PSCustomObject]@{
FileName = $File.Name
FullPath = $File.FullName
Status = "Success"
Error = ""
Timestamp = Get-Date
}
"Uploaded File: $($File.FullName)" | Out-Default
}
catch {
$UploadResults += [PSCustomObject]@{
FileName = $File.Name
FullPath = $File.FullName
Status = "Failed"
Error = $_.Exception.Message
Timestamp = Get-Date
}
"Failed: $($File.FullName)" | Out-Default
}
}
# If -Recursive, upload the subfolders and files
if ($Recursive) {
$SubFolders = Get-ChildItem -Path $LocalFolderPath -Directory -Recurse
foreach ($SubFolder in $SubFolders) {
$SubTargetFolderName = "$($TargetFolderName)$(($SubFolder.FullName).Replace($LocalFolderPath,'').Replace('\','/'))"
$Files = Get-ChildItem -Path ($SubFolder.FullName) -File
foreach ($File in $Files) {
try {
Add-PnPFile `
-Path ($File.FullName.ToString()) `
-Folder $SubTargetFolderName `
-Values @{"Title" = $($File.Name) } `
-ErrorAction Stop | Out-Null
$UploadResults += [PSCustomObject]@{
FileName = $File.Name
FullPath = $File.FullName
Status = "Success"
Error = ""
Timestamp = Get-Date
}
"Uploaded File: $($File.FullName)" | Out-Default
}
catch {
$UploadResults += [PSCustomObject]@{
FileName = $File.Name
FullPath = $File.FullName
Status = "Failed"
Error = $_.Exception.Message
Timestamp = Get-Date
}
"Failed: $($File.FullName)" | Out-Default
}
}
}
$UploadResults | Export-Csv `
-Path ".\UploadResults.csv" `
-NoTypeInformation
"Upload log saved to UploadResults.csv" | Out-Default
}
Parameters of the script
This script has five parameters, four of which are mandatory.
- -ClientId โ The Microsoft Entra ID application (client) ID used for authentication.
- -SiteURL โ The SharePoint Online site URL.
- -LocalFolderPath โ The location on your local computer containing the files to upload.
- -TargetFolderName โ The target folder or document library name.
- -Recursive โ The switch parameter enables the recursive files and folders to upload. Only the files on the parent folder level will be uploaded if not specified.
Test the script
Letโs put the script to the test.
.\Start-FolderUpload.ps1 `
-ClientId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' `
-SiteUrl 'https://<tenant>.sharepoint.com/sites/sitename' `
-TargetFolderName 'DummyDocs' `
-LocalFolderPath './temp' `
-Recursive
Replace the ClientId value with the Application (client) ID of your Microsoft Entra ID app registration. And watch the magic as it happens!

When uploading large numbers of files, SharePoint Online may temporarily throttle requests/return transient network errors. For production automation cases, you should consider implementing retry logic around Add-PnPFile operations to handle temporary failures and improve upload reliability.
Conclusion
PowerShell and SharePoint Online interaction through the PnP module is an excellent way to automate multiple tasks. Youโve learned in this post how to get started by uploading files and folders from a local location to a SharePoint Online document library.
Additionally, we provided a working script that you can use to upload files to SharePoint using PowerShell. Optionally, the script lets you specify whether to recursively upload files while maintaining the subfolder structure, giving you additional control.
Finally, one thing to remember is that the Add-PnPFile cmdlet does not tell you if the file with the same name already exists in the target library. If versioning is enabled in that document library, a new version of that file will be created. In contrast, if the versioning is disabled, the file will be overwritten without warning.
How do I upload a file to SharePoint Online using PowerShell?
Install the PnP PowerShell module, connect to your SharePoint Online site using Connect-PnPOnline, and upload files with the Add-PnPFile cmdlet. You must specify the local file path and the target document library or folder.
Can I upload multiple files to a SharePoint document library at once?
Yes. You can use Get-ChildItem to collect files from a local folder and then upload them in a loop with Add-PnPFile.
How do I upload an entire folder structure and keep subfolders in SharePoint?
Use a recursive upload script that enumerates child folders and uploads files to corresponding SharePoint folders. The Add-PnPFile cmdlet can automatically create missing subfolders during the upload process.
Do I need to create SharePoint subfolders before uploading files?
No. When uploading recursively, Add-PnPFile can create the target subfolder structure automatically if it does not already exist.
What authentication method should I use for scheduled tasks or automation?
For unattended scenarios such as scheduled tasks, Azure Automation, and CI/CD pipelines, use certificate-based authentication instead of interactive sign-in.
What happens if a file with the same name already exists in SharePoint?
If document library versioning is enabled, SharePoint creates a new version of the file. If versioning is disabled, the existing file may be overwritten without warning.



does this script work with scheduled Tasks?
Are the credentials cached or is the management shell due to aprooval linked with a token?
You must use certificate-based authentication instead of the interactive username+password for unattended use.
I created a script to automatically send files to sharepoint, but it is reporting an error: The command ‘Connect-PnPOnline’ was found in the module ‘PnP.PowerShell’, but it was not possible
load the module.