Frustrated trying to Maintain PCNS in a Multi-Domain Environment

For those of you who have deployed Microsoft Password Change Notification Service (PCNS) services in a large domain (or as in this case a complete forest with many domains), you will know the mission of ensuring that PCNS is installed everywhere.

Sure you can use GPO or ConfigMgr to ensure the service is installed on all DC, but sometimes one (or many) is missed or fails. This results in password changes being “lost”.

I have found the simplest way to fix this frustration is to use a simple PowerShell script to enumerate the forest, domains and domain controllers to test if the service is installed and active.

The following script is a simple example of how this can be achieved. At Integralis we have turned this into a monitor that reports into our monitoring platforms to automate the process (for large environments that does not have our agents on all domain controllers).

The approach is generic and will work for any service.

Tester – Service Status.ps1 – Tester to check the status of all PCNSSVC services on all current domain context DCs.
The scripts will look for all current domain context DC and query the status of the PCNSSVC service.
Possible status messages include:
– running
– stopped
– starting
– unreachable (the server is not avaiable on the network)
Version 1.0 – Base features
Version 1.3 – Added nice(r) message for unreachable
Import-Module ActiveDirectory
$ADForest = Get-ADForest
$ADForestDomainNamingMaster = $ADForest.DomainNamingMaster
$ADForestDomains = $ADForest.Domains
$ADForestSchemaMaster = $ADForest.SchemaMaster
$ADInfo = Get-ADDomain
$ADDomainDNSRoot = $ADInfo.DNSRoot
$ADDomainInfrastructureMaster = $ADInfo.InfrastructureMaster
$ADDomainPDCEmulator = $ADInfo.PDCEmulator
$ADDomainReadOnlyReplicaDirectoryServers = $ADInfo.ReadOnlyReplicaDirectoryServers
$ADDomainReplicaDirectoryServers = $ADInfo.ReplicaDirectoryServers
$ADDomainRIDMaster = $ADInfo.RIDMaster
Write-Verbose Discovering Domain Controllers in the AD Forest $ADForestName `r
ForEach ($Domain in $ADForestDomains)
{ ## OPEN ForEach Domain in ADForestDomains
$DomainDCs = Get-ADDomainController filter * server $Domain
ForEach ($DC in $DomainDCs)
{ ## OPEN ForEach DC in DomainDCs
$DCName = $DC.HostName
Write-Verbose Adding $DCName to ForestDC list `r
[array] $ForestDCs += $DC.HostName
} ## CLOSE ForEach DC in DomainDCs
} ## CLOSE ForEach Domain in ADForestDomains
$ForestDCsCount = $ForestDCs.count
Write-Verbose Initial discovery found $ForestDCsCount DCs `r
# Add all DC lists into $DomainControllers
$DomainControllers = $ForestDCs + $ADDomainReadOnlyReplicaDirectoryServers + $ADDomainReplicaDirectoryServers + $ADForestGlobalCatalogs
# Remove duplicate DCs from $DomainControllers
$DomainControllers = $DomainControllers | Select-Object Unique
# Sort the $DomainControllers DC list
$DomainControllers = $DomainControllers | Sort-Object
function Get-MrService {
param (
[string[]]$ServiceName = 'pcnssvc'
foreach ($Computer in $ComputerName) {
foreach ($Service in $ServiceName) {
$ServiceInfo = Get-Service Name $Service ComputerName $Computer ErrorAction SilentlyContinue
if (-not($ServiceInfo)) {
$ServiceInfo = @{
MachineName = $Computer
Name = $Service
Status = 'Unreachable'
ComputerName = $ServiceInfo.MachineName
Name = $ServiceInfo.Name
Status = $ServiceInfo.Status
foreach ($server in $DomainControllers) {
#Get-Service -ComputerName $server -Name PCNSSVC | select MachineName, Name, Status
Get-MrService ComputerName $server