0 votes

As there's no built-in way (at least that I can find) to create new reports I was going to try and create one myself using a scheduled task. I'm trying to create a report that will find all the inactive user accounts and send an email with those accounts so we can manually check them over. I know there is already an Inactive User Deleter task and I'm sort of modeling it after that, but once I have the inactive users I want to send an email with them in it. Is there a way I could get something like this to work? I'd like one email with all the users, not one email per user.

Thanks!

by (520 points)

1 Answer

0 votes
by (216k points)

Update 2018

Starting with version 2018.1 there are more than 200 built-in reports including the one for inactive users. You can customize the reports to meet your needs and create your own reports. For details, have a look at the following tutorial: https://www.adaxes.com/tutorials_ActiveDirectoryManagement_CreateReport.htm.

Original

Hello,

To accomplish your task, you need to create a Scheduled Task that performs the Run a program or PowerShell script action. The PwerShell script that this action launches will generate a report on inactive user accounts in Active Directory and send the generated report via email in an HTML format.

To create such a Scheduled Task:

  1. Launch Adaxes Administration Console.

  2. Launch the Create Scheduled Task wizard.

  3. On the 3rd step of the wizard, select the Show all object types checkbox and select the Domain-DNS object type. Alternatively, you can select the Organizational Unit object type if you want the script to generate reports only for users located in specific OUs.

  4. On the 4th step, click Add Action, select Run a program of PowerShell script, and paste the following script:

     Import-Module Adaxes
     $email = "recipient@company.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-AdmAccount -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())
  5. Customize the script for your needs.

  6. On the Activity Scope step of the wizard, add your domain to the activity scope of the task.

0

Thanks very much that worked quite well. Is there a resource somewhere that documents the Adaxes functions and such in the module? It'd be great to be able to use those functions for more scripts.

Thanks!

0

Hello,

Have a look at http://adaxes.com/sdk/.

0

This report works great. One issue I have run into is when on step 3 I use Organizational Unit instead of Domain-DNS, I get emails for every single OU under the root OU I select. Is there an easy way to avoid this, or would the powershell script need some significant tweaking?

0

Hello,

You can accomplish your task without script tweaking at all. On the 5th step of the Create Scheduled Task wizard, you need to define the Activity Scope for your Scheduled Task. To get reports for a specific Organizational Unit:

  1. Double-click the necessary Organizational Unit.
  2. In the Assignment Options dialog that appears, deselect the Child objects of this Organizational-Unit option.
  3. Select the This Organizational-Unit object option.
  4. Click OK two times and finish creation of the Scheduled Task.

In this way, you will include only the Organizational Unit you need in the Activity Scope of the Task, and the searching among the OUs included in that OU will be done by the script.

0

Got that part. Thanks!
However, now when I include an OU that has multiple sub-OUs, I don't seem to be able to exclude some of the sub-OUs. If I exclude them from the activity scope the report will still contain inactive users. Currently we have a sub-OU under our All Users OU that contains terminated employees. I want to exclude this OU from the report but include everything else under All Users in one report.

0

Hello,

This can be accomplished, but requires a slight modification of the script. In the modified version of the script you will be able to define a list of OUs that should be excluded from the report. You will need to specify the list of excluded OUs directly in the script. When the Scheduled Task is launched, the script will generate a report on all the users located in the target OU with the exception of the users located in the excluded sub-OUs.

If such a solution is OK with you, we'll assign our script guys to modify the script.

0

Hello,

Here's an updated version of the script that meets your requirements. In the script, $excludedOuDNs specifies a list of Distinguished Names (DNs) of the OUs that should be excluded from the report. Modify the script to your requirements and use it in the same manner as the script that you currently have.

Import-Module Adaxes
$email = "recipient@company.com" # TODO modify me
$inactivityDurationThreshold = "30" # Days
$excludedOuDNs = @("CN=Users,DC=domain,DC=com", "OU=Sales,DC=domain,DC=com") # TODO modify me

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

function IsDescendantOfExludedOu ($userDN, $excludedOuDNs)
{
    foreach ($ouDN in $excludedOuDNs)
    {
        if ($userDN.IsDescendantOf($ouDN))
        {
            return $True
        }
    }

    return $False
}

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-AdmAccount -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
        if (IsDescendantOfExludedOu $userDN $excludedOuDNs)
        {
            continue
        }
        $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())
0

Perfect!
That worked like a champ. Thank you so much!

0

I need to modify the above script so that it only reports users that have a mailbox. Everything else i would like to stay the same. I believe i need to add something to this piece, but i cant find the correct parameter:

$users = Search-AdmAccount -AccountInactive `  
 -TimeSpan $inactivityDurationThreshold `  
 -SearchBase $baseDN -UsersOnly `  
 -Server $domain -AdaxesService localhost 

Can anyone help with this?

Thanks

0

Hello,

You need to replace the following code

$user = Get-AdmUser $user -Properties "LastLogonDate" `
    -Server $domain -AdaxesService "localhost"

with this block:

$user = Get-AdmUser $user -Properties "LastLogonDate", "mailNickname", "homeMDB" `
    -Server $domain -AdaxesService "localhost"
if ([System.String]::IsNullOrEmpty($user.mailNickname) -or [System.String]::IsNullOrEmpty($user.homeMDB))
{
    continue
}

Find the full updated script below:

Import-Module Adaxes
$email = "recipient@company.com" # TODO modify me
$inactivityDurationThreshold = "30" # Days
$excludedOuDNs = @("CN=Users,DC=domain,DC=com", "OU=Sales,DC=domain,DC=com") # TODO modify me

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

function IsDescendantOfExludedOu ($userDN, $excludedOuDNs)
{
    foreach ($ouDN in $excludedOuDNs)
    {
        if ($userDN.IsDescendantOf($ouDN))
        {
            return $True
        }
    }

    return $False
}

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-AdmAccount -AccountInactive `
    -TimeSpan $inactivityDurationThreshold `
    -SearchBase $baseDN -UsersOnly `
    -Server $domain -AdaxesService localhost
if ($users)
{
    foreach ($user in $users)
    {       
        $user = Get-AdmUser $user -Properties "LastLogonDate", "mailNickname", "homeMDB" `
            -Server $domain -AdaxesService "localhost"
        if ([System.String]::IsNullOrEmpty($user.mailNickname) -or [System.String]::IsNullOrEmpty($user.homeMDB))
        {
            continue
        }
        $userDN = New-Object "Softerra.Adaxes.Ldap.DN" $user.DistinguishedName
        if (IsDescendantOfExludedOu $userDN $excludedOuDNs)
        {
            continue
        }
        $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())

Related questions

0 votes
1 answer

We manage employee user accounts in our on-premise Active Directory and synchronize them to Azure Active Directory using Azure AD Connect. We'd like to be able to generate ... if this is possible so we can easily identify user accounts that are truly inactive.

asked May 9, 2023 by RickWaukCo (320 points)
0 votes
1 answer

I have to do a weekly Inactiviy Report for Accounts that have not logged in for 30 days or more. 1 of the reports is for Internal users BUT there is an Account ... Adaxes and working on the product, and i need to get all my reporting done through Adaxes

asked Nov 14, 2022 by dtorannini (80 points)
0 votes
1 answer

Hello, I am having trouble updating adaxes custom attributes when creating a new user with the powershell module, would running the New-AdmUser also ... -EmployeeID $EmployeeID -MobilePhone $MobilePhone -OtherAttributes @{adm-CustomAttributeText1 = $title }

asked May 11, 2020 by bbartlett (20 points)
0 votes
1 answer

Is there a way to grey out the 'Password never expires' option when a admin creates a new user in the Adaxes admin portal? I'm in a closed environment and need to make sure that accounts are not created with this option set. Thanks for any information! -Chad

asked Apr 16, 2015 by chad156 (50 points)
0 votes
1 answer

Good Afternoon, Currently as far as I can tell when defining a new user we are forced to select a location for a newly created user before defining information like the ... If not, is there a better way of applying this sort of functionality? Regards Josh

asked Apr 21, 2014 by jtop (700 points)
3,589 questions
3,278 answers
8,303 comments
548,107 users