The userWorkstations attribute is a single-valued string. It is a comma delimited list of the NetBIOS names of the computers the user is allowed to logon to. If the attribute has no value, the user can logon to any computer in the domain. The Active Directory Users and Computers tool displays the value on the "Account" tab of the user properties. Click the "Log On To..." button to view or modify the computer names. The GUI parses the value to show the individual computer names on separate lines. The corresponding PowerShell parameter of the Set-ADUser cmdlet is LogonWorkstations.

The script requires a CSV file that specifies the users and the workstations to be added to and removed from the userWorkstations attribute. A header line should define the three fields: sAMAccountName, AddNames, and RemoveNames. If either AddNames or RemoveNames has more than one computer name, the names must be comma delimited and the entire value should be enclosed in double quotes. An example CSV file follows:

 

Bash/shell
Edit|Remove
sAMAccountName,AddNames,RemoveNames 
jwilson,minnesota, 
fjohnson,,oregon 
adanson,"utah,indiana","illinois,wisconsin" 
dfgrant,,"wisconsin,colorado,wyoming" 
 

In this example, user jwilson has one computer added and none removed. User fjohnson has no computers added and one removed. User adanson has two computers added and two removed. An example log file follows:

------------------------------------------------

------------------------------------------------ 
UpdateUsersWorkstations.ps1 
Version 1.0 - January 42019 
CSV file: c:\PowerShell\Example.csv 
Log file: .\TestUserWS.log 
Update users: False 
Started: 1/4/2019 4:19:34 PM 
------------------------------------------------ 
User: jwilson 
    Computers to add: minnesota 
    Computers to remove:  
    Existing value: oregon,wisconsin,utah 
    New value: oregon,wisconsin,utah,minnesota 
User: fjohnson 
    Computers to add:  
    Computers to remove: oregon 
    Existing value: oregon 
    All values cleared 
User: adanson 
    Computers to add: utah,indiana 
    Computers to remove: illinois,wisconsin 
    Existing value: utah,wisconsin,illinois 
    Computer indiana not found 
    New value: utah 
User: dfgrant 
    Computers to add:  
    Computers to remove: wisconsin,colorado,wyoming 
    User not found! 
------------------------------------------------ 
Finished: 1/4/2019 4:19:47 PM 

 

Notes:

  1. Before running the script, verify the values assigned to the variables $CsvFile, $LogFile, and $Update in the script. Default values are assigned near the top of the script.
  2. Computer names are only added if the name is not already included in the userWorkstations attribute, and the name is also not in the field of names to be removed.
  3. Computer names are only removed if they are included in the userWorkstations attribute.
  4. Computer names are first added, then removed. If a computer name appears in both the AddNames and RemoveNames fields, it is not added, but is removed if the name appears in the userWorkstations attribute.
  5. If the update would result in no computers in the userWorkstations attribute, the attribute is cleared.
  6. The script writes a log file documenting what was done, or what would be done if $Update is $False.
  7. Computers must be identified by NetBIOS name (sAMAccountName without any trailing "$" character).
  8. The script validates the computer names to be added, to make sure the computer object exists in the domain. No validation is performed on computer names to be removed, or on any existing values in the userWorkstations attribute.
  9. If the variable $Update is $True, users are updated. If $Update is $False, users will be not be updated, but the script will generate the log file detailing what it would do.
  10. When the userWorkstations attribute is modified, the order of the names can be changed. This does not matter.
  11. The CSV file can be created in Microsoft Excel, then exported in CSV format.

The above should allow the CSV file to be updated and reused. Changes already made will be skipped. Computers can be removed even if the name is not removed from the AddNames field. Any duplicate entries in either AddNames or RemoveNames are ignored. If a computer name is misspelled, the computer will be flagged as not found. Finally, all computer names are matched exactly. This means that specifying computer "West" to be removed will not remove computer "WestVirginia" or change "WestVirginia" into "Virginia".

The script follows:

 

PowerShell
Edit|Remove
<# -------------------------------------------------------------------------- 
   UpdateUserWorkstations.ps1 
   PowerShell script to update the userWorkstations attribute of Active 
   Directory users in bulk from the information in a CSV file. Computer names 
   can be added to and/or removed from the attribute. The corresponding 
   PowerShell property exposed by the Set-ADUser cmdlet is LogonWorkstations. 
 
   Author: Richard L. Mueller 
   Version 1.0 - January 42019 
 
   The CSV file specifies the users and the workstations to be added to and 
   removed from the userWorkstations attribute. A header line should define 
   the three fields sAMAccountName, AddNames, and RemoveNames. If either 
   AddNames or RemoveNames has more than one computer name, the names must be 
   comma delimited and the entire value should be enclosed in double quotes. 
-------------------------------------------------------------------------- #> 
 
# ============ Modify the values in this section for your situation ========= 
# Specify the CSV file. 
$CsvFile = "c:\Scripts\UserWorkstations.csv" 
 
# Specify the log file. 
$LogFile = ".\UpdateUserWorkstation.log" 
 
# If $Update is $True, user objects will be updated. Otherwise the script 
# will only generate the log file without updating users. 
$Update = $False 
# ================ End of section to modify ================================= 
 
# Script version and date. 
$Version = "Version 1.0 - January 4, 2019" 
 
Write-Host "Please Standby..." 
 
Try {Import-Module ActiveDirectory -ErrorAction Stop -WarningAction Stop} 
Catch 
{ 
    Write-Host "ActiveDirectory module (or DC with ADWS) not found!!" ` 
        -ForegroundColor Red -BackgroundColor Black 
    Write-Host "Script Aborted." -ForegroundColor Red -BackgroundColor Black 
    # Abort the script. 
    Break 
} 
 
# Retrieve all computer sAMAccountNames in the domain. 
Try 
{ 
    $NTNames = Get-ADComputer -Filter * | sAMAccountName 
} 
Catch 
{ 
    Write-Host "Error: AD modules not supported, script aborted." ` 
        -foregroundcolor red -backgroundcolor black 
    Break 
} 
 
# Add information to the log file. 
Try 
{ 
    Add-Content -Path $LogFile ` 
        -Value "------------------------------------------------" ` 
        -ErrorAction Stop 
} 
Catch 
{ 
    Write-Host ` 
        "Error: Logfile $LogFile invalid or protected, script aborted." ` 
        -foregroundcolor red -backgroundcolor black 
    Break 
} 
Add-Content -Path $LogFile -Value "UpdateUsersWorkstations.ps1" 
Add-Content -Path $LogFile -Value "$Version" 
Add-Content -Path $LogFile -Value "CSV file: $CsvFile" 
Add-Content -Path $LogFile -Value "Log file: $LogFile" 
Add-Content -Path $LogFile -Value "Update users: $Update" 
Add-Content -Path $LogFile -Value "Started: $((Get-Date).ToString())" 
Add-Content -Path $LogFile ` 
    -Value "------------------------------------------------" 
 
# Populate a hash table with the computer names. 
$SamNames = @{} 
ForEach ($Item In $NTNames) 
{ 
    $Name = $($Item.sAMAccountName) 
    # Remove any trailing "$" character. 
    If ($Name.EndsWith("$")) 
    { 
        $Name = $Name.SubString(0,$Name.Length - 1) 
    } 
    $SamNames.Add($Name, $True) 
} 
 
# Enumerate the users in the CSV file. 
$Users = Import-Csv -Path $CsvFile 
ForEach ($User In $Users) 
{ 
    # Retrieve values from the CSV for this user. 
    $ID = $User.sAMAccountName 
    Add-Content -Path $LogFile -Value "User: $ID" 
    # Convert the Workstations into arrays of computer names. 
    $Add = $User.AddNames 
    Add-Content -Path $LogFile -Value "    Computers to add: $Add" 
    If ($Add) {$AddComputers = $Add -Split ","} 
    Else {$AddComputers = $Null} 
    $Remove = $User.RemoveNames 
    Add-Content -Path $LogFile -Value "    Computers to remove: $Remove" 
    If ($Remove) {$RemoveComputers = $Remove -Split ","} 
    Else {$RemoveComputers = $Null} 
 
    # Retrieve the existing value of the userWorkstations attribute 
    # for the user. 
    Try 
    { 
        $Names = (Get-ADUser -Identity $ID -Properties userWorkstations).userWorkstations 
    } 
    Catch 
    { 
        Write-Host "Error: User $ID not found." ` 
            -foregroundcolor red -backgroundcolor black 
        Add-Content -Path $LogFile -Value "    User not found!" 
        # Skip this user. 
        Continue 
    } 
    Add-Content -Path $LogFile -Value "    Existing value: $Names" 
    # Convert into an array. 
    $arrNames = $Names -Split "," 
    # Convert into a hash table. 
    $CompNames = @{} 
    ForEach ($Item In $arrNames) 
    { 
        If (-Not $CompNames.ContainsKey($Item)) 
        { 
            $CompNames.Add($Item, $True) 
        } 
    } 
 
    $Changed = $False 
    # Enumerate the computer names in the CSV to be added. 
    ForEach ($Computer In $AddComputers) 
    { 
        $Computer = $Computer.Trim() 
        # Check that the computer object exists in the domain. 
        If ($SamNames.ContainsKey($Computer) -eq $True) 
        { 
            # Make sure the same computer is not to be removed. 
            If ($Remove -NotContains $Computer) 
            { 
                # Make sure existing workstations does not include 
                # this computer. 
                If ($CompNames.ContainsKey($Computer) -eq $False) 
                { 
                    # Add this computer name to the list of names. 
                    $Changed = $True 
                    $CompNames.Add($Computer, $True) 
                } 
            } 
        } 
        Else 
        { 
            Add-Content -Path $LogFile ` 
                -Value "    Computer $Computer not found" 
        } 
    } 
    # Enumerate the computer names in the CSV to be removed. 
    ForEach ($Computer In $RemoveComputers) 
    { 
        $Computer = $Computer.Trim() 
        # Make sure existing workstations includes this computer. 
        If ($CompNames.ContainsKey($Computer) -eq $True) 
        { 
            # Remove this computer name from the list of names. 
            $Changed = $True 
            $CompNames.Remove($Computer) 
        } 
    } 
    # Only update the user if there are computer names 
    # to be added or removed. 
    If ($Changed) 
    { 
        If ($CompNames.Count -eq 0) 
        { 
            # Clear userWorkstations. 
            If ($Update) 
            { 
                Set-ADUser -Identity $ID -Clear userWorkstations 
            } 
            Add-Content -Path $LogFile -Value "    All values cleared" 
        } 
        Else 
        { 
            # Convert the hash table into a string of comma delimited 
            # computer names. 
            $Names = $CompNames.Keys -Join "," 
            # Update computer names to the new list of names. 
            If ($Update) 
            { 
                Set-ADUser -Identity $ID -LogonWorkstations "$Names" 
            } 
            Add-Content -Path $LogFile -Value "    New value: $Names" 
        } 
    } 
    Else 
    { 
        Add-Content -Path $LogFile -Value "    No change" 
    } 
} 
 
# Update the log file. 
Add-Content -Path $LogFile -Value "------------------------------------------------" 
Add-Content -Path $LogFile -Value $("Finished: " + (Get-Date).ToString()) 
 
Write-Host "Done. See log file: $LogFile"