Control 3.3: Compliance and Regulatory Reporting - PowerShell Setup
This playbook provides PowerShell automation scripts for Control 3.3.
Prerequisites
# Install required modules
Install-Module -Name Microsoft.Graph -Force -AllowClobber
Install-Module -Name ExchangeOnlineManagement -Force -AllowClobber
Install-Module -Name PnP.PowerShell -Force -AllowClobber
# Connect to services
Connect-MgGraph -Scopes "Reports.Read.All", "Compliance.Read.All"
Connect-ExchangeOnline
Connect-PnPOnline -Url "https://[tenant].sharepoint.com/sites/AI-Compliance-Reports" -Interactive
Helper Function: HTML Report Generator
# Helper function to generate HTML compliance reports
function New-ComplianceHtmlReport {
param(
[Parameter(Mandatory=$true)]
[hashtable]$ControlStatus
)
$html = @"
<!DOCTYPE html>
<html>
<head>
<title>FSI Agent Governance - Compliance Report</title>
<style>
body { font-family: 'Segoe UI', sans-serif; margin: 20px; }
h1 { color: #0078d4; }
.compliant { color: green; }
.needs-attention { color: orange; }
.non-compliant { color: red; }
table { border-collapse: collapse; width: 100%; margin-top: 20px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #0078d4; color: white; }
.score { font-size: 24px; font-weight: bold; }
</style>
</head>
<body>
<h1>FSI Agent Governance Framework</h1>
<h2>Compliance Status Report</h2>
<p>Report Date: $($ControlStatus.ReportDate)</p>
<p>Report Type: $($ControlStatus.ReportType)</p>
<p class="score">Overall Score: $($ControlStatus.OverallComplianceScore)%</p>
</body>
</html>
"@
return $html
}
Control Status Report Generation
function New-ControlStatusReport {
param(
[Parameter(Mandatory=$true)]
[string]$OutputPath,
[ValidateSet("Weekly", "Monthly", "Quarterly")]
[string]$ReportType = "Weekly"
)
Write-Host "Generating $ReportType Control Status Report..." -ForegroundColor Cyan
# Define control status structure
$controlStatus = @{
ReportDate = Get-Date -Format "yyyy-MM-dd"
ReportType = $ReportType
Pillars = @(
@{
Name = "Pillar 1: Security"
Controls = @(
@{ Id = "1.1"; Name = "Restrict Agent Publishing"; Status = "Compliant"; LastReview = (Get-Date).AddDays(-7) },
@{ Id = "1.2"; Name = "Agent Registry Management"; Status = "Compliant"; LastReview = (Get-Date).AddDays(-14) },
@{ Id = "1.3"; Name = "SharePoint Content Governance"; Status = "Compliant"; LastReview = (Get-Date).AddDays(-10) }
# Add all Pillar 1 controls
)
ComplianceScore = 95
},
@{
Name = "Pillar 2: Management"
Controls = @(
@{ Id = "2.1"; Name = "Managed Environments"; Status = "Compliant"; LastReview = (Get-Date).AddDays(-5) },
@{ Id = "2.2"; Name = "Environment Groups"; Status = "Needs Attention"; LastReview = (Get-Date).AddDays(-30) }
# Add all Pillar 2 controls
)
ComplianceScore = 88
},
@{
Name = "Pillar 3: Reporting"
Controls = @(
@{ Id = "3.1"; Name = "Agent Inventory"; Status = "Compliant"; LastReview = (Get-Date).AddDays(-3) },
@{ Id = "3.2"; Name = "Usage Analytics"; Status = "Compliant"; LastReview = (Get-Date).AddDays(-7) }
# Add all Pillar 3 controls
)
ComplianceScore = 92
},
@{
Name = "Pillar 4: SharePoint"
Controls = @(
@{ Id = "4.1"; Name = "Agent Source Governance"; Status = "Compliant"; LastReview = (Get-Date).AddDays(-14) }
# Add all Pillar 4 controls
)
ComplianceScore = 97
}
)
}
# Calculate overall score
$overallScore = ($controlStatus.Pillars | ForEach-Object { $_.ComplianceScore } |
Measure-Object -Average).Average
$controlStatus.OverallComplianceScore = [math]::Round($overallScore, 1)
# Generate HTML report (uses helper function defined above)
$htmlReport = New-ComplianceHtmlReport -ControlStatus $controlStatus
$htmlReport | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "Report generated: $OutputPath" -ForegroundColor Green
Write-Host "Overall Compliance Score: $($controlStatus.OverallComplianceScore)%" -ForegroundColor $(
if ($controlStatus.OverallComplianceScore -ge 90) { "Green" }
elseif ($controlStatus.OverallComplianceScore -ge 75) { "Yellow" }
else { "Red" }
)
return $controlStatus
}
Regulatory Alignment Report
function New-RegulatoryAlignmentReport {
param(
[string]$Regulation = "All",
[string]$OutputPath = ".\RegulatoryAlignmentReport.html"
)
Write-Host "Generating Regulatory Alignment Report..." -ForegroundColor Cyan
$regulations = @{
"FINRA_4511" = @{
Name = "FINRA Rule 4511 - Books and Records"
Requirements = @(
@{ Requirement = "Retain business records for required period"; Control = "1.9, 2.13"; Status = "Compliant" },
@{ Requirement = "Maintain records in accessible format"; Control = "2.13, 3.1"; Status = "Compliant" },
@{ Requirement = "Preserve electronic communications"; Control = "1.7, 1.10"; Status = "Compliant" }
)
}
"SEC_17a-4" = @{
Name = "SEC Rule 17a-4 - Records Preservation"
Requirements = @(
@{ Requirement = "WORM storage for required records"; Control = "2.13"; Status = "Compliant" },
@{ Requirement = "Index and retrieve records"; Control = "3.1"; Status = "Compliant" },
@{ Requirement = "Third-party access letter"; Control = "2.13"; Status = "Compliant" }
)
}
"SOX_404" = @{
Name = "SOX Section 404 - Internal Controls"
Requirements = @(
@{ Requirement = "Document IT general controls"; Control = "2.8, 2.1"; Status = "Compliant" },
@{ Requirement = "Access control and segregation of duties"; Control = "2.8"; Status = "Compliant" },
@{ Requirement = "Change management controls"; Control = "2.3, 2.4"; Status = "Compliant" }
)
}
"GLBA_501b" = @{
Name = "GLBA Section 501(b) - Safeguards Rule"
Requirements = @(
@{ Requirement = "Protect NPI confidentiality"; Control = "1.5, 1.15"; Status = "Compliant" },
@{ Requirement = "Monitor for unauthorized access"; Control = "3.2, 1.8"; Status = "Compliant" },
@{ Requirement = "Third-party oversight"; Control = "2.7"; Status = "Compliant" }
)
}
}
# Calculate compliance by regulation
$summary = $regulations.GetEnumerator() | ForEach-Object {
$compliantCount = ($_.Value.Requirements | Where-Object { $_.Status -eq "Compliant" }).Count
$totalCount = $_.Value.Requirements.Count
[PSCustomObject]@{
Regulation = $_.Value.Name
Compliant = $compliantCount
Total = $totalCount
Percentage = [math]::Round(($compliantCount / $totalCount) * 100, 0)
}
}
Write-Host "Regulatory Alignment Summary:" -ForegroundColor Green
$summary | Format-Table -AutoSize
return $summary
}
Examination Package Generator
function New-ExaminationPackage {
param(
[Parameter(Mandatory=$true)]
[ValidateSet("FINRA", "SEC", "OCC", "State")]
[string]$Regulator,
[string]$OutputFolder = ".\ExamPackage"
)
Write-Host "Generating $Regulator Examination Ready Package..." -ForegroundColor Cyan
# Create output folder
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
# Define package contents by regulator
$packageContents = @{
"FINRA" = @{
"01-AI-Governance-Framework-Overview.pdf" = "Framework documentation"
"02-Agent-Inventory-Full-List.xlsx" = "Complete agent inventory"
"03-Supervisory-Procedures-WSP.pdf" = "Written Supervisory Procedures"
"04-Control-Status-Summary.pdf" = "Current control compliance status"
"05-Usage-Analytics-90-Days.xlsx" = "Agent usage data"
"06-Incident-Log.xlsx" = "Incident tracking log"
"07-Training-Completion-Records.xlsx" = "Staff training records"
"08-Policy-Documents/" = "All AI governance policies"
}
"SEC" = @{
"01-Records-Retention-Policy.pdf" = "17a-4 compliant retention policy"
"02-Agent-Interaction-Logs.xlsx" = "Customer interaction records"
"03-WORM-Storage-Certification.pdf" = "Storage compliance certification"
"04-Access-Control-Documentation.pdf" = "Access management evidence"
"05-Audit-Trail-Export.xlsx" = "Unified audit log export"
}
"OCC" = @{
"01-Third-Party-Risk-Assessment.pdf" = "Vendor risk documentation"
"02-Technology-Risk-Controls.pdf" = "IT control documentation"
"03-Business-Continuity-Plans.pdf" = "BCP documentation"
"04-Change-Management-Evidence.xlsx" = "Change control records"
"05-Security-Assessment-Results.pdf" = "Security testing evidence"
}
}
Write-Host "Package Contents for $Regulator Examination:" -ForegroundColor Yellow
$packageContents[$Regulator].GetEnumerator() | ForEach-Object {
Write-Host " - $($_.Key): $($_.Value)" -ForegroundColor Gray
}
# Create manifest
$manifest = @{
Regulator = $Regulator
GeneratedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
GeneratedBy = (Get-MgContext).Account
Contents = $packageContents[$Regulator]
Instructions = "Upload all documents to secure examination portal within 48 hours of request"
}
$manifest | ConvertTo-Json -Depth 3 | Out-File "$OutputFolder\MANIFEST.json"
Write-Host "Package manifest created at: $OutputFolder\MANIFEST.json" -ForegroundColor Green
return $manifest
}
Archive Report to SharePoint
function Save-ComplianceReportToSharePoint {
param(
[Parameter(Mandatory=$true)]
[string]$ReportPath,
[Parameter(Mandatory=$true)]
[string]$LibraryPath
)
Write-Host "Archiving report to SharePoint..." -ForegroundColor Cyan
$fileName = Split-Path $ReportPath -Leaf
try {
Add-PnPFile -Path $ReportPath -Folder $LibraryPath
Write-Host "Report archived: $LibraryPath/$fileName" -ForegroundColor Green
return $true
}
catch {
Write-Error "Failed to archive report: $_"
return $false
}
}
Usage Examples
# Generate control status report
New-ControlStatusReport -OutputPath ".\WeeklyComplianceReport.html" -ReportType "Weekly"
# Generate regulatory alignment report
New-RegulatoryAlignmentReport -Regulation "All"
# Generate examination package
New-ExaminationPackage -Regulator "FINRA" -OutputFolder ".\FINRA_Exam_Package"
Next Steps
- Portal Walkthrough - Manual configuration
- Verification & Testing - Test procedures
- Troubleshooting - Common issues
Updated: January 2026 | Version: v1.2