Creating Bulk Users in AD With CSV - powershell

I am working on a Power Shell script that will create bulk users in Active Directory. As stated in a previous post, I'm fairly new to Power Shell and want to be safe before I go live with anything. Below is the script, some of it borrowed. It will be reading data from the .csv noted in the Import-CSV cmdlet. Ideally, this would create the user and define the user's full name, first name, last name, username, SAM name, e-mail, title, description and manager. It would also set a password.
I would love any feedback on how the below script looks. Please let me know if you have any questions or need any additional information.
Import-Module activedirectory
$ADUsers = Import-CSV C:\scripts\hourlyimport.csv
foreach ($User in $ADUsers)
{
#read user data from each field in each row and assign the data to a variable as below
$Username = $User.username
$Firstname = $User.firstname
$Lastname = $User.lastname
$Password = $User.password
$OU = $User.ou
$Title = $User.title
$Manager = $User.manager
#check to see if the user already exists in AD
if (Get-ADUser -F {SamAccountName -eq $Username})
{
#if user does exist, give a warning
Write-Warning "A user account with username $Username already exist in Active Directory. Say wha?!?"
}
else
{
#if user does not exist then proceed to create the new user account
#account will be created in the OU provided by the $OU variable read from the CSV file
New-ADUser `
-SamAccountName $Username `
-UserPrincipalName "$Username#thredup.com" `
-Email "$Username#thredup.com" `
-Name "$Firstname $Lastname" `
-GivenName $Firstname `
-Surname $Lastname `
-Enabled $True `
-DisplayName "$Lastname, $Firstname" `
-Path $OU `
-Description "$Title" `
-Title "$Title" `
-Manager "$Manager" `
-AccountPassword (convertto-securestring $Password -AsPlainText -Force)
}
}

Looks pretty good to me. You are somewhat inconsistent with the use of ", for example you don't need to write -Title "$Title", you can leave away the " in this case.
You could probably do some fancy optimization with splatting here, reducing the assignments, but since it is a quite unknown feature among many readers I wouldn't go for it here.
One thing I'm missing is error handling. With the default settings, your script will print out errors when something goes wrong, but continue the loop. Is this intended? It is often good practice to state your intention explicitly by setting $ErrorActionPreference at the start of your script.

Related

My active directory accounts are not being created properly

Once I create the users using my script , It will create fine but I cant login using them and I will get errors like
Account Restrictions Are Preventing This User from Signing in.
The issue is I cant sign in with my users
Import-Module ActiveDirectory
$ADUsers = Import-Csv C:\users.csv
foreach ($User in $ADUsers) {
$Name = $User.Name
$username = $User.username
$password = $User.passcode
$Title=$User.Title
New-ADUser -Name $Name -SamAccountName $username -AccountPassword (ConvertTo-secureString $password -AsPlainText -Force) -Title $Title
Enable-AdAccount -Identity $username
}
Here is my script I would love to know if i am missing a parameter or something.
I have tried enabling the user accounts in my script yet that did not work. I was hoping to use these accounts to login.

Powershell Instance Parameter

I am automating the creation of user accounts in our Windows AD. I am trying to copy the permissions from one account to another,(like you would if you right click and copied a user inside of the "Active Directory Users and Computers" application) but when using the cmdlet 'New AD-User' and passing in a variable to the 'instance' parameter, it does nothing different than if I do not pass the variable at all.
This is what I am using to obtain the $userInstance variable:
$userInstance = Get-ADUser -Identity $department User
This is the code I am using to create a new user:
New-ADUser `
-SamAccountName $userName `
-UserPrincipalName "$userName#123.COM" `
-Name "$firstName $lastName" `
-GivenName $firstName `
-Surname $lastName `
-Enabled $true `
-DisplayName "$firstName $lastName" `
-City $city `
-PostalCode $zip `
-Company $company `
-State $state `
-EmailAddress $email `
-Department $department `
-Instance $userInstance `
-AccountPassword (ConvertTo-SecureString "1234" -AsPlainText -Force)
When I run this command, it does the same exact thing as if I ran this command without the instance parameter.
New-ADUser `
-SamAccountName $userName `
-UserPrincipalName "$userName#123.COM" `
-Name "$firstName $lastName" `
-GivenName $firstName `
-Surname $lastName `
-Enabled $true `
-DisplayName "$firstName $lastName" `
-City $city `
-PostalCode $zip `
-Company $company `
-State $state `
-EmailAddress $email `
-Department $department `
-AccountPassword (ConvertTo-SecureString "1234" -AsPlainText -Force)
Am I missing something? I do not understand what the 'instance' parameter is supposed to be doing if it only copies certain attributes that are easily obtainable(state, company, city). Is there something out there that actually copies a template account or do I need to write a loop that goes through every single attribute, permission, and group in the template account that provides some sort of meaning to my organization and assign them manually?
What exactly do you mean by "permission"?
Permissions on resources are set based on the objectSid of a user. Since this is unique to every user you can never "copy" them (and related permissions) to a new user.
Group memberships are stored on groups not on users. The memberOf attribute is just a "backLink" so this won't be copied neither.
Group membership needs to be added in a separate step, e.g. by using
Add-ADGroupMember
cmdlet in PowerShell...
"Permissions" might not have been the word I was looking for necessarily. I wanted to create the user and assign them the same groups and directory location as a previous User. I was able to add group membership to my new users by using the method stated by #Oliver Hauck earlier
Add-ADGroupMember
This aided me in my findings, but what I desired was to not have to write long, repetitive switch code for each new employee type, along with their groups, we could onboard. In hopes that someone sees this and doesn't feel intrigued in writing super long, boring switch code, I wanted to share how I achieved this if it helps anyone else in the future. I still obtained the $userInstance variable in the same way, but adding the -Property parameter defined to MemberOf
$userInstance = Get-ADUser -Identity $department User -Properties MemberOf
I then used the $userInstance variable to obtain the properties I needed from it (Groups to copy, Directory Path)
//Obtains the Path from the copied User, without their common name (CN) attached
$path = $userInstance.DistinguishedName.split(",",2)[-1]
I still created the new user with the same command as above but added the -Path parameter to assign the directory path to the New-ADUser cmd
New-ADUser `
-SamAccountName $userName `
-UserPrincipalName "$userName#123.COM" `
-Name "$firstName $lastName" `
-GivenName $firstName `
-Surname $lastName `
-Enabled $true `
-DisplayName "$firstName $lastName" `
-City $city `
-PostalCode $zip `
-Company $company `
-State $state `
-Path $path `
-EmailAddress $email `
-Department $department `
-AccountPassword (ConvertTo-SecureString "1234" -AsPlainText -Force)
And here is how I obtained and assigned the Group Membership to the New User
//Obtains the groups to be copied from the existing User
$refGroups = $userInstance.MemberOf
//Adds AD Group Membership to User
$refGroups | Add-ADGroupMember -Members $userName

Active directory migration with powershell

I need to migrate from AD Windows2003Forest to AD 2016. I have below script to create users in bulk. My requirement is to map the same SID of older AD to new AD. For example in older AD SID='xyz' then it should be the same in newAD too as SID='xyz'
I am having all the users data along with SID in CSV format & am using below PowerShell script which is somehow not working. As of advice or suggestions.
powershell code snippent:
#Enter a path to your import CSV file
$ADUsers = Import-csv C:\scripts\newusers.csv
foreach ($User in $ADUsers)
{
$Username = $User.username
$Password = $User.password
$Firstname = $User.firstname
$Lastname = $User.lastname
$Department = $User.department
$OU = $User.ou
$sid = $User.sid
$UserPrincipalName = $User.UserPrincipalName
$DistinguishedName = $User.DistinguishedName
#Check if the user account already exists in AD
if (Get-ADUser -F {SamAccountName -eq $Username})
{
#If user does exist, output a warning message
Write-Warning "A user account $Username has already exist in Active Directory."
}
else
{
#If a user does not exist then create a new user account
#Account will be created in the OU listed in the $OU variable in the CSV file; don’t forget to change the domain name in the"-UserPrincipalName" variable
New-ADUser `
-SamAccountName $Username `
-UserPrincipalName $UserPrincipalName `
-Name "$Firstname $Lastname" `
-GivenName $Firstname `
-Surname $Lastname `
-Enabled $True `
-ChangePasswordAtLogon $True `
-DisplayName "$Lastname, $Firstname" `
-Department $Department `
-DistinguishedName $DistinguishedName `
-SID $sid `
-Path $OU `
-AccountPassword (convertto-securestring $Password -AsPlainText -Force)
}
}
You won't be able to assign a SID as that's generated by the domain controller based on a RID. If trying to migrate to a new forest then you'll need to perform a proper AD migration. The old SIDs will be copied onto the migrated users' SID history attributes to allow permissions based on the old SID to still work.
If you simply want to upgrade to a newer version of AD then you're better off joining a newer domain controller to your existing Active Directory forest / domain. The forest functional level mush be 2003 or higher.
As a side note, I'd recommend then getting rid of the 2003 servers as soon as possible as these are no longer supported by Microsoft.

Move AD User using powershell

I know I can move an AD user using powershell. What i want to accomplish is moving a bunch of users based on their description. I have a csv file and in that csv their is a year of graduation column. I want all users that have a YOG from 2016 to 2022 moved to the High School OU.
I haven't tried writing the code yet. I was successful in powershell of grabbing user accounts based on dept but not description. Here is a some same data
"ID","FNAME","LNAME","BDATE","GRD","SCHID"
"111111","TEst","student1","19980601","2016","1480"
"222222","test","Student2","19980522","2017","1480"
"333333","test","Student3","19970813","2025","1479"
I've gone ahead and added the schoolcode to the csv file. I think this will be a lot easier to move the students to the correct ou based on this file. 1480 being elem, 1479 hs. Also here is the code I'm using toe create the AD accounts.
# Import active directory module for running AD cmdlets
Import-Module activedirectory
#Store the data from ADUsers.csv in the $ADUsers variable
$ADUsers = Import-csv userimport.csv
#Store report in log file in the $log variable
$log = "log.txt"
#Loop through each row containing user details in the CSV file
foreach ($User in $ADUsers)
{
#Read user data from each field in each row and assign the data to a variable as below
$Username = $User.ID
$Password = $User.BDATE
$Firstname = $User.FNAME
$Lastname = $User.LNAME
$Department = $User.GRD
$Company = $User.SCHID #This field refers to the OU the user account is to be moved to
#Check to see if the user already exists in AD
if (Get-ADUser -F {SamAccountName -eq $Username})
{
#If user does exist, give a warning
Write-Warning "A user account with username $Username already exist in Active Directory."
}
else
{
#User does not exist then proceed to create the new user account
"Processing started (on " + $date + "): " | Out-File $log -append
"--------------------------------------------" | Out-File $log -append
#Account will be created in the OU provided by the $OU variable read from the CSV file
New-ADUser `
-SamAccountName $Username `
-UserPrincipalName "$Username#clasd.net" `
-Name "$Firstname $Lastname" `
-GivenName $Firstname `
-Department "$Department" `
-Company "$Company" `
-EmailAddress "$Username#clasd.net" `
-Surname $Lastname `
-Enabled $True `
-Scriptpath "login.vbs" `
-DisplayName "$Firstname $Lastname" `
-Path "ou=users,ou=hs,dc=clasd,dc=net" `
-AccountPassword (convertto-securestring $Password -AsPlainText -Force) `
-ChangePasswordAtLogon $true
# Add User to Groups
#Add-ADPrincipalGroupMembership -Identity $Username -MemberOf "Elem","Elem Students"
Start-Sleep 3
# Move Users to appropiate OU based on School Code
$usr = import-csv userimport.csv
foreach ($User in $usr) {
if ($user.grd -in 2016){
Get-ADUser $User.ID | Move-ADObject -TargetPath 'OU=users,ou=hs,dc=clasd,dc=net'
}
}
}
}
As their AD Username is unique and already contained in your CSV, it's simply a case of checking if the GRD field is in the range 2016-2022 and then moving the account using the ID field:
$filepath = "C:\path\to\data.csv"
$csv = Import-CSV $filepath
foreach ($user in $csv) {
if ($user.GRD -in 2016..2022) {
Get-ADUser $user.ID | Move-ADObject -TargetPath 'OU=High School,DC=domain,Dc=com'
}
}
EDIT: Didn't see your comment that YOG is the Description field, and I've used GRD instead, let me know if this isn't correct?
EDIT2: My answer above would be run after every account is created not during your existing script, it is more efficient to put the account in the correct OU at creation like so:
foreach ($User in $ADUsers)
{
#Read user data from each field in each row and assign the data to a variable as below
$Username = $User.ID
$Password = $User.BDATE
$Firstname = $User.FNAME
$Lastname = $User.LNAME
$Department = $User.GRD
$Company = $User.SCHID #This field refers to the OU the user account is to be moved to
# Choose OU
Switch ($Department)
{
"2016" {$OU = 'OU=users,ou=hs,dc=clasd,dc=net'}
"2017" {$OU = 'OU=2017,OU=users,ou=hs,dc=clasd,dc=net'}
}
#Check to see if the user already exists in AD
if (Get-ADUser -F {SamAccountName -eq $Username})
{
#If user does exist, give a warning
Write-Warning "A user account with username $Username already exist in Active Directory."
}
else
{
#User does not exist then proceed to create the new user account
"Processing started (on " + $date + "): " | Out-File $log -append
"--------------------------------------------" | Out-File $log -append
#Account will be created in the OU provided by the $OU variable read from the CSV file
New-ADUser `
-SamAccountName $Username `
-UserPrincipalName "$Username#clasd.net" `
-Name "$Firstname $Lastname" `
-GivenName $Firstname `
-Department "$Department" `
-Company "$Company" `
-EmailAddress "$Username#clasd.net" `
-Surname $Lastname `
-Enabled $True `
-Scriptpath "login.vbs" `
-DisplayName "$Firstname $Lastname" `
-Path $OU `
-AccountPassword (convertto-securestring $Password -AsPlainText -Force) `
-ChangePasswordAtLogon $true
# Add User to Groups
#Add-ADPrincipalGroupMembership -Identity $Username -MemberOf "Elem","Elem Students"
Start-Sleep 3
}
}

Creating a Script to import users from a CSV to AD

I'm trying to build a script that will take a CSV with the fields
firstname, lastname, password
and create a user in AD in a specific OU with that info. I've done a bunch of googling, and this is what I've come up with (from this blog):
Import-Csv .\userImport.csv | ForEach-Object {
New-ADUser
-Name $_.DisplayName
-UserPrincipalName $_.UserPrincipalName
-SamAccountName $_.Username
-FirstName $_.FirstName
-DisplayName $_.DisplayName
-LastName $_.Lastname
-Path $_.Path
-AccountPassword (ConvertTo-SecureString $_.Password -AsPlainText -force)
-Enabled $True
-PasswordNeverExpires $True
-PassThru
}
I have a few questions:
I want to specify the OU in my command, instead of having it be in the CSV. Can I just change it to:
-Path OU=MyOU,DC=Domain,DC=Local
What is the -PassThru line for?
Is the -AccountPassword line correct? I got that from a blog that suggested this is the right way to take a password and set it as my AD user's password.
Do I need the PrincipalName, SamAccountName and DisplayName all as separate fields? This can be as minimal as possible, at least for now.
Any tips or changes you would make? This is my first time doing a script like this so I'm willing to learn.
Yes, you can specify parameters whichever way you like, they don't need to come from the input file.
-PassThru makes New-ADUser echo the created user object. By default the cmdlet doesn't return anything.
Yes, the -AccountPassword argument is correct, provided the password field from the CSV contains the plaintext password.
You don't necessarily have to have a separate CSV field for each parameter argument if you can construct an argument from existing field values. For instance, you most likely can create values like DisplayName or SamAccountName from first and last name, e.g. like this:
-SamAccountName ($_.firstname.Substring(0,1) + $_.lastname).ToLower()
-DisplayName ('{0} {1}' -f $_.firstname, $_.lastname)
You also don't need to specify every argument. For instance, the UPN (User Principal Name) will automatically be generated when omitted, and the display name will default to the name.
You can't wrap the lines like you have. PowerShell can't read your mind and won't know that you intend to continue the statement in the next line unless you tell it that or the statement is obviously incomplete. Use backticks to escape the linebreaks. Also, the parameters for first and last name are -GivenName and -Surname, not -FirstName and -LastName.
$csv = '.\userImport.csv'
$ou = 'OU=MyOU,DC=Domain,DC=Local'
Import-Csv $csv | ForEach-Object {
$name = '{0} {1}' -f $_.firstname, $_.lastname
$acct = ($_.firstname.Substring(0,1) + $_.lastname).ToLower()
$pw = ConvertTo-SecureString $_.password -AsPlainText -Force
New-ADUser -Name $name `
-SamAccountName $acct `
-GivenName $_.firstname `
-Surname $_.lastname `
-Path $ou `
-AccountPassword $pw `
-Enabled $true `
-PasswordNeverExpires $true `
-PassThru
}
OU - If you want them all in the same OU, you can hard code it (remember the quotes)
-Path "OU=MyOU,DC=Domain,DC=Local"
-PassThru
The -PassThru parameter lets you request output from cmdlets that return no output by default. (The PassThru Parameter: Gimme Output)
i.e. Instead of New-ADUser just executing and then returning you to the next line, it will actually print out the new user created info to the prompt.
Yes the -AccountPassword takes a SecureString as the argument. The command ConvertTo-SecureString converts a plain text string to a SecureString, which then can be passed to the -AccountPassword parameter
You don't need -UserPrincipalName. You do need -SamAccountName. -DisplayName can be changed to:
-DisplayName "$($_.FirstName) $($_.Lastname)"
This is a pretty standard script for mass producing accounts, so it is pretty good the way it is. The only change I would look at is -PasswordNeverExpires $True You typically only really set the Password Never Expires on Service accounts, so if you are creating plain old user accounts, you wouldn't need it.