When employees leave an organization, their OneDrive data often needs to be temporarily accessible to colleagues or managers for transition purposes. The traditional approach of adding additional owners to OneDrive accounts creates hidden costs and compliance risks that many IT administrators overlook.
This article demonstrates a PowerShell-based custom action that provides temporary access through time-limited sharing instead of permanent ownership assignment, preventing unlicensed storage accounts and reducing organizational costs.
Manage unlicensed OneDrive user accounts - SharePoint in Microsoft 365 | Microsoft Learn
Understanding the Root Cause
When a user account is disabled or deleted in Microsoft 365, their OneDrive should automatically enter a retention period and then be deleted. However, adding additional owners to OneDrive disrupts this lifecycle, creating unlicensed storage accounts that persist indefinitely.
OneDrive accounts with additional owners don’t get automatically cleaned up when the original user is deleted. These orphaned OneDrives continue consuming storage space without an associated license, creating ongoing costs and compliance risks.
Result: Accumulating storage costs and compliance headaches from OneDrives that should have been deleted months ago.
Instead of permanent ownership, create time-limited shares that expire automatically in 30 days. Steps?
- Create Azure AD App Registration
- Add required API permissions and grant admin consent
- Create client secret and note the values
- Update script variables with your App ID, Secret, and Tenant ID
Your Azure AD app needs:
- Files.ReadWrite.All – To create sharing permissions
- User.Read.All – To validate user accounts
Install Microsoft Graph PowerShell modules:
Install-Module Microsoft.Graph.Authentication -Force
Install-Module Microsoft.Graph.Users -Force
Install-Module Microsoft.Graph.Files -Force
Key Commands Explained
Get-MgUserDrive: Retrieves the user’s OneDrive
Get-MgDriveItemChild: Gets files/folders from OneDrive root
Invoke-MgInviteDriveItem: Creates the temporary sharing permission
Links:
Get-MgUserDrive (Microsoft.Graph.Files) | Microsoft Learn
Get-MgDriveItemChild (Microsoft.Graph.Files) | Microsoft Learn
Get-MgInviteDriveItem (Microsoft.Graph.Files) | Microsoft Learn
Working script:
# Azure AD App Configuration
$AppId = "12345678-1234-1234-1234-123456789012" # Your Azure AD App ID
$ClientSecret = "your-client-secret-value" # Your App Secret
$TenantId = "87654321-4321-4321-4321-210987654321" # Your Tenant ID
# User Configuration
$OriginalUser = "john.smith@yourcompany.com" # Departing employee
$NewUser = "manager@yourcompany.com" # User who needs access/manager
try {
# Create credential object
$SecureSecret = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($AppId, $SecureSecret)
# Connect to Microsoft Graph
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $Credential
Write-Output "Connected to Microsoft Graph"
# Get the user's OneDrive
Write-Output "Getting OneDrive for: $OriginalUser"
$drive = Get-MgUserDrive -UserId $OriginalUser | Where-Object {$_.Name -eq "OneDrive"}
if (-not $drive) {
Write-Error "OneDrive not found for $OriginalUser"
return
}
Write-Output "Found OneDrive: $($drive.Id)"
# Get all root items
Write-Output "Getting OneDrive root items..."
$rootItems = Get-MgDriveItemChild -DriveId $drive.Id -DriveItemId "root"
if (-not $rootItems) {
Write-Output "No items found in OneDrive"
return
}
Write-Output "Found $($rootItems.Count) items to share"
# Set 30-day expiration
$expirationDate = (Get-Date).AddDays(30).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
Write-Output "Setting expiration date: $expirationDate"
$successCount = 0
$errorCount = 0
# Share each root item
foreach ($item in $rootItems) {
Write-Output "Processing: $($item.Name)"
# Create sharing parameters
$shareParams = @{
recipients = @(@{
email = $NewUser
})
roles = @("read")
requireSignIn = $true
sendInvitation = $false
expirationDateTime = $expirationDate
}
try {
Invoke-MgInviteDriveItem -DriveId $drive.Id -DriveItemId $item.Id -BodyParameter $shareParams
$successCount++
Write-Output "Successfully shared: $($item.Name)"
}
catch {
$errorCount++
Write-Warning "Failed to share: $($item.Name) - $($_.Exception.Message)"
}
# Small delay to avoid throttling
Start-Sleep -Milliseconds 500
}
Write-Output ""
Write-Output "=== SUMMARY ==="
Write-Output "Original User: $OriginalUser"
Write-Output "Target User: $NewUser"
Write-Output "Successfully shared: $successCount items"
Write-Output "Failed to share: $errorCount items"
Write-Output "Access expires: $expirationDate"
Write-Output "==============="
}
catch {
Write-Error "Script failed: $($_.Exception.Message)"
}
finally {
# Disconnect from Graph
if (Get-MgContext) {
Disconnect-MgGraph
Write-Output "Disconnected from Microsoft Graph"
}
}
Understanding the Sharing Parameters
The $shareParams object contains the key configuration for temporary sharing:
$shareParams = @{
recipients = @(@{
email = $NewUser # Email of user receiving access
})
roles = @("read") # Permission level: "read" or "write"
requireSignIn = $true # User must authenticate to access
sendInvitation = $false # Don't send email notification
expirationDateTime = $expirationDate # When access expires (30 days)
}
Temporary sharing eliminates the unlicensed OneDrive problem while providing necessary business continuity. Access expires automatically, storage stays clean, and you avoid manual cleanup tasks.
The solution uses Azure AD app authentication for reliable enterprise automation.
Why This Approach Works Better?
| Additional Owners | Temporary Sharing |
| Creates unlicensed accounts | No storage impact |
| Permanent access | Auto-expires in 30 days |
| Manual cleanup needed | Fully automated |
| Compliance risks | Time-bounded access |
Stop creating orphaned OneDrives. Use time-limited sharing instead.



Leave a Reply