Our Story

Who we are and how we solve complex IT challenges.

Our Certifications

Microsoft certifications and partnerships validating our technical expertise.

Leadership

Meet the Experienced Leadership Team Driving WME’s Success

Advisory Updates

Expert guidance on Microsoft, security, and compliance developments.

Case Studies

Real-world outcomes from complex Microsoft-focused IT engagements.

Financial Industry

Secure technology solutions for regulated banks and financial institutions.

Healthcare

Secure Microsoft solutions for compliant, connected, and modern healthcare organizations.

Manufacturing

Cloud and security solutions supporting modern manufacturing operations.

Non-Profit

Cost-efficient Microsoft solutions for mission-driven organizations.

Public Sector

Microsoft-based IT services for secure public sector modernization.

High Tech

Scalable cloud, security, and staffing for fast-growing technology companies.

SMBs

Scalable cloud, security, and staffing for fast-growing technology companies.

Cloud Migration Services

Transition your workloads to the cloud securely for greater scalability and performance.

Data Migration Services

Securely transfer your business data with minimal downtime and maximum integrity.

Application Migration Services

Move your applications seamlessly to modern platforms with minimal business disruption.

Identity & Security Migration Services

Strengthen identity management and security while transitioning to modern Microsoft solutions.

IT Staffing

Connect with skilled IT professionals to strengthen your team and accelerate project delivery.

Accounting & Finance

Connect with experienced accounting and finance professionals to support your business goals.

Licensing Support

Discover the benefits of both CSP and On-premises licensing options and find the best fit for your unique business needs. From cost savings to flexibility, we’ve got you covered.

Power Platform

Unlock the full potential of the Microsoft Power Platform Suite to streamline operations, automate repetitive tasks, and gain real-time insights that drive business growth.

Sharepoint Solutions

Supercharge your business productivity and enhance visibility through our proven SharePoint expertise.

Security Solutions

Protect your business with proactive cybersecurity, compliance, and risk management solutions.

Endpoint Management

Secure, manage, and monitor every device with modern endpoint management solutions.

Automating Sensitivity Label and Encryption Removal in SharePoint Online with PowerShell

June 18, 2025
Encryption Removal in SharePoint Online with PowerShell

In modern enterprise environments, sensitivity labels and encryption policies are critical for protecting data. However, there are scenarios—such as data migration, restructuring, or administrative overrides—where these protections need to be temporarily removed. This blog post walks through a PowerShell script designed to automate the removal of Microsoft Purview sensitivity labels and encryption from files stored in SharePoint Online document libraries.

This script is particularly useful for IT administrators managing large-scale SharePoint environments where manual intervention would be inefficient and error-prone. It leverages the PnP PowerShell module and Microsoft Entra ID app registrations to authenticate and perform operations across multiple sites.

Requirements

To run this script, you will need PowerShell 7 installed along with the PnP.PowerShell module. You will also need four app registrations within Entra ID that are generated by PnP.Powershell. For more information on creating these app registrations, see https://pnp.github.io/powershell/articles/registerapplication.html.

Script

Here is the full script. You can copy and paste this to VS Code for modification. See below the script for an explanation of what the script does and what you might need to change.

# RUN FIRST BY ITSELF, NOT AS PART OF WHOLE SCRIPT EXECUTION
if ($cred -eq $null) {$cred = get-credential}

$temp_owner = "admin@domain.onmicrosoft.com" # temporary site collection administrator (should be account running script)
$justification_text = "Administrator Change." # justification to provide when label is removed
$pnp_clientID_1 = "<app id 1>" # application ID of Pnp.Powershell Entra ID app
$pnp_clientID_2 = "<app id 2>" # application ID of Pnp.Powershell Entra ID app
$pnp_clientID_3 = "<app id 3>" # application ID of Pnp.Powershell Entra ID app
$pnp_clientID_4 = "<app id 4>" # application ID of Pnp.Powershell Entra ID app
$SP_domain = "<sp domain>" # part between https:// and .sharepoint.com
$log_file = 'C:\Users\admin\Downloads\remove-label-SP.txt'
$completed_log = 'C:\Users\admin\Downloads\remove-label-completed-SP.txt'

$sites = import-csv 'C:\Users\admin\Downloads\Data explorer _ Microsoft Purview-SP.csv' # sites to process. exported from Purview reports.

# create log files
$date = get-date -format "MM-dd-yyyyTHH:mm:ss"
$f_log_file = ($log_file.replace(".txt","_$date.txt")).replace(":","_")
$f_log_file = $f_log_file.replace("C_","C:")
if ((test-path $f_log_file) -eq $false) {new-item -type file $f_log_file | out-null}
add-content $f_log_file "$date SCRIPT_START"

if ((test-path $completed_log) -eq $false) {new-item -type file $completed_log | out-null}
add-content $completed_log "$date SCRIPT_START"
$date = ""

# connect to SharePoint
$pnp_url = "https://" + $SP_domain + ".sharepoint.com/"
$pnp_clientID = $pnp_clientID_1
connect-pnponline -url $pnp_url -clientid $pnp_clientID -credential $cred
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") CONNECTED USING APP REG 1"

# set up progress bar
$count = 0
$total = $sites.count

ForEach ($site in $sites) {

# set counts for progress bar
$count = $count + 1
$percent = $count/$total
$percent_readable = $percent.tostring("P")
$percent_bar = $percent * 100
# display progress bar
write-progress "processing $count of $total - $percent_readable complete." -PercentComplete $percent_bar

$site_url = $site.URL

add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") START $site_url"

# connect to site
set-pnptenantsite $site_url -owners $temp_owner
connect-pnponline -url $site_url -clientid $pnp_clientID -credential $cred

# Get All Files from the document library - In batches of 500
$lists = get-pnplist | where-object -filterscript {$_.BaseTemplate -eq "101" -AND $_.ItemCount -ne "0" -AND $_.Title -ne "_catalogs/hubsite" -AND $_.Title -ne "Style Library" -AND $_.Title -notlike "PersistedManagedNavigationList*"}

$list_count = $lists.count
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") FOUND $list_count List(s)"

ForEach ($list in $lists) {
$list_title = $list.Title

add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") PROCESSING $list_title"

$list_items = Get-PnPListItem -List $list_title -pagesize 4000 | where-object -filterscript {$_.FieldValues['_DisplayName'] -like "L2*"}

# set up progress bar
$count = 0
$total = $list_items.count

$list_items_count = $list_items.count
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") FOUND $list_items_count Files with L2 Label"

$list_processing_count = 0

do {
ForEach ($item in $list_items) {
# set counts for progress bar
$count = $count + 1
$percent = $count/$total
$percent_readable = $percent.tostring("P")
$percent_bar = $percent * 100
# display progress bar
write-progress "processing $count of $total - $percent_readable complete." -PercentComplete $percent_bar

$file_url = $Item.FieldValues['FileRef']
$url = ($pnp_url + $file_url).replace(".com//",".com/")

try {
Unlock-PnPSensitivityLabelEncryptedFile -Url $url -JustificationText $justification_text -erroraction stop

add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") RELABELED $file_url"
}
catch {
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") ERR_RELAB $file_url $($_.Exception.Message)"

if ($($_.Exception.Message) -eq "ResourceBudgetExceeded") {
$id_rotated = 0
write-host "ResourceBudgetExceeded. Changing App Reg." -foregroundcolor red
if ($pnp_clientID -eq $pnp_clientID_1 -AND $id_rotated -eq 0) {
$pnp_clientID = $pnp_clientID_2
connect-pnponline -url $site_url -clientid $pnp_clientID -credential $cred
write-host "changed from client id 1 to client ID 2" -foregroundcolor yellow
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") CHANGED TO APP REG 2"
$id_rotated = 1
start-sleep -s 120
}
if ($pnp_clientID -eq $pnp_clientID_2 -AND $id_rotated -eq 0) {
$pnp_clientID = $pnp_clientID_3
connect-pnponline -url $site_url -clientid $pnp_clientID -credential $cred
write-host "changed from client id 2 to client ID 3" -foregroundcolor yellow
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") CHANGED TO APP REG 3"
$id_rotated = 1
start-sleep -s 120
}
if ($pnp_clientID -eq $pnp_clientID_3 -AND $id_rotated -eq 0) {
$pnp_clientID = $pnp_clientID_4
connect-pnponline -url $site_url -clientid $pnp_clientID -credential $cred
write-host "changed from client id 3 to client ID 4" -foregroundcolor yellow
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") CHANGED TO APP REG 4"
$id_rotated = 1
start-sleep -s 120
}
if ($pnp_clientID -eq $pnp_clientID_4 -AND $id_rotated -eq 0) {
$pnp_clientID = $pnp_clientID_1
connect-pnponline -url $site_url -clientid $pnp_clientID -credential $cred
write-host "changed from client id 4 to client ID 1" -foregroundcolor yellow
add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") CHANGED TO APP REG 1"
$id_rotated = 1
start-sleep -s 120
}
}
}

$list_processing_count++

$percent = 0
$percent_readable = ""
$percent_bar = 0
$file_url = ""
$url = ""

start-sleep -seconds 1
}
}
until ($list_processing_count -eq $list_items_count)

add-content -path $f_log_file -value "$(Get-Date -format "MM-dd-yyyyTHH:mm:ss") COMPLETED $list_title"

$list_title = ""
$list_items = ""
$list_items_count = 0
}

remove-pnpsitecollectionadmin -owners $temp_owner

$date = get-date -format "MM-dd-yyyyTHH:mm:ss"
add-content -path $f_log_file -value "$date COMPLETED $site_url"
add-content -path $completed_log -value "$date COMPLETED $site_url"
write-host "$date COMPLETED $site_url" -foregroundcolor green
$date = ""

$site_url = ""
$lists = ""
$list_count = ""
}

$date = get-date -format "MM-dd-yyyyTHH:mm:ss"
add-content -path $f_log_file -value "$date SCRIPT_END"
add-content -path $completed_log -value "$date SCRIPT_END"
$date = ""

Script Overview and Breakdown

1. Credential Handling

The script begins by checking if credentials are already stored. If not, it prompts the user to enter them:

if ($cred -eq $null) {$cred = get-credential}

Note that this needs to be run independently of the script. This will also need to be an account that is in Entra ID with at least SharePoint Administrator access. This account also either needs to be either temporarily excluded from MFA or have a policy in Entra ID Conditional Access temporarily applied to it so that it is not prompted to MFA. Setting up either of these options is out of scope for this blog post.

2. Variable Initialization

Next, variables are configured to assist with script execution. Follow the notes next to the variables for an explanation of the variable. Note that the site collection administrator should be the same account used above.

3. Logging

Two log files are created: one for general logging and another to track completed sites.

4. CSV Input

The script also imports a CSV file containing the list of SharePoint sites to process—typically exported from Microsoft Purview reports.

5. Connect to SharePoint

The script connects to SharePoint Online using the first app registration ID.

6. Identifying and Processing Labeled Files

Within each library, the script identifies files with the label specified in the variable.

7. Throttling and App Registration Rotation

Microsoft 365 APIs enforce throttling limits. If the script encounters a ResourceBudgetExceeded error, it automatically rotates to the next app registration ID and reconnects. This built-in resilience allows the script to continue processing without manual intervention.

8. Cleanup and Final Logging

After processing each site, the script removes the temporary admin and logs the completion. This ensures that permissions are not left elevated and that the process is fully auditable.

Conclusion

This PowerShell script is a powerful tool for SharePoint Online administrators needing to remove sensitivity labels and encryption at scale. It automates a complex, multi-step process while incorporating robust error handling, logging, and authentication management.

By leveraging Microsoft Purview reports, PnP PowerShell, and app registration rotation, the script ensures compliance and efficiency in sensitive data operations. Whether you’re preparing for a migration, restructuring your information architecture, or responding to an urgent business need, this script can save hours of manual work and reduce the risk of human error.

Disclaimer

All content provided on this blog is for information purposes only. Windows Management Experts, Inc. makes no representation as to the accuracy or completeness of any information on this site. Windows Management Experts, Inc. will not be liable for any errors or omissions in this information nor for the availability of this information. It is highly recommended that you consult one of our technical consultants, should you need any further assistance.

Windows Management Experts

Now a Microsoft Solutions Partner for:

✓ Data & AI

✓ Digital and App Innovation

✓ Infrastructure

✓ Security

The Solutions Partner badge highlights WME’s excellence and commitment. Microsoft’s thorough evaluation ensures we’re skilled, deliver successful projects, and prioritize security over everything. This positions WME in a global tech community, ready to innovate on the cloud for your evolving business needs.

Contact us: sales@winmgmtexperts.com

Share:

Facebook
Twitter
LinkedIn

Get Microsoft Updates Before They Cost You Downtime

Retirement dates, licensing changes, and security updates from a Microsoft-exclusive team, sent when they matter, not on a filler schedule.

More Posts

Copilot Cowork: Credit-Based Billing

Until now, Copilot Cowork has been included with M365 Copilot Premium licenses. Now that Cowork has moved out of public preview, Cowork is introducing a ...
Read Full Article
SharePoint OTP Retirement Is Coming in July 2026

SharePoint OTP Retirement Is Coming in July 2026 — What IT Admins Need to Do Before Access Breaks

Starting July 2026, external users who access OneDrive and SharePoint files through legacy SPO OTP links will start receiving access denied — silently, with no ...
Read Full Article
Power Virtual Agents Is Gone. Here's What Replaced It and Why It Matters.

Power Virtual Agents Is Gone. Here’s What Replaced It and Why It Matters.

If someone on your team still calls it “Power Virtual Agents,” they’re working from an outdated map. Microsoft retired the product on November 15, 2023 ...
Read Full Article

Get Microsoft Updates Before They Cost You Downtime

Retirement dates, licensing changes, and security updates from a Microsoft-exclusive team, sent when they matter, not on a filler schedule.
Subscription Form email