When an employee leaves your organization, it’s essential to secure their data. In some cases, you may not want to delete the user account immediately because the user may have critical data, files, or access to shared resources that are still required by the team. However, you also want to ensure that any shares or external permissions granted by this user on their OneDrive files and folders are revoked promptly to maintain security and confidentiality.
In this blog post, we’ll show you how to achieve this using a PowerShell script in combination with Microsoft Graph API. This approach allows you to automate the process of removing all shared permissions granted by a user, ensuring that no one can access the user’s OneDrive content through previously shared links or permissions.
Why you need this approach
When an employee departs, you typically follow standard security practices such as:
- Changing the password.
- Deactivating the account.
- Reviewing access to critical resources.
However, deleting or deactivating the account immediately might not be practical in every case. The user may have critical data stored in their OneDrive that the organization needs to access. In such situations, revoking all external and internal sharing permissions while leaving the account active can mitigate the risk of unauthorized access, giving you more time to handle the account in a measured way.
This is where PowerShell and Microsoft Graph come in. By leveraging these tools, you can automate the process of removing all shared permissions from the user’s OneDrive files and folders, ensuring that no external or internal parties retain access to the data.
Setting up the solution: PowerShell + Microsoft Graph
Before you begin, ensure you have an Azure AD app registered with the appropriate permissions to interact with Microsoft Graph on behalf of your organization.
Microsoft Graph App Permissions
You’ll need to grant the following API permissions to your registered Azure AD app:
- Files.ReadWrite.All: Allows the app to read and write all files accessible by the user.
- Sites.ReadWrite.All: Allows the app to read and write items in all site collections.
- User.Read.All: Allows the app to read full user profiles within the directory.
- Directory.Read.All: Grants read access to directory data.
Make sure you grant admin consent for these permissions so that the app can access any user’s data in your organization.
The PowerShell script
Here is a PowerShell script that revokes all sharing permissions from a user’s OneDrive, ensuring that any previously shared links or granted permissions are removed. This script leverages Microsoft Graph API to interact with OneDrive and perform the necessary tasks.
# Configuration: Set your Azure AD app credentials here
$clientId = "your-client-id"
$tenantId = "your-tenant-id"
$clientSecret = "your-client-secret"
# The user whose OneDrive shared permissions you want to remove
$userUPN = "user@example.com" # Replace with the user's UPN (User Principal Name, e.g., email address)
# Function to get an OAuth token from Azure AD
function Get-GraphToken {
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$body = @{
client_id = $clientId
scope = "https://graph.microsoft.com/.default"
client_secret = $clientSecret
grant_type = "client_credentials"
}
$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
return $token
}
# Initial Token Retrieval
$token = Get-GraphToken
# Function to retrieve all permissions for a given item with retry logic
function GetPermissions {
param (
[parameter(Mandatory = $true)]
$itemId,
$driveId
)
$permissionsUri = "https://graph.microsoft.com/v1.0/drives/$driveId/items/$itemId/permissions"
$retryCount = 0
$maxRetries = 5
$delay = 5
while ($retryCount -lt $maxRetries) {
try {
$permissions = Invoke-RestMethod -Headers @{Authorization = "Bearer $token"} -Uri $permissionsUri -Method Get
return $permissions.value
} catch {
if ($_.Exception.Response.StatusCode.Value__ -eq 429) {
Write-Host "Rate limit hit. Waiting for $delay seconds before retrying..." -ForegroundColor Yellow
Start-Sleep -Seconds $delay
$delay *= 2 # Exponential backoff
$retryCount++
} elseif ($_.Exception.Response.StatusCode.Value__ -eq 401) {
# Token expired or unauthorized, refresh the token
Write-Host "Unauthorized error (401). Refreshing token..." -ForegroundColor Yellow
$token = Get-GraphToken
$retryCount++
} else {
throw $_
}
}
}
Write-Host "Failed to retrieve permissions after $maxRetries retries. Skipping item." -ForegroundColor Red
return $null
}
# Function to remove a specific permission from an item with retry logic
function RemovePermission {
param (
[parameter(Mandatory = $true)]
$itemId,
$permissionId,
$driveId
)
$deleteUri = "https://graph.microsoft.com/v1.0/drives/$driveId/items/$itemId/permissions/$permissionId"
$retryCount = 0
$maxRetries = 5
$delay = 5
while ($retryCount -lt $maxRetries) {
try {
Invoke-RestMethod -Headers @{Authorization = "Bearer $token"} -Uri $deleteUri -Method Delete
Write-Host "Successfully removed permission: $permissionId from item: $itemId"
return
} catch {
if ($_.Exception.Response.StatusCode.Value__ -eq 429) {
Write-Host "Rate limit hit. Waiting for $delay seconds before retrying..." -ForegroundColor Yellow
Start-Sleep -Seconds $delay
$delay *= 2 # Exponential backoff
$retryCount++
} elseif ($_.Exception.Response.StatusCode.Value__ -eq 401) {
# Token expired or unauthorized, refresh the token
Write-Host "Unauthorized error (401). Refreshing token..." -ForegroundColor Yellow
$token = Get-GraphToken
$retryCount++
} else {
Write-Host "Failed to remove permission: $permissionId after $maxRetries retries. Skipping item." -ForegroundColor Red
throw $_
}
}
}
}
# Function to remove all shared permissions from a user's OneDrive
function RemoveAllSharedPermissions {
param (
[parameter(Mandatory = $true)]
$userUPN
)
# Get the user's OneDrive drive ID
$driveUri = "https://graph.microsoft.com/v1.0/users/$userUPN/drive"
$driveId = (Invoke-RestMethod -Headers @{Authorization = "Bearer $token"} -Uri $driveUri -Method Get).id
# Get all items in the user's OneDrive (including folders and files)
$rootUri = "https://graph.microsoft.com/v1.0/drives/$driveId/root/children"
$items = Invoke-RestMethod -Headers @{Authorization = "Bearer $token"} -Uri $rootUri -Method Get
# Process each item in OneDrive
foreach ($item in $items.value) {
Write-Host "Processing item: $($item.name)"
# Get all permissions for the item
$permissions = GetPermissions -itemId $item.id -driveId $driveId
# Skip the item if permissions could not be retrieved
if ($permissions -eq $null) {
continue
}
# Remove each permission
foreach ($permission in $permissions) {
Write-Host "Removing permission: $($permission.id) from item: $($item.name)"
RemovePermission -itemId $item.id -permissionId $permission.id -driveId $driveId
}
# If the item is a folder, recursively process its contents
if ($item.folder) {
ProcessFolderContents -folderId $item.id -driveId $driveId
}
}
Write-Host "Completed removing all shared permissions for user $userUPN."
}
# Function to process the contents of a folder recursively
function ProcessFolderContents {
param (
[parameter(Mandatory = $true)]
$folderId,
$driveId
)
$folderUri = "https://graph.microsoft.com/v1.0/drives/$driveId/items/$folderId/children"
$items = Invoke-RestMethod -Headers @{Authorization = "Bearer $token"} -Uri $folderUri -Method Get
foreach ($item in $items.value) {
Write-Host "Processing item in folder: $($item.name)"
# Get all permissions for the item
$permissions = GetPermissions -itemId $item.id -driveId $driveId
# Skip the item if permissions could not be retrieved
if ($permissions -eq $null) {
continue
}
# Remove each permission
foreach ($permission in $permissions) {
Write-Host "Removing permission: $($permission.id) from item: $($item.name)"
RemovePermission -itemId $item.id -permissionId $permission.id -driveId $driveId
}
# If the item is a folder, recursively process its contents
if ($item.folder) {
ProcessFolderContents -folderId $item.id -driveId $driveId
}
}
}
# Run the script to remove all shared permissions from the specified user's OneDrive
RemoveAllSharedPermissions -userUPN $userUPN
How the script works
- Token Generation: The script first generates an OAuth 2.0 token using client credentials from your Azure AD app. This token is used for all subsequent API calls to Microsoft Graph.
- Fetching OneDrive Items: The script retrieves all items (files and folders) from the user’s OneDrive.
- Removing Permissions: It iterates over each item, retrieving and removing all granted permissions. For folders, it recursively processes their contents.
- Rate Limiting & Token Expiration Handling: The script includes retry logic for API rate limits (HTTP
429
) and token expiration issues (HTTP401
). It waits and retries API calls when rate limits are hit, and it refreshes the token if it expires during execution.
Why this approach is important
This approach ensures that all sharing permissions granted by a departing employee are promptly revoked. While deleting or disabling an account can disrupt business operations, removing shared access from OneDrive files ensures the security and confidentiality of the data.
By leveraging Microsoft Graph API and PowerShell, you can automate this process and maintain control over data access within your organization, even as employees transition out.
Test the Script: Run the script in a controlled environment with a test user to ensure it works as expected.
Implement as Part of Offboarding: Integrate this process into your organization’s employee offboarding procedures to secure shared data while allowing more time for a thoughtful review of the departing employee’s account.
Review Permissions Regularly: Regularly review user accounts and shared permissions as part of a broader security strategy to mitigate risks from shared data.