PowerShell Setup: Control 2.21 - AI Marketing Claims and Substantiation
Last Updated: January 2026 Module Requirements: PnP.PowerShell, Microsoft.Graph Estimated Time: 30-45 minutes
Prerequisites
- PnP.PowerShell module installed:
Install-Module PnP.PowerShell -Scope CurrentUser - Microsoft.Graph module installed:
Install-Module Microsoft.Graph -Scope CurrentUser - SharePoint Admin or Site Collection Admin permissions
- Governance SharePoint site URL identified
Module Installation
# Install required modules
Install-Module PnP.PowerShell -Scope CurrentUser -Force
Install-Module Microsoft.Graph -Scope CurrentUser -Force
# Import modules
Import-Module PnP.PowerShell
Import-Module Microsoft.Graph
Script 1: Create AI Claims Inventory List
<#
.SYNOPSIS
Creates the AI Marketing Claims Inventory list in SharePoint.
.DESCRIPTION
Sets up the claims inventory list with all required columns
for tracking AI marketing claims per Control 2.21.
.PARAMETER SiteUrl
The URL of the governance SharePoint site.
.EXAMPLE
.\New-AIClaimsInventory.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/AIGovernance"
#>
param(
[Parameter(Mandatory=$true)]
[string]$SiteUrl
)
# Connect to SharePoint
Connect-PnPOnline -Url $SiteUrl -Interactive
# Create the list
$listName = "AI Marketing Claims Inventory"
$list = New-PnPList -Title $listName -Template GenericList
# Add columns
Add-PnPField -List $listName -DisplayName "Claim Text" -InternalName "ClaimText" -Type Note
Add-PnPField -List $listName -DisplayName "Claim Category" -InternalName "ClaimCategory" -Type Choice `
-Choices "Performance","Capability","Comparative","Predictive","Efficiency"
Add-PnPField -List $listName -DisplayName "Agent/Product" -InternalName "AgentProduct" -Type Text
Add-PnPField -List $listName -DisplayName "Target Channel" -InternalName "TargetChannel" -Type Choice `
-Choices "Website","Email","Social Media","Sales Collateral","Press Release","Multiple"
Add-PnPField -List $listName -DisplayName "Governance Zone" -InternalName "GovernanceZone" -Type Choice `
-Choices "Zone 1 - Personal","Zone 2 - Team","Zone 3 - Enterprise"
Add-PnPField -List $listName -DisplayName "Substantiation File" -InternalName "SubstantiationFile" -Type URL
Add-PnPField -List $listName -DisplayName "Status" -InternalName "ClaimStatus" -Type Choice `
-Choices "Draft","Under Review","Approved","Rejected","Retired"
Add-PnPField -List $listName -DisplayName "Submitted By" -InternalName "SubmittedBy" -Type User
Add-PnPField -List $listName -DisplayName "Submission Date" -InternalName "SubmissionDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Compliance Reviewer" -InternalName "ComplianceReviewer" -Type User
Add-PnPField -List $listName -DisplayName "Review Date" -InternalName "ReviewDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Approval Date" -InternalName "ApprovalDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Next Review Date" -InternalName "NextReviewDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Review Comments" -InternalName "ReviewComments" -Type Note
Write-Host "AI Marketing Claims Inventory list created successfully." -ForegroundColor Green
# Disconnect
Disconnect-PnPOnline
Script 2: Create Substantiation Evidence Library
<#
.SYNOPSIS
Creates the substantiation evidence document library.
.PARAMETER SiteUrl
The URL of the governance SharePoint site.
.EXAMPLE
.\New-SubstantiationLibrary.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/AIGovernance"
#>
param(
[Parameter(Mandatory=$true)]
[string]$SiteUrl
)
# Connect to SharePoint
Connect-PnPOnline -Url $SiteUrl -Interactive
# Create document library
$libraryName = "AI Claims Substantiation"
New-PnPList -Title $libraryName -Template DocumentLibrary
# Enable versioning
Set-PnPList -Identity $libraryName -EnableVersioning $true -MajorVersions 50 -EnableMinorVersions $false
# Create folder structure
$categories = @(
"Performance Claims",
"Capability Claims",
"Comparative Claims",
"Predictive Claims",
"Efficiency Claims"
)
foreach ($category in $categories) {
Add-PnPFolder -Name $category -Folder $libraryName
Write-Host "Created folder: $category" -ForegroundColor Cyan
}
Write-Host "Substantiation library created successfully." -ForegroundColor Green
# Disconnect
Disconnect-PnPOnline
Script 3: Export Claims Inventory Report
<#
.SYNOPSIS
Exports the AI claims inventory to CSV for compliance reporting.
.PARAMETER SiteUrl
The URL of the governance SharePoint site.
.PARAMETER OutputPath
Path for the output CSV file.
.EXAMPLE
.\Export-AIClaimsReport.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/AIGovernance" -OutputPath "C:\Reports\AIClaimsReport.csv"
#>
param(
[Parameter(Mandatory=$true)]
[string]$SiteUrl,
[Parameter(Mandatory=$true)]
[string]$OutputPath
)
# Connect to SharePoint
Connect-PnPOnline -Url $SiteUrl -Interactive
# Get all claims
$listName = "AI Marketing Claims Inventory"
$claims = Get-PnPListItem -List $listName -PageSize 500
# Build report data
$reportData = @()
foreach ($claim in $claims) {
$reportData += [PSCustomObject]@{
ClaimID = $claim.Id
ClaimText = $claim.FieldValues.ClaimText
Category = $claim.FieldValues.ClaimCategory
AgentProduct = $claim.FieldValues.AgentProduct
Channel = $claim.FieldValues.TargetChannel
Zone = $claim.FieldValues.GovernanceZone
Status = $claim.FieldValues.ClaimStatus
SubmissionDate = $claim.FieldValues.SubmissionDate
ApprovalDate = $claim.FieldValues.ApprovalDate
NextReviewDate = $claim.FieldValues.NextReviewDate
HasSubstantiation = if ($claim.FieldValues.SubstantiationFile) { "Yes" } else { "No" }
}
}
# Export to CSV
$reportData | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Host "Claims report exported to: $OutputPath" -ForegroundColor Green
Write-Host "Total claims: $($reportData.Count)" -ForegroundColor Cyan
# Summary statistics
$summary = $reportData | Group-Object -Property Status | Select-Object Name, Count
Write-Host "`nClaims by Status:" -ForegroundColor Yellow
$summary | Format-Table -AutoSize
# Disconnect
Disconnect-PnPOnline
Script 4: Identify Claims Due for Review
<#
.SYNOPSIS
Identifies AI claims due for quarterly review.
.PARAMETER SiteUrl
The URL of the governance SharePoint site.
.PARAMETER DaysAhead
Number of days to look ahead for upcoming reviews. Default: 14
.EXAMPLE
.\Get-ClaimsDueForReview.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/AIGovernance"
#>
param(
[Parameter(Mandatory=$true)]
[string]$SiteUrl,
[int]$DaysAhead = 14
)
# Connect to SharePoint
Connect-PnPOnline -Url $SiteUrl -Interactive
$listName = "AI Marketing Claims Inventory"
$cutoffDate = (Get-Date).AddDays($DaysAhead)
# Build CAML query for approved claims with upcoming review dates
$camlQuery = @"
<View>
<Query>
<Where>
<And>
<Eq>
<FieldRef Name='ClaimStatus'/>
<Value Type='Choice'>Approved</Value>
</Eq>
<Leq>
<FieldRef Name='NextReviewDate'/>
<Value Type='DateTime'>$($cutoffDate.ToString("yyyy-MM-dd"))</Value>
</Leq>
</And>
</Where>
</Query>
</View>
"@
$claimsDue = Get-PnPListItem -List $listName -Query $camlQuery
if ($claimsDue.Count -eq 0) {
Write-Host "No claims due for review in the next $DaysAhead days." -ForegroundColor Green
} else {
Write-Host "Claims due for review:" -ForegroundColor Yellow
foreach ($claim in $claimsDue) {
Write-Host " - ID: $($claim.Id) | $($claim.FieldValues.ClaimText.Substring(0, [Math]::Min(50, $claim.FieldValues.ClaimText.Length)))..." -ForegroundColor Cyan
Write-Host " Review Due: $($claim.FieldValues.NextReviewDate)" -ForegroundColor Gray
}
}
# Disconnect
Disconnect-PnPOnline
Validation Script
<#
.SYNOPSIS
Validates Control 2.21 configuration.
.PARAMETER SiteUrl
The URL of the governance SharePoint site.
#>
param(
[Parameter(Mandatory=$true)]
[string]$SiteUrl
)
Connect-PnPOnline -Url $SiteUrl -Interactive
$results = @()
# Check 1: Claims Inventory List exists
$claimsList = Get-PnPList -Identity "AI Marketing Claims Inventory" -ErrorAction SilentlyContinue
$results += [PSCustomObject]@{
Check = "Claims Inventory List exists"
Status = if ($claimsList) { "PASS" } else { "FAIL" }
}
# Check 2: Substantiation Library exists
$substLib = Get-PnPList -Identity "AI Claims Substantiation" -ErrorAction SilentlyContinue
$results += [PSCustomObject]@{
Check = "Substantiation Library exists"
Status = if ($substLib) { "PASS" } else { "FAIL" }
}
# Check 3: Required columns exist
$requiredColumns = @("ClaimText", "ClaimCategory", "ClaimStatus", "SubstantiationFile", "NextReviewDate")
$listFields = Get-PnPField -List "AI Marketing Claims Inventory"
$missingColumns = $requiredColumns | Where-Object { $_ -notin $listFields.InternalName }
$results += [PSCustomObject]@{
Check = "Required columns configured"
Status = if ($missingColumns.Count -eq 0) { "PASS" } else { "FAIL - Missing: $($missingColumns -join ', ')" }
}
# Display results
Write-Host "`nControl 2.21 Validation Results:" -ForegroundColor Yellow
$results | Format-Table -AutoSize
Disconnect-PnPOnline
Complete Configuration Script
<#
.SYNOPSIS
Complete AI marketing claims configuration for Control 2.21
.DESCRIPTION
Executes end-to-end marketing claims setup including:
- Claims inventory list creation
- Substantiation evidence library setup
- Folder structure creation
- Claims status export
.PARAMETER SiteUrl
SharePoint site URL for governance
.PARAMETER OutputPath
Path for output reports
.EXAMPLE
.\Configure-Control-2.21.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/AIGovernance" -OutputPath ".\MarketingClaims"
.NOTES
Last Updated: January 2026
Related Control: Control 2.21 - AI Marketing Claims and Substantiation
#>
param(
[Parameter(Mandatory=$true)]
[string]$SiteUrl,
[string]$OutputPath = ".\MarketingClaims-Report"
)
try {
Write-Host "=== Control 2.21: AI Marketing Claims Configuration ===" -ForegroundColor Cyan
# Connect to SharePoint
Connect-PnPOnline -Url $SiteUrl -Interactive
# Ensure output directory exists
New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
# Step 1: Create or verify Claims Inventory List
Write-Host "`n[Step 1] Configuring Claims Inventory List..." -ForegroundColor Cyan
$listName = "AI Marketing Claims Inventory"
$claimsList = Get-PnPList -Identity $listName -ErrorAction SilentlyContinue
if (-not $claimsList) {
Write-Host " Creating list: $listName" -ForegroundColor Yellow
New-PnPList -Title $listName -Template GenericList
# Add columns
Add-PnPField -List $listName -DisplayName "Claim Text" -InternalName "ClaimText" -Type Note
Add-PnPField -List $listName -DisplayName "Claim Category" -InternalName "ClaimCategory" -Type Choice `
-Choices "Performance","Capability","Comparative","Predictive","Efficiency"
Add-PnPField -List $listName -DisplayName "Agent/Product" -InternalName "AgentProduct" -Type Text
Add-PnPField -List $listName -DisplayName "Target Channel" -InternalName "TargetChannel" -Type Choice `
-Choices "Website","Email","Social Media","Sales Collateral","Press Release","Multiple"
Add-PnPField -List $listName -DisplayName "Governance Zone" -InternalName "GovernanceZone" -Type Choice `
-Choices "Zone 1 - Personal","Zone 2 - Team","Zone 3 - Enterprise"
Add-PnPField -List $listName -DisplayName "Substantiation File" -InternalName "SubstantiationFile" -Type URL
Add-PnPField -List $listName -DisplayName "Status" -InternalName "ClaimStatus" -Type Choice `
-Choices "Draft","Under Review","Approved","Rejected","Retired"
Add-PnPField -List $listName -DisplayName "Submitted By" -InternalName "SubmittedBy" -Type User
Add-PnPField -List $listName -DisplayName "Submission Date" -InternalName "SubmissionDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Compliance Reviewer" -InternalName "ComplianceReviewer" -Type User
Add-PnPField -List $listName -DisplayName "Review Date" -InternalName "ReviewDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Approval Date" -InternalName "ApprovalDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Next Review Date" -InternalName "NextReviewDate" -Type DateTime
Add-PnPField -List $listName -DisplayName "Review Comments" -InternalName "ReviewComments" -Type Note
Write-Host " [CREATED] $listName with all columns" -ForegroundColor Green
} else {
Write-Host " [EXISTS] $listName" -ForegroundColor Yellow
}
# Step 2: Create or verify Substantiation Library
Write-Host "`n[Step 2] Configuring Substantiation Library..." -ForegroundColor Cyan
$libraryName = "AI Claims Substantiation"
$substLib = Get-PnPList -Identity $libraryName -ErrorAction SilentlyContinue
if (-not $substLib) {
Write-Host " Creating library: $libraryName" -ForegroundColor Yellow
New-PnPList -Title $libraryName -Template DocumentLibrary
# Enable versioning
Set-PnPList -Identity $libraryName -EnableVersioning $true -MajorVersions 50
# Create folder structure
$categories = @(
"Performance Claims",
"Capability Claims",
"Comparative Claims",
"Predictive Claims",
"Efficiency Claims"
)
foreach ($category in $categories) {
Add-PnPFolder -Name $category -Folder $libraryName
Write-Host " Created folder: $category" -ForegroundColor Cyan
}
Write-Host " [CREATED] $libraryName with folder structure" -ForegroundColor Green
} else {
Write-Host " [EXISTS] $libraryName" -ForegroundColor Yellow
}
# Step 3: Export current claims status
Write-Host "`n[Step 3] Exporting claims inventory..." -ForegroundColor Cyan
$claims = Get-PnPListItem -List $listName -PageSize 500 -ErrorAction SilentlyContinue
if ($claims -and $claims.Count -gt 0) {
$claimsExport = $claims | ForEach-Object {
[PSCustomObject]@{
ClaimID = $_.Id
ClaimText = $_.FieldValues.ClaimText
Category = $_.FieldValues.ClaimCategory
AgentProduct = $_.FieldValues.AgentProduct
Channel = $_.FieldValues.TargetChannel
Zone = $_.FieldValues.GovernanceZone
Status = $_.FieldValues.ClaimStatus
SubmissionDate = $_.FieldValues.SubmissionDate
ApprovalDate = $_.FieldValues.ApprovalDate
NextReviewDate = $_.FieldValues.NextReviewDate
HasSubstantiation = if ($_.FieldValues.SubstantiationFile) { "Yes" } else { "No" }
}
}
$claimsExport | Export-Csv -Path "$OutputPath\ClaimsInventory.csv" -NoTypeInformation
Write-Host " Exported $($claimsExport.Count) claims" -ForegroundColor Cyan
# Summary by status
Write-Host "`n=== Claims Summary ===" -ForegroundColor Cyan
$claimsExport | Group-Object -Property Status | ForEach-Object {
Write-Host " $($_.Name): $($_.Count)"
}
} else {
Write-Host " No claims found in inventory" -ForegroundColor Yellow
}
Write-Host "`n[PASS] Control 2.21 configuration completed successfully" -ForegroundColor Green
}
catch {
Write-Host "[FAIL] Error: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "[INFO] Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Yellow
exit 1
}
finally {
# Cleanup connections
Disconnect-PnPOnline -ErrorAction SilentlyContinue
}
Back to Control 2.21 | Portal Walkthrough | Verification Testing | Troubleshooting