Skip to content

jocoli/BasicPowerShellScripts-JoshuaLinang

Repository files navigation

PowerShell scripts for Active Directory Automation

Description: A collection of basic PowerShell scripts created for practice and learning to automate the following PowerShell functions:

  1. Add users in bulk from a .csv file
  2. Change user passwords in bulk from a .csv file
  3. Remove users in bulk from a .csv file Plus a script to get SamAccountName from user to act as a unique identifier

Possible Improvements There is still much to learn with PowerShell automation and Active Directory, here are some possible improvements that come to mind:

  1. Use -WhatIf parameters, to see what a command does without executing it. (explained more in remove users section)
  2. Improved naming conventions and structure to better follow PowerShell and Active Directory best practices (explained more in add users section)

1. Add users in bulk

JackedProgrammer's Automate Active Directory with PowerShell Playlist

was a very helpful resource for this script. The main idea of this script was to take the information from MOCK_DATA.csv and create users from it. What needed to be done first was to remap attributes from the .csv to their equivalent in Active Directory.

For example,

First_Name -> GivenName Last_Name -> SurName

This would be very helpful later on.

$SyncFieldMap=@{
    First_Name="GivenName"
    Last_Name="SurName"
    Department="Department"
    Title="Title"
    Start_Date="Start_Date"
    Temp_Password="Temp_Password" 
}

Nothing else here needed to be changed except to add a hashtable to map Departments to their equivalent Organizational Unit in Active Directory.

$DepartmentOU_Map = @{
    "Sales"="OU=Sales-Users, DC=mydomain, DC=com"
    "Finance"="OU=Finance-Users, DC=mydomain, DC=com"
    "Human Resources"="OU=HR-Users, DC=mydomain, DC=com"
    "IT"="OU=IT-Users, DC=mydomain, DC=com"
    "Marketing"="OU=Marketing-Users, DC=mydomain, DC=com"
}

We will see this used later on.

I also have a function that takes in the .csv we are using, the delimiter which by default is ',', and the SyncFieldMap.

function Get-EmployeeFromCsv{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [string]$FilePath,
        [Parameter(Mandatory)]
        [string]$Delimiter,
        [Parameter(Mandatory)]
        [hashtable]$SyncFieldMap
    )

    try{
        $SyncProperties=$SyncFieldMap.GetEnumerator()
        $Properties=ForEach($Property in $SyncProperties){
            @{Name=$Property.Value;Expression=[scriptblock]::Create("`$_.$($Property.Key)")}
        }
        Import-Csv -Path $FilePath -Delimiter $Delimiter | Select-Object -Property $Properties
    }catch{
        Write-Error $_.Exception.Message
    }
}

Here we use GetEnumerator() to give us an object that allows us to iterate through the hashtable. Then we use this to be able to map the .csv fields to the correct SyncFieldMap value.

Once we have our .csv correctly mapped we can work on creating the user from the csv.

We load in the .csv with:

$Employees = Get-EmployeeFromCsv -FilePath "MOCK_DATA.csv" -Delimiter "," -SyncFieldMap $SyncFieldMap

Then we start with our logic. For every employee from the .csv, we want to get their names and department. We especially need their names to create their unique identifier which in this case, I use the SamAccountName and UserPrincipalName.

    $givenName = $Employee.GivenName.Trim()
    $surName = $Employee.Surname.Trim()
    $department = $Employee.Department

    $cleanFirst = $givenName -replace "[^a-zA-Z0-9]"
    $cleanLast = $surName -replace "[^a-zA-Z0-9]"


    $trySamAccountName = ($cleanFirst.Substring(0,1) + $cleanLast).ToLower()
    $samAccountName = $trySamAccountName
    $upn = "$trySamAccountName@mydomain.com"
    $i = 1

What is most important here are the givenName (First_Name) and the surName (Last_Name) and how they are used to create the SamAccountName and the UserPrincipalName (upn). My thought process was to set up the SamAccountName and UPN to be first name initial followed by last name in all lowercase. So for me, Joshua Linang, it would be jlinang.

Immediately I thought of one issue that could come from this.

  • What if naming convention causes two users to conflict with each other? For example, John Doe and Jane Doe would both be assigned jdoe.

My .csv does not have any names that would cause this issue, but it is still something to think about. I decided if I were to run into this issue, I will solve it by checking if the user's suggested SamAccountName is taken, if it is then I would append a number and increment until it is unique. So if jdoe is taken the next jdoe would be jdoe1. I don't believe this would completely solve this issue but it would be a good start for now.

After dealing with that, I discovered another issue:

  • Some names with special characters would cause some issues. For example, Anne-marie O'Donnelly would be converted to ao'donnelly.

This is a problem because parsing behaves differently when reading "'", it might interpret it as the end of a string.

To solve this we follow a slightly more strict naming convention, where it does not allow special characters.

    $cleanFirst = $givenName -replace "[^a-zA-Z0-9]"
    $cleanLast = $surName -replace "[^a-zA-Z0-9]"

This removes all non-alphanumeric characters from the first name and last name. This gets rid of the possible issue of the compiler needed to deal with potential special characters that can cause it to behave irregularly.

After this, the rest is straight forward, once it is confirmed that the suggest SamAccountName is unique we will begin to create the user.

    # Create Users in Active Directory
    $NewUserParams = @{
        Name = "$givenName $surName"
        GivenName = $givenName
        Surname = $surName
        SamAccountName = $samAccountName
        UserPrincipalName = $upn
        AccountPassword = ($Employee.Temp_Password | ConvertTo-SecureString -AsPlainText -Force)
        Enabled = $true
        Path = $DepartmentOU_Map[$department]
        ChangePasswordAtLogon = $true
    }

    New-ADUser @NewUserParams

Here we fill out the necessary parameters to fill out for each new user then create the user with those parameters.

Here is the result of the script: add Users

1.5 Generate a .csv file with unique identifiers

This file is a little bit more straight forward. In the original MOCK_DATA.csv which holds the new employee information, it does not contain a unique identifier. The unique identifier which is the SamAccountName would need to be recreated.

Note: I have begun to handle the possibility of running into users with similar account names, such as jdoe1 and jdoe2. But I haven't yet found a proper solution. For MOCK_DATA.csv we don't need to consider this since all the names are unique.

Once we get the SamAccountName, using the same method when creating it in the addBulkUsersFromCsv file, we create a new .csv with that unique identifier along with other user information. This is good because now with access with the unique identifier, it is easier to manage their accounts.

We will see this when we try to manipulate their accounts in the next sections.

2. Change users' passwords in bulk

Using two files, getSamAccountNames.csv and newPasswords.csv, this script changes passwords from each user in the .csv file.

Both files have the same amount of entries including the header which is 201. If they are compared to each other and found to be unequal to each other, the passwords will not change.

The script is a basic for loop that iterates through all 201 elements and uses the password from newPasswords.csv to change the user's password of the equivalent line in getSamAccountNames.csv. So on line 13 of newPasswords.csv would be the new password of user on line 13 in getSamAccountNames.csv.

If we enable Active Directory's advanced features (can be toggled on 'View') then we will have access to the 'Attribute Editor' in a user's properties. One of the attributes here we want to look at is 'pwdLastSet' and normally this is set to '(never)'. However when you update the passwords especially while using this script, that attribute gets updated:

password update

3. Remove users in bulk

The final script that I created was removeBulkUsersFromCsv.csv. When I was watching JackedProgrammer's tutorials he mentioned that he first disabled the user accounts, even setting an expiration date on the account, before removing it. This I believe is the best practice. However I decided to not set an expiration date because I wanted it to delete immediately so I can see the results without having to do those extra steps. Safety was still needed in case the script behaves abnormally. For this, I decided to implement a dry run functionality. I implemented it to see what users will be deleted without them actually being deleted so I can confirm that it is correct. Though an even better solution may be to use the -WhatIf parameter, which would allow you to see the effect of a command without executing it, which is a whole level above what the dry run does here.

The script will look through the getSamAccountNames.csv to see what users to remove from the Active Directory. It checks if the users is enabled on the domain, if it is disabled the script will remove the user. If it is enabled, the script will disable and then remove the user.

remove Users Script

Here is the result in the Active Directory after running the script:

remove Users AD

About

A collection of basic PowerShell scripts created for practice and learning, focusing on automation and Active Directory management.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors