Forum Discussion

Baron164's avatar
Baron164
Brass Contributor
May 31, 2022
Solved

Need help updating User Objects from CSV

I am attempting to update a script that will import a csv filled with user information such as Department, Title, Location, Phone Number, etc and merge it with the account information in Active Direc...
  • LainRobertson's avatar
    LainRobertson
    Jun 02, 2022

    Baron164 

     

    Here's a basic script that deals with the additional considerations mentioned above.

     

    It does have some limits as noted within the script itself, but I'd wager you're not going to come across those anyway.

     

    With respect to the attribute mappings between the CSV and Active Directory's attributes, the function named Get-CsvToADData is where you would make any necessary changes.

     

    As an aside, it does not require the ActiveDirectory module, making it a bit more portable. This adds a handful of extra lines but the portability gain is easily worth the cost.

     

    [cmdletbinding()]
    Param(
        [parameter(Mandatory=$true)][string]$Path
    )
    
    <#
        .SYNOPSIS
        Updates an existing Active Directory object matching on employeeID.
    
        .DESCRIPTION
        Relatively basic script that features just enough error checking to not make a mess of Active Directory.
        It hasn't been written to cope with multi-valued attributes.
    #>
    
    #region Function definitions.
    # Define the CSV-to-AD attribute mappings.
    function Get-CsvToADData([System.Object] $User, [System.Collections.HashTable] $Replacements, [System.Collections.Generic.List[string]] $Clearances)
    {
        if ($Replacements -isnot [System.Collections.HashTable] -or $Clearances -isnot [System.Collections.Generic.List[string]])
        {
            return($false);
        }
    
        #region Clear any previous values.
        $Replacements.Clear();      # Attributes to be updated.
        $Clearances.Clear();        # Attributes to be cleared.
        #endregion
    
        try
        {
            # The basic 1-to-1 mappings.
            if ([string]::IsNullOrWhiteSpace($User.last_name)) { $Clearances.Add("sn"); } else { $Replacements.Add("sn", $User.last_name); };
            if ([string]::IsNullOrWhiteSpace($User.mi)) { $Clearances.Add("initials"); } else { $Replacements.Add("initials", $User.mi); };
            if ([string]::IsNullOrWhiteSpace($User.first_name)) { $Clearances.Add("givenName"); } else { $Replacements.Add("givenName", $User.first_name); };
            if ([string]::IsNullOrWhiteSpace($User.emp_title)) { $Clearances.Add("title"); } else { $Replacements.Add("title", $User.emp_title); };
            if ([string]::IsNullOrWhiteSpace($User.phone)) { $Clearances.Add("telephoneNumber"); } else { $Replacements.Add("telephoneNumber", $User.phone); };
            if ([string]::IsNullOrWhiteSpace($User.ext4)) { $Clearances.Add("ipPhone"); } else { $Replacements.Add("ipPhone", $User.ext4); };
            if ([string]::IsNullOrWhiteSpace($User.fax_num)) { $Clearances.Add("facsimileTelephoneNumber"); } else { $Replacements.Add("facsimileTelephoneNumber", $User.fax_num); };
            if ([string]::IsNullOrWhiteSpace($User.office_street)) { $Clearances.Add("streetAddress"); } else { $Replacements.Add("streetAddress", $User.office_street); };
            if ([string]::IsNullOrWhiteSpace($User.office_city)) { $Clearances.Add("l"); } else { $Replacements.Add("l", $User.office_city); };
            if ([string]::IsNullOrWhiteSpace($User.st)) { $Clearances.Add("st"); } else { $Replacements.Add("st", $User.st); };
            if ([string]::IsNullOrWhiteSpace($User.off_zip)) { $Clearances.Add("postalCode"); } else { $Replacements.Add("postalCode", $User.off_zip); };
    
            # Mappings involving multiple attributes and/or transformations.
            #region physicalDeliveryOfficeName
            $Has_office = -not [string]::IsNullOrWhiteSpace($User.office);
            $Has_locat  = -not [string]::IsNullOrWhiteSpace($User.locat);
    
            if (-not ($Has_office -or $Has_locat))
            {
                $Clearances.Add("physicalDeliveryOfficeName");
            }
            elseif ($Has_office -and $Has_locat)
            {
                $Replacements.Add("physicalDeliveryOfficeName", "$($User.office) $($User.locat)");
            }
            elseif ($Has_office)
            {
                $Replacements.Add("physicalDeliveryOfficeName", $User.office);
            }
            else
            {
                $Replacements.Add("physicalDeliveryOfficeName", $User.locat);
            };
            #endregion
        }
        catch
        {
            $Replacements.Clear();
            $Clearances.Clear();
            throw;
        }
    
        return($Replacements.Count + $Clearances.Count -gt 0);
    }
    #endregion
    
    #region Script body.
    try
    {
        # Knock up an Active Directory searcher.
        #region Create an Active Directory searcher.
        $RootDSE = [adsi]"LDAP://RootDSE";
        $CurrentServer = $RootDSE.Properties["dNSHostName"][0].ToLowerInvariant();                  # Used here and there to ensure all read and write operations are using the same server during this run.
        $DefaultNamingContext = $RootDSE.Properties["defaultNamingContext"][0];
        $RootDSE.Dispose();
    
        $Searcher = [adsisearcher]::new([adsi]"LDAP://$CurrentServer/$DefaultNamingContext");
    
        Write-Warning "Connected to domain controller $CurrentServer";
        #endregion
    
        #region Create the lists that will hold the changes.
        $Replacements = [System.Collections.Hashtable]::new();
        $Clearances   = [System.Collections.Generic.List[string]]::new();
        #endregion
    
        Import-Csv -Path $Path | ForEach-Object {
            $User = $_;
    
            # Check employeeID is not empty otherwise we're wasting our time.
            if (-not [string]::IsNullOrWhiteSpace($User.XREF_NUM))
            {
                $EmployeeID = $User.XREF_NUM;
    
                #region Check if exactly one user can be found in Active Directory.
                $Searcher.Filter = "(&(objectClass=user)(objectCategory=person)(employeeID=$EmployeeID))";
                $Results = $Searcher.FindAll();
                #endregion
    
                #region If exactly one result was returned, proceed.
                if (($Results.Count -eq 1) -and (Get-CsvToADData -User $User -Replacements $Replacements -Clearances $Clearances))
                {
                    #region Fetch the Active Directory user and make any relevant changes.
                    try
                    {
                        # Fetch the user from Active Directory.
                        $ADUser = $Results[0].GetDirectoryEntry();
    
                        # Perform the relevant clearing and/or updating.
                        foreach ($Attribute in $Replacements.Keys)
                        {
                            if ($ADUser.Properties.Contains($Attribute))
                            {
                                # If the Active Directory attribute already has a value, we can simply change it.
                                $ADUser.Properties[$Attribute].Value = $Replacements[$Attribute];
                            }
                            else
                            {
                                # If the Active Directory attribute is null (not to be confused with an empty string), then we need to add a key-value pair.
                                $null = $ADUser.Properties[$Attribute].Add($Replacements[$Attribute]);
                            }
                        }
    
                        foreach ($Attribute in $Clearances)
                        {
                            # Clear the value from any attribute where no matching value was provided in the CSV.
                            $ADUser.Properties[$Attribute].Clear() ;
                        }
    
                        $ADUser.CommitChanges();
                    }
                    catch
                    {
                        # Electing to bail out of the script entirely by re-throwing the exception. Alternatively, you could elect to simply continue the loop but re-throwing is the lesser evil.
                        throw;
                    }
                    finally
                    {
                        if ($ADUser -is [System.DirectoryServices.DirectoryEntry])
                        {
                            $ADUser.Dispose();
                        }
                    }
                    #endregion
                }
                else
                {
                    Write-Warning "Skipping EmployeeID ""$EmployeeID"". Results = $($Results.Count).";
                    $Results.Dispose();
                }
                #endregion
    
            }
        }
    }
    catch
    {
        throw;
    }
    finally
    {
        if ($Searcher)
        {
            $Searcher.Dispose();
        }
    }
    #endregion

     

    Cheers,

    Lain

Resources