Script Repository


Users Password Self-Service information

May 18, 2020
1742

This script e-mails a report on all users and includes information about Password Self-Service enrollment. To schedule the report, create a Scheduled Task configured for the Domain-DNS object type.

Script 1: Report embedded into email body

Parameters:

  • $ouDNs - Specifies the distinguished names (DNs) of the Organizational Units in which users must be located to be included into the report. For infortion on how to get the DNs, see Get the DN of an Active Directory Object.
  • $reportType - specifies the type of report to be generated. The report can include only enrolled, only not enrolled or all users.
  • $to - specifies email addresses of the recipient(s) of the report;
  • $subject - specifies the email message subject;
  • $reportHeader - specifies the email message header;
  • $reportFooter - specifies the email message footer.
Edit Remove
PowerShell
$ouDNs = @("CN=Users,DC=domain,DC=com", "OU=Sales,DC=domain,DC=com") # TODO modify me. Set to @() for search all users

# $reportType = "Enrolled" # TODO: uncomment the type you need
# $reportType = "Not enrolled"
 $reportType = $NULL

# E-mail settings
$to = "recipient@domain.com" # TODO: modify me
$subject = "Password Self-Service statistics" # TODO: modify me
$reportHeader = "<b>Password Self-Service statistics.</b><br/><br/>" # TODO: modify me
$reportFooter = "<hr /><p><i>Please do not reply to this e-mail, it was sent to you for notification purposes only.</i></p>" # TODO: modify me

function GetUserGuids($dn, $list)
{
    $searcher = $Context.BindToObjectByDN($dn)
    $searcher.SearchFilter = "(sAMAccountType=805306368)"
    $searcher.SearchScope = "ADS_SCOPE_SUBTREE"
    $searcher.PageSize = 500
    $searcher.SetPropertiesToLoad(@("objectGUID"))
    
    try
    {
        $searchResultIterator = $searcher.ExecuteSearch()
        $searchResults = $searchResultIterator.FetchAll()
        
        $searchResults | %%{[void]$list.Add([Guid]$_.Properties["objectGUID"].Value)}
    }
    finally
    {
        # Release resources
        if ($searchResultIterator){ $searchResultIterator.Dispose() }
    }
}

# Bind to the 'Password Self-Service Statistics' container
$passwordSelfServiceStatisticsPath = $Context.GetWellKnownContainerPath("PasswordSelfServiceStatistics")
$passwordSelfServiceStatistics = $Context.BindToObject($passwordSelfServiceStatisticsPath)

# Get the enrollment report
$reportIsBeingGenerated = $True
do
{
    try
    {
        $report = $passwordSelfServiceStatistics.GetReport("ADM_PSSREPORTTYPE_ENROLLMENT")
    }
    catch [System.Runtime.InteropServices.COMException]
    {
        if ($_.Exception.ErrorCode -eq "-2147024875")
        {
            # Report is being generated. Wait 10 seconds
            Start-Sleep -Seconds 10
            continue
        }
        else
        {
            $reportIsBeingGenerated = $False
            $Context.LogMessage($_.Exception.Message, "Error")
            return
        }
    }
    
    if ($report.GenerateDate -lt [System.Datetime]::UtcNow.AddHours(-1))
    {
        $passwordSelfServiceStatistics.ResetReportCache("ADM_PSSREPORTTYPE_ENROLLMENT")
    }
    else
    {
        $reportIsBeingGenerated = $False
    }
}
while ($reportIsBeingGenerated)

# Get user guids
$userGuids = New-Object "System.Collections.Generic.HashSet[System.Guid]"
foreach ($dn in $ouDNs)
{
    GetUserGuids $dn $userGuids
}

# Build the report
$html = New-Object "System.Text.StringBuilder"
[void]$html.Append($reportHeader)
[void]$html.Append("<table border='1' width='100%%'><tr>")
[void]$html.Append("<th>Name</th>")
[void]$html.Append("<th>Parent</th>")
[void]$html.Append("<th>Enrolled</th>")
[void]$html.Append("<th>Effective Policy</th>")
[void]$html.Append("<th>Date/Time</th>")
[void]$html.Append("<th>Enrollment Invitation</th>")
[void]$html.Append("</tr>")
$records = $report.Records
for ($i = 0; $i -lt $records.Count; $i++)
{
    $record = $records.GetRecord($i)
    
    # Get user information
    $userPath = $NULL
    $userDisplayName = $NULL
    $userParentCanonicalName = $NULL
    $userAccountIsEnabled = $NULL
    $userIsEnrolled = $NULL
    $userAccountIsExpired = $NULL
    $userInfo = $record.GetUserInfo([ref]$userPath, [ref]$userDisplayName, [ref]$userParentCanonicalName, 
        [ref]$userAccountIsEnabled, [ref]$userIsEnrolled, [ref]$userAccountIsExpired)
    
    $path = New-Object Softerra.Adaxes.Adsi.AdsPath $userPath
    $guid = [Guid]$path.Guid
    
    if ($userGuids.Count -ne 0 -and !$userGuids.Contains($guid))
    {
        continue
    }
    
    if (($reportType -eq "Enrolled" -and !$userIsEnrolled) -or 
        ($reportType -eq "Not enrolled" -and $userIsEnrolled))
    {
        continue
    }
    
    # Get event date
    $eventDate = $record.EventDate
    if ($eventDate -eq [DateTime]::MinValue)
    {
        $eventDate = $NULL
    }
    
    # Get policy information
    $policyPath = $NULL
    $policyName = $NULL
    $policyInfo = $record.GetEnrollmentPolicyInfo([ref]$policyPath, [ref]$policyName)
    
    if ($userIsEnrolled)
    {
        $userIsEnrolled = "Yes ($policyName)"
    }
    else
    {
        $userIsEnrolled = "No"
    }
    
    # Get invitation info
    $successSendDate = New-Object System.Datetime 0
    $errorMessage = $NULL
    $record.GetSendInvitationInfo([ref]$successSendDate, [ref]$errorMessage)
    if ([System.String]::IsNullOrEmpty($errorMessage) -and $successSendDate -ne [Datetime]::MinValue)
    {
        $enrollmentInvitation = $successSendDate
    }
    else
    {
        $enrollmentInvitation = $errorMessage
    }
    
    # Get effective policy information
    $effectivePolicyPath = $NULL
    $effectivePolicyName = $NULL
    $record.GetEffectivePolicyInfo([ref]$effectivePolicyPath, [ref]$effectivePolicyName)
    
    # Add information to the report
    [void]$html.Append("<tr><td>$userDisplayName</td><td>$userParentCanonicalName</td><td>$userIsEnrolled</td><td>$effectivePolicyName</td><td>$eventDate</td><td>$enrollmentInvitation</td></tr>")
}

[void]$html.Append("</table>")
[void]$html.Append($reportFooter)

# Send mail
$Context.SendMail($to, $subject, $NULL, $html.ToString())

Script 2: Report attached as CSV

Parameters:

  • $ouDNs - Specifies the distinguished names (DNs) of the Organizational Units in which users must be located to be included into the report. For infortion on how to get the DNs, see Get the DN of an Active Directory Object.
  • $reportType - specifies the type of report to be generated. The report can include only enrolled, only not enrolled or all users.
  • $csvFilePath - Specifies a path to the CSV file that will be temporary created.
  • $removeCSVFile - Specifies whether the CSV file will be removed after emailing the report.
  • $to - specifies email addresses of the recipient(s) of the report;
  • $subject - specifies the email message subject;
  • $from - Specifies the email address from which the notification will be sent.
  • $message - Specifies the email notification message.
  • $smtpServer - Specifies the SMTP server that will be used to send the notification.
Edit Remove
PowerShell
$ouDNs = @("CN=Users,DC=domain,DC=com", "OU=Sales,DC=domain,DC=com") # TODO modify me. Set to @() for search all users

# $reportType = "Enrolled" # TODO: uncomment the type you need
# $reportType = "Not enrolled"
 $reportType = $NULL

# CSV file settings
$csvFilePath = "C:\scripts\report.csv" # TODO: modify me
$removeCSVFile = $True # TODO: modify me

# Mail settings
$to = "recipient@domain.com" # TODO: modify me
$from = "noreply@localhost" # TODO: modify me
$smtpServer = "mailserver.domain.com" # TODO: modify me
$subject = "Password Self-Service statistics" # TODO: modify me
$message = "Password Self-Service statistics" # TODO: modify me

function GetUserGuids($dn, $list)
{
    $searcher = $Context.BindToObjectByDN($dn)
    $searcher.SearchFilter = "(sAMAccountType=805306368)"
    $searcher.SearchScope = "ADS_SCOPE_SUBTREE"
    $searcher.PageSize = 500
    $searcher.SetPropertiesToLoad(@("objectGUID"))
    
    try
    {
        $searchResultIterator = $searcher.ExecuteSearch()
        $searchResults = $searchResultIterator.FetchAll()
        
        $searchResults | %%{[void]$list.Add([Guid]$_.Properties["objectGUID"].Value)}
    }
    finally
    {
        # Release resources
        if ($searchResultIterator){ $searchResultIterator.Dispose() }
    }
}

# Bind to the 'Password Self-Service Statistics' container
$passwordSelfServiceStatisticsPath = $Context.GetWellKnownContainerPath("PasswordSelfServiceStatistics")
$passwordSelfServiceStatistics = $Context.BindToObject($passwordSelfServiceStatisticsPath)

# Get the enrollment report
$reportIsBeingGenerated = $True
do
{
    try
    {
        $report = $passwordSelfServiceStatistics.GetReport("ADM_PSSREPORTTYPE_ENROLLMENT")
    }
    catch [System.Runtime.InteropServices.COMException]
    {
        if ($_.Exception.ErrorCode -eq "-2147024875")
        {
            # Report is being generated. Wait 10 seconds
            Start-Sleep -Seconds 10
            continue
        }
        else
        {
            $reportIsBeingGenerated = $False
            $Context.LogMessage($_.Exception.Message, "Error")
            return
        }
    }
    
    if ($report.GenerateDate -lt [System.Datetime]::UtcNow.AddHours(-1))
    {
        $passwordSelfServiceStatistics.ResetReportCache("ADM_PSSREPORTTYPE_ENROLLMENT")
    }
    else
    {
        $reportIsBeingGenerated = $False
    }
}
while ($reportIsBeingGenerated)

# Get user guids
$userGuids = New-Object "System.Collections.Generic.HashSet[System.Guid]"
foreach ($dn in $ouDNs)
{
    GetUserGuids $dn $userGuids
}

# Build the report
$reportRecords = New-Object System.Collections.ArrayList
$records = $report.Records
for ($i = 0; $i -lt $records.Count; $i++)
{
    $record = $records.GetRecord($i)
    
    # Get user information
    $userPath = $NULL
    $userDisplayName = $NULL
    $userParentCanonicalName = $NULL
    $userAccountIsEnabled = $NULL
    $userIsEnrolled = $NULL
    $userAccountIsExpired = $NULL
    $userInfo = $record.GetUserInfo([ref]$userPath, [ref]$userDisplayName, [ref]$userParentCanonicalName, 
        [ref]$userAccountIsEnabled, [ref]$userIsEnrolled, [ref]$userAccountIsExpired)
    
    $path = New-Object Softerra.Adaxes.Adsi.AdsPath $userPath
    $guid = [Guid]$path.Guid
    
    if ($userGuids.Count -ne 0 -and !$userGuids.Contains($guid))
    {
        continue
    }
    
    if (($reportType -eq "Enrolled" -and !$userIsEnrolled) -or 
        ($reportType -eq "Not enrolled" -and $userIsEnrolled))
    {
        continue
    }
    
    # Get event date
    $eventDate = $record.EventDate
    if ($eventDate -eq [DateTime]::MinValue)
    {
        $eventDate = $NULL
    }
    
    # Get policy information
    $policyPath = $NULL
    $policyName = $NULL
    $policyInfo = $record.GetEnrollmentPolicyInfo([ref]$policyPath, [ref]$policyName)
    
    if ($userIsEnrolled)
    {
        $userIsEnrolled = "Yes ($policyName)"
    }
    else
    {
        $userIsEnrolled = "No"
    }
    
    # Get invitation info
    $successSendDate = New-Object System.Datetime 0
    $errorMessage = $NULL
    $record.GetSendInvitationInfo([ref]$successSendDate, [ref]$errorMessage)
    if ([System.String]::IsNullOrEmpty($errorMessage) -and $successSendDate -ne [Datetime]::MinValue)
    {
        $enrollmentInvitation = $successSendDate
    }
    else
    {
        $enrollmentInvitation = $errorMessage
    }
    
    # Get effective policy information
    $effectivePolicyPath = $NULL
    $effectivePolicyName = $NULL
    $record.GetEffectivePolicyInfo([ref]$effectivePolicyPath, [ref]$effectivePolicyName)
    
    # Add information to the report
    $reportRecord = [PSCustomObject][ordered]@{
        "Name" = $userDisplayName
        "Parent" = $userParentCanonicalName
        "Enrolled" = $userIsEnrolled
        "Effective Policy" = $effectivePolicyName
        "Date/Time" = $eventDate
        "Enrollment Invitation" = $enrollmentInvitation
    }
    $reportRecords.Add($reportRecord)
}

# Export to CSV
$reportRecords | Sort-Object @sortParameters | Export-csv -NoTypeInformation -Path $csvFilePath

# Send mail 
Send-MailMessage -To $to -from $from -SmtpServer $smtpServer -Subject $subject -Body $message -Attachments $csvFilePath

if ($removeCSVFile)
{
    # Remove temporary file
    Remove-Item $csvFilePath -Force
}

Comments ( 3 )
avatar
Andrew Hancock
September 25, 2019

I'm not that great with Powershell and this worked great for us after a little tweaking to fit our environment.
Thanks!

avatar
Ben Smith
November 25, 2019

This is great, but can you modify it to instead output an excel file or .csv?

avatar
Support
November 27, 2019

Hello Ben,

We added the script that generates the report as a CSV file and sends it as attachment via email.

Leave a comment