It’s Free to Read Sensitivity Labels, But Assigning Sensitivity Labels Requires Payment
In early 2023, Microsoft introduced Graph APIs to read and apply sensitivity labels. In an article published after the APIs became available, I describe how to use the extractSensitivityLabels API to read sensitivity label information for Office documents and PDFs stored in SharePoint Online. Since then, I’ve used the API to report sensitivity label information in other scripts, including one to generate a report about files stored in a SharePoint Online document library. All in all, there’s no issue in finding sensitivity labels assigned to files.
Assigning sensitivity labels to files is a different matter. Microsoft is quite happy for tenants to read and report sensitivity labels that are in place, but they consider the assignSensitivityLabel API to be an “advanced API.” When I wrote my 2023 article, securing approval to use the API required going through some contortions with Microsoft bureaucracy, and I gave up after wasting several hours with zero results.
Enabling Protected APIs
The process is easier now because Microsoft now allows tenants to use advanced APIs by enabling apps that run the APIs by associating the apps with Azure subscriptions. Apparently, paying to use an advanced API is considered the “additional validation, beyond permission and consent, before you can use them.”
The full set of metered APIs is documented online. Currently, the set comprises the APIs needed to backup/export Teams chat and channel conversations (high-capacity APIs), Teams change notifications, and the API to assign sensitivity labels to files.
Working Out How to Assign Sensitivity Labels
As we all know, there’s plenty new in Microsoft 365 to keep anyone fully occupied, so I didn’t go back to test the assignSensitivityLabel API. But loyal readers of this website prompted for more information. The only answer was to break out Visual Studio code and consult the documentation for the assignSensitivityLabel API.
Linking an Entra ID App with an Azure Subscription
The first thing you’ll need is a suitable Entra ID app registration. You can reuse an app or create a new one. The app serves two purposes. Its service principal holds the permissions needed to interact with data (the content of SharePoint Online sites in this case) and the app identifier is how Azure knows that the app is authorized to use the metered API. The app needs an X.509 certificate to authenticate. Make sure that the app has consent to use the Sites.ReadWrite.All application permission as the app needs to be able to update documents in document libraries.
The next step is to link the app registration with an Azure subscription. The link allows Microsoft to charge for any calls the app makes to the metered API. If a suitable resource group doesn’t exist in the Azure subscription, you’ll need to create one before linking the app to the subscription. You’ll also need to know the GUIDs for the app and the Azure subscription.
Here’s the command I entered in the Azure cloud shell to create the association. It’s easier to prepare the command in a text editor and then paste it into the cloud shell (use CTRL-Shift-V). Figure 1 shows the response.
az resource create --resource-group SensitivityLabels --name AssignLabels --resource-type Microsoft.GraphServices/accounts --properties "{""appId"": ""43685887-266e-46cf-915b-ea69bd84fe25""}" --location Global --subscription 35429342-a1a5-4427-9a2d-551840f2ed25
Failure to associate an app with an Azure subscription before using it to run a metered API results in 402 errors.
Choosing a Sensitivity Label to Apply
Now that we have an app with the right permissions linked to an Azure subscription, we can find some documents to label. When you call the assignSensitivityLabel API, the code must pass the immutable identifier for the label to assign. You can find this information in two ways:
- Connect to Exchange Online and then the compliance endpoint and run the Get-Label cmdlet. The cmdlet returns all known sensitivity labels in the tenant. Some of the labels might be solely used for container management. Only labels available for application to files can be used with the assignSensitivityLabel API.
- Run the Get-MgBetaUserSecurityInformationProtectionSensitivityLabel cmdlet from the Microsoft Graph PowerShell SDK. This is the equivalent of running the List sensitivity labels Graph API and returns the set of labels published to the signed-in user.
Here’s an example of using the Get-Label cmdlet to find sensitivity labels that you can apply to files:
Connect-ExchangeOnline Connect-IPPSSession [array]$Labels = Get-Label | Where-Object {$_.ContentType -like "*File*"} $Labels | Format-Table ImmutableId, DisplayName ImmutableId DisplayName ----------- ----------- 2fe7f66d-096a-469e-835f-595532b63560 Public 8b652c9a-a8b7-40ec-bb1a-c5334b1b7fef No Encryption fb0975b2-1ea1-4c3c-850c-e859e690d282 Partner-Accessible Content 27451a5b-5823-4853-bcd4-2204d03ab477 Internal
Connecting to the Graph
To use a metered API, we need to connect to the Microsoft Graph PowerShell SDK using the identifier of the application associated with the Azure subscription. The connection request also passes the tenant identifier (to tell the Graph which tenant to use) and the thumbprint of the certificate loaded into the app. Here’s the connection command:
Connect-MgGraph -NoWelcome -AppId $AppId -TenantId $TenantId -CertificateThumbprint $CertThumbprint
Running the Get-MgContext cmdlet afterward should reveal that the connection authentication is AppOnly (meaning that application permissions are used) together with the scopes (permissions) available to the app. In this instance, we see that Sites.ReadWrite.All permission is available. This permission allows the app to read and write data in any SharePoint Online site in a tenant (write access is needed to update a file with a label).
Get-MgContext ClientId : 43685887-266e-46cf-915b-ea69bd84fe25 TenantId : a662313f-14fc-43a2-9a7a-d2e27f4f3478 Scopes : {Sites.ReadWrite.All} AuthType : AppOnly TokenCredentialType : ClientCertificate CertificateThumbprint : F79286DB88C21491110109A0222348FACF694CBD CertificateSubjectName : SendCertificateChain : False Account : AppName : AssignSensitivityLabels
From this point, the code needs to find a target site, select a drive in the site, and find the files in the drive. All of this is done with standard Graph commands like those used to in the script described in the article about how to report the contents of a document library.
I refactored that script to assign sensitivity labels to supported files that don’t already have labels. You can download the script to assign sensitivity labels from GitHub. The scripts use a mixture of Microsoft Graph PowerShell SDK cmdlets and regular Graph requests run using the Invoke-MgGraphRequest cmdlet.
Applying Sensitivity Labels to Documents
What’s different is when the time comes to apply a sensitivity label to an unlabeled file. The assignSensitivityLabel API is straightforward. You need to know:
- The identifier for the drive (document library) storing the target file.
- The identifier for the target file.
- The identifier for the sensitivity label to assign to the target file.
- A request body (payload) for the POST request. The only mandatory item in the payload is the identifier for the sensitivity label. You can also pass a justification and an assignment method.
The request body is passed as a hash table. Here’s an example:
$AssignmentPayload = @{} $AssignmentPayload.Add("AssignmentMethod", "Auto") $AssignmentPayload.Add("Justification", "Trying out the assignSensivityLabel API") $AssignmentPayload.Add("SensitivityLabelId", "27451a5b-5823-4853-bcd4-2204d03ab477")
With everything in place, posting the request to the API is a matter of doing something like this:
Write-Host ("Assigning sensitivity label to {0}" -f $File.Name) $Uri = ("https://graph.microsoft.com/v1.0/drives/{0}/items/{1}/assignSensitivityLabel" -f $Drive.Id, $File.Id) Try { Invoke-MgGraphRequest -Method "POST" -Uri $Uri -Body $AssignmentPayload -OutputType PSObject } Catch { Write-Host ("Failed to assign sensitivity label to file {0}" -f $File.Name ) -ForegroundColor Red }
The URI used for the request will look something like this after inserting the values for the drive and file identifiers:
https://graph.microsoft.com/v1.0/drives/b!VjyyeCWK3EGr4WX2V2zF5eWwPtRGxu9AgzwoRtLDUpDOiuY22Vv_SJt-wwtCTPyp/items/0142U6OAA3Z4BZZNZEDFHZ36VBP6I2ZQ2G/assignSensitivityLabel
If everything goes right, SharePoint Online accepts the request to label the file and the label will show up in Office UIs, SharePoint views, and so on.
Some Points about Assigning Sensitivity Labels
It’s nice to have the ability to assign sensitivity labels to Office documents and PDF files programmatically. However, you do need to take some points into consideration, including:
- Automatically assigned sensitivity labels (for instance, using an auto-label policy) do not take precedence over labels assigned manually. For this reason, the script ignores files that already have a label. If you wanted, you could check the assignment method in the label information returned by the extractSensitivityLabels API for a document and update the sensitivity label if the label being applied has a higher priority than the existing label and the assignment label is “auto” (applied by an auto-label policy).
- Only modern formats of Office documents and PDF files support sensitivity labels. Scripts should ignore other file types.
- Sensitivity labels that use Double-Key encryption cannot be read by the extractSensitivityLabels API.
- The assignSensitivityLabel API runs in the background so the update of the file with a sensitivity happens asynchronously. The API queues a job that waits until the target file is locked or available for update and then applies the sensitivity label. The update should be visible within ten minutes of running the request, but I’ve seen updates take longer to complete.
One issue I noticed is that if you attempt to update the sensitivity label for a document that is still being processed by SharePoint Online (for instance, to update encryption due to a previous label change), the update applied by the API appears to succeed but nothing happens.
I also observed that many internal server errors occur when attempting to read sensitivity label information from PDFs. The script therefore ignores errors of this nature when handling PDFs.
Costs
Each successful use of the assignSensitivityLabel API costs U.S. $0.00185 (see metered API list). Figure 2 shows the costs incurred while testing the script to apply labels (seemingly to about 75 files). You can see that the cost is assigned to the name of the resource created in Azure.
Unless you plan to assign labels to tens of thousands of files, the cost won’t be very high. This metered API doesn’t rack up charges like those incurred for translating SharePoint Online documents.
Not a Replacement for Auto-Label Policies
The assignSensitivityLabel API is not a substitute for auto-label policies. These policies are designed to process large quantities of SharePoint Online documents across a tenant by identifying target files using criteria like sensitive information types or trainable classifiers. The API exists to deal with situations like labeling a small number of documents in selected sites. If you want to incorporate smarts like automatic identification of target documents, you’ll need to develop code to find and identify documents to label.
You might never need to assign sensitivity labels programmatically, but it’s nice to know that it’s possible if the necessity arises.
The Real Person!
The Real Person!
Tony, does the $0.00185 charge also apply to reading a document’s Sensitivity Label? Or is that free?
The Real Person!
The Real Person!
Or I could just read the first section header in your article “It’s Free to Read Sensitivity Labels, But Assigning Sensitivity Labels Requires Payment” and find my answer there. LOL
It’s quite common for people to overlook facts stated in an article. I wouldn’t worry too much… All is well.
No worries…
The Real Person!
The Real Person!
Anything in particular you need to do to label PDF-files? I’m getting this error: StatusCode: 415, ReasonPhrase: ‘Unsupported Media Type’, Version: 2.0, Content: System.Net.Http.StreamContent.
Seems like the issue is for all PDFs.
It’s a while since I looked at the code but I do recall processing PDFs. Maybe something has changed in the interim. I’ll check.
According to the developers, you haven’t enabled support for PDFs in the tenant:
Set-SPOTenant -EnableSensitivityLabelforPDF $true
https://learn.microsoft.com/en-us/purview/sensitivity-labels-sharepoint-onedrive-files#adding-support-for-pdf
It could also be that the PDFs are eSigned. When this happens, applying a label invalidates the eSignature, so the API fails.
The Real Person!
The Real Person!
Great writeup Tony, thanks! Some gotchas here for sure.
The Real Person!
The Real Person!
Yes, through the Office365 Portal.
The app that you’re using must be associated with an Azure subscription so that you can pay to use the API. If this isn’t done, the API won’t work. Something is obviously missing in the setup that you’re using. As I can’t see your setup, I can’t be much more help than to say you should check every aspect of the setup you’re using (app, permissions, subscription, API call) to be 100% positive that something small is not amiss.
Being able to assign sensitivity labels through Office 365 means nothing except that the steps to enable sensitivity labels within your tenant have been taken. We’re dealing with a different app here (your app) and it’s that app must must be authorized to use the assignSensitivityLabels API.
The Real Person!
The Real Person!
Yes, I’m able to label them just normal via the Office365 editor.
And the app you are using to assign sensitivity labels is associated with an Azure subscription? You can’t assign sensitivity labels with delegated permissions.
The Real Person!
The Real Person!
Hi Tony, great post!
I’ve run into an issue with the assignSensitivityLabel API. I’m getting this error:
{
“error”: {
“code”: “notSupported”,
“message”: “Sensitivity label cannot be assigned due to unsupported feature.”,
“innerError”: {
“code”: “unprocessableEntity”
}
}
}
Any idea what might be causing this? Have you seen it before?
Thanks!
Nope. Haven’t seen that one before. What type of file are you processing?
The Real Person!
The Real Person!
Standard docx files…
Thanks for the response 🙂
Never had a problem with standard Word documents… I assume you can apply a sensitivity labels to these files by opening them in Word?