How to Upload Files to SharePoint Online Using Microsoft Graph API and PowerShell
Table of content
If you’re managing files in SharePoint and want to automate uploads—you’re in the right place. upload files to SharePoint Online with PowerShell, Whether you’re building automated scripts, scheduling reports, or handling migrations, using the Microsoft Graph API with PowerShell is one of the most flexible and modern ways to do this.
In this guide, I’ll walk you through a detailed, real-world way to upload files to a SharePoint document library securely using Graph API and a registered Azure AD app. This is something I use across many of my client projects and internal automation tasks.
What You’ll Need
Before we get started, make sure you have the following:
-
An active Microsoft 365 tenant
-
Global Admin or Application Admin rights in Azure AD
-
A SharePoint site where you want to upload the file
-
PowerShell with PnP.PowerShell module installed
Register an App in Azure AD
This section covers on how to Register Azure AD App for SharePoint Graph API Access; to interact with Graph API securely, we need to register an app in Azure AD:
-
Go to Azure Portal
-
Navigate to Azure Active Directory → App registrations → New registration
-
Name it something like Sharepoint_AT and Register it
-
Once done, copy the Application (client) ID and Directory (tenant) ID — you’ll need both later.
Assign Graph API Permissions
Now give the app the ability to talk to SharePoint:
-
Under the same app, go to API permissions → Add a permission
-
Select Microsoft Graph → Application permissions
-
Search for Sites.ReadWrite.All and add
-
Click Grant admin consent for your org
This permission ensures the app has access only to SharePoint sites you explicitly grant access to — very secure.
Permission for specific site
Now to only add permission on a specific SharePoint site, proceed as below:
1. Go to API permissions and click on Add a permission
2. Select SharePoint
3. Now, select Application permissions
6. Check Sites.Selected , Sites.FullControl.All and click on Add Permission
7. Click on Grant admin consent and done
Granting SharePoint Site Access via Graph Explorer
Once you’ve added the sites.selected graph api permission to your app registration, the app doesn’t automatically get access to any SharePoint sites. You need to explicitly grant access to the specific site you want to work with.
Here’s how to do it using Microsoft Graph Explorer, step-by-step
Step-by-Step: Grant write Access to a Specific SharePoint Site
- Open Graph Explorer
- Now, We need the sharepointsiteid to apply the permission
- In, Graph Explorer, select get SharePoint site based on relative path of the site under SharePoint sites option
- In place of {host-name} provide your SharePoint domain address and in place of {server-relative-path} provide your site address like in the below. https://graph.microsoft.com/v1.0/sites/tenantname.sharepoint.com:/sites/yoursitename
- You can get the tenantname and sitename from your site like below
- Now, use the info and run the query to get the SiteId like below. Copy the second full guid(blackBox) under ID
- Now, Change the HTTP method (top-left dropdown) to POST
- In the Request URL, paste the following — replace the placeholder with your SharePoint site ID:
- Click on Request Body, and paste this JSON (don’t forget to replace the values): https://graph.microsoft.com/v1.0/sites/<Your_SharePoint_Site_ID>/permissions
Boom, now all completed so we can use PowerShell script to Automate the file upload to SharePoint
Generate a Client Secret
If you want to use a secret proceed as below:
1. Go to Azure portal
2. Go to your Azure app which you created
3. Click on Certificates & secrets
4. Click on New client secret
5. Create your secret and save it securely
Upload content to SharePoint using GraphAPI PowerShell
To connect to your SharePoint site using the Azure AD application and upload files securely with Microsoft Graph API, you’ll need the following:
-
The SharePoint site URL (where the file will be uploaded)
-
The Azure app Client ID (from App Registration)
-
The Azure app secret (or certificate thumbprint if you’re using a cert-based auth)
-
The file path to the document you want to upload
-
Here’s the PowerShell script that makes it all work: This script works for uploading all the files which are under the folder. Also, The script will check if the files re already uploaded then it will skip it!
This is the main part—uploading a file using Graph API.
Param (
$Tenant = "xxx",
$ClientID = "xxx,
$Secret = 'xxx',
$SharePoint_SiteID = "xxx",
$SharePoint_Path = "https://xxx.sharepoint.com/sites/TeamsRecording/Shared%20Documents",
$SharePoint_ExportFolder = "Recording",
$LocalFolder = "C:\Users\$($env:USERNAME)\OneDrive - xxx\Recordings"
)
Function Write_Log {
param($Message_Type, $Message)
$MyDate = "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
Write-Host "$MyDate - $Message_Type : $Message"
}
# ─── 1️⃣ Authenticate once
$Body = @{
client_id = $ClientID
client_secret = $Secret
scope = "https://graph.microsoft.com/.default"
grant_type = 'client_credentials'
}
Write_Log INFO "SharePoint connexion"
$Graph_Url = "https://login.microsoftonline.com/$Tenant.onmicrosoft.com/oauth2/v2.0/token"
Try {
$AuthorizationRequest = Invoke-RestMethod -Uri $Graph_Url -Method Post -Body $Body
Write_Log SUCCESS "Connected to SharePoint"
} Catch {
Write_Log ERROR "Connexion to SharePoint failed"
Exit 1
}
$Access_token = $AuthorizationRequest.access_token
$Header = @{
Authorization = $Access_token
"Content-Type" = "application/json"
}
# ─── 2️⃣ Find your DriveID once
$SharePoint_Graph_URL = "https://graph.microsoft.com/v1.0/sites/$SharePoint_SiteID/drives"
Write_Log INFO "Getting SharePoint site info"
Try {
$Result = Invoke-RestMethod -Uri $SharePoint_Graph_URL -Method GET -Headers $Header
Write_Log SUCCESS "Got SharePoint site info"
} Catch {
Write_Log ERROR "Getting SharePoint site info failed"
Exit 1
}
$DriveID = $Result.value |
Where-Object { $_.webURL -eq $SharePoint_Path } |
Select-Object -ExpandProperty id
# ─── 3️⃣ Loop through every file under $LocalFolder
$files = Get-ChildItem -Path $LocalFolder -File
Write_Log INFO "Found $($files.Count) files in '$LocalFolder'"
foreach ($f in $files) {
$File_Path = $f.FullName
$FileName = $f.Name
Write_Log INFO "Checking: $FileName"
# 3.1 Check if already exists
$checkUrl = "https://graph.microsoft.com/v1.0/sites/${SharePoint_SiteID}/drives/${DriveID}/root:/${SharePoint_ExportFolder}/${FileName}"
try {
Invoke-RestMethod -Uri $checkUrl -Method GET -Headers $Header -ErrorAction Stop
Write_Log INFO "Skipping '$FileName'—already uploaded."
continue
}
catch {
# 404 means not found: proceed to upload
if ($_.Exception.Response.StatusCode.Value__ -ne 404) {
Write_Log ERROR "Error checking existence of '$FileName': $_"
continue
}
}
Write_Log INFO "Preparing to upload: $FileName"
# 3.2 create upload session
$createUploadSessionUri =
"https://graph.microsoft.com/v1.0/sites/${SharePoint_SiteID}/drives/${DriveID}" +
"/root:/${SharePoint_ExportFolder}/${FileName}:/createUploadSession"
Try {
$uploadSession = Invoke-RestMethod `
-Uri $createUploadSessionUri -Method POST -Headers $Header -ContentType "application/json"
Write_Log SUCCESS "Session created for $FileName"
} Catch {
Write_Log ERROR "Preparing upload for $FileName failed"
continue
}
# 3.3 read the file & upload in one shot
$fileInBytes = [System.IO.File]::ReadAllBytes($File_Path)
$fileLength = $fileInBytes.Length
$headers = @{ 'Content-Range' = "bytes 0-$($fileLength-1)/$fileLength" }
Write_Log INFO "Uploading $FileName"
Try {
Invoke-RestMethod `
-Method Put `
-Uri $uploadSession.uploadUrl `
-Body $fileInBytes `
-Headers $headers
Write_Log SUCCESS "Uploaded $FileName"
} Catch {
Write_Log ERROR "Failed to upload $FileName"
}
}
Write_Log SUCCESS "All file uploads completed."
Final Thoughts
This method is incredibly powerful and secure when you’re working across different tenants or automating data flows into SharePoint.
A few closing thoughts:
-
Rotate secrets regularly and use Azure Key Vault for storing secrets securely
-
You can easily convert this to use certificates instead of secrets for extra security
-
Always test with test data before touching production libraries
🛡️ Bonus Tip: Automate Microsoft Teams Recordings to SharePoint
If you’re using Microsoft Teams for meetings and recordings, one common problem is that those recordings expire or get deleted after a certain retention period. By combining Graph API with PowerShell or Logic Apps, you can automatically move or copy Teams recordings to a dedicated SharePoint document library.
This not only prevents accidental deletion but also ensures that all recordings are retained for compliance, training, or auditing purposes. If you’d like a step-by-step guide on that, let me know and I’ll publish a full blog soon!
If you want help building reusable modules or Logic Apps around this — just drop me a line on TechEuc.com or if you are facing any issues, you can comment or would need this implemented then you can check out my freelance profile too.
This method is incredibly powerful and secure when you’re working across different tenants or automating data flows into SharePoint.
A few closing thoughts:
-
Rotate secrets regularly and use Azure Key Vault for storing secrets securely
-
You can easily convert this to use certificates instead of secrets for extra security
-
Always test with test data before touching production libraries
If you want help building reusable modules or Logic Apps around this — just drop me a line on TechEuc.com