Blog about anything related to my learnings
  • About
  • posts
bluesky
Blog about anything related to my learnings
POSTS

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"

Error creating minor version exceeding 511 Max of 511 versions Deletion of minor versions failed

    © Blog about anything related to my learnings 2026
    bluesky