0 votes

We are running a daily or weekly job that is updating certain attributes in AD for 9000k users. It appears to not be cycling through after the 10 minute stop. We need the Adaxes system to "pick up where it left off" and keep modifying the accounts.

ex. first 700 users are completed in the 10 minutes but it doesn't start again automatically with 701.

by (3.2k points)
0

Hello,

How do you import the users? Is it done with the help of a Scheduled Task in Adaxes?

0

yes, we have a scheduled task that runs every Friday night at 10PM.

We use the sAMAccountName, manager and a custom attribute column. The initial run works for the first 700-900 or so but doesn't continue.

0

Hello,

Could you post here or send us the script you are using?

0
Import-Module Adaxes

$csvFilePath = "\\server.com\D$\CSV\UpdatedUsersManager.csv" # TODO: modify me
$accountPasswordColumn = "AccountPassword" # TODO: modify me
$sAMAccountNameColumn = "sAMAccountName" # TODO: modify me

# E-mail settings
$recipient = "adaxesalerts@server.com" # TODO: Modify me
$subject = "Error Report: Import data from csv" # TODO: Modify me
$reportHeader = "<h1><b>Error Report: Import data from csv</b></h1><br/>"# TODO: Modify me
$reportFooter = "<hr /><p><i>Please do not reply to this e-mail, it has been sent to you for notification purposes only.</i></p>" # TODO: modify me

$domainName = $Context.GetObjectDomain("%distinguishedName%")

$report = New-Object "System.Text.StringBuilder"
try
{
    $importedUsers  = Import-Csv -Path $csvFilePath -ErrorAction Stop
}
catch
{
    $message = "An error occurred while importing CSV file '$csvFilePath'. Error: " + $_.Exception.Message
    $Context.LogMessage($message, "Warning")
    [void]$report.Append("<li>$message</li>")
    $importedUsers = @()
}

foreach ($userFromCSV in $importedUsers)
{
    $userObject = @{}
    $accountPassword = $NULL
    foreach ($property in $userFromCSV.PSObject.Properties)
    {
        $propertyName = $property.Name
        $value = $property.Value

        if($propertyName -ieq $accountPasswordColumn -and !([System.String]::IsNullOrEmpty($value)))
        {
            $accountPassword = ConvertTo-SecureString -AsPlainText $value -Force
            continue
        }
        elseif ($propertyName -ieq $accountPasswordColumn -and [System.String]::IsNullOrEmpty($value))
        {
            continue
        }

        if ([System.String]::IsNullOrEmpty($value))
        {
            continue
        }

        if ($value -ieq "True" -or $value -ieq "False")
        {
            $value = [System.Boolean]::Parse($value)
        }

        $userObject.Add($propertyName, $value)
    }

    # Check whether the user exists
    $userIdentity = $userObject.$sAMAccountNameColumn
    try
    {
        $userExists = Get-AdmUser -Identity $userIdentity `
            -AdaxesService localhost -ErrorAction Stop -Server $domainName
    }
    catch
    {
        $message = "$userIdentity`: An error occurred while searching user. Error: " + $_.Exception.Message
        $Context.LogMessage($message, "Warning")
        [void]$report.Append("<li>$message</li>")
        continue
    }

    # If user exists, update account
    try
    {
        Set-AdmUser -Identity $userExists.DistinguishedName -Replace $userObject `
            -AdaxesService localhost -Server $domainName -ErrorAction Stop
    }
    catch
    {
        $message = "$userIdentity`: An error occurred while updating user. Error: " + $_.Exception.Message
        $Context.LogMessage($message, "Warning")
        [void]$report.Append("<li>$message</li>")
    }

    if ([System.String]::IsNullOrEmpty($accountPassword))
    {
        continue
    }

    try
    {
        Set-AdmAccountPassword -Identity $userExists.DistinguishedName -NewPassword $accountPassword `
            -Reset -Server $domainName -AdaxesService localhost -ErrorAction Stop
    }
    catch
    {
        $message = "$userIdentity`: An error occurred while updating the password for user. Error: " + $_.Exception.Message
        $Context.LogMessage($message, "Warning")
        [void]$report.Append("<li>$message</li>")
    }
}

if ($report.Length -eq 0)
{
    return
}

# Build html
$html = $reportHeader + "<ul style=""list-style: none;"">" + $report.ToString() + "</ul>" + $reportFooter

# Send report
$Context.SendMail($recipient, $subject, $NULL, $html)
0

Hello,

Could you specify whether the script should be executed applying Adaxes workflow (i.e. Business rules, Security Roles, etc.)?

If it is not required, we will update the script for you accordingly and it will update all the users without stopping.

If Adaxes workflow is required, we will update the script to run in a separate session. However, there is no possibility to estimate how long the task will run in this case.

0

We are just using a custom command right now. When it is resolved we want to use it in a scheduled task that runs the command. We understand that the script stops after 10 minutes to ensure it doesn't "run away" and would like to keep this but just have it keep cycling through until it is finished the csv file.

1 Answer

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

Hello,

Find the updated script below.

$waitTimeSeconds = 9 * 60 # TODO: modify me. Time in seconds

$scriptBLock = {
    [Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

    $admNS = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
    $admService = $admNS.GetServiceDirectly("localhost")

    $csvFilePath = "\\server.com\D$\CSV\UpdatedUsersManager.csv" # TODO: modify me
    $identityColumnName = "sAMAccountName" # TODO: modify me
    $identityPropertyLdapName = "sAMAccountName" # TODO: modify me
    $customColumnNames = @{
        "AccountPassword" = "unicodePwd"
    } # TODO: modify me
    $skipColumns = @() # TODO: modify me

    # E-mail settings
    $recipient = "adaxesalerts@server.com" # TODO: Modify me
    $from = "noreply@domain.com" # TODO: Modify me
    $subject = "Error Report: Import data from csv" # TODO: Modify me
    $smtpServer = "mail.domain.com" # TODO: Modify me
    $reportHeader = "<h1><b>Error Report: Import data from csv</b></h1><br/>"# TODO: Modify me
    $reportFooter = "<hr /><p><i>Please do not reply to this e-mail, it has been sent to you for notification purposes only.</i></p>" # TODO: modify me

    function SendMail ($html)
    {
        Send-MailMessage -to $recipient -From $from -Subject $subject -Body $html -SmtpServer $smtpServer -BodyAsHtml
    }

    # Import CSV
    $report = New-Object "System.Text.StringBuilder"
    try
    {
        $importedUsers = Import-Csv -Path $csvFilePath -ErrorAction Stop | Where {(-not([System.String]::IsNullOrEmpty($_.$identityColumnName)))}
    }
    catch
    {
        $message = "An error occurred while importing CSV file '$csvFilePath'. Error: " + $_.Exception.Message
        Write-Warning $message
        $html = $reportHeader + "<h3>$message</h3>" + $reportFooter
        SendMail $html
        return
    }

    # Create user searcher
    $searcher = $admService.OpenObject("Adaxes://%distinguishedName%", $NULL, $NULL, 0)
    $searcher.PageSize = 500
    $searcher.SetPropertiesToLoad(@($identityPropertyLdapName))

    # Create properties mapping
    $propertyMap = @{}
    foreach ($property in $importedUsers[0].PsObject.Properties)
    {
        if (($property.Name -eq $identityColumnName) -or ($skipColumns -contains $property.Name))
        {
            continue
        }

        if ($customColumnNames.ContainsKey($property.Name))
        {
            $propertyMap.Add($customColumnNames[$property.Name], $property.Name)
        }
        else
        {
            $propertyMap.Add($property.Name, $property.Name)
        }
    }
    $propertiesForUpdate = @($propertyMap.Keys)

    $filter = New-Object "System.Text.StringBuilder"
    $usersFromCSV = @{}
    for ($i = 0; $i -lt $importedUsers.Length; $i++)
    {
        # Get user identity
        $userFromCSV = $importedUsers[$i]
        $identity = $userFromCSV.$identityColumnName

        if ($usersFromCSV.ContainsKey($identity))
        {
            $usersFromCSV[$identity] = $NULL
            continue
        }
        else
        {
            $usersFromCSV.Add($identity, @{
                "Path" = $NULL
                "UserData" = $userFromCSV
            })
        }

        # Build filter
        [void]$filter.Append([Softerra.Adaxes.Ldap.FilterBuilder]::Create($identityPropertyLdapName, $identity))
        $remainder = 0
        [void][System.Math]::DivRem($i, 500, [ref]$remainder)
        if ((($i -ne 0) -and ($remainder -eq 0)) -or ($i -eq $importedUsers.Length - 1))
        {
            # Search users
            $searcher.SearchFilter = "(&(sAMAccountType=805306368)(|" + $filter.ToString() + "))"
            try
            {
                $searchResultIterator = $searcher.ExecuteSearch()
                $searchResults = $searchResultIterator.FetchAll()

                foreach ($searchResult in $searchResults)
                {
                    $name = $searchResult.Properties[$identityPropertyLdapName].Value
                    $userInfo = $usersFromCSV[$name]
                    if ($userInfo -eq $NULL)
                    {
                        continue
                    }
                    elseif ($userInfo.Path -ne $NULL)
                    {
                        $usersFromCSV[$name] = $NULL
                        continue
                    }

                    $userInfo.Path = $searchResult.AdsPath
                }
            }
            finally
            {
                # Release resources
                if ($searchResultIterator) { $searchResultIterator.Dispose() }
            }

            # Clear filter
            $filter.Length = 0
            $filter.Capacity = 0
        }
    }

    # Update users
    $errorReport = @{
        "NotFound" = New-Object "System.Text.StringBuilder"
        "FoundMoreThanOne" = New-Object "System.Text.StringBuilder"
        "UpdateOperationErrors" = New-Object "System.Text.StringBuilder"
    }

    foreach ($item in $usersFromCSV.GetEnumerator())
    {
        if ($item.Value -eq $NULL)
        {
            # Add user to report
            [void]$errorReport["FoundMoreThanOne"].Append("<li>" + $item.Key + "</li>")
            continue
        }
        elseif ($item.Value.Path -eq $NULL)
        {
            # Add user to report
            [void]$errorReport["NotFound"].Append("<li>" + $item.Key + "</li>")
            continue
        }

        # Bind to a user
        $user = $admService.OpenObject($item.Value.Path, $NULL, $NULL, 0)

        # Set properties
        foreach ($ldapPropertyName in $propertyMap.Keys)
        {
            $columnName = $propertyMap[$ldapPropertyName]
            $user.Put($ldapPropertyName, $item.Value.UserData.$columnName)
        }

        try
        {
            # Commit changes
            $user.SetInfoEx($propertiesForUpdate)
        }
        catch
        {
            $errorMessage = "<li>$($item.Key) - $($_.Exception.Message)</li>"
            [void]$errorReport["UpdateOperationErrors"].Append($errorMessage)
        }
    }

    if ($errorReport["FoundMoreThanOne"].Length -eq 0 -and
        $errorReport["NotFound"].Length -eq 0 -and
        $errorReport["UpdateOperationErrors"].Length -eq 0)
    {
        return # No errors
    }

    $html = New-Object "System.Text.StringBuilder"
    [void]$html.Append($reportHeader)
    if ($errorReport["NotFound"].Length -ne 0)
    {
        [void]$html.Append("Names of users that were not found:<ul>")
        [void]$html.Append($errorReport["NotFound"].ToString())
        [void]$html.Append("</ul><br/>")
    }
    if ($errorReport["FoundMoreThanOne"].Length -ne 0)
    {
        [void]$html.Append("Found more than one user with the following name:<ul>")
        [void]$html.Append($errorReport["FoundMoreThanOne"].ToString())
        [void]$html.Append("</ul><br/>")
    }
    if ($errorReport["UpdateOperationErrors"].Length -ne 0)
    {
        [void]$html.Append("Errors that occured when updating users:<ul>")
        [void]$html.Append($errorReport["UpdateOperationErrors"].ToString())
        [void]$html.Append("</ul><br/>")
    }
    [void]$html.Append($reportFooter)

    # Send mail
    SendMail $html
}

# Start Windows PowerShell as a separate process and run the script block in that process
$job = Start-Job -ScriptBlock $scriptBlock
Wait-Job -Job $job -Timeout $waitTimeSeconds

if ($job.State -ne "Completed")
{
    $Context.LogMessage("The operation did not complete within the allowed timeout of $waitTimeSeconds seconds. " + 
        "It will be moved to a separate instance and completed on the background.", "Warning")
    return
}

# Get output from separate process
Receive-Job -Job $job
0

Thank you it worked as expected.

Related questions

0 votes
1 answer

Have a csv file of users that I need to import into Adaxes. I had initially found an article for this, but upon going today, it gave me an error (looks like it was deleted). Thank you

asked Nov 19, 2022 by wangl (20 points)
0 votes
1 answer

We are working with an HR package that will send us a CSV file every 4 hours with a list of users that need to be created, modified or deleted from our environment. The CSV ... change, etc.) Is there a script that can manage all of that on a scheduled basis?

asked Sep 2, 2020 by RayBilyk (230 points)
0 votes
1 answer

Hello, I was wondering, is it possible to update existing user accounts in AD by CSV import? More specifically, updating user account passwords by CSV file? Regards, Jay

asked Oct 21, 2013 by jaypaterson (90 points)
0 votes
1 answer

We are looking for if Adaxes has a report we can run that will tell us if there are multiple users using the same or similar passwords? Is there any tool that we can ... as another users? I appreciate any information you may be able to provide. Thank You,

asked Feb 27 by Jeff.Briand (60 points)
0 votes
1 answer

We would like to auto enroll our entire company into Adaxes for self service but do not want to use the question/answer format but instead their phone numbers (provided by HR department). Is this possible? Best, Evangelos

asked Oct 1, 2021 by evangelos (60 points)
3,346 questions
3,047 answers
7,777 comments
544,976 users