How to revoke all OneDrive shares for a departing employee using PowerShell and Microsoft Graph

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 (HTTP 401). 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.