Read CSV and input into Active Directory through PowerShell - powershell

I export a database into CSV. In the CSV there contains fields such as employeeid, first name, last name, email, position, description, and various fields. I am trying to populate those fields and all the other fields into Active Directory. The extracted CSV fields don't directly correlate to Active Directory. because of this, I need to check for double entries; if any.
To do this, I take the email address of a user in the CSV and do a search in Active Directory for the email address. I then try to take the same users employeeid and check to see if in Active Directory employeeid attribute is filled. If the employeeid attribute is filled, skip, if not, fill in the attribute from the CSV file.
I am having trouble inputting data from the CSV to Active Directory and also doing the check if employeeID exists in the attribute.
Import-Module ActiveDirectory
# ERROR REPORTING ALL
Set-StrictMode -Version latest
$newpath = "C:\Scripts\Test Files\book1.csv"
$log = "C:\Scripts\Test Files\import_employeeID.log"
$date = Get-Date
$addn = (Get-ADDomain).DistinguishedName
$dnsroot = (Get-ADDomain).DNSRoot
$i = 1
function Start-Commands {
Update-Users
}
function Update-Users {
"Processing started (on " + $date + "): " | Out-File $log -Append
"---------------------------------------" | Out-File $log -Append
Import-CSV $newpath | ForEach-Object {
$UserEmail = $_.EMAILID
$EmployeeID = $_.EMPLID
$LastName = $_.LAST_NAME
$FIRSTNAME = $_.FIRST_NAME
$UserEmail
} -Properties EmployeeID
$CheckUser = $user.employeeid
if ($CheckUser) {
Write-Host "$UserEmail $EmployeeID UPDATED"
"$UserEmail $EmployeeID UPDATED `r" | Out-File $log -Append
} else {
Get-ADUser -Identity $User | Set-ADUser -EmployeeID $EmployeeID
Get-ADUser -Identity $User | Set-ADUser -Replace #{info=$EmployeeID}
}
$i++
}
"--------------------------------------------" + "`r`n" | Out-File $log -Append
}
Write-Host "STARTED SCRIPT`r`n"
Start-Commands
Write-Host "STOPPED SCRIPT"

Related

Need to update attributes for AD target users such as ObjectSid, msExchMasterAccountSid from a CSV file

I am currently testing the following scenario and looking to automate it defining and validating parameters.
I have put together the following cmdlets to get the script to work calling line-by-line, but what I ultimately like is for this to look at a list of users in a CSV file. From this file, I would like to use two columns with the UserPrincipalName headers, such as:
SourceUser | TargetUser
The idea would be to run a script and replace the following:
#create variables
$sourceUser = "TestUser1#old.domain.com"
$targetUser = "TestUser1#new.domain.com"
$sourceusername,$sourcedomain = $sourceUser -split ("#")
$targetusername,$targetdomain = $targetUser -split ("#")
$SourceAccount = Get-ADUser $sourceusername -server $sourcedomain -Properties objectSid
$TargetAccount = Get-ADUser $targetusername -Server $targetdomain 
#get the objectSid of the source account
$objectSid = $SourceAccount.objectSid
#copy source account objectSid to target account msExchMasterAccountSid
$TargetAccount | Set-ADUser -Replace #{"msExchMasterAccountSid"=$objectSid}
#enable target account
$TargetAccount | Enable-ADAccount
#disable the source account
$SourceAccount | Disable-ADAccount
#move the migrated user into prod OU
$TargetAccount | Move-ADObject -TargetPath "OU=Test,OU=Users,DC=new,DC=domain,DC=com"
I found already a couple of parameters that I believe would help to achieve two things such as the target domain and target OU:
[CmdletBinding()]
Param(
#target domain
[parameter(Mandatory,Position=1)]
[ValidateScript({Get-ADDomain -Identity $_})]
[String]$Domain,
#target OU
[parameter(Position=2)]
[ValidateScript({Get-ADOrganizationalUnit -Identity $_})]
[String]$TargetOu
)
Is there anyone able to help me put all this script together, please? 🙂
Thanks
A draft of a script I develop after sometime:
Clear-Host
#parameters
Import-Module ActiveDirectory
#Start region >>> fake reading in a csv file
$SourceDestinationUsers = #'
SourceUser, DestinationUser
test#source.com, test#destination.com
'# | ConvertFrom-Csv
#endregion >>> fake reading in a CSV file
function Invoke-UserMove
{
[CmdletBinding()]
param()
ForEach ($User in $SourceDestinationUsers)
{
Write-Host 'Processing...'
Write-Host (' SourceUser {0}' -f $User.SourceUser)
Write-Host (' DestinationUser {0}' -f $User.DestinationUser)
Write-Host '__ Source Account __'
$GADU_Params_1 = [ordered]#{
Identity = $User.SourceUser.split('#')[0]
Server = $User.SourceUser.split('#')[1]
Properties = 'objectSid', 'SamAccountName'
}
$GADU_Params_1
$SourceAccount = Get-ADUser #GADU_Params_1
Write-Host '__ Target Account __'
$GADU_Params_2 = [ordered]#{
Identity = $User.DestinationUser.Split('#')[0]
Server = $User.DestinationUser.Split('#')[1]
}
$GADU_Params_2
$TargetAccount = Get-ADUser #GADU_Params_2
Write-Host 'Making changes...'
try
{
$TargetAccount | Set-AdUser -Replace #{'SamAccountName' = $SourceAccount.SamAccountName }
}
catch
{
Write-Host "Accounts have been processed succesfully..."
}
try
{
$TargetAccount | Enable-ADAccount
}
catch
{
Write-Host "Accounts have been processed succesfully..."
}
try
{
$SourceAccount | Disable-ADAccount
}
catch
{
Write-Host "Accounts have been processed succesfully..."
}
try
{
$TargetAccount | Move-ADObject -TargetPath "OU=Test,OU=Users,DC=new,DC=domain,DC=com"
}
catch
{
Write-Host "Accounts have been processed succesfully..."
}
}
Write-Host "Completed"
}
Invoke-UserMove
It worked for me and I did achieve what I needed.
OK, let's say your CSV file contains something like
SourceUser, TargetUser
TestUser1#old.domain.com,Testuser1#new.domain.com
obviously, in reality your csv file would consist of more than one source and target pair.
Now starting with the code you provided, put that in brackets under a foreach loop, and feed the csv data one record at a time through the pipeline. Something like this
Import-csv MyCsvFile.csv |
foreach {
#create variables
$sourceUser = $_.SourceUser
$targetUser = $_.TargetUser
$sourceusername,$sourcedomain = $sourceUser -split ("#")
$targetusername,$targetdomain = $targetUser -split ("#")
$SourceAccount = Get-ADUser $sourceusername -server $sourcedomain -Properties objectSid
$TargetAccount = Get-ADUser $targetusername -Server $targetdomain
#get the objectSid of the source account
$objectSid = $SourceAccount.objectSid
#copy source account objectSid to target account msExchMasterAccountSid
$TargetAccount | Set-ADUser -Replace #{"msExchMasterAccountSid"=$objectSid}
#enable target account
$TargetAccount | Enable-ADAccount
#disable the source account
$SourceAccount | Disable-ADAccount
#move the migrated user into prod OU
$TargetAccount | Move-ADObject -TargetPath "OU=Test,OU=Users,DC=new,DC=domain,DC=com"
}
I didn't fix up the indenting for you, but you get the idea.

powershell how to find out if a variable is part of another variable

I have pasted my code below and pulled out everything that is already working, so I only have the part that isn't working as intended.
I am trying to put the EmployeeID, from a csv, in front of the Description field in AD. I can get that part to work, but the beginning of the If statement where I try to check if the $ID is already in the description fails; it just keeps adding it every time the script runs.
I have tried making both the $ID and $Description type as string with Out-String, and I have left that out, but it's the same result. I have tried -notcontains, -notmatch, and -notlike (which I believe is the correct one to use), but none work. I have even put my variables in a text file to make sure they are pulling the correct information.
I am still learning all of the intricacies of Powershell. Can anyone see what I'm doing wrong?
# Get script Start Time (used to measure run time)
$startDTM = (Get-Date)
#Null out variables
$Users = $Null
$ID = $Null
$Users = Import-Csv .\ImportADUsers\Test-Import-user-data.csv
Import-Module ActiveDirectory
$path = Split-Path -parent ".\ImportADUsers\*.*"
#Create log date
$logdate = Get-Date -Format yyyy-MM-dd-THH.mm.ss
$logfile = $path + "\logs\$logdate.logfile.txt"
# Enumerate the users, one line at a time.
# This assumes the first line is a header line defining the fields.
ForEach ($User In $Users)
{
# Retrieve values from the csv.
$ID = $User.HRRef
# Retrieve the sAMAccountName of the user from AD.
$UserDN = (Get-ADUser -LDAPFilter "(employeeID=$ID)").sAMAccountName
$ID | Out-File $logfile -Append
$IDString = $ID | Out-String
#Retrieve the Description of the user from AD.
$Description = Get-ADUser -Identity $UserDN -Properties description
$Description = $Description.description | Out-String
$Description | Out-File $logfile -Append
# Make sure there is only one user with this employeeID.
If ($UserDN.Count -eq 1)
{
IF ($Description -notlike $IDString) {Set-ADUser -Identity $UserDN
-Description "$($ID) - $($Description)" }
}
Else {"User with ID $ID either not found, or more than one user found."
| Out-File $logfile -Append}
#Log error for users that are not in Active Directory or EmployeeID
#found more than once
}
#Finish
#The lines below calculates how long it takes to run this script
# Get End Time
$endDTM = (Get-Date)
# Echo Time elapsed
"Elapsed Time: $(($endDTM-$startDTM).totalminutes) minutes"
#Append the minutes value to the text file
"Import took $(($endDTM-$startDTM).totalminutes) minutes to complete." |
Out-File $logfile -Append
#SCRIPT ENDS
Your string comparison is incorrect. Below is how to fix it.
The change: -notlike $IDString => -notlike "*$ID*"
ForEach ($User In $Users)
{
# Retrieve values from the csv.
$ID = $User.HRRef
$ID | Out-File $logfile -Append
# Retrieve the SAMAccountName of the user from AD.
$UserDN = (Get-ADUser -LDAPFilter "(employeeID=$ID)").SAMAccountName
#Retrieve the Description of the user from AD.
$Description = (Get-ADUser -Identity $UserDN -Properties description).Description
$Description | Out-File $logfile -Append
# Make sure there is only one user with this employeeID.
If ($UserDN.Count -eq 1 -and $Description -notlike "*$IDString*")
{
Set-ADUser -Identity $UserDN -Description "$ID - $Description"
}
Else
{
"User with ID $ID either not found, or more than one user found." | Out-File $logfile -Append
}
}

AD samAccountname scripting error

I'm trying to input a csv file, which contains Givename & Lastname information of the users. When I run the command listed below samAccountName does not give me the expected output.
Please see below and let me know what should be corrected.
Input:
GivenName,LastName,Password,TargetOU,Description,Manager
Jeffrey,Terry,Pass12,"OU=Users,DC=mmc,DC=local",mmc user,knadella
A,King,Pass13,"OU=Users,DC=mmc,DC=local",mmc user,knadella
Chris ,Charles,Pass14,"OU=Users,DC=mmc,DC=local",mmc user,knadella
Command:
$samAccountName = ($csvcontent.GivenName.Substring(0,1))+( $csvcontent.LastName)
Current Output:
J A C Terry King Charles
Desired Output:
ATerry, AKing and CCharles
Please assist.Thank you!!
You are aggregating all of the details in one go, combining the results of the GivenNamecolumn (J A C) with the results of the LastName column (Terry King Charles)`
This loops over each user:
foreach($user in $csvcontent){
[array]$samAccountName += $user.GivenName[0] + $user.LastName
}
Output:
JTerry AKing CCharles
Going to give you what I use about 30 times a day. The way you are looking to create it will break some login options.
# <FirstLetterGivingName><LastName> for example
# WGates (William Gates)
$sam = $_.GivenName.substring(0,1)+$_.Lastname
That is a good thing, but as the company grows, you will start to run into an issue where the usernames are the same. This is not a perfect solution, but having GivenName (0,3) will give you the first three letters. This will usually fix this issue. It is really rare if you hit someone with the same first three letters and lastname, but it could happen.
$sam = $_.GivenName.substring(0,3)+$_.Lastname
I have also seen companies do this, but advise not to do this, because it would be hard for the user to remember the login.
$sam = $_.GivenName.substring(0,1)+$_.Lastname.substring(0,7)
This script has been used thousands of times, but has been edited a little for this post.
#Test to make sure your output looks correct
#You can do this by running the following:
#Import-csv ".\import_create_ad_users.csv" | Out-GridView
# ERROR REPORTING ALL
Set-StrictMode -Version latest
Import-Module ActiveDirectory
#----------------------------------------------------------
#STATIC VARIABLES
#----------------------------------------------------------
$path = Split-Path -parent $MyInvocation.MyCommand.Definition
$newpath = $path + ".\import_create_ad_users.csv"
$log = $path + ".\create_ad_users.log"
$date = Get-Date
$i = 1
#$addn = (Get-ADDomain).DistinguishedName
#$dnsroot = (Get-ADDomain).DNSRoot
$DNdom = Get-ChildItem -Path Ad:\ | where {$_.Name -eq "Configuration"}
$addn = ($DNdom.DistinguishedName -split "," ,2)[1]
$wmiDomain = Get-WmiObject Win32_NTDomain -Filter "DnsForestName = '$( (Get-WmiObject Win32_ComputerSystem).Domain)'"
$dnsroot = $wmiDomain.DomainName + ".local"
#----------------------------------------------------------
#START FUNCTIONS
#----------------------------------------------------------
Function Start-Commands
{
Create-Users
}
Function Create-Users
{
"Processing started (on " + $date + "): " | Out-File $log -append
"--------------------------------------------" | Out-File $log -append
Import-CSV $newpath | ForEach-Object {
If (($_.GivenName -eq "") -Or ($_.LastName -eq ""))
{
Write-Host "[ERROR]`t Please provide valid GivenName and LastName. Processing skipped for line $($i)`r`n"
"[ERROR]`t Please provide valid GivenName and LastName. Processing skipped for line $($i)`r`n" | Out-File $log -append
}
Else
{
# Replace dots / points (.) in names, because AD will error when a
# name ends with a dot (and it looks cleaner as well)
$replace = $_.Lastname.Replace(".","")
If($replace.length -lt 4)
{
$lastname = $replace
}
Else
{
$lastname = $replace.substring(0,4)
}
# Create sAMAccountName according to this 'naming convention':
# <FirstLetterInitialGivingName><LastName> for example
# WGates (William Gates)
$sam = $_.GivenName.substring(0,1)+$_.Lastname
Try { $exists = Get-ADUser -LDAPFilter "(sAMAccountName=$sam)" }
Catch { }
If(!$exists)
{
# Set all variables according to the table names in the Excel
# sheet /import CSV. The names can differ in every project, but
# if the names change, make sure to change it below as well.
$setpass = ConvertTo-SecureString -AsPlainText $_.Password -force
Try
{
Write-Host "[INFORMATION]`t User is now being built : $($sam)"
"[INFORMATION]`t User is now being built : $($sam)" | Out-File $log -append
New-ADUser $sam -path $_.TargetOU -GivenName $_.GivenName -Initials $_.Initials `
-Surname $_.LastName -UserPrincipalName ($sam + "#" + $dnsroot) -DisplayName ($_.GivenName + " " + $_.LastName) `
-Description $_.Description -Manager $_.Manager -AccountPassword $setpass -Enabled $TRUE -ChangePasswordAtLogon $TRUE
Write-Host "[INFORMATION]`t Created a new user named : $($sam)"
"[INFORMATION]`t Created new user named: $($sam)" | Out-File $log -append
$dn = (Get-ADUser $sam).DistinguishedName
# Rename the object to a good looking name
$newdn = (Get-ADUser $sam).DistinguishedName
Rename-ADObject -Identity $newdn -NewName ($_.GivenName + " " + $_.LastName)
Write-Host "[INFORMATION]`t Renamed the user $($sam) to $($_.GivenName) $($_.LastName)`r`n"
"[INFORMATION]`t Renamed the user $($sam) to $($_.GivenName) $($_.LastName)`r`n" | Out-File $log -append
}
Catch
{
Write-Host "[ERROR]`t Oops, something went wrong: $($_.Exception.Message)`r`n"
}
}
Else
{
Write-Host "[SKIP]`t User $($sam) ($($_.GivenName) $($_.LastName)) already exists or returned an error!`r`n"
"[SKIP]`t User $($sam) ($($_.GivenName) $($_.LastName)) already exists or returned an error!" | Out-File $log -append
}
}
Else
{
Write-Host "[SKIP]`t User ($($_.GivenName) $($_.LastName)) will be skipped for processing!`r`n"
"[SKIP]`t User ($($_.GivenName) $($_.LastName)) will be skipped for processing!" | Out-File $log -append
}
$i++
}
"Processing ended (on " + $date + "): " | Out-File $log -append
"--------------------------------------------" + "`r`n" | Out-File $log -append
}
Write-Host "***************************SCRIPT HAS STARTED***************************"
Write-Host "***************************SCRIPT HAS STARTED***************************"
Write-Host "***************************SCRIPT HAS STARTED***************************`r`n"
Start-Commands
Write-Host "***************************SCRIPT HAS FINISHED***************************"
Write-Host "***************************SCRIPT HAS FINISHED***************************"
Write-Host "***************************SCRIPT HAS FINISHED***************************"
Your CSV would have the following headers:
GivenName LastName LoginName Description Password Manager TargetOU
You need to itterate each item and not the whole array at once:
$samAccountName = $csvcontent | % {
($_.GivenName.Substring(0,1))+($_.LastName)
}

Powershell to CSV compare

Hello I'm trying to use some code I found on here to compare a CSV with Active Directory. I have a csv file with a list of users. I want to check this file and see if anything in the "Email" column from the spreadsheet matches an email address from AD. If it does, I want to list that email address and include the Canonical Name from AD so I can easily see what OU the user account is in.
This is what I'm working with:
$path = "C:\Scripts\Import.csv"
$outpath = "C:\Scripts\Export.csv"
$csv = Import-Csv $path
Import-Module ActiveDirectory
foreach ($line in $csv)
{
$User = Get-ADUser -LDAPFilter "(&(objectclass=user)(mail=$($line.Email)))" -Properties CanonicalName
If ($User -ne $Null) {"User does exist in OU" + $line.Email + $User.CanonicalName}
Else {"User not found in AD OU - " + $line.Email}
}
I've been able to modify this to suit my needs but I'm having some trouble piping the results out to a CSV file. Running the script as it's shown above outputs what I want to the screen but I'd like to have it in a CSV format. If I do something like:
$Results = foreach ($line in $csv)
and then use
$Results | export-CSV $outpath -NotypeInformation
I get the csv created but it just includes a the string value, header for Length and then a numeric value for each line. I can use Out-File to send the results to a txt file, which includes the same results that were displayed on the screen, but I'd really like this to be a csv, not txt file. I believe I need to reference the properties of the csv file and AD in order to build these into my export file but I'm having trouble doing that as I'm not sure how to build in the status of whether the user was found or not.
Any assistance would be appreciated.
UPDATE - Final code
This is the final code I went with. This compares the users in the csv with AD users in the parent and child domain. It uses the email address field to match the users. It grabs the Canonical Name so I can see the OU the user is in and if the user is not found, it reports that in the Canonical Name field.
$path = "$env:userprofile\Desktop\InFile.csv"
$outpath = "$env:userprofile\Desktop\OutFile.csv"
# Importing CSV file
Import-Module ActiveDirectory
$Users = Import-Csv $path |
ForEach-Object {
Write-Progress -Activity "Comparing Imported File to Active Directory" -Status "Processing $($_.Email)"
# Comparing CSV file to Domain.com users
If (
$Value1 = (Get-ADUser -LDAPFilter "(&(objectclass=user)(mail=$($_.Email)))" -server Domain.com -Properties CanonicalName).CanonicalName)
{$_ | Add-Member -MemberType NoteProperty -Name CanonicalName -Value $value1 -PassThru}
# Comparing CSV file to child.Domain.com users
ElseIF (
$Value2 = (Get-ADUser -LDAPFilter "(&(objectclass=user)(mail=$($_.Email)))" -server child.Domain.com-Properties CanonicalName).CanonicalName)
{$_ | Add-Member -MemberType NoteProperty -Name CanonicalName -Value $value2 -PassThru}
# Writing output for users not found in either domain
Else {$_ | Add-Member -MemberType NoteProperty -Name CanonicalName -Value "Email Address not found in Active Directory" -PassThru}
#Exporting to CSV file
New-Object -TypeName pscustomobject -Property #{
Email = $_.Email
CanonicalName = $_.CanonicalName
LastName = $_."Last Name"
FirstName = $_."First Name"
}
} | Select-Object LastName, Firstname, Email, CanonicalName | Sort-Object CanonicalName | Export-CSV $outpath -NoTypeInformation
I'm not sure what resulting CSV should look like, so this code just adds CannonicalName using Calculated Properties to Import.Csv and saves it as Export.Csv.
$path = "C:\Scripts\Import.csv"
$outpath = "C:\Scripts\Export.csv"
Import-Module ActiveDirectory
Import-Csv -Path $path |
Select-Object -Property *, #{
n = 'CanonicalName'
e = {(Get-ADUser -LDAPFilter "(&(objectclass=user)(mail=$($_.Email)))" -Properties CanonicalName).CanonicalName}
} | Export-Csv -Path $outpath -NoTypeInformation
Update
This version will create a new CSV file with 3 columns: UserExistInOu, Email and CanonicalName if any:
Import-Csv -Path $path | ForEach-Object {
$UserExistInOu = $false
if($CanonicalName = (Get-ADUser -LDAPFilter "(&(objectclass=user)(mail=$($_.Email)))" -Properties CanonicalName).CanonicalName)
{
$UserExistInOu = $true
}
New-Object -TypeName pscustomobject -Property #{
UserExistInOu = $UserExistInOu
Email = $_.Email
CanonicalName = $CanonicalName
}
} | Export-Csv -Path $outpath -NoTypeInformation

I'm trying to get these two variables out of a function and into a txt or csv file

I have an interesting problem. I've created a function to get all the users in an OU and check the last time they logged into to any one of the domain controllers. Now my problem is, i get the correct output in the powershell window, I just cant seem to get the output into a txt or csv file. Any help would be much appreicated
$currentdate = Get-Date
$currentdate | Out-File report.txt -Append
function Get-ADUserLastLogon([string]$userName,$array,$datetime)
{
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
Write-Host $username "last logged on at:" $dt "`n"
}
$users = get-aduser -filter * -SearchBase "ou=Example,dc=Adatum,dc=co,DC=nz" -pr DistinguishedName
$i = 0
write-host "Getting all users in $ou"
while($true)
{
$i++
Get-ADUserLastLogon -UserName $users[$i] -array $i
if($users[$i] -eq $null)
{
break
}
$usrindex = $i
$dt | out-file report.txt -Append
$username | out-file report.txt -Append
}
In your function, create an array of objects, where each object is of type System.Object and has properties of type NoteProperty and named after UserName, LastLogon etc. you can then export that array to a CSV easily. See my answer to the following question for an example : How to use two echo commands to print data on one line using PowerShell