Queue list classification
- Last Updated: May 1, 2026
- 5 minute read
- Semaphore
- Documentation
This sample PowerShell script automates the queueing of classification jobs for SharePoint Online site libraries using the Progress Data Cloud API. It is an alternative to manually triggering classification from the List Classifications tab in the Progress Data Cloud interface.
Script overview
The Invoke-ListClassification.ps1 script reads a CSV file containing site URLs and list IDs, and queues a classification job for each library. The workflow is:
- Authenticates to the Progress Data Cloud using an API key.
- Imports a CSV file with
SiteUrlandListIdcolumns. - For each row:
- Resolves the Site Library ID via the PDC API.
- Queues a classification job for that library.
- Outputs a summary of processed and failed entries.
Prerequisites
Before running the script, ensure the following:
- PowerShell 5.1 or PowerShell 7+.
- A valid Progress Data Cloud API key for an appropriately permissioned user. For information about API key management, see the API key management script in the Use Progress Data Cloud guide.
- A CSV file with
SiteUrlandListIdcolumns. See Input file format for details. - The site libraries must already be deployed and configured in Progress Data Cloud.
Parameters
| Parameter | Required | Description |
|---|---|---|
InputFile |
Yes | Path to a CSV file containing SiteUrl and ListId columns. |
CloudUrl |
Yes | The base URI of the Progress Data Cloud API (for example, https://tenant.data.progress.cloud). |
ApiKey |
Yes | The API key used to authenticate with Progress Data Cloud. |
Usage examples
Queue classification for all libraries in a CSV:
.\Invoke-ListClassification.ps1 -InputFile .\lists.csv `
-CloudUrl 'https://tenant.data.progress.cloud' `
-ApiKey '***'
Queue classification with verbose logging:
.\Invoke-ListClassification.ps1 -InputFile .\lists.csv `
-CloudUrl 'https://tenant.data.progress.cloud' `
-ApiKey '***' `
-Verbose
Write output to a log file:
.\Invoke-ListClassification.ps1 -InputFile .\lists.csv `
-CloudUrl 'https://tenant.data.progress.cloud' `
-ApiKey '***' 6>log.txt
Preview actions without making changes (WhatIf):
.\Invoke-ListClassification.ps1 -InputFile .\lists.csv `
-CloudUrl 'https://tenant.data.progress.cloud' `
-ApiKey '***' `
-WhatIf
Important:
For enhanced security, consider storing your API key in a secure keystore rather than passing it as a plain text parameter. You can retrieve the key from your preferred secure storage solution (such as Azure Key Vault, HashiCorp Vault, or Windows Credential Manager).
Input file format
Create a CSV file with SiteUrl and ListId columns. For example:
SiteUrl,ListId
https://tenant.sharepoint.com/sites/Finance,7b9c1f9a-1234-5678-9abc-def012345678
https://tenant.sharepoint.com/sites/Marketing,8c0d2e0b-2345-6789-0bcd-ef0123456789
PowerShell script
<#
.SYNOPSIS
Queues classification jobs for site libraries listed in a CSV, using the Progress Data Cloud API.
.DESCRIPTION
This script authenticates to the Progress Data Cloud using an API key, reads a CSV containing site URLs
and list IDs, looks up each corresponding Site Library ID, and queues a classification job for
each library. It implements script-level cmdlet binding and optional verbose logging.
The workflow:
1. Authenticate: Get an access token from $CloudUrl/token.
2. Build authorization header.
3. Import CSV with columns: SiteUrl, ListId.
4. For each row: resolve Site Library ID via GET /api/sitelibrary, then POST /api/classify/{id}.
5. Log successes/failures and provide a final summary.
.PARAMETER InputFile
Path to a CSV file containing site/library entries.
The CSV must include columns: SiteUrl, ListId.
Example:
SiteUrl,ListId
https://tenant.sharepoint.com/sites/Finance,7b9c1f9a-1234-5678-9abc-def012345678
.PARAMETER CloudUrl
The base URI of the Progress Data Cloud tenant (e.g., https://tenant.data.progress.cloud). Must be a valid URI.
.PARAMETER ApiKey
The API key used to connect to Progress Data Cloud for an appropriately permissioned user.
.INPUTS
None. This script does not accept pipeline input.
.OUTPUTS
Information messages to the host/log. The script writes summary counts (Processed/Failed).
No pipeline objects are emitted by default.
.NOTES
- Requires PowerShell 5.1 or PowerShell 7+.
- Uses script-level [CmdletBinding()] to support -Verbose, -WhatIf, and -Confirm.
- Strict mode is enabled; failures are surfaced with $ErrorActionPreference = 'Stop'.
- Web requests include retry logic with exponential backoff.
.EXAMPLE
PS> .\Invoke-ListClassification.ps1 -InputFile .\lists.csv -CloudUrl 'https://tenant.data.progress.cloud' -ApiKey '***'
Runs the classification queueing against entries found in lists.csv.
#>
#Requires -Version 5.1
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory, Position = 0)]
[ValidateNotNullOrEmpty()]
[ValidateScript({ Test-Path $_ -PathType Leaf })]
[string]$InputFile,
[Parameter(Mandatory, Position = 1)]
[ValidateNotNullOrEmpty()]
[uri]$CloudUrl,
[Parameter(Mandatory, Position = 2)]
[ValidateNotNullOrEmpty()]
[string]$ApiKey
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Invoke-WithRetry {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[scriptblock]$ScriptBlock,
[int]$MaxAttempts = 3,
[int]$InitialDelaySeconds = 2
)
$attempt = 0
$delay = $InitialDelaySeconds
while ($attempt -lt $MaxAttempts) {
try {
return & $ScriptBlock
}
catch {
$attempt++
$statusCode = $null
try { $statusCode = $_.Exception.Response.StatusCode.Value__ } catch { }
Write-Verbose ("Attempt {0} failed{1}: {2}" -f $attempt,
($(if ($statusCode) { " (HTTP $statusCode)" } else { "" })), $_.Exception.Message)
if ($attempt -ge $MaxAttempts) {
throw
}
Start-Sleep -Seconds $delay
$delay = [math]::Min($delay * 2, 30)
}
}
}
function Get-CloudToken {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[uri]$BaseUrl,
[Parameter(Mandatory)]
[string]$ApiKey
)
$uri = [uri]::new($BaseUrl, '/token')
$body = @{
grant_type = 'apikey'
key = $ApiKey
}
$requestParams = @{
Method = 'Post'
ContentType = 'application/x-www-form-urlencoded'
Uri = $uri
Body = $body
ErrorAction = 'Stop'
}
Write-Verbose "Requesting token from $($uri.AbsoluteUri)"
$response = Invoke-WithRetry -ScriptBlock { Invoke-RestMethod @requestParams }
if (-not $response.access_token) {
throw "Token response missing 'access_token'."
}
return [string]$response.access_token
}
function New-AuthHeader {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Token
)
return @{
Authorization = "Bearer $Token"
Accept = 'application/json'
}
}
function Get-SiteLibraryId {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[uri]$BaseUrl,
[Parameter(Mandatory)]
[hashtable]$Headers,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$SiteUrl,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$ListId
)
$eSite = [System.Web.HttpUtility]::UrlEncode($SiteUrl)
$eList = [System.Web.HttpUtility]::UrlEncode($ListId)
$relative = "/api/sitelibrary?listId=$eList&siteUrl=$eSite"
$uri = [uri]::new($BaseUrl, $relative)
$requestParams = @{
Method = 'Get'
Uri = $uri
Headers = $Headers
ErrorAction = 'Stop'
}
Write-Verbose "Getting Site Library for listId=$ListId siteUrl=$SiteUrl"
$response = Invoke-WithRetry -ScriptBlock { Invoke-RestMethod @requestParams }
if (-not $response.id) {
throw "Response did not contain an 'id' for listId=$ListId."
}
return [string]$response.id
}
function Invoke-Classification {
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory)]
[uri]$BaseUrl,
[Parameter(Mandatory)]
[hashtable]$Headers,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$SiteLibraryId
)
$relative = "/api/classify/$SiteLibraryId"
$uri = [uri]::new($BaseUrl, $relative)
$requestParams = @{
Method = 'Post'
Uri = $uri
Headers = $Headers
ErrorAction = 'Stop'
}
if ($PSCmdlet.ShouldProcess("SiteLibraryId=$SiteLibraryId", "Queue classification")) {
Write-Verbose "Queueing classification for SiteLibraryId=$SiteLibraryId"
Invoke-WithRetry -ScriptBlock { Invoke-RestMethod @requestParams } | Out-Null
}
}
# --- Input: load and validate CSV ---
$lists = Import-Csv -Path $InputFile
$expectedColumns = 'SiteUrl', 'ListId'
foreach ($col in $expectedColumns) {
if (-not $lists | Get-Member -Name $col -MemberType NoteProperty) {
throw "Input CSV must contain column '$col'."
}
}
if (-not $lists -or $lists.Count -eq 0) {
throw "No rows found in CSV '$InputFile'."
}
# --- Auth ---
try {
$token = Get-CloudToken -BaseUrl $CloudUrl -ApiKey $ApiKey
$header = New-AuthHeader -Token $token
}
catch {
Write-Error "Authentication failed: $($_.Exception.Message)"
exit 1
}
# --- Process ---
$processed = 0
$failed = 0
foreach ($list in $lists) {
try {
Write-Verbose "Getting the Site Library ID for ListId=$($list.ListId)"
$siteLibraryId = Get-SiteLibraryId -BaseUrl $CloudUrl -Headers $header -SiteUrl $list.SiteUrl -ListId $list.ListId
Write-Verbose "Queueing classification for site library $siteLibraryId"
Invoke-Classification -BaseUrl $CloudUrl -Headers $header -SiteLibraryId $siteLibraryId
$processed++
Write-Information "Queued classification for ListId=$($list.ListId) (SiteLibraryId=$siteLibraryId)"
}
catch {
$failed++
Write-Warning "Failed for ListId=$($list.ListId): $($_.Exception.Message)"
}
}
Write-Information "Finished queueing classification. Processed=$processed; Failed=$failed"