Script repository
The scripts email a report containing all users and information about their password self-service enrollment. To execute the script, create a scheduled task configured for the Domain object type and add a managed domain to the Activity Scope. The domain does not determine the users to include into the report and will only be used to execute the scheduled task itself.
Script 1: Report embedded into email body
Parameters
$ouDNs- the distinguished names (DNs) of the Organizational Units. Only users from the OUs will be included into the report. For information on how to get the DNs, see Get the DN of a directory object. To search in all the managed domains, set the variable to an empty array.$reportType- the type of report to be generated. Can only beEnrolledorNot enrolled.$to- email addresses of the report recipients.$subject- the email notification subject.$reportHeader- the email notification header.$reportFooter- the email notification footer.
$ouDNs = @("CN=Users,DC=domain,DC=com", "OU=Sales,DC=domain,DC=com") # TODO modify me.
$reportType = "Enrolled" # TODO: modify me
# 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.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
$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- the distinguished names (DNs) of the Organizational Units. Only users from the OUs will be included into the report. For information on how to get the DNs, see Get the DN of a directory object. To search in all the managed domains, set the variable to an empty array.$reportType- the type of report to be generated. Can only beEnrolledorNot enrolled.$to- email addresses of the report recipients.$from- the email address from which the notification will be sent.$smtpServer- the SMTP server that will be used to send the notification.$subject- the email notification subject.$reportHeader- the email notification header.$reportFooter- the email notification footer.$csvFilePath- a path to the CSV file that will be temporary created.$removeCSVFile- Set to$trueto delete the CSV file after delivery.
$ouDNs = @("CN=Users,DC=domain,DC=com", "OU=Sales,DC=domain,DC=com") # TODO modify me.
$reportType = "Enrolled" # TODO: modify me
# CSV file settings
$csvFilePath = "C:\scripts\report.csv" # TODO: modify me
$removeCSVFile = $True # TODO: modify me
# Email 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
You must be signed in to comment.
Andrew Hancock
I'm not that great with Powershell and this worked great for us after a little tweaking to fit our environment.
Thanks!
Ben
This is great, but can you modify it to instead output an excel file or .csv?
Support
Hello Ben,
We added the script that generates the report as a CSV file and sends it as attachment via email.