0 votes

We need an amendment to the following script to look at the existing Decription filed and NOT replace if it starts 'ADM'

$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 = "\\asp-adadaxes.admi.com\D$\CSV\UpdatedUsersMLF.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 = "myaddress@domain.com" # TODO: Modify me
    $from = "noreply@domain.com" # TODO: Modify me
    $subject = "Error Report: Import data from csv" # TODO: Modify me
    $smtpServer = "myserver.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
by (3.2k points)

1 Answer

0 votes
by (289k 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\Share\Users.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 = "recipient@domain.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
    {
        [System.Array]$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]
            if ($ldapPropertyName -eq "description")
            {
                # Check existing value of description
                try
                {
                    $value = $user.Get("description")
                }
                catch
                {
                    $value = ""
                }

                if ($value.StartsWith("ADM", "CurrentCultureIgnoreCase"))
                {
                    continue
                }
            }

            $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

On this script we are getting the following error in the operation execution logging for the task.
"Unable to index into an object of type System.Management.Automation.PSObject."

Please advise.

0

Hello,

There was a mistake in the script, you need to replace the line

$importedUsers = Import-Csv -Pat $csvFilePath -ErrorAction Stop | Where {(-not([System.String]::IsNullOrEmpty($_.$identityColumnName)))} 

with the following:

[System.Array]$importedUsers = Import-Csv -Path $csvFilePath -ErrorAction Stop | Where {(-not([System.String]::IsNullOrEmpty($_.$identityColumnName)))} 

We updated the script in our previous post, you can just re-copy it.

0

Thank you that worked perfectly.

Related questions

0 votes
1 answer

Receive "Index operation failed; the array index evaluated to null. Stack trace: at &lt;ScriptBlock&gt;, &lt;No file&gt;: line 104&gt;" and "Index operation failed; the ... $GroupName, $GroupDN." } } #foreach write-output "" Write-Output "" Stop-Transcript

asked Apr 14, 2022 by jbahou (20 points)
0 votes
1 answer

I have an account that I am using as the "run as" account to run PowerShell scripts for several different custom commands. I would like to be able to update the ... a script that updates the credentials used to run a script in an existing custom command?

asked Oct 18, 2021 by KelseaIT (320 points)
0 votes
1 answer

Good afternoon, I'm looking to generate a script to allow automation of updating job titles using a spreadsheet. To do this we would use a spreadsheet generated by ... in calling the file. Please let me know if you require any additional information Regards

asked Nov 16, 2020 by jtop (700 points)
0 votes
1 answer

We are using the following the script and would like to have it updated to use a CSV file instead of the list option. Thank you in advance for your assistance. ... error occurred when updating user. Error: " + $_.Exception.Message, "Warning") }

asked Jun 5, 2017 by willy-wally (3.2k points)
0 votes
1 answer

Hi, looking for a solution or script to remove a user from all Online/O365/AAD groups. The article I found is not working anymore and also I didn't found any script in your repository. Thanks!

asked Jul 21, 2023 by wintec01 (1.5k points)
3,552 questions
3,242 answers
8,243 comments
547,828 users