Check if a value exists in one csv file and add static text to another csv field with Powershell - powershell

I need to check if the column value from the first csv file, exists in any of the three other csv files and return a value into a new csv file, in order of precedence.So if the username field from allStaff.csv exists in the list of usernames in the sessionVPNct.csv file, put the static text into the final csv file as 'VPN'. If it does not exist, check the next csv file: sessionCRXct.csv then put the static text 'CRX', if not check the last csv file: sessionTMSct.csv then put the static text: TM if not the put the static text 'none' into the final csv file.
I have four csv files as below:
1. allStaff.csv
2. VPN.csv
3. CRX.csv
4. TMS.csv
I have imported the csv files into variables as below:
$allUsers = Import-Csv -Path "C:\allStaff.csv"
$vpn = Import-Csv -Path "C:\VPN.csv" | Select-Object -ExpandProperty UserName
$crx = Import-Csv -Path "C:\CRX.csv" | Select-Object -ExpandProperty UserName
$tms = Import-Csv -Path "C:\TMS.csv" | Select-Object -ExpandProperty UserName
The $allUsers variable displays the following:
Firstname LastName Username Position Area
--------- -------- -------- -------- ----
Joe Bloggs jbloggs Gardener Maintenance
Jim Smith jsmith Accountant Finance
Bob Seger bseger HR Advisor Human Resources
Adam Boson aboson Customer Support IT
Adele bree abree Payroll Finance
The $vpn variable displays the following:
Username
--------
jbloggs
jsmith
The $crx variable displays the following:
Username
--------
jbloggs
jsmith
bseger
The $tms variable displays the following:
Username
--------
jbloggs
jsmith
bseger
aboson
Then I have the following line to start the result csv file
$result = $allUsers | Select-Object *,ConnectionMethod
Not quite sure how to do the final query, which I believe should be an if else loop to go through all rows in the $result variable, and check the other csv if the username field exists, then return the static text.
$result | Export-Csv -Path "C:\allStaffConnections.csv"
This is how I need the final allStaffConnections.csv file to be displayed.
Firstname LastName Username Position Area ConnectionMethod
--------- -------- -------- -------- ---- --------------
Joe Bloggs jbloggs Gardener Maintenance VPN
Jim Smith jsmith Accountant Finance VPN
Bob Seger bseger HR Advisor Human Resources CRX
Adam Boson aboson Customer Support IT TMS
Adele bree abree Payroll Finance none
Am I on the right track with the below code?
$allUsers = Import-Csv -Path "C:\allStaff.csv"
$vpn = Import-Csv -Path "C:\VPN.csv" | Select-Object -ExpandProperty UserName
$crx = Import-Csv -Path "C:\CRX.csv" | Select-Object -ExpandProperty UserName
$tms = Import-Csv -Path "C:\TMS.csv" | Select-Object -ExpandProperty UserName
$vpnText = 'VPN'
$crxText = 'CRX'
$txsText = 'TMS'
$noneText = 'none'
$allUsersExtended = $allUsers | Select-Object *,ConnectionMethod
$results = $allUsersExtended.ForEach(
{
if($vpn -Contains $PSItem.UserName) {
# add $vpnText to ConnectionMethod column for that row in the $result
$PSItem.ConnectionMethod = $vpnText
}elseif($crx -Contains $PSItem.UserName) {
# add $crxText to ConnectionMethod column for that row in the $result
$PSItem.ConnectionMethod = $crxText
}elseif($tms -Contains $PSItem.UserName) {
# add $txsText to ConnectionMethod column for that row in the $result
$PSItem.ConnectionMethod = $tmsText
}else {
# add $noneText to ConnectionMethod column for that row in the $result
$PSItem.ConnectionMethod = $noteText
}
})
$results | Export-Csv -Path "C:\allStaffConnections.csv" -NoTypeInformation
This gives me an empty allStaffConnections.csv file.
I have run the code line by line and can get as far as:
$allUsersExtended = $allUsers | Select-Object *,ConnectionMethod
Which gives me the extra column "ConnectionMethod", but after running the loop, it gives me an empty allStaffConnections.csv file.

here is one way to do the job. [grin] it presumes that you only want to 1st connection type found. if you want all of them [for instance, JBloggs has all 3 types listed], you will need to concatenate them.
what it does ...
fakes reading in the CSV files
when ready to use real data, comment out or remove the entire #region/#endregion section and use Get-Content.
iterates thru the main collection
uses a switch to test for membership in each connection type list
this breaks out of the switch when it finds a match since it presumes you only want the 1st match. if you want all of them, then you will need to accumulate them instead of breaking out of the switch block.
sets the $ConnectionType as appropriate
builds a PSCO with all the wanted props
this likely could be shortened by using Select-Object, a wildcard property selector, and a calculated property.
sends it out to the $Results collection
shows it on screen
saves it to a CSV file
the code ...
#region >>> fake reading in CSV files
# in real life, use Import-CSV
$AllUsers = #'
FirstName, LastName, UserName, Position, Area
Joe, Bloggs, jbloggs, Gardener, Maintenance
Jim, Smith, jsmith, Accountant, Finance
Bob, Seger, bseger, HR Advisor, Human Resources
Adam, Boson, aboson, Customer Support, IT
Adele, bree, abree, Payroll, Finance
'# | ConvertFrom-Csv
$Vpn = #'
UserName
jbloggs
jsmith
'# | ConvertFrom-Csv
$Crx = #'
UserName
jbloggs
jsmith
bseger
'# | ConvertFrom-Csv
$Tms = #'
UserName
jbloggs
jsmith
bseger
aboson
'# | ConvertFrom-Csv
#endregion >>> fake reading in CSV files
$Results = foreach ($AU_Item in $AllUsers)
{
# this presumes you want only the 1st connection type found
# if you want all of them, then you will need to concatenate them
switch ($AU_Item.UserName)
{
{$_ -in $Vpn.UserName}
{
$ConnectionType = 'VPN'
break
}
{$_ -in $Crx.UserName}
{
$ConnectionType = 'CRX'
break
}
{$_ -in $Tms.UserName}
{
$ConnectionType = 'TMS'
break
}
default
{
$ConnectionType = 'None'
}
}
[PSCustomObject]#{
FirstName = $AU_Item.FirstName
LastName = $AU_Item.LastName
UserName = $AU_Item.UserName
Position = $AU_Item.Position
Area = $AU_Item.Area
ConnectionTYpe = $ConnectionType
}
}
# on screen
$Results
# send to CSV
$Results |
Export-Csv -LiteralPath "$env:TEMP\brokencrow_-_UserConnectionType.csv" -NoTypeInformation
truncated on screen output ...
FirstName : Joe
LastName : Bloggs
UserName : jbloggs
Position : Gardener
Area : Maintenance
ConnectionTYpe : VPN
[*...snip...*]
FirstName : Adele
LastName : bree
UserName : abree
Position : Payroll
Area : Finance
ConnectionTYpe : None
the CSV file content from brokencrow_-_UserConnectionType.csv ...
"FirstName","LastName","UserName","Position","Area","ConnectionTYpe"
"Joe","Bloggs","jbloggs","Gardener","Maintenance","VPN"
"Jim","Smith","jsmith","Accountant","Finance","VPN"
"Bob","Seger","bseger","HR Advisor","Human Resources","CRX"
"Adam","Boson","aboson","Customer Support","IT","TMS"
"Adele","bree","abree","Payroll","Finance","None"

Related

Powershell: How to import csv find a value in a row, list of the column data and over-write some coumns?

I think it's easiest to show what I have and what I'm trying to get as an output instead of me trying to verbalize what I'm doing and what I've tried.
Input is a csv that contains:
Full Name
Color
Access Type
Access ID
John Smith
Blue
Full_Acccess
jsmith
John Smith
Blue
Partial_Access
John Smith
Red
No_Access
Bill Bob
Red
Full_Access
Bill Bob
Pink
Access_1
Lisa Smith
Green
Access_2
I will call this script each time and look for "Full Name". Then if "Access ID" is empty, I want to populate Access ID (from a variable) and write out the "Output" below to a variable and send that as the body of an email.
So run script, look for "Full Name". In this case, it will look for and finds "Bill Bob" and since Access ID is empty, it will write "Rob Bob" (a variable value) to "Access ID" but also send email body of output below
OutPut:
Full Name: Bill Bob
Color: Red, Pink
Date: 03-21-2021
Access_Type: Full_Access, Access_1
Access ID: Rob Bob
So it's showing a unique Full Name (not all three rows), but also all values of Color and Access Type for "Full Name"
I've got this working using:
Import-CSV |Group-Object -Propert 'Full Name' | Select-Object #{Name = "Full Name"; Expression = {(_.Group.'Full Name' | Select-Object -Unique)}},
#{Name = "Access Type"; Expression = {($_.Group.'Access Type' | Select-Object -Unique) -join ","}},
#{Name = "Color"; Expression = {($_.Group.'Color' | Select-Object -Unique)}},
#{Name = "New Name"; Expression = {($_.Group.'New Name' | Select-Object -Unique)}} |
Where-Object {$_.'Full Name' -eq $PersonName}
Where I'm stuck:
Script will be called and imports CSV and searches for "Bill Bob". Then I want to list all the columns for Bill AND write the new Access_ID value back into the CSV.
Then I call the script again and look for John Smith, and then again for Lisa Smith, etc..
Thanks all!
I think I got what you were going for, here's what I would use:
Function Get-UserAccess ([string]$fullname,[string]$csvpath){
# Export your output
$csv = Import-CSV $csvpath
$filtered = $csv | Where-Object {$_.'Full Name' -eq $fullname}
$output = [pscustomobject]#{
'Full Name' = $fullname
'Access Type'=($filtered.'Access Type' | Select -Unique) -join ','
'Color' =($filtered.'Color' | Select -Unique) -join ','
'Access ID' =($filtered.'Access ID' | Select -Unique) -join ','
}
# check if Access ID is empty on Full_Access users
if (-not $output.'Access ID' -and
($output.'Access Type' -split ',') -contains 'Full_Access') {
# Set your AccessID here
$AccessID = Read-Host "Enter a new access ID for $fullname"
# Update the Access ID in the csv:
$entry = ([Collections.Generic.List[Object]]$csv).findIndex({
$args[0].'Full Name' -eq $fullname -and
$args[0].'Access Type'-eq 'Full_Access'})
$csv[$entry].'Access ID' = $AccessID
Write-Host "Updating $csvpath with new access ID for $fullname"
$csv | Export-Csv $csvpath -Force -NoTypeInformation
# Return output with Access ID
$output.'Access ID' = $AccessID
return $output
} Else { Return $output }
}
And some example usage with the data from your question:
PS C:\> Get-UserAccess 'Bill Bob' 'C:\temp\temp.csv' | fl
Enter a new access ID for Bill Bob: Rob Bob
Updating C:\temp\temp.csv with new access ID for Bill Bob
Full Name : Bill Bob
Access Type : Full_Access,Access_1
Color : Red,Pink
Access ID : Rob Bob
PS C:\> Get-UserAccess 'John Smith' 'C:\temp\temp.csv' | fl
Full Name : John Smith
Access Type : Full_Access,Partial_Access,No_Access
Color : Blue,Red
Access ID : jsmith,

How to retrieve associated value from second CSV in Foreach

How would I go about obtaining the value of a secondary field in a CSV, while foreaching results in another CSV?
Ex: List of computers, then search for serial in list, then get response.
CSV1 has list of computer names, serials ,and vendor IDs.
CSV2 has list of vendor IDS, and responsible party
Sample code:
$systems = import-csv -path "K:\systems.csv"
$vendors = import-csv -path "K:\vendors.csv"
$vendortable= #{}
$vendors | ForEach-Object {
$vendortable[$_.vendorid] = New-Object -Type PSCustomObject -Property #{
'ID' = $_.vendorid
'Managed' = $_.Managed
}
}
Foreach ($computer in $systems){
$host = $system.Host
if ($vendortable.key -match $system.vendorid){
$xvendor = $vendortable.Managed
}
$vendorobj = New-Object PSObject -property #{
'Host'=$System.host
'Mgr=$xvendor
}
$vendorobj |Select-object "Host","Mgr" |Export /////////
This returns an object of All values System.Object[] in the vendor table, not just one.
CSV1
Host,Serial,VendorID
A15,gtjk123,9001
C15,gtjk456,6402
T15,gtjk678,2301
S15,gtjk103,0101
CSV2
VendorID,Managed
9001,Trancom
6402,Stratus
2301,Psycorp
0101,Dell
Let's break this down into steps.
You want to iterate over the entire vendor list.
You want to retrieve the associated host from CSV1 and combine with manager (based on your select statement)
What you can do is make a lookup table of CSV1 using Group-Object and the -AsHashTable parameter specifying the "key" as vendorid.
$csv1 = #'
Host,Serial,VendorID
A15,gtjk123,9001
C15,gtjk456,6402
T15,gtjk678,2301
S15,gtjk103,0101
'# | ConvertFrom-Csv | Group-Object -Property vendorid -AsHashTable
Now you can run through each item of CSV2 and extract the required info.
$csv2 = #'
VendorID,Managed
9001,Trancom
6402,Stratus
2301,Psycorp
0101,Dell
'# | ConvertFrom-Csv
$csv2 | foreach {
[PSCustomObject]#{
Host = $csv1[$_.vendorid].host
Managed = $_.managed
}
}
Host Managed
---- -------
A15 Trancom
C15 Stratus
T15 Psycorp
S15 Dell
If you wanted to get all the properties
$csv2 | foreach {
[PSCustomObject]#{
Host = $csv1[$_.vendorid].host
Managed = $_.managed
VendorID = $_.vendorid
Serial = $csv1[$_.vendorid].serial
}
}
Host Managed VendorID Serial
---- ------- -------- ------
A15 Trancom 9001 gtjk123
C15 Stratus 6402 gtjk456
T15 Psycorp 2301 gtjk678
S15 Dell 0101 gtjk103

Compare multiple elements in an object against multiple elements in another object of a different array

Say [hypothetically], I have two .CSVs I'm comparing to try and see which of my current members are original members... I wrote a nested ForEach-Object comparing every $name and $memberNumber from each object against every other object. It works fine, but is taking way to long, especially since each CSV has 10s of thousands of objects. Is there another way I should approach this?
Original_Members.csv
Name, Member_Number
Alice, 1234
Jim , 4567
Current_Members.csv
Alice, 4599
Jim, 4567
$currentMembers = import-csv $home\Desktop\current_members.csv |
ForEach-Object {
$name = $_.Name
$memNum = $_."Member Number"
$ogMembers = import-csv $home\Desktop\original_members.csv" |
ForEach-Object {
If ($ogMembers.Name -eq $name -and $ogMembers."Member Number" -eq $memNum) {
$ogMember = "Yes"
}
Else {
$ogMember = "No"
}
}
[pscustomobject]#{
"Name"=$name
"Member Number"=$memNum
"Original Member?"=$ogMember
}
} |
select "Name","Member Number","Original Member?" |
Export-CSV "$home\Desktop\OG_Compare_$(get-date -uformat "%d%b%Y").csv" -Append -NoTypeInformation
Assuming both of your files are like the below:
Original_Members.csv
Name, Member_Number
Alice, 1234
Jim, 4567
Current_Members.csv
Name, Member_Number
Alice, 4599
Jim, 4567
You could store the original member names in a System.Collections.Generic.HashSet<T> for constant time lookups, instead of doing a linear search for each name. We can use System.Linq.Enumerable.ToHashSet to create a hashset of string[] names.
We can then use Where-Object to filter current names by checking if the hashset contains the original name with System.Collections.Generic.HashSet<T>.Contains(T), which is an O(1) method.
$originalMembers = Import-Csv -Path .\Original_Members.csv
$currentMembers = Import-Csv -Path .\Current_Members.csv
$originalMembersLookup = [Linq.Enumerable]::ToHashSet(
[string[]]$originalMembers.Name,
[StringComparer]::CurrentCultureIgnoreCase
)
$currentMembers |
Where-Object {$originalMembersLookup.Contains($_.Name)}
Which will output the current members that were original members:
Name Member_Number
---- -------------
Alice 4599
Jim 4567
Update
As requested in the comments, If we want to check both Name and Member_Number, we can concatenate both strings to use for lookups:
$originalMembers = Import-Csv -Path .\Original_Members.csv
$currentMembers = Import-Csv -Path .\Current_Members.csv
$originalMembersLookup = [Linq.Enumerable]::ToHashSet(
[string[]]($originalMembers |
ForEach-Object {
$_.Name + $_.Member_Number
}),
[StringComparer]::CurrentCultureIgnoreCase
)
$currentMembers |
Where-Object {$originalMembersLookup.Contains($_.Name + $_.Member_Number)}
Which will now only return:
Name Member_Number
---- -------------
Jim 4567

Get-ADUser account name whose email address matches any of the emails indicated in csv file

I have a comma-separated csv file as below (first row is the header):
category;email.1;email.2;email.3;email.4;email.5;email.6
category1;sample#gmail.com;;;;;
category2;;;sample2#gmail.com;;;
category3;;sample3#gmail.com;;sample44#hotmail.com;;sample55#gmail.com
and so on...
Now I import it:
$emails = Import-CSV -Encoding Default -Delimiter ";" "c:\temp\myFile.csv"
And finally, for each row in the csv I want to get the AD user account name whose email address matches any from email.1 to email.6
So I try this using a foreach loop but I have no idea what have to put within it to get what I want.
$emails | foreach {
$userAccountName = Get-ADUser -filter {something}
# do some stuff with $userAccountName
}
Note: I would like a generic solution taking into account that in future can be more than 6 emails by category. Also take into account that some emails can be empty for the category.
Once imported you can enumerate the headers matching a pattern
$emails = Import-CSV -Encoding Default -Delimiter ";" "c:\temp\myFile.csv"
$EmailHeaders = ($Emails[0].psobject.Properties|Where-Object Name -like 'email.*').Name
ATM this returns:
> $EmailHeaders
email.1
email.2
email.3
email.4
email.5
email.6
Nest foreachs's to iterate the emails per row which are populated.
Get-ADUser commented out for testing
foreach($Row in $Emails){
foreach($EmailHeader in $EmailHeaders){
if($Email=$Row.$EmailHeader){
[PSCustomObject]#{
Category = $Row.Category
Email_x = $EmailHeader
Email = $Email
SamAccountName= $Null #(Get-ADUser -Filter {EmailAddress -eq "$Email"} -Properties SamAccountName).SamAccountName
}
}
}
}
Category Email_x Email SamAccountName
-------- ------- ----- --------------
category1 email.1 sample#gmail.com
category2 email.3 sample2#gmail.com
category3 email.2 sample3#gmail.com
category3 email.4 sample44#hotmail.com
category3 email.6 sample55#gmail.com

Combining two commands into one and exporting to csv file in Powershell

Below is the code I'm using get data from the output of two commands, I then put them into two separate array's. When I combine the arrays the output looks how I would expect, but when I do select and try to output, it has gaps and not formatted correct. How get I get this to output nice to a csv file?
Example code:
$a = get-agentserver
$NewCSV = $a | ForEach-Object {
New-Object PSObject -Prop #{
'Client Name' = ($_."Name" -Split '\.(?!\d)')[0]
'Policy Type' = $_."AgentServerType"
'Backup State' = $_."BackupStatus"
'logon' = $_."LogonAccountTestStatus"
'account' = $_."LogonAccount"
}
} | Select "Client Name","Policy Type","Backup State","logon","account"
#$NewCSV
$l = foreach($i in $a.name){
get-definition -agentserver $i}
$l | convertto-csv | out-file t1.csv
$m = import-csv t1.csv | select agentserver,name,selectionsummary
$defcsv = $m | foreach-object{
new-object psobject -prop #{
'Policy Name' = $_.Name
'Backup Selection' = $_.selectionsummary
}
} | select "Policy Name","Backup Selection"
#$defcsv
$hope = $NewCSV + $defcsv
$hope2 = $hope | select "Client Name","Policy Name","Policy Type","Backup Selection"
$hope2
Ex output $hope(that look right to me)
Client Name : Name
Policy Type : Ndmp
Backup State : Unknown
logon : Succeeded
account : ndmp_user
Policy Name : Diff Bakcup
Backup Selection : COMMON, D: (Partial)
Ex output of $hope2(which is killing me how to fix)
Client Name Policy Name Policy Type Backup Selection
----------- ----------- ----------- ----------------
Name Windows
Name Windows
Name Windows
Name Windows
Name Windows
Name Ndmp
Diff Bakcup - Name ,... COMMON, D: (Partial)
ArchiveJob_Backup_to_Tape Name \e$ (Partial), ...
Diff Bakcup - Name ,... COMMON, D: (Partial)
ArchiveJob_Backup_to_Tape Name\e$ (Partial), ...
Diff Bakcup - Name ,... COMMON, D: (Partial)
Name Backup BLR_Pro... /root_vdm/IN-BLR400-FS-C...
I have cleaned up my code and tried to put my command outputs into one variable and iterate through it in one go, which looks much nicer, but the output result in the same as above in my $hope2 output. It is leaving a big gap under two of the header "Policy Name" and "Backup Selection". Is there a way to use regex to remove those particular spaces only under those two columns in Powershell?
This is the new code I am running using
$agentserver = get-agentserver
$agentserver | convertto-csv | select-object -skip 2 | out-file t2.csv
$agentserver = import-csv t2.csv -Header server,id,type,accountstate,logonaccount
$budefinition = foreach($i in $a.name){
get-backupdefinition -agentserver $i}
$budefinition | convertto-csv | out-file t1.csv
$converted_budef = import-csv t1.csv | select agentserver,name,selectionsummary
$a = $agentserver + $converted_budef
$NewCSV = $a | ForEach-Object {
New-Object PSObject -Prop #{
'Client Name' = ($_."server" -Split '\.(?!\d)')[0]
'Policy Type' = $_."type"
'Backup State' = $_."BackupStatus"
'logon' = $_."LogonAccountTestStatus"
'account' = $_."LogonAccount"
'Policy Name' = ($_.Name -replace ","," ")
'Backup Selection' = ($_.selectionsummary -replace ","," ")
}
} | Select "Client Name","Policy Name","Policy Type","Backup Selection"
$NewCSV
Example of what I am trying to accomplish would look like this, that I can then use the export-csv and have a nice csv doc.
Client Name Policy Name Policy Type Backup Selection
----------- ----------- ----------- ----------------
NAME Diff Bakup Windows Common D
NAME Archive Ndmp /root_vdm/
After doing $NewCSV | fl I get a output of two separate list as shown below and I need them to all be in one. Any ideas how to fix it in my code above?
Client Name : Name
Policy Name :
Policy Type : Ndmp
Backup Selection :
Client Name :
Policy Name : Diff Bakcup
Policy Type :
Backup Selection : COMMON D: (Partial)