Created
April 2, 2025 08:56
-
-
Save AlexanderHolmeset/2b2eb127d8a412a23401b4e8037bd6ec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Alexander Holmeset | |
# Version 1.0 | |
# This script is designed to extract sensitivity labels from files in SharePoint document libraries in one tenant and apply them to the same files in another tenant. | |
# It uses Microsoft Graph API to interact with SharePoint and sensitivity labels. | |
# The script connects to two different tenants using app registrations and retrieves files from SharePoint document libraries. | |
# App regstiration permissions source: Directory.Read.All, Files.Read.All, Sites.Read.All and InformationProtectionPolicy.Read.All | |
# App regstiration permissions target: Directory.Read.All, Files.ReadWrite.All, Sites.Read.All and InformationProtectionPolicy.Read.All | |
# Parameters for the app registration source tenant | |
$ClientId = "xxxxxxx" | |
$TenantId = "xxxxxxx" | |
$ClientSecret = "xxxxxxx" | |
# Parameters for the app registration target tenant | |
$ClientId2 = "xxxxxxx" | |
$TenantId2 = "xxxxxxx" | |
$ClientSecret2 = "xxxxxxx" | |
# File types to check | |
$filetypes = "docx", "docm", "xlsx", "xlsm", "xlsb", "pptx", "ppsx", "pdf" | |
# Define the hash table with lookup values. This is used to map the sensitivity labels to their corresponding values in the new tenant. | |
# The keys are the sensitivity labels from the source tenant, and the values are the corresponding labels in the target tenant. | |
# You can add more mappings as needed. | |
$lookupTable = @{ | |
"Internal" = "InternalRestricted" | |
"Public" = "PublicUnrestricted" | |
"Confidential" = "ConfidentialRestricted" | |
} | |
# Convert the Client Secret to a SecureString | |
$SecureClientSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force | |
# Create a PSCredential object with the Client ID and Secure Client Secret | |
$ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ClientId, $SecureClientSecret | |
# Connect to Microsoft Graph using the Tenant ID and Client Secret Credential in the source tenant | |
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential | |
# Initialize the output array | |
$OutputData = @() | |
# Function to get all items in a drive (document library) recursively | |
function Get-AllDriveItems { | |
param ( | |
[Parameter(Mandatory)] | |
[string]$DriveId, | |
[Parameter()] | |
[string]$ParentId, | |
[Parameter()] | |
[string]$CurrentPath = "" | |
) | |
$Items = @() | |
if ($ParentId) { | |
# Write-Host "Processing folder with ParentId: $ParentId" -ForegroundColor Cyan | |
try { | |
$ParentItem = Get-MgDriveItem -DriveId $DriveId -DriveItemId $ParentId -ExpandProperty 'children' -ErrorAction Stop | |
$DriveItems = $ParentItem.Children | |
if($DriveItems) { | |
Write-Host "Found $($DriveItems.Count) child items in folder '$($ParentItem.Name)' (ParentId: $ParentId)" -ForegroundColor Green | |
} else { | |
Write-Host "No child items found in folder '$($ParentItem.Name)' (ParentId: $ParentId)" -ForegroundColor Yellow | |
} | |
} catch { | |
Write-Warning "Failed to get children for item ID '$ParentId'. Error: $_" | |
return $Items | |
} | |
} else { | |
Write-Host "Processing root folder" -ForegroundColor Cyan | |
try { | |
$RootItem = Get-MgDriveRoot -DriveId $DriveId -ExpandProperty 'children' -ErrorAction Stop | |
$DriveItems = $RootItem.Children | |
if($DriveItems) { | |
Write-Host "Found $($DriveItems.Count) items in root folder" -ForegroundColor Green | |
} else { | |
Write-Host "No items found in root folder" -ForegroundColor Yellow | |
} | |
} catch { | |
Write-Error "Failed to get root item and its children. Error: $_" | |
return $Items | |
} | |
} | |
if (-not $DriveItems) { | |
Write-Verbose "No items found at this level." | |
return $Items | |
} | |
foreach ($Item in $DriveItems) { | |
# Update the file path | |
if ($CurrentPath -eq "") { | |
$ItemPath = "/$($Item.Name)" | |
} else { | |
$ItemPath = "$CurrentPath/$($Item.Name)" | |
} | |
# Add the ItemPath property to the item | |
$Item | Add-Member -NotePropertyName 'ItemPath' -NotePropertyValue $ItemPath | |
$Items += $Item | |
Write-Host "Found item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Green | |
if ($Item.Folder) { | |
Write-Host "Recursing into folder: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Cyan | |
if ($Item.Id) { | |
$Items += Get-AllDriveItems -DriveId $DriveId -ParentId $Item.Id -CurrentPath $ItemPath | |
} else { | |
Write-Warning "Item '$($Item.Name)' has a null ID and cannot be processed." | |
} | |
} | |
} | |
return $Items | |
} | |
# Now, process all SharePoint sites and their document libraries | |
Write-Host "Starting to process all SharePoint sites and their document libraries..." -ForegroundColor Magenta | |
try { | |
# Retrieve all SharePoint sites | |
$sites = Get-MgSite -All | Where-Object { $_.WebUrl -notlike "*personal*" } | |
} catch { | |
Write-Error "Failed to retrieve SharePoint sites. Error: $_" | |
exit | |
} | |
foreach ($site in $sites) { | |
try { | |
Write-Host "Processing site: $($site.DisplayName) <$($site.WebUrl)>" -ForegroundColor Magenta | |
# Get all document libraries (drives) in the site | |
$drives = Get-MgSiteDrive -SiteId $site.Id -ErrorAction Stop | |
foreach ($drive in $drives) { | |
Write-Host "Processing document library: $($drive.Name)" -ForegroundColor Cyan | |
$driveId = $drive.Id | |
# Get all items in the document library starting from the root | |
$AllItems = Get-AllDriveItems -DriveId $driveId | |
Write-Host "Total items retrieved in document library '$($drive.Name)': $($AllItems.Count)" -ForegroundColor Green | |
foreach ($Item in $AllItems) { | |
Write-Host "Processing item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Gray | |
# Check if item is a file | |
if ($Item.File) { | |
try { | |
# Get versions for the item | |
$labels = @() | |
If($filetypes -contains (($item.ItemPath).split(".")[1]) ){ | |
write-Host "Filetype: $($item.ItemPath).split(".")[1]" -ForegroundColor Gray | |
$labels = (Invoke-GraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/drives/$($driveId)/items/$($Item.Id)/extractSensitivityLabels" -ContentType "application/json" -ErrorAction Stop).labels | |
if($labels){ | |
foreach ($label in $labels) { | |
$labelname = @() | |
$labelname = (Invoke-GraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/security/informationProtection/sensitivityLabels/$($label.sensitivityLabelId)" -ContentType "application/json" -ErrorAction Stop).name | |
Write-Host "Sensitivity label: $($label.name)" -ForegroundColor Yellow | |
$OutputData += [PSCustomObject]@{ | |
SiteName = $site.DisplayName | |
LibraryName = $drive.Name | |
ItemName = $Item.Name | |
ItemPath = $Item.ItemPath | |
ItemId = $Item.Id | |
SensitivityLabel = $labelname | |
} | |
} | |
} | |
} | |
} catch { | |
Write-Warning "Failed to get versions for item '$($Item.Name)'. Error: $_" | |
continue | |
} | |
} | |
} | |
} | |
} catch { | |
Write-Warning "Failed to process site $($site.DisplayName). Error: $_" | |
continue | |
} | |
} | |
Disconnect-MgGraph | |
# Convert the Client Secret to a SecureString | |
$SecureClientSecret2 = ConvertTo-SecureString -String $ClientSecret2 -AsPlainText -Force | |
# Create a PSCredential object with the Client ID and Secure Client Secret | |
$ClientSecretCredential2 = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ClientId2, $SecureClientSecret2 | |
# Connect to Microsoft Graph using the Tenant ID and Client Secret Credential | |
Connect-MgGraph -TenantId $TenantId2 -ClientSecretCredential $ClientSecretCredential2 | |
# Now, process all SharePoint sites and their document libraries | |
Write-Host "Starting to process all SharePoint sites and their document libraries..." -ForegroundColor Magenta | |
try { | |
# Retrieve all SharePoint sites | |
$sites = Get-MgSite -All | Where-Object { $_.WebUrl -notlike "*personal*" } | |
} catch { | |
Write-Error "Failed to retrieve SharePoint sites. Error: $_" | |
exit | |
} | |
$LabelsDestination = (Invoke-GraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/security/informationProtection/sensitivityLabels/" -ContentType "application/json" -ErrorAction Stop | ConvertTo-Json | ConvertFrom-Json).value | |
foreach ($site in $sites) { | |
try { | |
#Write-Host "Processing site: $($site.DisplayName) <$($site.WebUrl)>" -ForegroundColor Magenta | |
# Get all document libraries (drives) in the site | |
$drives = Get-MgSiteDrive -SiteId $site.Id -ErrorAction Stop | |
foreach ($drive in $drives) { | |
#Write-Host "Processing document library: $($drive.Name)" -ForegroundColor Cyan | |
$driveId = $drive.Id | |
# Get all items in the document library starting from the root | |
$AllItems = Get-AllDriveItems -DriveId $driveId | |
#Write-Host "Total items retrieved in document library '$($drive.Name)': $($AllItems.Count)" -ForegroundColor Green | |
foreach ($Item in $AllItems) { | |
#Write-Host "Processing item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Gray | |
# Check if item is a file | |
if ($Item.File) { | |
try { | |
# Get versions for the item | |
$labels = @() | |
If($filetypes -contains (($item.ItemPath).split(".")[1]) ){ | |
#Find file in source tenant | |
$labels = $OutputData | Where-Object{$_.SiteName -contains $site.DisplayName -and $_.LibraryName -contains $drive.Name -and $_.ItemName -contains $Item.Name -and $_.ItemPath -contains $Item.ItemPath} | |
# If there are labels, process them | |
if($labels){ | |
foreach ($label in $labels) { | |
# Define the variable to check | |
$valueToCheck = "$($label.SensitivityLabel)" | |
$resultLabel = @() | |
# Lookup the value in the hash table | |
if ($lookupTable.ContainsKey($valueToCheck)) { | |
$resultLabel = $lookupTable[$valueToCheck] | |
Write-Output "The value for '$valueToCheck' is '$resultLabel'." | |
} else { | |
Write-Output "The value '$valueToCheck' is not found in the lookup table." | |
$resultLabel = $($label.SensitivityLabel) | |
} | |
$newlabel = @() | |
$newlabel = $LabelsDestination | Where-Object{$_.name -contains $resultLabel} | |
Write-Host "Setting sensitivity label: $($newlabel.id) $($resultLabel)" -ForegroundColor Yellow | |
Set-MgDriveItemSensitivityLabel -DriveId $driveId -DriveItemId $Item.Id -SensitivityLabelId $newlabel.id -ErrorAction Stop | |
exit | |
} | |
} | |
} | |
} catch { | |
Write-Warning "Failed to get versions for item '$($Item.Name)'. Error: $_" | |
continue | |
} | |
} | |
} | |
} | |
} catch { | |
Write-Warning "Failed to process site $($site.DisplayName). Error: $_" | |
continue | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment