Password security is critical in enterprise environments, and Fine-Grained Password Policies (FGPP) provide granular control over password requirements for different user groups. However, monitoring FGPP compliance and detecting policy violations manually is time-consuming and error-prone.
I built a three-part automated solution to address these challenges:
- A PowerShell script that monitors FGPP policy changes and sends email alerts
- A script that compares compliance reports and alerts on new violations
- A web-based CSV viewer for analyzing compliance data
Why FGPP Monitoring Matters
FGPP allows different password requirements for different groups - executives might need 15-character passwords while service accounts have different rules. The challenge is ensuring these policies remain properly applied over time.
Common issues include:
- Users moved out of policy groups
- New policies created without proper assignments
- Policy conflicts causing unexpected behavior
- Accounts falling back to default domain policies
Script 1: FGPP Policy Change Monitor
The first script tracks changes to FGPP policies themselves, storing baseline data in JSON format and sending email alerts when changes are detected.
Smart Change Detection
The comparison logic prevents false alerts when group order changes by sorting before comparison:
# Compare AppliesTo groups (sort by DN to make order-insensitive)
$currentAppliesToSorted = $currentPolicy.AppliesTo | Sort-Object DN
$previousAppliesToSorted = $previousPolicy.AppliesTo | Sort-Object DN
$currentAppliesToJson = ($currentAppliesToSorted | ConvertTo-Json -Depth 10 -Compress)
$previousAppliesToJson = ($previousAppliesToSorted | ConvertTo-Json -Depth 10 -Compress)
if ($currentAppliesToJson -ne $previousAppliesToJson) {
# Actual change detected - add to modifications list
$modification = [PSCustomObject]@{
PolicyName = $policyName
ChangeType = "AppliesTo Modified"
Previous = $previousPolicy.AppliesTo
Current = $currentPolicy.AppliesTo
}
$changes.ModifiedPolicies += $modification
}
The second script compares CSV compliance reports to identify users who newly fall out of compliance.
Automatic File Detection
The script finds the two most recent compliance reports automatically:
function Get-RecentCSVFiles {
param([string]$FolderPath, [string]$Pattern)
$csvFiles = Get-ChildItem -Path $FolderPath -Filter $Pattern |
Sort-Object LastWriteTime -Descending
if ($csvFiles.Count -lt 2) {
Write-Log "Info: Need at least 2 CSV files to compare. Found: $($csvFiles.Count)"
return $null
}
$newestFile = $csvFiles[0] # File B (newest)
$secondNewestFile = $csvFiles[1] # File A (second newest)
return @{
FileA = $secondNewestFile
FileB = $newestFile
}
}
New Violation Detection
The script specifically identifies new non-compliance issues rather than all violations:
foreach ($record in $NewerData) {
$identifier = $record.$IdentifierColumn
# Skip records with missing or empty identifiers
if (-not $identifier -or $identifier.Trim() -eq "" -or $identifier -eq "?") {
Write-Log "Skipping record with missing/empty identifier: '$identifier'"
continue
}
# Check if this is a non-compliant record
$isNonCompliant = $record.ComplianceStatus -like "*non-compliant*"
if ($isNonCompliant) {
if (-not $olderDataHash.ContainsKey($identifier)) {
# New user appearing as non-compliant
$newNonCompliant += [PSCustomObject]@{
Identifier = $identifier
ChangeType = "New Non-Compliant User"
Record = $record
}
}
}
}
Email Alerts
The script generates clean HTML emails explaining the business impact:
$html += @"
<div class="summary">
<h2>Security Compliance Issue Detected</h2>
<p><strong>$($NewNonCompliantUsers.Count)</strong> users have been identified with new FGPP non-compliance issues.</p>
</div>
<div class="impact-section">
<h3>Why This Matters</h3>
<p>Fine-Grained Password Policy non-compliance represents a significant security risk:</p>
<ul class="impact-list">
<li>Accounts may not meet required password complexity standards</li>
<li>Increased vulnerability to password-based attacks</li>
<li>Potential regulatory compliance violations</li>
</ul>
</div>
"@
The email alerts have two options, the first is the basic mode, that contain the information and changes to compliance:
Then there is the options for a more "detailed" information emails with the same data, the contains "why this matter" and "urgent action required":
Web-Based CSV Viewer
The third component is an HTML application for viewing and analyzing compliance CSV files, this can be used with the emails or without.
File Upload and Processing
The viewer supports drag-and-drop CSV upload which requires the CSV from the first script:
Papa.parse(file, {
header: true,
skipEmptyLines: true,
complete: function(results) {
csvData = results.data;
filteredData = [...csvData];
displayData();
showDataSection();
},
error: function(error) {
alert('Error parsing CSV: ' + error.message);
}
});
Dynamic Table Generation
The viewer creates responsive tables with compliance-specific styling:
function displayData() {
const headers = Object.keys(filteredData[0]);
tableHeader.innerHTML = '<tr>' +
headers.map(header => `<th>${header}</th>`).join('') +
'</tr>';
tableBody.innerHTML = filteredData.map(row => {
return '<tr>' + headers.map(header => {
let cellValue = row[header] || '';
let cellClass = '';
// Add styling for specific columns
if (header === 'ComplianceStatus') {
cellClass = cellValue.toLowerCase().includes('non-compliant') ?
'status-non-compliant' : 'status-compliant';
} else if (header === 'RiskLevel') {
cellClass = `risk-${cellValue.toLowerCase()}`;
}
return `<td class="${cellClass}" title="${cellValue}">${cellValue}</td>`;
}).join('') + '</tr>';
}).join('');
}
Real-time Search
The application includes live search across all columns:
function filterData() {
const searchTerm = searchBox.value.toLowerCase().trim();
if (!searchTerm) {
filteredData = [...csvData];
} else {
filteredData = csvData.filter(row => {
return Object.values(row).some(value =>
value && value.toString().toLowerCase().includes(searchTerm)
);
});
}
displayData();
}
This is show the website looks at the "drag and drop" screen:
When a CSV file is provided you get a list of people that are outside FGPP compliance which you can search:
File Structure
The solution uses this directory structure:
C:\Scripts\FGPP\
├── FGPP-Monitor.ps1
├── FGPP-Compliance-Monitor.ps1
├── compliance_monitor.log
├── Reports\
│ ├── FGPP_Compliance_Report_20250819_161813.csv
│ └── FGPP_Compliance_Report_20250820_161814.csv
└── fgpp-compliance.html
The combination of automated monitoring, intelligent comparison logic, and user-friendly data visualization creates a comprehensive FGPP compliance solution that reduces manual effort while improving security oversight.