We use cookies to improve your experience.
By your continued use of this site you accept such use.
For more details please see our privacy policy and cookies policy.

Script Repository

Users Password Self-Service information

October 06, 2023 Views: 3338

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 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 a 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.Criteria = New-AdmCriteria "user"
    $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 a 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.
  • $from - Specifies the email address from which the notification will be sent.
  • $smtpServer - Specifies the SMTP server that will be used to send the notification.
  • $subject - Specifies the email message subject.
  • $message - Specifies the email notification message.
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.Criteria = New-AdmCriteria "user"
    $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 Sep 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 Nov 25, 2019
This is great, but can you modify it to instead output an excel file or .csv?
avatar
Support Nov 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
Loading...

Got questions?

Support Questions & Answers