0 votes

Right now I have a scheduled task that runs on all user accounts and sends and HTML email to users when their password will expire. The problem with this method is that it runs on every account, every day, which causes a lot of spam in the Adaxes logs for "No Operations Executed". This clutters the Management History log view when trying to determine what changes have been made to an account.

What I am considering is to convert the password notification to a Custom Command in Adaxes. Then I want to create a new scheduled task that runs a powershell script to search a list of OUs in AD for user accounts with expiring passwords. The script would then execute the Custom Command only on accounts found in the search. This would lead to log entries on the user objects only being created when the command actually sends an email.

I have a template script that does search a list of OUs based on an the Adaxes searcher and an LDAP filter, but I'm not sure how to adapt that to look for users with expiring passwords. I could use some help sorting that out.

by (1.4k points)
0

I think I've figured it out by combining my searcher template with the script from the Soon-To-Expire Passwords report script. I'm getting an error with line 89 and the msDS-ResultantPSO property however. Here is my current script.

# Get parameter values
$days = 14 # TO DO: Modify Me - How soon password expires
$now = Get-Date
$nowInt64 = $now.ToFileTime()
$threshold = $now.AddDays($days)
$thresholdInt64 = $threshold.ToFileTime()
$commandID = "{d8ff58b9-8131-4041-9861-2b73a02983e1}" # TO DO: Modify Me

# List of OUs to search
$ouDNs = @("OU=User OU 1,DC=domain,DC=com")
$ouDNs += "OU=User OU 2,DC=domain,DC=com"
$ouDNs += "OU=User OU 3,DC=domain,DC=com"

[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

# Connect to the Adaxes service
$admNS = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$admService = $admNS.GetServiceDirectly("localhost")

Function FindUsers ($ouPath, $officesInfo) {
    $searcher = New-Object "Softerra.Adaxes.Adsi.Search.DirectorySearcher" $NULL, $False
    $searcher.SearchParameters.PageSize = 500
    $searcher.SearchParameters.SearchScope = "ADS_SCOPE_SUBTREE"
    $searcher.SearchParameters.BaseObjectPath = "$ouPath"
    ### Build search filter ###
    # We filter out user accounts whose password never expires or must be changed at next logon,
    # as well as interdomain trust accounts and accounts required to use smart cards
    $filterUsers = "(sAMAccountType=805306368)"
    $filterPasswordLastSet = "(!(pwdLastSet=0))"
    $filterPasswordNeverExpires = "(!(userAccountControl:1.2.840.113556.1.4.803:=65536))"
    $filterSmartCardLogon = "(!(userAccountControl:1.2.840.113556.1.4.803:=262144))"
    $filterInterdomainTrust = "(!(userAccountControl:1.2.840.113556.1.4.803:=2048))"
    $filterDisableUsers = "(!(userAccountControl:1.2.840.113556.1.4.803:=2))"
    $filter = "(&" + $filterUsers + $filterPasswordLastSet + $filterPasswordNeverExpires + $filterSmartCardLogon + $filterInterdomainTrust + $filterDisableUsers +")"
    $searcher.SearchParameters.Filter = $filter
    $searcher.SearchParameters.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
    # Load these properties
    $searcher.SetPropertiesToLoad(@("msDS-UserPasswordExpiryTimeComputed","msDS-ResultantPSO","pwdLastSet","distinguishedName","userPrincipalName"))
    $result = $searcher.ExecuteSearch()
    $users = $result.FetchAll()
    $result.Dispose()

    # Map domain names to maximum password age specified in the default domain password policy
    $domainToMaxPwdAge = @{}

    # Map policy DN to its name
    $policyDnToName = @{}

    # Custom column identifiers
    $effectivePolicyColumnID = "{a9cc0207-e218-4392-aff2-01222f74b001}"
    $passwordExpiresColumnID = "{41055ea7-4c35-4025-ba3a-5d65871b4a59}"

    # Find users with soon-to-expire passwords
    ForEach ($user in $users) {
        $username = $user.Properties["userPrincipalName"].Value
        $dn = $user.Properties["distinguishedName"].Value
        $expiryTime = $user.GetPropertyByName("msDS-UserPasswordExpiryTimeComputed").Values[0]

        If ($expiryTime -eq $NULL) # Fine-Grained Password Policies not supported
        {
            # Get the maximum password age from the default domain password policy
            $domain = $Context.GetObjectDomain($user.AdsPath.DN)
            If (-not $domainToMaxPwdAge.Contains($domain))
            {
                # Bind to the default naming context
                $nc = $Context.BindToObjectEx("Adaxes://$domain", $False)
                # Get the value of the maxPwdAge property
                $maxPwdAgeLargeInt = $nc.Get("maxPwdAge")
                # Remember the value
                $domainToMaxPwdAge[$domain] =
                    [Softerra.Adaxes.Adsi.AdsLargeInteger]::ToInt64($maxPwdAgeLargeInt)
            }

            $maxPwdAge = $domainToMaxPwdAge[$domain]
            If ($maxPwdAge -eq 0x8000000000000000) # no maximum password age
            {
                $expiryTime = $maxPwdAge
            }
            Else
            {
                $passwordLastSet = $user.GetPropertyByName("pwdLastSet").Values[0]
                $expiryTime = $passwordLastSet - $maxPwdAge
            }
        }

        If (($expiryTime -gt $nowInt64) -and ($expiryTime -lt $thresholdInt64))
        {
            # Get effective policy name
            #$policyDN = $user.GetPropertyByName("msDS-ResultantPSO").Values[0]
            $policyDN = $NULL
            If ([String]::IsNullOrEmpty($policyDN))
            {
                $policyName = "<Default Domain Policy>"
            }
            Else
            {
                If (-not $policyDnToName.Contains($policyDN))
                {
                    $policyDNObj = New-Object Softerra.Adaxes.LDAP.DN $policyDN
                    $policyDnToName[$policyDN] = $policyDNObj.Leaf.Value
                }
                $policyName = $policyDnToName[$policyDN]
            }
            $columnValues = @{ $effectivePolicyColumnID=$policyName; $passwordExpiresColumnID=$expiryTime }
            #$Context.Items.Add($user, $columnValues, $NULL)

            # Bind to the user
            $userObj = $admService.OpenObject("Adaxes://$dn", $NULL, $NULL, 0)

            # Execute Custom Command
            $userObj.ExecuteCustomCommand($commandID)
        }
    }
}

ForEach ($ouDN in $ouDNs)
{
    # Bind to OU
    $ou = $Context.BindToObjectByDN($ouDN)

    # Build report part for current OU
    $ouName = [Softerra.Adaxes.Utils.ObjectNameHelper]::GetObjectName($ou, 'IncludeParentPath')

    # Add users
    $ouMatches = $NULL
    $ouMatches = FindUsers $ou.ADsPath $ouName
}

1 Answer

0 votes
by (215k points)

Hello,

The error occurs because msDS-ResultantPSO is an operational attribute whose value is calculated by a domain controller on request. You should use the Get-ADUserResultantPasswordPolicy cmdlet to obtain the attribute value.

For your information, you can configure Adaxes logging to not log the Execute scheduled task operation. It should prevent creation of the "No Operations Executed" log records. For details, see https://www.adaxes.com/help/?Logging.EditServiceLogSettings.html. The settings should be like the following: image.png

Related questions

0 votes
1 answer

I want to send reminder emails that a users password is going to expire when their password is going to expire in 14, 7, 3, and 1 days. I have setup 4 ... a daily PowerShell script, where I do the actual password expiration check in the PowerShell script?

asked Aug 9, 2017 by HDClown (1.2k points)
0 votes
1 answer

Can you help us understand how the password expiration is sourced, is it calculated by Adaxes or just displayed based on an AD attribute? We have some users that apparently do ... the field "Password Expiration Date" but we don't know how that is populated.

asked Jul 28, 2015 by theckel (3.1k points)
0 votes
1 answer

Hi, we are using a scheduled job in Adaxes to notify users that their password will expire in x days. Now, we as IT were approached by Marketing to set up all ... ;/div&gt; &lt;/body&gt; &lt;/html&gt; Your help would be highly appreciated kind regards Ingemar

asked Mar 2, 2015 by ijacob (6.1k points)
0 votes
1 answer

I need a report script that will email me a list of the users with these field values: DN,cn,display name,%adm-PasswordExpires% Thanks!

asked Aug 8, 2012 by mpaul (2.7k points)
0 votes
1 answer

We are evaluating the product and would like to let users of AD to change password in self service page. We would like to set a 90 days change password policy, ... self service page? Is it achievable (with customization and batch program)? Thanks in advance.

asked Apr 27 by eric (250 points)
2,245 questions
2,007 answers
5,494 comments
19,927 users