Entra / Microsoft 365 ยท Compliance & audit
Update sensitivity labels
Applies or replaces sensitivity labels on files in a SharePoint document library, respecting label priority and optional removal.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -AppId $AppId -TenantId $TenantId -CertificateThumbprint $CertThumbprint
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([string] $TenantId = "",[string] $AppId = "",[string] $CertThumbPrint = "")function Get-DriveItems {[CmdletBinding()]param ([Parameter()]$DriveId,[Parameter()]$FolderId)# Get data for a folder and its children[array]$Data = Get-MgDriveItemChild -DriveId $DriveId -DriveItemId $FolderId -All# Split the data into files and folders[array]$Folders = $Data | Where-Object {$_.folder.childcount -gt 0} | Sort-Object Name$Global:TotalFolders = $TotalFolders + $Folders.Count[array]$Files = $Data | Where-Object {$null -ne $_.file.mimetype}If ($RemoveLabels -eq $True) {$RequestBody = @{}$RequestBody.Add("sensitivityLabelId", "")}# Process the files to find each has a sensitivity label. If not, label itForEach ($File in $Files) {$Status = $false$FileType = $File.Name.Split(".")[1]If ($FileType -in $ValidFileTypes) {$OKToApplyLabel = $false; $OldSensitivityLabelName = $nullTry {# Exract the existing sensitivity label information for the file$SensitivityLabelName = $null; $SensitivityLabelInfo = $null$Uri = ("https://graph.microsoft.com/V1.0/sites/{0}/drive/items/{1}/extractSensitivityLabels" -f $Site.Id, $File.Id)[array]$SensitivityLabelInfo = Invoke-MgGraphRequest -Uri $Uri -Method post -OutputType PsObject -ErrorAction Stop} Catch {Write-Host ("Error retrieving sensitivity label information for {0} ({1})" -f $File.Name, $_.Exception.Message) -ForegroundColor Red$OKToApplyLabel = $true}# Check the label information for the current file[array]$LabelsFound = $SensitivityLabelInfo.Labels}If ($RemoveLabels -eq $False) {If ($LabelsFound.Count -eq 0) {# Go ahead and label if no existing labels are found.$OKToApplyLabel = $true} Else {# Check if the file already has the sensitivity label we want to apply. If it does, skip it.If ($SensitivityLabelInfo.Labels[0].sensitivitylabelId -eq $SensitivityLabelId) {Write-Host ("Skipping {0} as it already has the sensitivity label we want to apply" -f $File.Name) -ForegroundColor YellowContinue}# Check the priority of the existing label against the priority of the new label we want to apply. We only want to apply the new label if it has a higher priority (lower number) than the existing label, otherwise we could end up applying a lower priority label over a higher priority one, which would not be good![int]$ExistingLabelPriority = $SensitivityLabelsFiles | Where-Object {$_.Id -eq $LabelsFound[0].sensitivityLabelId} | Select-Object -ExpandProperty PriorityIf ($ReplacementLabelPriority -gt $ExistingLabelPriority) {$OKToApplyLabel = $true} Else {Write-Host ("Skipping {0} as it has an existing sensitivity label {1} with higher or equal priority than the new label applied by this process" -f $File.Name, $LabelsFound[0].Name) -ForegroundColor YellowContinue}}}# After all the checks, if we have determined that it's OK to apply the new label, go ahead and run the cmdletIf ($OKToApplyLabel -eq $true -and $RemoveLabels -eq $false) {Write-Host ("Applying sensitivity label {0} to {1}" -f $NewSensitivityLabelName, $File.Name) -ForegroundColor GreenTry {Set-MgDriveItemSensitivityLabel -DriveId $DriveId -DriveItemId $File.Id -SensitivityLabelId $SensitivityLabelId -ErrorAction Stop$Status = $true} Catch {Write-Host ("Error applying label {0} to {1} ({2})" -f $SensitivityLabelName, $File.Name, $_.Exception.Message) -ForegroundColor RedContinue}}# If we're removing labels, do it hereIf ($RemoveLabels -eq $true) {Write-Host ("Removing sensitivity label from {0}" -f $File.Name) -ForegroundColor GreenTry {Set-MgDriveItemSensitivityLabel -DriveId $DriveId -DriveItemId $File.Id -SensitivityLabelId "" -ErrorAction Stop# Could also have done this with# Invoke-MgGraphRequest -Uri $Uri -Method Post -Body $RequestBody -ErrorAction Stop$Status = $true} Catch {Write-Host ("Error removing sensitivity labels from {0} ({1})" -f $File.Name, $_.Exception.Message) -ForegroundColor RedContinue}}# Collect information for the reportIf ($File.LastModifiedDateTime) {$LastModifiedDateTime = Get-Date $File.LastModifiedDateTime -format 'dd-MMM-yyyy HH:mm'} Else {$LastModifiedDateTime = $null}If ($File.CreatedDateTime) {$FileCreatedDateTime = Get-Date $File.CreatedDateTime -format 'dd-MMM-yyyy HH:mm'} Else {$FileCreatedDateTime = $null}If ($LabelsFound) {$OldSensitivityLabelName = $SensitivityLabelsFiles | Where-Object {$_.Id -eq $LabelsFound[0].sensitivityLabelId} | Select-Object -ExpandProperty Name}If ($Status -eq $true) {$ReportLine = [PSCustomObject]@{FileName = $File.NameFolder = $File.parentreference.nameSize = (FormatFileSize $File.Size)Created = $FileCreatedDateTimeAuthor = $File.CreatedBy.User.DisplayNameLastModified = $LastModifiedDateTime'Last modified by' = $File.LastModifiedBy.User.DisplayName'Old Label Name' = If ($OldSensitivityLabelName) {$OldSensitivityLabelName} Else {"No existing label"}'Action' = If ($RemoveLabels -eq $true) {"Removed label"} Else {"Applied label"}Timestamp = Get-Date -Format 'dd-MMM-yyyy HH:mm:ss'}$ReportData.Add($ReportLine)}}# Process the foldersForEach ($Folder in $Folders) {Write-Host ("Processing folder {0} ({1} files/size {2})" -f $Folder.Name, $Folder.folder.childcount, (FormatFileSize $Folder.Size))Get-DriveItems -Drive $DriveId -FolderId $Folder.Id}}function FormatFileSize {# Format File Size nicelyparam ([parameter(Mandatory = $true)]$InFileSize)If ($InFileSize -lt 1KB) { # Format the size of a document$FileSize = $InFileSize.ToString() + " B"}ElseIf ($InFileSize -lt 1MB) {$FileSize = $InFileSize / 1KB$FileSize = ("{0:n2}" -f $FileSize) + " KB"}Elseif ($InFileSize -lt 1GB) {$FileSize = $InFileSize / 1MB$FileSize = ("{0:n2}" -f $FileSize) + " MB"}Elseif ($InFileSize -ge 1GB) {$FileSize = $InFileSize / 1GB$FileSize = ("{0:n2}" -f $FileSize) + " GB"}Return $FileSize}# Disconnect from any previous Graph SDK sessionDisconnect-MgGraph -ErrorAction SilentlyContinue# The script is designed to run in app-only mode, so you need to create a registered app, make sure that the app can use the# metered APIs, and grant it the permissions listed above. Then insert the app details in the variables listed below to connect# in app-only mode using a certificate thumbprint. See https://aka.ms/graph-metered-overview for more information about the metered APIs and how to set up an app to use them.# Insert values for your tenant and app here$CertThumbprint = "6FBD36F588E12DE291100371E16219863E399818"# Connect to the Microsoft GraphConnect-MgGraph -NoWelcome -AppId $AppId -TenantId $TenantId -CertificateThumbprint $CertThumbprintWrite-Host "Setting up to assign Sensitivity labels to all files in the target document libraries..."$Global:SensitivityLabelsAvailable = $true[array]$Global:ValidfileTypes = "docx", "pptx", "xlsx", "pdf", "doc", "ppt", "xls"# Find the siteWrite-Host "Looking for matching sites..."[array]$Sites = Get-MgSite -Search ($SiteName)If (!($Sites)) { # Nothing foundWrite-Host "No matching sites found - exiting"break}If ($Sites.Count -eq 1) { # Only one site found - go ahead$Global:Site = $Sites[0]$SiteName = $Site.DisplayNameWrite-Host "Found site to process:" $SiteName} ElseIf ($Sites.Count -gt 1) {# More than one site found. Ask which to useClear-Host[int]$i = 1Write-Host "More than one matching site was found. We need you to select a site to report."Write-Host " "ForEach ($SiteOption in $Sites) {Write-Host ("{0}: {1} ({2})" -f $i, $SiteOption.DisplayName, $SiteOption.Name); $i++}Write-Host ""[Int]$Answer = Read-Host "Enter the number of the site to use"If (($Answer -gt 0) -and ($Answer -le $i)) {[int]$Si = ($Answer-1)$SiteName = $Sites[$Si].DisplayNameWrite-Host ("OK. Selected site is {0}" -f $Sites[$Si].DisplayName)$Global:Site = $Sites[$Si]}}If (!($Site)) {Write-Host ("Can't find the {0} site - script exiting" -f $Uri)break}# Find the document libraries in the site[array]$Drives = Get-MgSiteDrive -SiteId $Site.IdIf (!($Drives)) {Write-Host "No document libraries found in the site" -ForegroundColor RedBreak} Else {$Drives = $Drives | Where-Object {$_.Name -ne 'Preservation Hold Library'}}If ($Drives.Count -eq 1) { # Only one drive found - go ahead$Drive = $Drives$DriveName = $Drive.NameWrite-Host "Found drive to process:" $DriveName} Elseif ($Drives.Count -gt 1) { # More than one drive found. Ask which to useClear-Host; Write-Host "More than one drive found in site. We need you to select a drive to report."; [int]$i=1Write-Host " "ForEach ($DriveOption in $Drives) {Write-Host ("{0}: {1}" -f $i, $DriveOption.Name); $i++}Write-Host ""[Int]$Answer = Read-Host "Enter the number of the drive to use"If (($Answer -gt 0) -and ($Answer -le $i)) {[int]$Si = ($Answer-1)$DriveName = $Drives[$Si].NameWrite-Host "OK. Selected drive is" $Drives[$Si].Name$Drive = $Drives[$Si]}}If (!($Drive)) {Write-Host ("Can't find the selected drice ({0}) - script exiting" -f $Uri) ; break}If ($NewSensitivityLabelName.tolower() -ne "remove") {Write-Host ("Checking if it's possuble to apply the sensitivity label {0} to files in the {1} document library for the {2} site, replacing any existing label if the new label has a higher priority than the existing one." -f $NewSensitivityLabelName, $DriveName, $SiteName) -ForegroundColor Green$Global:RemoveLabels = $falseWrite-Output "Fetching sensitivity labels defined for the tenant..."# Find sensitivity labels for the tenant[array]$SensitivityLabels = Get-MgSecurityDataSecurityAndGovernanceSensitivityLabel -All# Reduce the set to the sensitivity labels that can be applied to files$SensitivityLabelsFiles = [System.Collections.Generic.List[Object]]::new()ForEach ($Label in $SensitivityLabels) {If ($Label.additionalProperties.applicableTo -like "*file*") {$ReportLine = [PSCustomObject]@{Id = $Label.IdName = $Label.NamePriority = $Label.PriorityHasProtection = $Label.HasProtection}$SensitivityLabelsFiles.Add($ReportLine)}}[int]$Global:ReplacementLabelPriority = $SensitivityLabelsFiles | Where-Object {$_.Name -eq $NewSensitivityLabelName} | Select-Object -ExpandProperty Priority# Have we found a sensitivity label to apply? We will know if we have found a priority value for that label...If ($ReplacementLabelPriority) {$Global:SensitivityLabelId = $SensitivityLabelsFiles | Where-Object {$_.Name -eq $NewSensitivityLabelName} | Select-Object -ExpandProperty IdWrite-Host ("Found chosen sensitivity label to apply: {0} (priority {1})" -f $NewSensitivityLabelName, $ReplacementLabelPriority) -ForegroundColor Green} Else {Write-Host ("Can't find the sensitivity label {0} to apply - script exiting" -f $NewSensitivityLabelName) -ForegroundColor Redbreak}} Else {$Global:RemoveLabels = $trueWrite-Output "Option Selected to remove sensitivity labels from labeled files."}# Start the ball rolling to process files in the target document library[datetime]$StartProcessing = Get-Date$Global:TotalFolders = 1# Create output list and CSV file$Global:ReportData = [System.Collections.Generic.List[Object]]::new()$CSVOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + ("\Files {0}-{1} library.csv" -f $Site.displayName, $DriveName)# Get the items in the root, including child foldersWrite-Host "Checking files to process sensitivity labels..."Get-DriveItems -Drive $Drive.Id -FolderId "root"[datetime]$EndProcessing = Get-Date$ElapsedTime = ($EndProcessing - $StartProcessing)$FilesPerMinute = [math]::Round(($ReportData.Count / ($ElapsedTime.TotalSeconds / 60)), 2)Write-Host ""Write-Host ("Processed {0} files in {1} folders in {2}:{3} minutes ({4} files/minute)" -f `$ReportData.Count, $TotalFolders, $ElapsedTime.Minutes, $ElapsedTime.Seconds, $FilesPerMinute)$ReportData | Out-GridView -Title ("Files in {0} document library for the {1} site" -f $DriveName, $SiteName)# Generate the report in either Excel worksheet or CSV format, depending on if the ImportExcel module is availableIf (Get-Module ImportExcel -ListAvailable) {$ExcelGenerated = $TrueImport-Module ImportExcel -ErrorAction SilentlyContinue$ExcelOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\SharePoint Labels.xlsx"$ReportData | Export-Excel -Path $ExcelOutputFile -WorksheetName "SharePoint Labels" -Title ("SharePoint Labels {0}" -f (Get-Date -format 'dd-MMM-yyyy')) -TitleBold -TableName "SharePointLabels"} Else {$CSVOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\SharePoint Labels.CSV"$ReportData | Export-Csv -Path $CSVOutputFile -NoTypeInformation -Encoding Utf8}If ($ExcelGenerated -eq $true) {Write-Host ("SharePoint labels report is available in Excel workbook {0}" -f $ExcelOutputFile)} Else {Write-Host ("SharePoint labels report is available in CSV file {0}" -f $CSVOutputFile)}
Parameters
ParameterDefaultNotes
-TenantId""Microsoft Entra tenant ID for app-only Graph authentication.-AppId""Application (client) ID for the app registration used to connect.-CertThumbPrint""Certificate thumbprint for app-only Graph authentication.Attribution
Author
Office365itpros