Skip to content

Instantly share code, notes, and snippets.

@AlexanderHolmeset
Created April 2, 2025 08:56
Show Gist options
  • Save AlexanderHolmeset/2b2eb127d8a412a23401b4e8037bd6ec to your computer and use it in GitHub Desktop.
Save AlexanderHolmeset/2b2eb127d8a412a23401b4e8037bd6ec to your computer and use it in GitHub Desktop.
# 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