0 votes

Tried using the following script below found in another thread here but get an error message from Adaxes saying that the cmdlet is not valid --

Import-Module Adaxes
$email = "myemail@mycompany.com" # TODO modify me
$inactivityDurationThreshold = "30" # Days

$baseDN = "%distinguishedName%"
$domain = $Context.GetObjectDomain($baseDN)

function GetObjectDisplayName($objectDN)
{    
    $objectPath = New-Object -TypeName "Softerra.Adaxes.Adsi.AdsPath"`
        -ArgumentList @($null, $objectDN)    
    return [Softerra.Adaxes.Utils.ObjectNameHelper]::GetObjectName(
        $objectPath, "IncludeParentPath")
}

$htmlBuilder = New-Object "System.Text.StringBuilder"
$htmlBuilder.append("<html><head>")
$htmlBuilder.append("<meta http-equiv=""Content-Type""`
    content=""text/html charset=UTF-8""></head>")
$htmlBuilder.append("<body>")
$baseObjectDisplayName = GetObjectDisplayName($baseDN)
$htmlBuilder.appendFormat(
    "<p>Inactive Users (<b>{0}</b>)</p>",
    $baseObjectDisplayName)
$htmlBuilder.append("<table width=""100%%"" border=""1"">")
$htmlBuilder.append("<tr>")
$htmlBuilder.append("<th>User Name</th>
    <th>Parent</th><th>Last Logon</th>")
$htmlBuilder.append("</tr>")

# Find inactive users
$users = Search-AdAccount -AccountInactive `
    -TimeSpan $inactivityDurationThreshold `
    -SearchBase $baseDN -UsersOnly `
    -Server $domain -AdaxesService localhost
if ($users)
{
    foreach ($user in $users)    
    {        
        $user = Get-AdmUser $user -Properties "LastLogonDate"`
            -Server $domain -AdaxesService "localhost"
        $userDN = New-Object "Softerra.Adaxes.Ldap.DN" $user.DistinguishedName
        $parentDisplayName = GetObjectDisplayName($userDN.Parent.ToString())        
        $htmlBuilder.append("<tr>")        
        $htmlBuilder.appendFormat("<td>{0}</td>", $user.Name)        
        $htmlBuilder.appendFormat("<td>{0}</td>", $parentDisplayName)        
        $htmlBuilder.appendFormat("<td>{0}</td>", $user.LastLogonDate)        
        $htmlBuilder.append("</tr>")    
    }
}

$htmlBuilder.append("</table>")
$htmlBuilder.append("</body></html>")
$Context.SendMail($email, "[AD Report] Inactive Users", $NULL,
    $htmlBuilder.ToString())

Originally it said Search-AdmAccount was not valid, so I tested and "AdAccount" worked. Any idea what the problem is?

the exact error --

The term 'Search-AdAccount' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

by (100 points)

1 Answer

0 votes
by (294k points)
selected by
Best answer

Hello Adrian,

Have a look at the following script from our repository: http://www.adaxes.com/script-repository ... s-s196.htm.

0

Hi Support2,

Thanks for that link. It works, however, it generates an e-mail for each individual account that falls into that category. How can I fix this so that it's just all in one e-mail?

0

Hello Adrian,

The Scheduled Task executing the script must be configured for Organizational Unit Object type and the Activity Scope must contain Organizational Units. When adding OUs to the Activity Scope, you need to select This Organizational-Unit object. Finally you should have something like the following:

0

Thank you, that worked! However, I'd like to tweak this to only include accounts that are still enabled. I think, ideally I'd like to have two separate reports. One that shows accounts that are enabled but have been inactive for some time, and then those that are disabled but are still in the system. I'd like to clean up AD to the point we don't have disabled accounts lingering for more than 3 months. What would be the best way to accomplish this?

0

Hello Adrian,

Find the updated script below. It will send a notification with two tables, one with enabled inactive users and the other with disabled inactive users. You just need to replace the script in your Scheduled Task with the updated one.

$to = "recipient@company.com" # TODO: modify me
$inactivityDurationThreshold = "30" # Days
$sortBy = "name" # TODO: modify me
$sortDirection = "Ascending" # TODO: modify me
$baseDN = "%distinguishedName%" # TODO: modify me

function GetObjectDisplayName($objectDN)
{
   $objectPath = New-Object -TypeName "Softerra.Adaxes.Adsi.AdsPath"`
       -ArgumentList @($null, $objectDN)    
   return [Softerra.Adaxes.Utils.ObjectNameHelper]::GetObjectName(
       $objectPath, "IncludeParentPath")
}

$htmlBuilder = New-Object "System.Text.StringBuilder"
[void]$htmlBuilder.Append("<html><head>")
[void]$htmlBuilder.Append("<meta http-equiv=""Content-Type""`
   content=""text/html charset=UTF-8""></head>")
[void]$htmlBuilder.Append("<body>")
$baseObjectDisplayName = GetObjectDisplayName($baseDN)
[void]$htmlBuilder.AppendFormat(
   "<p>Inactive Users (<b>{0}</b>)</p>",
   $baseObjectDisplayName)

# Find inactive users
$searcher = $Context.BindToObjectByDN($baseDN)
$searcher.PageSize = 500
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$date = [System.DateTime]::UtcNow.AddDays(-$inactivityDurationThreshold)
$dateGenerilized = [Softerra.Adaxes.Utils.Transform]::ToGeneralizedTime($date)
$dateFileTime = $date.ToFileTimeUTC().ToString()
$searcher.SearchFilter = "(&(sAMAccountType=805306368)(|(&(!(lastLogonTimestamp=*))(whenCreated<=$dateGenerilized))(lastLogonTimestamp<=$dateFileTime)))"
$searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
$searcher.SetPropertiesToLoad(@("name", "sAMAccountName", "distinguishedName"))
$sortOption = New-Object "Softerra.Adaxes.Adsi.AdmSortOption"
$sortOption.PropertyName = $sortBy
$sortOption.Direction = $sortDirection
$searcher.Sort = $sortOption

try
{
    $searchResultIterator = $searcher.ExecuteSearch()
    $searchResults = $searchResultIterator.FetchAll()

    # Add inactive users to report
    $enabledUsers = New-Object "System.Text.StringBuilder"
    $disabledUsers = New-Object "System.Text.StringBuilder"
    if ($users.Count -eq 0)
    {
        return
    }

    foreach ($searchResult in $searchResults)
    {
        $user = $Context.BindToObject($searchResult.AdsPath)
        $inactivityDuration = $user.Get("adm-InactivityDuration")
        if ($inactivityDuration -lt $inactivityDurationThreshold)
        {
            continue
        }

        $userDN = New-Object "Softerra.Adaxes.Ldap.DN" $searchResult.Properties["distinguishedName"].Value
        $parentDisplayName = GetObjectDisplayName($userDN.Parent.ToString())
        $fullname = $searchResult.Properties["name"].Value
        $username = $searchResult.Properties["sAMAccountName"].Value
        $record = "<tr><td>$fullname</td><td>$username</td><td>$parentDisplayName</td><td>$inactivityDuration day(s)</td></tr>"

        if ($user.AccountDisabled)
        {
            [void]$disabledUsers.Append($record)
        }
        else
        {
            [void]$enabledUsers.Append($record)
        }
    }

    # Build tables
    $htmlTable = "<table width=""100%%"" border=""1""><tr><th>User Name</th><th>User Logon Name (pre-Windows 2000)</th><th>Parent</th><th>Inactivity Duration</th></tr>"

    # Enabled users
    if ($enabledUsers.Length -ne 0)
    {
        [void]$htmlBuilder.Append("<p>")
        [void]$htmlBuilder.Append("<b>Enable users</b>")
        [void]$htmlBuilder.Append($htmlTable)
        [void]$htmlBuilder.Append($enabledUsers.ToString())
        [void]$htmlBuilder.Append("</table>")
        [void]$htmlBuilder.Append("</p>")
    }

    # Disabled users
    if ($disabledUsers.Length -ne 0)
    {
        [void]$htmlBuilder.Append("<p>")
        [void]$htmlBuilder.Append("<b>Disabled users</b>")
        [void]$htmlBuilder.Append($htmlTable)
        [void]$htmlBuilder.Append($disabledUsers.ToString())
        [void]$htmlBuilder.Append("</table>")
        [void]$htmlBuilder.Append("</p>")
    }

    # Finish building report
    $htmlBuilder.Append("</body></html>")

    # Send report
    $Context.SendMail($to, "[AD Report] Inactive Users", $NULL, $htmlBuilder.ToString())
}
finally
{
    # Release resources
    $searchResultIterator.Dispose()
}
0

Thank you! Will try this now...

edit -- This worked perfectly. Thank you.
Maybe just as an added bonus, is there a way to export the reports to a CSV file and attach them to the e-mail?

0

Hello Adrian,

Yes, it is possible. Find the updated script below. The script will send an email notification with two CSV files attached. The files will contain inactive disabled users and inactive enabled users.

$inactivityDurationThreshold = "30" # Days
$sortBy = "name" # TODO: modify me
$sortDirection = "Ascending" # TODO: modify me
$baseDN = "%distinguishedName%" # TODO: modify me

# CSV file settings
$csvFilesFolder = "C:\Scripts" # TODO: modify me
$removeCSVFile = $True # TODO: modify me

# E-mail settings
$to = "recipient@domain.com" # TODO: modify me
$subject = "[AD Report] Inactive Users" # TODO: modify me
$from = "noreply@domain.com" # TODO: modify me
$smtpServer = "mail.domain.com" # TODO: modify me

function GetObjectDisplayName($objectDN)
{
   $objectPath = New-Object -TypeName "Softerra.Adaxes.Adsi.AdsPath"`
       -ArgumentList @($null, $objectDN)   
   return [Softerra.Adaxes.Utils.ObjectNameHelper]::GetObjectName(
       $objectPath, "IncludeParentPath")
}

$htmlBuilder = New-Object "System.Text.StringBuilder"
[void]$htmlBuilder.Append("<html><head>")
[void]$htmlBuilder.Append("<meta http-equiv=""Content-Type""`
   content=""text/html charset=UTF-8""></head>")
[void]$htmlBuilder.Append("<body>")
$baseObjectDisplayName = GetObjectDisplayName($baseDN)
[void]$htmlBuilder.AppendFormat(
   "<p>Inactive Users (<b>{0}</b>)</p>",
   $baseObjectDisplayName)

# Find inactive users
$searcher = $Context.BindToObjectByDN($baseDN)
$searcher.PageSize = 500
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$date = [System.DateTime]::UtcNow.AddDays(-$inactivityDurationThreshold)
$dateGenerilized = [Softerra.Adaxes.Utils.Transform]::ToGeneralizedTime($date)
$dateFileTime = $date.ToFileTimeUTC().ToString()
$searcher.SearchFilter = "(&(sAMAccountType=805306368)(|(&(!(lastLogonTimestamp=*))(whenCreated<=$dateGenerilized))(lastLogonTimestamp<=$dateFileTime)))"
$searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
$searcher.SetPropertiesToLoad(@("name", "sAMAccountName", "distinguishedName"))
$sortOption = New-Object "Softerra.Adaxes.Adsi.AdmSortOption"
$sortOption.PropertyName = $sortBy
$sortOption.Direction = $sortDirection
$searcher.Sort = $sortOption

try
{
    $searchResultIterator = $searcher.ExecuteSearch()
    $searchResults = $searchResultIterator.FetchAll()

    # Add inactive users to report
    if ($users.Count -eq 0)
    {
        return
    }

    $csvEnabledUsers = New-Object "System.Collections.ArrayList"
    $csvDisabledUsers = New-Object "System.Collections.ArrayList"
    $htmlEnabledUsers = New-Object "System.Text.StringBuilder"
    $htmlDisabledUsers = New-Object "System.Text.StringBuilder"
    foreach ($searchResult in $searchResults)
    {
        $user = $Context.BindToObject($searchResult.AdsPath)
        $inactivityDuration = $user.Get("adm-InactivityDuration")
        if ($inactivityDuration -lt $inactivityDurationThreshold)
        {
            continue
        }

        $userDN = New-Object "Softerra.Adaxes.Ldap.DN" $searchResult.Properties["distinguishedName"].Value
        $parentDisplayName = GetObjectDisplayName($userDN.Parent.ToString())
        $fullname = $searchResult.Properties["name"].Value
        $username = $searchResult.Properties["sAMAccountName"].Value
        $htmlRecord = "<tr><td>$fullname</td><td>$username</td><td>$parentDisplayName</td><td>$inactivityDuration day(s)</td></tr>"
        $csvRecord = New-Object PSObject -Property @{
            "User Name" = $fullname;
            "User Logon Name (pre-Windows 2000)" = $username;
            "Parent" = $parentDisplayName;
            "Inactivity Duration" = "$inactivityDuration day(s)";
        }

        if ($user.AccountDisabled)
        {
            [void]$htmlDisabledUsers.Append($htmlRecord)
            $csvDisabledUsers.Add($csvRecord)
        }
        else
        {
            [void]$htmlEnabledUsers.Append($htmlRecord)
            $csvEnabledUsers.Add($csvRecord)
        }
    }

    # Build tables
    $htmlTable = "<table width=""100%%"" border=""1""><tr><th>User Name</th><th>User Logon Name (pre-Windows 2000)</th><th>Parent</th><th>Inactivity Duration</th></tr>"

    # Enabled users
    $attachement = @()
    if ($htmlEnabledUsers.Length -ne 0)
    {
        # Add HTML records
        [void]$htmlBuilder.Append("<p>")
        [void]$htmlBuilder.Append("<b>Enable users</b>")
        [void]$htmlBuilder.Append($htmlTable)
        [void]$htmlBuilder.Append($htmlEnabledUsers.ToString())
        [void]$htmlBuilder.Append("</table>")
        [void]$htmlBuilder.Append("</p>")

        # Create csv file
        $filePath = "$csvFilesFolder\%name% Enabled users.csv"
        $csvEnabledUsers | Export-Csv -Path $filePath -NoTypeInformation
        $attachement += $filePath
    }

    # Disabled users
    if ($htmlDisabledUsers.Length -ne 0)
    {
        # Add HTML records
        [void]$htmlBuilder.Append("<p>")
        [void]$htmlBuilder.Append("<b>Disabled users</b>")
        [void]$htmlBuilder.Append($htmlTable)
        [void]$htmlBuilder.Append($htmlDisabledUsers.ToString())
        [void]$htmlBuilder.Append("</table>")
        [void]$htmlBuilder.Append("</p>")

        # Create CSV file
        $filePath = "$csvFilesFolder\%name% Disabled users.csv"
        $csvDisabledUsers | Export-Csv -Path $filePath -NoTypeInformation
        $attachement += $filePath
    }

    # Finish building report
    $htmlBuilder.Append("</body></html>")

    # Send report
    Send-MailMessage -To $to -From $from -Subject $subject -SmtpServer $smtpServer -Body $htmlBuilder.ToString() -BodyAsHtml -Attachments $attachement

    if ($removeCSVFile)
    {
        foreach ($path in $attachement)
        {
            Remove-Item -Path $path -Confirm:$False -Force
        }
    }
}
finally
{
    # Release resources
    $searchResultIterator.Dispose()
}
0

Thank you again!

It seems my requirements are constantly evolving. Thank you so much for helping me with this.

Would it be possible to add a column to the report that shows if the account has a mailbox in exchange? would it also be possible to show the size of the mailbox? If not, just knowing if that particular AD account has a mailbox attached would be great!

Thanks so much for your prompt and detailed replies.

0

Hello Adrian,

Find the updated script below. We added the column that shows whether the user has a mailbox. We did not add a column for mailbox size as it will significantly increase the time of script execution.

$inactivityDurationThreshold = "30" # Days
$sortBy = "name" # TODO: modify me
$sortDirection = "Ascending" # TODO: modify me
$baseDN = "%distinguishedName%" # TODO: modify me

# CSV file settings
$csvFilesFolder = "C:\Scripts" # TODO: modify me
$removeCSVFile = $True # TODO: modify me

# E-mail settings
$to = "recipient@domain.com" # TODO: modify me
$subject = "[AD Report] Inactive Users" # TODO: modify me
$from = "noreply@domain.com" # TODO: modify me
$smtpServer = "mail.domain.com" # TODO: modify me

function GetObjectDisplayName($objectDN)
{
   $objectPath = New-Object -TypeName "Softerra.Adaxes.Adsi.AdsPath"`
       -ArgumentList @($null, $objectDN)   
   return [Softerra.Adaxes.Utils.ObjectNameHelper]::GetObjectName(
       $objectPath, "IncludeParentPath")
}

$htmlBuilder = New-Object "System.Text.StringBuilder"
[void]$htmlBuilder.Append("<html><head>")
[void]$htmlBuilder.Append("<meta http-equiv=""Content-Type""`
   content=""text/html charset=UTF-8""></head>")
[void]$htmlBuilder.Append("<body>")
$baseObjectDisplayName = GetObjectDisplayName($baseDN)
[void]$htmlBuilder.AppendFormat(
   "<p>Inactive Users (<b>{0}</b>)</p>",
   $baseObjectDisplayName)

# Find inactive users
$searcher = $Context.BindToObjectByDN($baseDN)
$searcher.PageSize = 500
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$date = [System.DateTime]::UtcNow.AddDays(-$inactivityDurationThreshold)
$dateGenerilized = [Softerra.Adaxes.Utils.Transform]::ToGeneralizedTime($date)
$dateFileTime = $date.ToFileTimeUTC().ToString()
$searcher.SearchFilter = "(&(sAMAccountType=805306368)(|(&(!(lastLogonTimestamp=*))(whenCreated<=$dateGenerilized))(lastLogonTimestamp<=$dateFileTime)))"
$searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
$searcher.SetPropertiesToLoad(@("name", "sAMAccountName", "distinguishedName", "mailNickname", "msExchHomeServerName"))
$sortOption = New-Object "Softerra.Adaxes.Adsi.AdmSortOption"
$sortOption.PropertyName = $sortBy
$sortOption.Direction = $sortDirection
$searcher.Sort = $sortOption

try
{
    $searchResultIterator = $searcher.ExecuteSearch()
    $searchResults = $searchResultIterator.FetchAll()

    # Add inactive users to report
    if ($users.Count -eq 0)
    {
        return
    }

    $csvEnabledUsers = New-Object "System.Collections.ArrayList"
    $csvDisabledUsers = New-Object "System.Collections.ArrayList"
    $htmlEnabledUsers = New-Object "System.Text.StringBuilder"
    $htmlDisabledUsers = New-Object "System.Text.StringBuilder"
    foreach ($searchResult in $searchResults)
    {
        $user = $Context.BindToObject($searchResult.AdsPath)
        $inactivityDuration = $user.Get("adm-InactivityDuration")
        if ($inactivityDuration -lt $inactivityDurationThreshold)
        {
            continue
        }

        $userDN = New-Object "Softerra.Adaxes.Ldap.DN" $searchResult.Properties["distinguishedName"].Value
        $parentDisplayName = GetObjectDisplayName($userDN.Parent.ToString())
        $fullname = $searchResult.Properties["name"].Value
        $username = $searchResult.Properties["sAMAccountName"].Value
        if (!([System.String]::IsNullOrEmpty($searchResult.Properties["mailNickname"].Value)) -and !([System.String]::IsNullOrEmpty($searchResult.Properties["msExchHomeServerName"].Value)))
        {
            $mailboxStatus = "Yes"
        }
        else
        {
            $mailboxStatus = "No"
        }
        $htmlRecord = "<tr><td>$fullname</td><td>$username</td><td>$parentDisplayName</td><td>$inactivityDuration day(s)</td><td>$mailboxStatus</td></tr>"
        $csvRecord = New-Object PSObject -Property @{
            "User Name" = $fullname;
            "User Logon Name (pre-Windows 2000)" = $username;
            "Parent" = $parentDisplayName;
            "Inactivity Duration" = "$inactivityDuration day(s)";
            "Mailbox Enabled" = $mailboxStatus;
        }

        if ($user.AccountDisabled)
        {
            [void]$htmlDisabledUsers.Append($htmlRecord)
            $csvDisabledUsers.Add($csvRecord)
        }
        else
        {
            [void]$htmlEnabledUsers.Append($htmlRecord)
            $csvEnabledUsers.Add($csvRecord)
        }
    }

    # Build tables
    $htmlTable = "<table width=""100%%"" border=""1""><tr><th>User Name</th><th>User Logon Name (pre-Windows 2000)</th><th>Parent</th><th>Inactivity Duration</th><th>Has a mailbox</th></tr>"

    # Enabled users
    $attachement = @()
    if ($htmlEnabledUsers.Length -ne 0)
    {
        # Add HTML records
        [void]$htmlBuilder.Append("<p>")
        [void]$htmlBuilder.Append("<b>Enable users</b>")
        [void]$htmlBuilder.Append($htmlTable)
        [void]$htmlBuilder.Append($htmlEnabledUsers.ToString())
        [void]$htmlBuilder.Append("</table>")
        [void]$htmlBuilder.Append("</p>")

        # Create csv file
        $filePath = "$csvFilesFolder\%name% Enabled users.csv"
        $csvEnabledUsers | Export-Csv -Path $filePath -NoTypeInformation
        $attachement += $filePath
    }

    # Disabled users
    if ($htmlDisabledUsers.Length -ne 0)
    {
        # Add HTML records
        [void]$htmlBuilder.Append("<p>")
        [void]$htmlBuilder.Append("<b>Disabled users</b>")
        [void]$htmlBuilder.Append($htmlTable)
        [void]$htmlBuilder.Append($htmlDisabledUsers.ToString())
        [void]$htmlBuilder.Append("</table>")
        [void]$htmlBuilder.Append("</p>")

        # Create CSV file
        $filePath = "$csvFilesFolder\%name% Disabled users.csv"
        $csvDisabledUsers | Export-Csv -Path $filePath -NoTypeInformation
        $attachement += $filePath
    }

    # Finish building report
    $htmlBuilder.Append("</body></html>")

    # Send report
    Send-MailMessage -To $to -From $from -Subject $subject -SmtpServer $smtpServer -Body $htmlBuilder.ToString() -BodyAsHtml -Attachments $attachement

    if ($removeCSVFile)
    {
        foreach ($path in $attachement)
        {
            Remove-Item -Path $path -Confirm:$False -Force
        }
    }
}
finally
{
    # Release resources
    $searchResultIterator.Dispose()
}
0

Thank you, as always.

As far as mailbox size increasing time of script execution, how long would you estimate? If it's a scheduled report, it probably won't matter too much.

Would it be possible to insert that in a separate script in the event I'd like to try and see how long it takes?

0

Hello Adrian,

Checking inactivity and mailbox size are very time consuming operations. It is not recommended to include both in one task.

We recommend using two Scheduled Tasks. One will write mailbox size into a text property (e.g. CustomAttributeText1) and the second will send a report with inactive users including the mailbox size from the text property. You can find the script for the first task in our repository: http://www.adaxes.com/script-repository ... e-s454.htm. If the solution meets your needs, we will update the report script for you.

Related questions

0 votes
0 answers

We have an inactive user task which runs daily that disables accounts after 30 days of inactivity. We had an example yesterday of a user account which had been disabled ... Are you able to provide any explanation of how this could have happened? Regards Andy

asked Sep 12, 2017 by Andy_W (40 points)
0 votes
1 answer

Hi I'm trying to add your report from here but whenever I run it, I get 2 errors for each user which seem to correspond to the following 2 lines in the ... "user" $Context.DirectorySearcher.AddCriteria($criteria) But I still get the same error's. Thanks Matt

asked Oct 16, 2023 by chappers77 (2.0k points)
0 votes
0 answers

Nevermind - we figured out the issue was with a changed GPO that nobody knew changed.

asked Dec 22, 2014 by danftasc (440 points)
0 votes
1 answer

My Create user form has a User pattern for User Logon Name (pre-Windows 2000) . The pattern is not working any more. I need to remove danish character æ,ø,å and spaces. Can you help me?

asked Jun 28, 2019 by debarase (100 points)
0 votes
0 answers

Hi, I try to put template users creation in place (cf http://www.adaxes.com/tutorials_WebInte ... eation.htm) Evrything is OK but i'm trying to restrict the target OU display ... interface, so is not not a read acces issue... Where can i look ? Best regards

asked Oct 27, 2017 by Nicolas.lussier (190 points)
3,589 questions
3,278 answers
8,303 comments
548,136 users