Bulk approve files in SharePoint (With new Teams based Approvals)

Ashley Cave 20 Reputation points
2025-12-03T11:42:40.2366667+00:00

There is no method to approve in bulk files added to a document library using the new Approvals that go via Teams. While it is possible to request multiple items for approval, bulk approving or approving via automation is not working.

Within Power Automate, the existing "Set content approval status" does not work with the new approvals

Action 'Set_content_approval_status' failed: {"Message":"Content moderation is not enabled on the library","Succeeded":false,"ApprovalErrorCode":"contentModerationNotEnabled", ...

Attempting to set the _ApprovalStatus field via PowerShell (using PnP) does not work either, it will instead just leave the value at 0 ('Not submitted').

Set-PnPListItem -List $LibraryName -Identity $ItemId -Values @{"_ApprovalStatus" = 3}

I also attempted to set the _ApprovalAssignedTo, _ApprovalRespondedBy, and _ApprovalSentBy without luck.

There is a need to upload existing files (tens of thousands) that are already approved into the document library as a one-off import, so this bulk operation only needs to be run once. Manually approving these files is NOT an option.

Can someone help point me to a solution to this?


Edit: Look into Answer comments for ways around this

Microsoft 365 and Office | SharePoint | Development
0 comments No comments
{count} votes

Answer accepted by question author
  1. Steven-N 14,835 Reputation points Microsoft External Staff Moderator
    2025-12-03T12:43:34.0666667+00:00

    Hi Ashley Cave

    Thank you for reaching out to Microsoft Q&A forum

    Based on my research, the reason of this behavior because the new Approvals feature in SharePoint is fundamentally different from classic content approval. Classic approval relies on the ModerationStatus column and can be automated via Power Automate or PnP.

    In contrast, modern Approvals is powered by the Teams Approvals service and manages its own metadata fields, which are system-controlled. This design prevents direct updates through PowerShell or Flow actions. As far as I know, Microsoft currently does not provide a native bulk approval option for modern Approvals.

    You can review the full details in the official documentation here: Approvals in Lists & Document Libraries.

    However, from my perspective view, you can try below workaround methods to see if it can help you in this situation

    Kindly temporarily turn Require content approval ON for the target library, bulk mark existing files as Approved using the classic ModerationStatus field, then (optionally) turn it OFF again after migration, link instruction: Require approval of items in a list or library

    User's image

    You can try to use this cmdlet:

    <# 
    Notes:
    - Runs once for historical items. Do NOT use for the new Teams/Approvals feature.
    - Tested with PnP.PowerShell 2.x on PowerShell 7.x.
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteUrl,            # e.g. https://contoso.sharepoint.com/sites/Records
        [Parameter(Mandatory=$true)]
        [string]$LibraryName,        # e.g. "Documents" or "Contracts"
        [switch]$DisableModerationAfter, # add -DisableModerationAfter to turn OFF when done
        [int]$PageSize = 2000,
        [int]$MaxDegreeOfParallelism = 4
    )
    Write-Host "Connecting to $SiteUrl ..." -ForegroundColor Cyan
    Connect-PnPOnline -Url $SiteUrl -Interactive
    # 1) Ensure moderation (classic content approval) is ON
    Write-Host "Enabling classic content approval on '$LibraryName' ..." -ForegroundColor Cyan
    Set-PnPList -Identity $LibraryName -EnableModeration $true
    # 2) Pull items in pages; skip folders; skip already approved
    #    ModerationStatus values: Approved=0, Rejected=1, Pending=2, Draft=3, Scheduled=4
    Write-Host "Querying items in '$LibraryName' ..." -ForegroundColor Cyan
    $items = Get-PnPListItem -List $LibraryName -PageSize $PageSize -Fields "ID","FileLeafRef","FSObjType","OData__ModerationStatus"
    # Filter to files (FSObjType = 0) and not Approved (<> 0)
    $targets = $items | Where-Object { $_["FSObjType"] -eq 0 -and ($_.FieldValues["OData__ModerationStatus"] -ne 0) }
    Write-Host ("Items to approve: {0}" -f $targets.Count) -ForegroundColor Yellow
    if ($targets.Count -eq 0) {
        Write-Host "No items require approval. Exiting." -ForegroundColor Green
        return
    }
    # 3) Approve in parallel batches with simple retry
    $throttle = [System.Threading.SemaphoreSlim]::new($MaxDegreeOfParallelism, $MaxDegreeOfParallelism)
    $errors = [System.Collections.Concurrent.ConcurrentBag[object]]::new()
    $scriptBlock = {
        param($item, $LibraryName)
        $attempts = 0
        do {
            try {
                # Set classic ModerationStatus to Approved (0)
                Set-PnPListItem -List $LibraryName -Identity $item.Id -Values @{ "OData__ModerationStatus" = 0 } -ErrorAction Stop
                Write-Host ("Approved: ID={0}, Name={1}" -f $item.Id, $item.FieldValues["FileLeafRef"]) -ForegroundColor Green
                return
            }
            catch {
                $attempts++
                if ($attempts -lt 5) {
                    $delay = :Min(30, 5 * $attempts) # backoff up to 30s
                    Write-Host ("Retry {0} for ID={1}: {2}. Waiting {3}s..." -f $attempts, $item.Id, $_.Exception.Message, $delay) -ForegroundColor DarkYellow
                    Start-Sleep -Seconds $delay
                }
                else {
                    Write-Host ("FAILED after retries: ID={0}: {1}" -f $item.Id, $_.Exception.Message) -ForegroundColor Red
                    $global:errors.Add([PSCustomObject]@{
                        Id   = $item.Id
                        Name = $item.FieldValues["FileLeafRef"]
                        Error= $_.Exception.Message
                    })
                    return
                }
            }
        } while ($true)
    }
    Write-Host "Approving items in parallel (max $MaxDegreeOfParallelism) ..." -ForegroundColor Cyan
    $jobs = @()
    foreach ($t in $targets) {
        $throttle.Wait()
        $jobs += Start-Job -ScriptBlock {
            param($t, $LibraryName)
            Import-Module PnP.PowerShell
            # Reconnect inside job using the current context token (PnP cmdlets handle this via Connection persistence in PS7).
            # If your environment requires explicit reconnection, use Connect-PnPOnline with -Credentials or -Interactive here.
            & $using:scriptBlock $t $LibraryName
        } -ArgumentList $t, $LibraryName
        # Release semaphore when job completes
        Register-ObjectEvent -InputObject $jobs[-1] -EventName StateChanged -SourceIdentifier ("job_" + $jobs[-1].Id) -Action {
            $throttle.Release() | Out-Null
        } | Out-Null
    }
    # Wait for jobs
    $jobs | ForEach-Object { Receive-Job -Job $_ -Wait -AutoRemoveJob | Out-Null }
    # 4) Optional: turn OFF moderation again (return to normal library behavior)
    if ($DisableModerationAfter.IsPresent) {
        Write-Host "Disabling classic content approval on '$LibraryName' ..." -ForegroundColor Cyan
        Set-PnPList -Identity $LibraryName -EnableModeration $false
    }
    # 5) Summaries
    if ($errors.Count -gt 0) {
        Write-Host "Some items failed to approve. Writing errors to BulkApproveErrors.csv ..." -ForegroundColor Yellow
        $errors | Export-Csv -Path "BulkApproveErrors.csv" -NoTypeInformation -Encoding UTF8
    } else {
        Write-Host "All targeted items set to Approved." -ForegroundColor Green
    }
    Write-Host "Done."
    
    

    For more information: https://learn.microsoft.com/en-us/sharepoint/dev/business-apps/power-automate/guidance/require-doc-approval

    Hope my answer will help you, for any further concern, kindly let me know in the comment section.


    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".     

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.