Remove Expired Sharing Links and Update Sharing Link Role to Read
Updating the expiration time and role for sharing links is still limited. Although it seems possible through the Graph API Update permission, I have been unable to pass the roles as body parameters.
Graph Explorer Attempts
Using the following script:
Invoke-PnPGraphMethod -Url "v1.0/drives/$driveId/items/$driveItemId/permissions/$($ShareLink.Id)" -Method Patch -Content @{ roles = @("read") }
threw the error message
“Invalid input: No Information provided to update the specifed permission”
From the UI
Anyone link can’t be edited
Organisation link can’t be edited
Specific people links can be edited
Using the network tab from the browser developer tools enables identifying the endpoint to update specific people links only.
Attempting to use the endpoint on non-specific people links does not work, unfortunately.
From the Graph Explorer
THe Microsoft Graph Update permission endpoint provides the ability to update sharing link permissions. However, testing the endpoint did not yield the anticipated results.
- Non-anonymous links can’t be modified through the Microsoft Graph endpoint.
- Trying to update the role property to read as shown in the example failed
- Trying to update the role property to read with expiration date returns 200 OK code but failed to update both role and expirationDateTime.
Sample script to update specific people sharing link role from write to read and delete expired sharing links or links created more than x days
#Parameters
$siteUrl = Read-Host -Prompt "Enter site collection URL";
$daysToKeep = Read-Host -Prompt "Enter the number of days to keep sharinglinks if no expired date is set";
$dateTime = (Get-Date).toString("dd-MM-yyyy-hh-ss")
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$fileName = "SharedLinks-PnP" + $dateTime + ".csv"
$ReportOutput = $directorypath + "\Logs\"+ $fileName
#Connect to PnP Online
Connect-PnPOnline -Url $siteUrl -Interactive
write-host $("Start time " + (Get-Date))
$global:Results = @();
#get site id
# Extract the domain and site name
$uri = New-Object System.Uri($siteurl)
$domain = $uri.Host
$siteName = $uri.AbsolutePath
# Construct the new URL
$RestMethodUrl = "v1.0/sites/$($domain):$($siteName)?$select=id"
$site = (Invoke-PnPGraphMethod -Url $RestMethodUrl -Method Get -ConsistencyLevelEventual)
$siteId = $site.id
#get drive id
$url = 'v1.0/drives/'+$driveId+'/root:/'+$($relativeUrl)+'?$select=id'
$drives = (Invoke-PnPGraphMethod -Url "v1.0/sites/${siteId}/drives?$select=webUrl,id" -Method Get).Value
function Get-ListItems_WithUniquePermissions{
param(
[Parameter(Mandatory)]
[Microsoft.SharePoint.Client.List]$List
)
$selectFields = "ID,HasUniqueRoleAssignments,FileRef,FileLeafRef,FileSystemObjectType"
$Url = $siteUrl + '/_api/web/lists/getbytitle(''' + $($list.Title) + ''')/items?$select=' + $($selectFields)
$nextLink = $Url
$listItems = @()
$Stoploop =$true
while($nextLink){
do{
try {
$response = invoke-pnpsprestmethod -Url $nextLink -Method Get
$Stoploop =$true
}
catch {
write-host "An error occured: $_ : Retrying" -ForegroundColor Red
$Stoploop =$true
Start-Sleep -Seconds 30
}
}
While ($Stoploop -eq $false)
$listItems += $response.value | where-object{$_.HasUniqueRoleAssignments -eq $true}
if($response.'odata.nextlink'){
$nextLink = $response.'odata.nextlink'
} else{
$nextLink = $null
}
}
return $listItems
}
function getSharingLink($_object,$_type,$_siteUrl,$_listUrl,$_listid)
{
$sharingSettings = Invoke-PnPSPRestMethod -Method Post -Url "$($siteUrl)/_api/web/Lists(@a1)/GetItemById(@a2)/GetSharingInformation?@a1='{$($_listId)}'&@a2='$($_object.Id)'&`$Expand=permissionsInformation,pickerSettings" -ContentType "application/json;odata=verbose" -Content "{}"
ForEach ($ShareLink in $sharingSettings.permissionsInformation.links)
{
$linkDetails = $shareLink.linkDetails
if($linkDetails.ShareTokenString){
$action = $null
#update expiration date to be created date + 180 days if created date + 180 days is less than today otherwise delete the sharing link
$CurrentDateTime = Get-Date
$createdDate = Get-Date -Date $linkDetails.Created
# delete any sharing links created more than 180 days ago
$expirationDate = $createdDate.AddDays($daysToKeep)
if($expirationDate -lt $CurrentDateTime -or ($linkDetails.Expiration -ne "" -and (Get-Date -Date $linkDetails.Expiration) -lt $CurrentDateTime))
{
#Instead using the rest api call, use the Remove-PnPFileSharingLink or Remove-PnPFolderSharingLink
$url = "$siteUrl/_api/web/Lists('$_listid')/GetItemById($($_object.Id))/UnshareLink"
$varBody = '{"linkKind":'+ $linkDetails.LinkKind +',"shareId":"'+ $linkDetails.ShareId +'"}'
$action = "Deleted"
$sharingInfo = invoke-pnpsprestmethod -Url $url -Method Post -Content $varBody
}
else {
if($linkDetails.IsEditLink -eq $true)
{
if($linkDetails.LinkKind -notin (3,5) ) #3 is Organization Edit Link and #5 is AnonymousEditLink, #the RESTAPI is unable to update the organization and anonymous link
{
$varBody = '{"request":{"createLink":true,"settings":{"linkKind":'+ $linkDetails.LinkKind +',"role":1,"shareId":"'+ $linkDetails.ShareId +'"}}}'
$url = "$siteUrl/_api/web/Lists('$_listid')/GetItemById($($_object.Id))/ShareLink"
$sharingInfo = invoke-pnpsprestmethod -Url $url -Method Post -Content $varBody
$action = "Updated to View Only"
$linkDetails = $sharingInfo.SharinglinkInfo
}
else {
#Organisation and anonymous links can't be updated using the /ShareLink endpoint. An altenative solution could be to delete and recreate the sharing link as view
#delete link
#recreate as viewonly link
}
}
$invitees = (
$linkDetails.Invitations |
ForEach-Object { $_.Invitee.email }
) -join '|'
$result = New-Object PSObject -property $([ordered]@{
ItemID = $item.Id
ShareId = $linkDetails.ShareId
ShareLink = $linkDetails.Url
Invitees = $invitees
Name = $_object.FileLeafRef ?? $_object.Title
Type = $_type -eq 1 ? "Folder" : "File"
RelativeURL = $_object.FileRef ?? ""
LinkAccess = "ViewOnly"
Created = Get-Date -Date $linkDetails.Created
CreatedBy = $linkDetails.CreatedBy.email
LastModifiedBy = $linkDetails.LastModifiedBy.email
LastModified = $LastModified
ShareLinkType = $linkDetails.LinkKind
Expiration = $linkDetails.Expiration
BlocksDownload = $linkDetails.BlocksDownload
RequiresPassword = $linkDetails.RequiresPassword
PasswordLastModified = $linkDetails.PasswordLastModified
PassLastModifiedBy = $linkDetails.PasswordLastModifiedBy.email
HasExternalGuestInvitees = $linkDetails.HasExternalGuestInvitees
HasAnonymousLink = $linkDetails.HasAnonymousLink
AllowsAnonymousAccess = $linkDetails.AllowsAnonymousAccess
ShareTokenString = $linkDetails.ShareTokenString
Action = $action
})
$global:Results +=$result;
}
}
}
}
#Exclude certain libraries
$ExcludedLists = @("Access Requests", "App Packages", "appdata", "appfiles", "Apps in Testing", "Cache Profiles", "Composed Looks", "Content and Structure Reports", "Content type publishing error log", "Converted Forms",
"Device Channels", "Form Templates", "fpdatasources", "Get started with Apps for Office and SharePoint", "List Template Gallery", "Long Running Operation Status", "Maintenance Log Library", "Images", "site collection images"
, "Master Docs", "Master Page Gallery", "MicroFeed", "NintexFormXml", "Quick Deploy Items", "Relationships List", "Reusable Content", "Reporting Metadata", "Reporting Templates", "Search Config List", "Site Assets", "Preservation Hold Library",
"Site Pages", "Solution Gallery", "Style Library", "Suggested Content Browser Locations", "Theme Gallery", "TaxonomyHiddenList", "User Information List", "Web Part Gallery", "wfpub", "wfsvc", "Workflow History", "Workflow Tasks", "Pages")
Write-Host "Processing site $siteUrl" -Foregroundcolor "Red";
#getSharingLink $ctx $web "site" $siteUrl "";
$ll = Get-PnPList -Includes BaseType, Hidden, Title,HasUniqueRoleAssignments,RootFolder | Where-Object {$_.Hidden -eq $False -and $_.Title -notin $ExcludedLists } #$_.BaseType -eq "DocumentLibrary"
Write-Host "Number of lists $($ll.Count)";
foreach($list in $ll)
{
$listUrl = $list.RootFolder.ServerRelativeUrl;
#Get all list items in batches
$ListItems = Get-ListItems_WithUniquePermissions -List $list
ForEach($item in $ListItems)
{
$type= $item.FileSystemObjectType;
getSharingLink $item $type $siteUrl $listUrl $list.Id;
}
}
$global:Results | Export-CSV $ReportOutput -NoTypeInformation
#Export-CSV $ReportOutput -NoTypeInformation
Write-host -f Green "Sharing Links Update Report Generated Successfully!"
write-host $("End time " + (Get-Date))