Auto Trimming version - Minor Major
# ===== Settings =====
$clientId = "xxxxxxxx"
$dateTime = Get-Date -Format "yyyy-MM-dd-HH-mm-ss"
$CertPath = Read-Host "Please enter Certificate (.PFX) Path"
$CertPassword = Read-Host "Please enter Certificate Password" -AsSecureString
$Org = "contoso.onmicrosoft.com"
$fileUrl = "https://contoso.sharepoint.com/sites/test/Documents/test_resh_retentionlabel.docx"
$invocation = Get-Variable -Name MyInvocation -ValueOnly
$directoryPath = Split-Path $invocation.MyCommand.Path
function Get-LibraryNameFromFileUrl {
param([string]$fileUrl, [string]$siteUrl)
# Remove base site URL
$relative = $fileUrl.Replace($siteUrl, "")
# Trim leading "/"
if ($relative.StartsWith("/")) {
$relative = $relative.Substring(1)
}
# First path segment = library name
$libraryName = $relative.Split("/")[0]
return $libraryName
}
# ===== Helper: Extract the Web (site) URL from a full file URL =====
function Get-WebUrlFromFileUrl {
param([string]$fileUrl)
$u = [System.Uri]$fileUrl
$segments = $u.AbsolutePath.Trim('/').Split('/')
if ($segments.Length -ge 2 -and ($segments[0] -ieq 'sites' -or $segments[0] -ieq 'teams')) {
return "$($u.Scheme)://$($u.Host)/$($segments[0])/$($segments[1])"
}
else {
return "$($u.Scheme)://$($u.Host)"
}
}
# ===== Helper: Run an operation with exponential backoff retries =====
function Invoke-WithRetry {
param(
[Parameter(Mandatory=$true)][ScriptBlock]$Operation,
[int]$MaxRetries = 8,
[int]$BaseDelaySeconds = 2,
[string]$Context = ""
)
for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) {
try {
return & $Operation
}
catch {
$msg = $_.Exception.Message
$transient = ($msg -match '429' -or $msg -match 'throttl' -or $msg -match '5\d\d' -or $msg -match 'temporar' -or $msg -match 'timeout')
if ($attempt -lt $MaxRetries) {
$delay = [Math]::Pow(2, $attempt - 1) * ($transient ? $BaseDelaySeconds : 1)
Log-Message "Attempt $attempt failed ($Context): $msg. Retrying in $delay sec..." "Info"
Start-Sleep -Seconds $delay
}
else {
Log-Message "Operation failed after $MaxRetries attempts ($Context): $msg" "Error"
throw
}
}
}
}
# ===== Connect to SharePoint using PnP (App-only with certificate) =====
try {
$webUrl = Get-WebUrlFromFileUrl -fileUrl $fileUrl
Log-Message "Connecting to site: $webUrl" "Info"
Connect-PnPOnline -Url $webUrl `
-ClientId $clientId `
-Tenant $Org `
-CertificatePath $CertPath `
-CertificatePassword $CertPassword
# Cmdlet syntax reference: Connect-PnPOnline (App-only with certificate) [Docs] [1](https://github.com/PowerShell/PowerShell/issues/24892)
$web = Invoke-WithRetry -Context "Get-PnPWeb" -Operation { Get-PnPWeb }
Log-Message "Connected to web: $($web.Title)" "Success"
}
catch {
Log-Message "PnP connection failed: $_" "Error"
exit 1
}
# ===== Resolve server-relative path and parent folder =====
$serverRelativeUrl = ([System.Uri]$fileUrl).AbsolutePath
if (-not $serverRelativeUrl) {
Log-Message "Could not resolve server-relative URL from fileUrl: $fileUrl" "Error"
exit 1
}
# ===== Pre-flight: Get the list item backing the file =====
try {
$listItem = Invoke-WithRetry -Context "Get-PnPFile -AsListItem" -Operation {
Get-PnPFile -Url $serverRelativeUrl -AsListItem -ErrorAction Stop
}
$file = Invoke-WithRetry -Context "Get-PnPFile -As file" -Operation {
Get-PnPFile -Url $serverRelativeUrl -ErrorAction Stop
}
if (-not $listItem -or -not $listItem.Id) {
throw "List item not found for $serverRelativeUrl"
}
Log-Message "Located file item Id=$($listItem.Id)" "Success"
}
catch {
Log-Message "The file/list item was not found: $serverRelativeUrl. $_" "Error"
exit 1
}
$list = Get-LibraryNameFromFileUrl -fileUrl $fileUrl -siteUrl $webUrl
# ===== Loop: update Title and publish (major) each time =====
for ($j = 1; $j -le 3; $j++) {
try{
for ($i = 1; $i -le 511; $i++) {
$newTitle = "test $i"
Log-Message "[$i/550] Setting Title='$newTitle'" "Info"
# Update Title on the list item
Invoke-WithRetry -Context "Set-PnPListItem (Title=$newTitle)" -Operation {
Set-PnPListItem -List $list `
-Identity $listItem.Id `
-Values @{ "Title" = $newTitle } `
-ErrorAction Stop
}
}
Invoke-WithRetry -Context "Set-PnPFileCheckedIn (Major)" -Operation {
$file.Publish("Major version published by script")
$file.Update()
Invoke-PnPQuery
}
# Major check-in publishes a major version [Docs] [4](https://pnp.github.io/powershell/cmdlets/Set-PnPFileCheckedIn.html)
Log-Message "Updated Title and published major version (Title='$newTitle')" "Success"
}
catch {
Log-Message "Error at iteration $i (Title='$newTitle'): $_" "Error"
continue
}
}
Log-Message "Completed Title updates from 'test 1' to 'test 550' with major versions." "Success"
