For a small project, I want to create AD users from a small CSV file. I made jobs (PowerShell scripts) that run every few minutes, to pick-up the user creation based on the status of the user I define in the script. Per example; when a user is created in the AD, I change the status to addmailbox. Then another job will come and add the mailbox.
Everything is going well, but I noticed a bug. The way I am editing the status in the CSV file is wrong, because I change the status for all users in the CSV when I only want to change one. But I can't seem to get any other method working.
#When encountering any error, stop the script.
$ErrorActionPreference = "Stop";
#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv
if($FileExists -eq $true) {
$Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
foreach ($user in $Users) {
$Status = $User.Status;
$userPrincipalName = $User.userPrincipalName;
$SAMAccountName = $User.SAMAccountName;
$userInstance = $User.userInstance;
$Name = $User.Name;
$displayName = $User.DisplayName;
$Path = '"' + $User.Path + '"';
$GivenName = $User.GivenName;
$Surname = $User.Surname;
$SIP = $userPrincipalName;
if ($Status -eq "CreateUser") {
try {
#create user
Import-Module ActiveDirectory;
New-ADUser -SAMAccountName $SAMAccountName -Instance $userInstance -Name $name -DisplayName $displayName -Path "correct.path.com" -GivenName $givenname -Surname $surname -userPrincipalName $userprincipalname -AccountPassword (ConvertTo-SecureString -String "bla" -AsPlainText -Force) -PassThru | Enable-ADAccount;
#change status
(Get-Content C:\inetpub\wwwroot\melle\data.csv) | Foreach-Object {$_ -replace 'CreateUser','AddMailbox'} | Out-File C:\inetpub\wwwroot\melle\data.csv
#exit on completion
Exit(Write-Host 'User was created in AD.');
} Catch {
#write any errors to error log.
$_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
}
} else {
Exit(Write-Host 'No user with status CreateUser was found.');
}
}
}else {
Exit(Write-Host 'No data.csv file was found.');
}
The way I do it now is (Get-Content C:\inetpub\wwwroot\melle\data.csv) | Foreach-Object {$_ -replace 'CreateUser','AddMailbox'} | Out-File C:\inetpub\wwwroot\melle\data.csv but I want to define it for only the row that the script is talking to in the foreach.
I tried searching for a solution on here and different sites, but I wasn't able to get the solutions others are using to work on my script. Some scripts were too advanced for me to understand.
What would be the correct approach to only change the status for the line I'm working on in the loop?
UPDATE: Solved
Working script:
#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv
if($FileExists -eq $true) {
$Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
$UsersThatNeedToBeCreated = $Users | Where-Object {$_.Status -eq 'CreateUser'};
# Check that we have users that need to be created, might need fine-tuning.
if($UsersThatNeedToBeCreated -eq $null -or $UsersThatNeedToBeCreated.Count -eq 0){
# Write-Host doesn't have a meaningful return code, so you can separate those lines.
Exit(Write-Host 'No user with status CreateUser was found.');
}else{
# This way it's only run once
Import-Module ActiveDirectory;
}
$Users | ForEach-Object {
if($_.Status -eq 'CreateUser'){
try {
#create user
New-ADUser -SAMAccountName $_.SAMAccountName -Instance "'" + $_.userInstance + "'" -Name $_.name -DisplayName $_.displayName -Path "working.path.here" -GivenName $_.givenname -Surname $_.surname -userPrincipalName $_.userprincipalname -AccountPassword (ConvertTo-SecureString -String "Welcome01" -AsPlainText -Force) -PassThru | Enable-ADAccount;
#change status
$_.Status = 'AddMailbox';
} Catch {
#write any errors to error log.
$_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
}
}
}
$Users | ConvertTo-Csv | Out-File 'C:\inetpub\wwwroot\melle\data.csv'
Exit(Write-Host 'User was created in AD.');
}else {
Exit(Write-Host 'No data.csv file was found.');
}
As you already noticed, your current approach doesn't work. You're grabbing the whole content of the CSV and replace every instance. That Import-Csv statement actually gives you a data structure that allows you to change values. You just need to write it back afterwards.
This approach still is going to be error prone and you will run into issues with it. If I'm understanding you correctly, you want to keep track of the state and have multiple scripts that do different things. Sooner or later you will encounter situations where they overwrite each others changes and/or lock due to competing access requests. If you want to do it this way you should consider using a database that supports row based locking (most probably do). Otherwise you will need to find a way to make them run sequentially or implement row based locking yourself.
That said, one possible solution could look like the following. I haven't run it but the basic structure should be right. An example with this for overwriting changes would be the long time it can take to create a mailbox. As the file is only read at the start of the script and written at the end, it might change in-between.
#When encountering any error, stop the script.
$ErrorActionPreference = "Stop";
#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv
if($FileExists -eq $true) {
$Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
$UsersThatNeedToBeCreated = $Users | Where-Object {$_.Status -eq 'CreateUser'};
# Check that we have users that need to be created, might need fine-tuning.
if($$UsersThatNeedToBeCreated -eq $null -or $UsersThatNeedToBeCreated.Count -eq 0){
# Write-Host doesn't have a meaningful return code, so you can separate those lines.
Write-Host 'No user with status CreateUser was found.'
Exit;
}else{
# This way it's only run once
Import-Module ActiveDirectory;
}
$Users | ForEach-Object {
$Status = $User.Status;
$userPrincipalName = $User.userPrincipalName;
$SAMAccountName = $User.SAMAccountName;
$userInstance = $User.userInstance;
$Name = $User.Name;
$displayName = $User.DisplayName;
$Path = '"' + $User.Path + '"';
$GivenName = $User.GivenName;
$Surname = $User.Surname;
$SIP = $userPrincipalName;
if($_.Status -eq 'CreateUser'){
try {
#create user
New-ADUser -SAMAccountName $SAMAccountName -Instance $userInstance -Name $name -DisplayName $displayName -Path "correct.path.com" -GivenName $givenname -Surname $surname -userPrincipalName $userprincipalname -AccountPassword (ConvertTo-SecureString -String "bla" -AsPlainText -Force) -PassThru | Enable-ADAccount;
# change status
$_.Status = 'AddMailbox';
} Catch {
# write any errors to error log.
$_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
}
}
}
$Users | ConvertTo-Csv -NoTypeInformation | Out-File 'C:\inetpub\wwwroot\melle\data.csv'
Write-Host 'User was created in AD.'
Exit
}else {
Write-Host 'No data.csv file was found.'
Exit
}
Related
I'm creating a script for adding multiple users in Active Directory. I stumbled upon this link, when I couldn't get the guide described in the question to work either. I then tried one of the solutions in the comments
Import-Module ActiveDirectory
# this defaults to csv fields delimited by a comma. If your CSV file uses a different
# character, then add parameter '-Delimiter' followed by the actual character
$ADUsers = Import-Csv -Path 'C:\Users\Desktop\Powershell files\EM-mis-new-AD.csv'
# the Where-Object clause is just a precaution to omit records that have no username value
$ADUsers | Where-Object { $_.username -match '\S'} | ForEach-Object {
$Username = $_.username
if (Get-ADUser -Filter "SamAccountName -eq '$Username'" -ErrorAction SilentlyContinue) {
Write-Warning "A user account with SamAccountName '$Username' already exist in Active Directory."
}
else {
$Firstname = $_.firstname
$Lastname = $_.lastname
# use splatting on cmdlets that use a lot of parameters
$userParams = #{
SamAccountName = $Username
UserPrincipalName = "$Username#Mydomain.com"
Name = "$Firstname $Lastname"
GivenName = $Firstname
Surname = $Lastname
Enabled = $true
DisplayName = "$Firstname, $Lastname"
Path = $_.ou
AccountPassword = (ConvertTo-SecureString $_.Password -AsPlainText -Force)
ChangePasswordAtLogon = $true
}
# create the user and report back
New-ADUser #userParams
Write-Host "Created new user '$Username' with initial password: $($_.Password)"
}
}
Here is my CSV file
firstname;lastname;username;password;ou
Mette;Frederiksen;MeFr;Password1;OU=Salg,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Sussi;Hart;SuHa;Password1;OU=Salg,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Ove;Tylstrup;OvTy;Password1;OU=Salg,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Karlos;Mondolez;KaMo;Password1;OU=Lager,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Anne;Otto;AnOt;Password1;OU=Lager,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Dennis;Ågard;DeÅg;Password1;OU=Lager,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Helena;Riss;HeRi;Password1;OU=Okonomi,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
Risa;Lamende;RiLa;Password1;OU=Okonomi,OU=Users,OU=RGD Aarhus,DC=rgd,DC=local
However, when I run the above code nothing happens
PS C:\Users\RGDAdmin> C:\Users\RGDAdmin\Documents\ADUser.ps1
PS C:\Users\RGDAdmin>
When I add the Delimiter parameter, I get this
Created new user 'KaMo' with initial password: Password1
New-ADUser : The directory service was unable to allocate a relative identifier
At C:\Users\RGDAdmin\Documents\ADUser.ps1:31 char:9
+ New-ADUser #userParams
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (CN=Anne Otto,OU...DC=rgd,DC=local:String) [New-ADUser], ADException
+ FullyQualifiedErrorId :
ActiveDirectoryServer:8208,Microsoft.ActiveDirectory.Management.Commands.NewADUser
PS. I know the password is bad practice in terms of passwords
Your file is delimited by semicolons, so you will definitely need to specify the -Delimiter parameter. But the documentation has a caveat:
To specify a semicolon (;) enclose it in single quotation marks.
So it should look like this:
$ADUsers = Import-Csv -Delimiter ';' -Path 'C:\Users\Desktop\Powershell files\EM-mis-new-AD.csv'
If that still results in that RID error, then there's possibly something wrong on the server. Can you create users manually using AD Users and Computers?
Try reviewing this. I don't have access to ActiveDirectory to test it myself.
#helpers
function usernameIsNotBlank {
[CmdletBinding()]
param(
$Username
)
[regex]$rx = "\S"
return $rx.match($Username)
}
function usernameDoesNotAlreadyExist {
[CmdletBinding()]
param(
$Username
)
$UserDoesNotExist = $true
$UserObject = $(
try {
Get-ADUser $Username
}
catch {
$null
}
)
if ($null -ne $UserObject) {
$UserDoesNotExist = $false
Write-Verbose "$Username already exists"
}
else {
$UserDoesNotExist = $true
}
return $UserDoesNotExist
}
function suppliedUsernameIsAvailable {
[CmdletBinding()]
param(
$Username
)
return ((usernameIsNotBlank -Username $Username) -and (usernameDoesNotAlreadyExist -Username $Username))
}
#script
$OriginalVerbose = $VerbosePreference
$VerbosePreference = "Continue"
Import-Module ActiveDirectory
$CSV = "C:\Users\Desktop\Powershell file\EM-mis-new-AD.csv"
$Data = Import-CSV $CSV
foreach ($Line in $Data) {
if (suppliedUsernameIsAvailable($Line.username)) {
New-ADUser -Name "$Line.firstname $Line.lastname" -GivenName "$Line.firstname" -Surname "$Line.lastname" -SamAccoutnname "$(Line.username)#mydomain.com" -AccountPassword (ConvertTo-SecureString $Line.password -AsPlainText -Force) -ChangePasswordAtLogon $true -Path "$Line.ou"
}
}
$VerbosePreference = $OriginalVerbose
I am pretty new to powershell and have a code that I found. I had it working but now it is no longer working. I didn't change anything with the variable so I am not sure what is going on. Here is a link to a Screenshot of the code and error. Please let me know if you need any other information
https://imgur.com/a/ntEhdoV
Thank you!
Import-Module activedirectory
$ADUsers = Import-csv 'C:\Users\Desktop\Powershell files\EM-mis-new-AD.csv'
foreach ($User in $ADUsers)
{
$Username = $User.username
$Password = $User.password
$Firstname = $User.firstname
$Lastname = $User.lastname
$OU = $User.ou
$Password = $User.Password
if (Get-ADUser -F {SamAccountName -eq $Username})
{
Write-Warning "A user account with username $Username already exist in Active Directory."
}
else
{
New-ADUser `
-SamAccountName $Username `
-UserPrincipalName "$Username#Mydomain" `
-Name "$Firstname $Lastname" `
-GivenName $Firstname `
-Surname $Lastname `
-Enabled $True `
-DisplayName "$Firstname, $Lastname" `
-Path $OU `
-AccountPassword (convertto-securestring $Password -AsPlainText -Force) -ChangePasswordAtLogon $True
}
}
Error:
Get-ADUser : Variable: 'Username' found in expression: $Username is not defined.
At C:\Users\jcarnovale\Desktop\Testing if.ps1:22 char:6
if (Get-ADUser -F {SamAccountName -eq $Username})
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidArgument: (:) [Get-ADUser], ArgumentException
FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.GetADUse
You probably want to check that you have a good username before proceeding in the script, like:
$Username = $User.username
...
if(!$Username) {
throw "Username was empty!"
}
Also, try changing the Get-ADUser filter to use a string:
if (Get-ADUser -F "SamAccountName -eq $Username")
{
}
You didn't show us anything of the imported CSV file itself and I think the main problem is in there.
Import-Csv by default expects the comma (,) to be used as delimiter character. If that is not the case in your file, you need to add parameter -Delimiter followed by the character that is used as separator in your file (like -Delimiter ';' if your file uses the semicolon).
Please check that first, so the Import-Csv cmdlet can parse the file correctly.
Next, it could be that there are empty values in the username column and if so, the code should skip these rows.
Also, as commented, the -Filter parameter needs a double-quoted string "Property -eq 'something'" in which a variable like $username is expanded, instead of a scriptblock {..}
Finally, I'd recommend using Splatting on cmdlets that take many properties instead of using backticks.
Try
Import-Module ActiveDirectory
# this defaults to csv fields delimited by a comma. If your CSV file uses a different
# character, then add parameter '-Delimiter' followed by the actual character
$ADUsers = Import-Csv -Path 'C:\Users\Desktop\Powershell files\EM-mis-new-AD.csv'
# the Where-Object clause is just a precaution to omit records that have no username value
$ADUsers | Where-Object { $_.username -match '\S'} | ForEach-Object {
$Username = $_.username
if (Get-ADUser -Filter "SamAccountName -eq '$Username'" -ErrorAction SilentlyContinue) {
Write-Warning "A user account with SamAccountName '$Username' already exist in Active Directory."
}
else {
$Firstname = $_.firstname
$Lastname = $_.lastname
# use splatting on cmdlets that use a lot of parameters
$userParams = #{
SamAccountName = $Username
UserPrincipalName = "$Username#Mydomain.com"
Name = "$Firstname $Lastname"
GivenName = $Firstname
Surname = $Lastname
Enabled = $true
DisplayName = "$Firstname, $Lastname"
Path = $_.ou
AccountPassword = (ConvertTo-SecureString $_.Password -AsPlainText -Force)
ChangePasswordAtLogon = $true
}
# create the user and report back
New-ADUser #userParams
Write-Host "Created new user '$Username' with initial password: $($_.Password)"
}
}
I'm attempting to script the creation of accounts in active directory using a csv file. Unfortunately I'm new to PowerShell and scripting in general and am facing difficulty managing a foreach() loop which is intended to handle each of the columns in the csv document I import.
I suspect that I cannot define my variables in one block after foreach() like I have, but am unsure and would appreciate some guidance. Specifically, the purpose of the code is read a csv document and assign each item to a variable that it then uses to create a service account with those values.
The Ideal situation is one where I can create a csv with several hundred rows, execute the script and end with several hundred matching service accounts.
$SVC = (import-csv C:\users\me\desktop\Test.csv -header("Name", "Pass", "WhatDo", "Location", "Domain")) `
foreach($variable in %SVC) {
$name = $SVC.Name
$Pass = $SVC.Pass
$WhatDo = $SVC.WhatDo
$Location = $SVC.Location
$Domain = $SVC.Domain
New-ADuser `
-Name $name `
-AccountPassword (Convertto-SecureString $Pass -AsPlainText -Force) `
-CannotChangePassword $true `
-Description $WhatDo `
-DisplayName $name `
-Enabled $true `
-GivenName $name `
-PasswordNeverExpires $True `
-Office $Location `
-Path "OU=Service-Accounts, Ou=Accunts, OU=_CORP, DC=$Domain, DC=net" `
-SamAccountName $name `
-UserPrincipleName $name + "#" + $domain + ".net" `
Start-Sleep -Seconds 15
Get-ADUser `
-Identity $name | Add-ADPrincipalGroupMembership `
-MemberOf "Group1","Group2","Group3"
}
There are quite a few things wrong in your code, not just the foreach.
But let's start with that: foreach ($variable in $SVC) means that $variable will have the the current item inside your loop, yet you are accessing $SVC in your loop which is still referring to the original collection. $variable is not a good name either, so you should change that to something more meaningful. Also, you wrote %SVC instead of $SVC.
You are also using backtick (`) a lot, sometimes incorrectly. You should only use it when your cmdlet invokation spans multiple lines. In the case of Import-Csv it's not, yet there's backtick at the end. There's also one on the last line of New-ADUser. Some prefer to use Parameter Splatting instead of backticks, but's thats a matter of taste.
Considering you are creating service accounts, I would write the first part like this:
$serviceAccounts = Import-Csv C:\users\me\desktop\Test.csv -Header Name,Pass,WhatDo,Location,Domain
foreach($serviceAccount in $serviceAccounts) {
Then inside your loop you can access the indivdual properties through $serviceAccount:
$name = $serviceAccount.Name
Also, PowerShell expands variables when using double quotes, so -UserPrincipleName can be written like this: -UserPrincipleName "$name#$domain.net"
I prefer using ForEach-Object rather than foreach.
It would be something like:
$SVC = (Import-CSV C:\users\me\desktop\Test.csv -header("Name", "Pass", "WhatDo", "Location", "Domain"))
$SVC | ForEach-Object {
New-ADuser `
-Name $_.Name `
-AccountPassword (Convertto-SecureString $_.Pass -AsPlainText -Force) `
-CannotChangePassword $true `
-Description $_.WhatDo `
-DisplayName $_.Name `
-Enabled $true `
-GivenName $_.Name `
-PasswordNeverExpires $True `
-Office $_.Location `
-Path "OU=Service-Accounts, Ou=Accunts, OU=_CORP, DC=$Domain, DC=net" `
-SamAccountName $_.Name `
-UserPrincipleName $_.Name + "#" + $_.Domain + ".net"
Start-Sleep -Seconds 15
Get-ADUser `
-Identity $_.Name | Add-ADPrincipalGroupMembership `
-MemberOf "Group1","Group2","Group3"
}
$_ represents the current item in the pipeline. ($SVC in your case, which was the wrong variable anyways.) It's less code and I think it's a cleaner way of doing things!
Rename $SVC to $SVCs then foreach ($SVC in $SVCs)
Here is an example how you can do it.(Don't forget the delimiter)
$csv = Import-Csv -Path C:\temp\csv.csv -Header "a","b","c" -Delimiter ";"
foreach($row in $csv){
$row.a
$row.b
$row.c
}
Here are some more examples how foreach works:
Take a look https://ss64.com/ps/foreach.html
Examples
Loop through an array of strings:
$trees = #("Alder","Ash","Birch","Cedar","Chestnut","Elm")
foreach ($tree in $trees) {
"$tree = " + $tree.length
}
Loop through a collection of the numbers, echo each number unless the number is 2:
foreach ($num in 1,2,3,4,5) {
if ($num -eq 2) { continue } ; $num
}
Loop through a collection of .txt files:
foreach ($file in get-ChildItem *.txt) {
Echo $file.name
}
I'm trying to import users from a csv file using ADUser powershell cmdlet and here's the script
Import-Module ActiveDirectory
$csvcontent = Import-CSV -Path "C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\import_create_ad_users_2a.csv"
foreach ($user in $csvcontent) {
$samAccountName = $user.GivenName.substring(0,1).ToLower()+$user.LastName.ToLower()
$userPrincinpal = $samAccountName+"#mmc.local"
New-ADUser
-AccountPassword (ConvertTo-SecureString -AsPlainText $user.Password -force)`
-ChangePasswordAtLogon $false`
-Company “mmc LLP.”`
-DisplayName ($user.GivenName+""+$user.Lastname)`
-userprincipalname $userPrincinpal`
-SamAccountName $samAccountName` -Name ($user.GivenName+""+$user.Lastname)`
-Path “CN=Users,DC=mmc,DC=local”`
-state $user.County`
-givenname $user.GivenName`
-surname $user.Lastname`
-description ($user.Description)`
-Enabled $true`
Add-ADGroupMember "mmc_Users" $samAccountName;
}
But when I run the command in powershell, I get a prompt as listed below and I would like to import all the users listed in the csv file without any user intervention.
cmdlet New-ADUser at command pipeline position 1
Supply values for the following parameters:
Name:
Please review the script and let me know how to fix this.
FYI - Powershell beginner
Thanks,
Karthik
Backticks are generally worth avoiding. They work by escaping the next character, which on the end of a line is the newline character so it allows the command to continue. However its too easy to end up with a space after the backtick that you can't see, which then ends up getting escaped and not the newline. That doesn't seem to be the case above, but as TessellatingHeckler pointed out you were missing one after New-ADUser.
A better solution (to keep the code from going too far horizontal) would be to use splatting like this:
Import-Module ActiveDirectory
$csvcontent = Import-CSV -Path "C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\import_create_ad_users_2a.csv"
foreach ($user in $csvcontent) {
$samAccountName = $user.GivenName.substring(0,1).ToLower()+$user.LastName.ToLower()
$userPrincinpal = $samAccountName+"#mmc.local"
$NewUserParams = #{
AccountPassword = (ConvertTo-SecureString -AsPlainText $user.Password -Force)
ChangePasswordAtLogon = $false
Company = “mmc LLP.”
DisplayName = ($user.GivenName+""+$user.Lastname)
userprincipalname = $userPrincinpal
SamAccountName = $samAccountName
Name = ($user.GivenName+""+$user.Lastname)
Path = “CN=Users,DC=mmc,DC=local”
state = $user.County
givenname = $user.GivenName
surname = $user.Lastname
description = ($user.Description)
Enabled = $true
}
New-ADUser #NewUserParams
Add-ADGroupMember "mmc_Users" $samAccountName
}
This works by creating a hashtable #{ } with each of the parameters in it that you want to set and then sending that hashtable to the cmdlet with the special # character.
Few things that I think look wrong, but lets try to fix it. Changing the name is best done after the user has been created. This will limit the script from failing.
Backticks can be used for someone who is just learning how to code and it allows you to see the code in a more logical way. You could also create an array as suggested, but that can get complicated and not give correct results.
Lets break down the script below. First we call ActiveDirectory Module, then we call the CSV. That part works great.
We can test it by using the following code that was provided:
Import-Module ActiveDirectory
$csvcontent = Import-CSV -Path "C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\import_create_ad_users_2a.csv"
$csvcontent | Out-GridView
This should display something with your raw data, but an example is:
GivenName | LastName | Password | Description | Country
Karthik | CSVScript | 79HKJ#p8 | UserTest | Norway
Once we can confirm that the columns are correct. We can run the script
When you use the Import-CSV it imports the columns that you defined as a pipline($_.GivenName). This allows us not to create another variable. Calling it from the Import-CSV cmdlet will only use the fields that you provide in the raw data(CSV file).
You can save the following as a PS_Script called something like NewUser_CSV.ps1
The script below will only look at what you put into the columns. If something is not correct, that means the data in the CSV is wrong. This is a basic add AD users using a CSV file with no major error handling.
We will use the Transcript cmdlet to gather a log
#RUN AS ADMIN!
Start-Transcript -Path "C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\import_create_ad_users_2a.log"
Import-Module ActiveDirectory
$csvcontent = Import-CSV -Path "C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\import_create_ad_users_2a.csv"
$csvcontent | ForEach-Object {
$sam = $_.GivenName.substring(0,1)+$_.Lastname
$setpass = ConvertTo-SecureString -AsPlainText $_.Password -force
Try
{
New-ADUser $samAccountName `
-Path "CN=_s,DC=mmc,DC=local" `
-GivenName $_.GivenName `
-Surname $_.LastName `
-UserPrincipalName ($samAccountName + "#mmc.local")`
-DisplayName ($_.GivenName + " " + $_.LastName) `
-Description $_.Description `
-Enabled $TRUE `
-Company "mmc LLP." `
-State $_.Country `
-AccountPassword $setpass `
-ChangePasswordAtLogon $False `
-AccountPassword $setpass
$newdn = (Get-ADUser $samAccountName).DistinguishedName
Rename-ADObject -Identity $newdn -NewName ($_.GivenName + " " + $_.LastName)
}
Catch
{
Write-Host "[ERROR]`t Oops, something went wrong: $($_.Exception.Message)`r`n"
}
}
Stop-Transcript
I really hope this helps you out and gets the task done for you. Good luck with learning PowerShell.
I really really need your help. I got a task to create/import users from csv to AD.
But I have two problems
not all the user have all the attributes filled = there are empty "cells" in CSV
Two custom attributes contains dash - and PSH is returning error that this is not allowed.
I tried to search, there are some solutions but I am not sure how to implement it to my file (| ForEach-Object {$CSV = $_ ?????. Cant make it work.
Here is what I have so far
Import-Module ActiveDirectory
$ErrorActionPreference = 'SilentlyContinue'
$ScriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$log = "$ScriptDir\Error-Import.log"
$log1 = "$ScriptDir\Success-Import.log"
$date = Get-Date
$inputFile = Import-CSV $ScriptDir\allusers.csv
Function cUser
{
"ERROR LOG from (" + $date + ") :" | Out-File $log -Append
"————————————————-" | Out-File $log -Append
"Following accounts has been successfully created (" + $date + ") :" | Out-File $log1 - Append
"————————————————-" | Out-File $log1 -Append
foreach($line in $inputFile)
{
$sam = $line.samaccoutnname
#check if user already exists
$exists = Get-ADUser -LDAPFilter "(sAMAccountName=$sam)"
if (!$exists)
{
# when does not exists - load data from CSV and set as variable
$gn = $line.givenName
$sn = $line.sn
$title = $line.title
$stAd=$line.streetAddress
$upn = $line.sAMAccountName + "`#targetrange.local"
$city=$line.l
$st=$line.st
$postc=$line.postalCode
$ehrd=$line.award-hireDate
$comp=$line.company
$dvs=$line.division
$edvs=$line.award-subdivision
$dept=$line.department
$st=$line.street
$tarA=$line.targetAddress
$dn=$line.displayName
$ext2=$line.extensionAttribute2
$ext4=$line.extensionAttribute4
$ext5=$line.extensionAttribute5
$alias = $line.sAMAccountName
$cn=$line.cn
$hdir=$line.unixHomeDirectory
$desc=$line.description
$pat = $line.OU
$pwd = $line.pass
$spass =ConvertTo-SecureString -AsPlainText -Force -String $pwd
$aexp=$line.accountExpires
$Expconv=(Get-Date $aexp ).ToFileTime()
New-ADUser -SamAccountName $alias -UserPrincipalName $upn -GivenName $gn -Surname $sn -title $title -StreetAddress $stAd -l $city -State $st -PostalCode $postc -award-hireDate $ehrd -Company $comp -Division $dvs -award-subdivision $edvs -Department $dept -street $st -targetAddress $tarA -extensionAttribute2 $ext2 -extensionAttribute4 $ext4 -extensionAttribute5 $ext5 -DisplayName $dn -Name $cn -unixHomeDirectory $hdir -Description $Desc -path $pat -AccountExpirationDate $Expconv -AccountPassword $spass -PassThru | Enable-ADAccount
$exists1 = Get-ADUser -LDAPFilter "(sAMAccountName=$sam)"
if ($exists1)
{
"User successfully created : " + $sam | Out-File $log1 -Append
}
Else
{
"SKIPPED – MISSING OR INCORRECT VALUES : " + $sam | Out-File $log -Append
}
}
# if user already exists - skip and write to logfile
Else
{
"SKIPPED – ALREADY EXISTS OR DUPLICATE ALIAS : " + $sam | Out-File $log -Append
}
}
"————————————————-" + "`n" | Out-File $log -Append
}
createUsers
# finito
}
If you can help, it would save my life :-) How to skip empty cells??? And to make attributes with - working - apostrophe is enough? 'award-hiredate' ?
You mentioned that you wanted to skip empty cells. I am not sure how far you wanted to take that but here is something I think does the trick. You have the line.
$inputFile = Import-CSV $ScriptDir\allusers.csv
I would propose it gets updated as follows
$inputFile = Import-CSV $ScriptDir\allusers.csv | Where-Object{$_.psobject.properties.value -notcontains ""}
What this will do is break each entry into an array of its values. If any of the values are empty or "" then they will not be matched into the $inputFile.
Count EventID EventType
----- ------- ---------
14 Information
2 7040 Information
2 0
1 8224 Information
The above data was put into the filter and the output was
Count EventID EventType
----- ------- ---------
2 7040 Information
1 8224 Information
As you can see the lines with empty data are filtered out and the data structure is still preserved.
The Hyphen
As for the hyphen - issue I'm assuming the error you are getting is Unexpected token '-Type' in expression or statement. Guessing it is trying to treat the -hireData in $line.award-hireDate. What you are trying to do is the best in this case. Just use quotes around the parameter name. You would do the same if the parameter had a space in it.
$line."award-hireDate"
$line."award hireDate"