I have a CSV with the company contacts in.
I want to create a script to read the line of the CSV and chack against the current stored in AD.
When I read the contact from AD and then read the line from the CSV, in the screen they look the same but they are failing the does not equal to function.
My question is:
Is it best the read each part of the contact i.e.:
contact.firstname and compare to compare.firstname
contact.lastname and compare to compare.lastname
contact.emailaddress and compare to compare.emailaddress
using a group of If commands? Or is there an easier way to do it?
You could always start out somewhere, and then look for the perfect way. A slim example to help contributors get started on something that works for you could be:
$csv = import-csv C:\Temp\test.csv -Delimiter ";"
This will give you an object like this
firstname lastname email
--------- -------- -----
john doe john#doe.com
johnny doe johnny#doe.com
jim doe jim#doe.com
This object can now be parsed in several ways. Depending on the string variable you would like to compare, i suggest you use ForEach-Object(or it's %{} abbreviation)
Like so:
$csv | % {
if ($_.Firstname -eq $Contact.Firstname) {Write-host "Match!" -fo green}
else{Write-host "No match :(" -fo red}
}
You will be able to loop through all the objects of the csv file, one by one, and compare their note properties.
Hope it helps you get started.
Related
I have a csv file called newuser.csv that is generated daily and it's written on top of the old csv file, I created a separate csv called newuser2.csv that I could use to update only the new data from the newuser.csv file.
Here is an example
newuser.csv contains
givenName sn description title
Angel test US Empliyee IT Support Contractor
James test US Empliyee IT Support Contractor
John test US Empliyee IT Support Contractor
newuser2.csv contains
givenName sn description title
Angel test US Empliyee IT Support Contractor
newuser.csv over-written every day
newuser2.csv I created and I would like to pick up changes from the newuser.csv and update and enter those changes to newuser2.csv append.
I tried
#(Import-Csv C:\scripts\test\newusers.csv) + #(Import-Csv C:\scripts\test\newusers2.csv) | Export-Csv C:\scripts\test\newusers2.csv -Append
This did not work as it duplicates the data each time I run the script.
I would really applicate any help as this would save me so much time.
I think the gentle point #MathiasR.Jessen is making, is that we think you are using the wrong word :-). There is different ways to combine data, Overwriting is the -Force parameter e.g.
File1
A
B
File2
C
File1 Overwriting File 2
A
B
Append is "adding to the end" e.g.
File1
A
B
File2
C
File1 Appended to File 2
A
B
C
The word I believe you actually mean is Update. e.g. I want to update the list of users from file1 with the changes in file2. There is no "easy" parameter to do this because it's a more complicated process. For example, you have to choose your "unique identifier" as in, what uniquely identifies the user in order for you to update the right row. Typically this is something like usernames. In this case, without usernames, you can't simply use givenName as your only unique identifier because there may be multiple people with the name "John". Instead you have to (at the very least) see if the user matches by givenName and sn. (EDIT: Can use employeeID as unique identifier)
In this case, I read in the users, updated lists, and loop through both to find users who are in both lists and update the user information. I then export the list.
#Master list
$Users = Import-Csv C:\scripts\test\users.csv
#Update list
$NewUsers = Import-Csv C:\scripts\test\newusers.csv
#Loop through each user and make changes
foreach($User in $Users){
#Loop through new users
foreach($NewUser in $NewUsers){
#See if the user matches by employeeID
if($User.employeeID -eq $NewUser.employeeID)){
#Update User information
$User.description = $NewUser.description
$User.title = $NewUser.title
}
}
}
#Export the updated list
$Users | Export-Csv C:\scripts\test\updatedUsers.csv -Force
I have two .csv files, one is a dump from a third party application the other is a dump from Active Directory (using PowerShell).
The third party .csv looks something like this
e-mail address
Name
Title
Dept
john#work.com
John
Engineer
Office
mary#work.com
Mary
Supervisor
Factory
The AD .csv looks something like this
e-mail address
Name
Title
Dept
john#work.com
John
Engineer
Main Office
mary#work.com
Mary
Team Supervisor
Factory
Is there a way (ideally in PowerShell) that the two .csv files can be read and the differences highlighted and exported to third file.
e.g for the row containing john#work.con the Dept value is different, for the row containing mary#work.com the Title is different
The output would look something like this - where "Please update" has been entered into the required cell.
e-mail address
Name
Title
Dept
john#work.com
John
Engineer
Please update
mary#work.com
Mary
Please update
Factory
Regards
Philip
Here is one way you can do it, using Group-Object, using these CSVs as example:
$csv1 = #'
e-mail address,Name,Title,Dept
john#work.com,John,Engineer,Office
mary#work.com,Mary,Supervisor,Factory
guy#work.com,Guy,Supervisor,Factory
'# | ConvertFrom-Csv
$csv2 = #'
e-mail address,Name,Title,Dept
john#work.com,John,Engineer,Main Office
mary#work.com,Mary,Team Supervisor,Factory
otherguy#work.com,Other Guy,Team Supervisor,Factory
'# | ConvertFrom-Csv
Group both objects by their e-mail address property and then if the group count is equal to 1, skip that object since there is nothing to compare, else, compare each property against each other and update the property value of one of the objects (the object with index 0 in this case, which will be the one we return):
$csv1 + $csv2 | Group-Object 'e-mail address' | ForEach-Object {
if($_.Count -eq 1) { return }
# following assumes there will be no more than 2 elements!
# it also assumes both CSVs have the same column names!
foreach($prop in $_.Group[0].PSObject.Properties.Name) {
if($_.Group[0].$prop -ne $_.Group[1].$prop) {
$_.Group[0].$prop = 'Please Update'
}
}
$_.Group[0]
} | Format-Table
The result we can expect from above example, as you can see, guy and otherguy are not taken into consideration for the comparison:
e-mail address Name Title Dept
-------------- ---- ----- ----
john#work.com John Engineer Please Update
mary#work.com Mary Please Update Factory
I'm developing a script that connects to Azure AD and extracts failed login for any user so I'm probably going to get more than a row for user.
I have this code in a foreach (there is anything after part of code):
$ConvertedOutput | Select-Object #{Label="UserId"; Expression={$_.UserId}},
#{Label="CreationTime"; Expression={$_.CreationTime}},
#{Label="UserAgent"; Expression={$FinalOutput[0]."Value"}},
#{Label="Operation"; Expression={$_.Operation}},
#{Label="LogonError"; Expression={$_.LogonError}},
#{Label="ClientIP"; Expression={$_.ClientIP}} | Format-Table
How can I prevent from forming multiple tables? I only wanted the table for the first record, then additional records under the same table.
Thanks
here is the output
# Create an empty array to store your results in
[array]$results = #()
# This is your existing loop
foreach (...) {
...
$ConvertedOutput = <your existing code>
...
# Append your object to the results array
$results += $ConvertedOutput | Select-Object ... <your existing code>
}
# Now your results object contains all of the values from inside your loop
# So let's display that!
Write-Output $results
Welcome to stack overflow. I general, it is recommended to supply sample (fake) data in text format rather then pictures (of just headers), the makes life easier for us to answer your question.
Reading your code part, it doesn't add much value unless you planned to further extend it. All expressions generate the same keys and values as the original object, meaning that you can simplify this to just: Select-Object UserId, CreationTime, UserAgent, Operation, LogonError, LogonError, ClientIP or even: Select-Object * (or just omit the complete Select-Object), if you do not select a column subset.
With regards to your question,
By default PowerShell normally concatenates the output by it self, meaning that there is probably something else (that you are not sharing, e.g. a Write-Host command) that causes the data to be released preliminary from the pipeline.
Let me show this with fictive object lists created on the fly from three separate CSV lists:
$Result = &{
ConvertFrom-Csv #'
Id,FirstName,LastName
1,Nancy,Davolio
2,Andrew,Fuller
3,Janet,Leveling
'#
ConvertFrom-Csv #'
Id,FirstName,LastName
4,Margaret,Peacock
5,Steven,Buchanan
6,Michael,Suyama
'#
ConvertFrom-Csv #'
Id,FirstName,LastName
7,Robert,King
8,Laura,Callahan
9,Anne,Dodsworth
'#
}
With the above command, $Result contains the following data:
PS C:\> $Result
Id FirstName LastName
-- --------- --------
1 Nancy Davolio
2 Andrew Fuller
3 Janet Leveling
4 Margaret Peacock
5 Steven Buchanan
6 Michael Suyama
7 Robert King
8 Laura Callahan
9 Anne Dodsworth
One important thing to mention here, is that the columns of the three list should be have the same columns (or at least the first row should contain all expected columns), see: Not all properties displayed
If this doesn't help you further, I recommend you to add you more details to your question.
I'm trying to use PowerShell to import two CSV files, one CSV is called accts and one is called names. The accts file has 8 numbers and the names files has 4 names. I need to output a combination where the end goal is to output a list of 32 words with the prefix of STX. I'll take this list of 32 and create 32 AD objects. Ideally I need to also run a check to see if the word/AD object already exists.
accts: 12345 23456 34567 45678 56789 67890 78901 89012
names: john dave joe mike
Output should be:
stx-12345-john
stx-23456-john
stx-34567-john
stx-45678-john
stx-56789-john
stx-67890-john
stx-78901-john
stx-89012-john
stx-12345-dave
stx-23456-dave
stx-34567-dave
stx-45678-dave
stx-56789-dave
stx-67890-dave
stx-78901-dave
stx-89012-dave
etc.
I've thought about trying to use arrays, but perhaps that would be to complex. Any advice?
Hmm i feel like you shouldn't be working on an AD with Powershell if this is to far stretched for you at this time. Yes you would use arrays and just loop through them.
So i've exploded my code as much as i can so you can learn the logic behind it.
$accts = #("12345","23456","34567","45678","56789","67890","78901","89012")
$names = #("john","dave","joe","mike")
$adobject = #()
foreach($n in $names){
# for each name, go through the accts and add the prefix
foreach($a in $accts){
#store all items in an array
$adobject += "stx-"+$a+"-"+$n
}
}
cls
# go through your array you just created
foreach($o in $adobject){
if(get-aduser $o){
Write-host "user $o already exists"
}else{
new-aduser $o #create user
}
}
Little tasks like this are the best way to learn coding and logic. You should be able to translate this to using CSV's.
I'm learning powershell right now.
I need to import a CSV like this:
lastname,firstname
lastname,firstname
lastname,firstname
etc
Then create a list of usernames no longer then 8 characters and check for collisions.
I have found bits and pieces of scripting around but not sure how to tie it all together.
I use Import-Csv to import my file.csv:
$variablename = import-csv C:\path\to\file.csv
but then I am not sure if I just import it into an array or not. I am not familiar with how for loops work in powershell exactly.
Any direction? Thanks.
There are a couple of concepts that are central to understanding PowerShell. Firstly, remember that you are always working with objects. So after importing your CSV file, your $variablename will refer to a collection of sub-objects.
Secondly, you can use the PowerShell pipeline to send the output of one cmdlet to the input of another. Some cmdlets will understand if you send them a collection, and automatically process each row.
If think what you're looking for though is the foreach-object cmdlet, which will allow you to run code against each item in the collection. Code inside the foreach-object block can refer to the $_ automatic variable which will contain the current object.
Assuming your CSV file is well formatted and has a header row with the column names, you can refer to each column by name e.g. $_.lastname & $_.firstname.
To put it all together:
import-csv C:\path\to\file.csv |
foreach-object {
write-host "Processing: $($_.lastname), $($_.firstname)"
# logic here to calculate username and create AD account
}
PowerShell can have a bit of a learning curve if you are coming from a different scripting environment. Here are a couple of resources that I've found helpful:
PowerShell 'gotchas' http://www.rlmueller.net/PSGotchas.htm
Keith Hill's Effective PowerShell: https://rkeithhill.wordpress.com/2009/03/08/effective-windows-powershell-the-free-ebook/
Also, check out the Technet Script Center, where there are many hundreds of Active Directory scripts. https://technet.microsoft.com/en-us/scriptcenter/bb410849.aspx
The script below should help you grasp a few concepts on how to work with csvs and manipulate data using PowerShell.
# the code below uses a 'here string' to mimic the import of a csv.
$users = #'
smith,b
smith,bob
smith,bobby
smith,sonny
smithson,john
smithson,jane
smithers,rob
'# -split "`r*`n"
$users |
ConvertFrom-Csv -Header 'surname','firstname' |
Select-Object #{Name='username'; Expression={"$($_.surname)$($_.firstname) "}}, surname, firstname |
Group-Object { $_.username.Substring(0,8).Trim() } |
Select-Object #{Name='username'; Expression={$_.Name}}, Count |
Format-Table -AutoSize
The $users | line takes the list of $users and pipes into the next command.
The ConvertFrom-Csv -Header... line converts the string into a csv.
The Select-Object #{Name... line creates an expression alias, which concatenates surname+forename. You'll notice the extra 8 spaces we append to the end of the string so we know we will have at least 8 characters in the string.
The Group-Object {... line groups the username, using the first 8 characters, if available. The .Trim() gets rid of any trailing spaces.
The Select-Object #{Name='username'... line takes the Name field from the group-object and renames to username and also shows the count from the grouping operation.
The Format-Table -AutoSize line is purely for output formatting to the console and gives you an output like the one below.
username Count
-------- -----
smithb 1
smithbob 2
smithson 3
smithers 1
An amended version of the above code, which you can use on your real csv. Change the surname, firstname column names to suit your csv.
# you would use the code below, to import your list of names
# uncomment the `# -Header surname,firstname` bit if your csv has no headers
$users = Import-Csv -Path 'c:\path\to\names.csv' # -Header surname,firstname
$users |
Select-Object #{Name='username'; Expression={"$($_.surname)$($_.firstname) "}}, surname, firstname |
Group-Object { $_.username.Substring(0,8).Trim() } |
Select-Object #{Name='username'; Expression={$_.Name}}, Count