Prevent Powershell ForEach from Adding Superfluous Quotes - powershell

I have a colon-delimited CSV like so,
Mailbox:Users
AcmeCorp:("jsmith","trex")
XyzCorp:("sjobs","bfranklin")
It is then added to a variable:
$file = import-csv file.csv -delimiter :
Now, I work with it:
foreach ($record in $file) {
$record.Users | % { get-aduser $_ }
}
write-host in the foreach loop reports $record.Users is ("jsmith","trex")
However, get-aduser (or other cmdlets) complains that it cannot find an object with identity '("jsmith","trex")'.
How do you prevent foreach from adding single quotes to parameter?
I've already tried Users like "sjobs","bfranklin" but import-csv strips the double quotes off of sjobs
Or better, how do you pass a list of users to foreach?
PowerShell version 4

The quotes are not being added by PS. They are right there in your file. Here is a snippet to give you a start in the right direction:
$file = Import-csv file.csv -delimiter ':'
foreach($row in $file){
foreach($user in $row.Users.split(',')){
Get-ADUser ($user.replace('(','').Replace('"','').replace(')',''))
}
}

I think the issue here is that you think that you are importing an array of strings as the value of Users, but you in fact are just importing a single string. There are a couple of options here, using the split method as you have already discovered:
$file = import-csv file.csv -delimiter ':'
ForEach($Record in $File){
$Record.Users.Trim('()').Split(',') | ForEach{
Get-ADUser $_.Trim('"')
}
}
So the $Record.Users.Trim('()') removes the parenthesis from the string. Then it uses .Split(',') to split the string into an array of strings. Then it passes that to an inner ForEach loop when gets the ADUser from each string, after removing the quotes from around it (with .Trim('"')).
Alternatively you could have one user per line, so your CSV would appear like:
Mailbox:User
AcmeCorp:jsmith
AcmeCorp:trex
XyzCorp:sjobs
XyzCorp:bfranklin
Then after importing the CSV you could just group by the mailbox if needed, say, to add users per mailbox or something:
$File = Import-CSV C:\Path\To\File.csv -delimiter ':'
$MBGroups = $File | Group Mailbox
ForEach($Record in $MBGroups){
$Mailbox = $Record.Name
$Users = $Record.Group | ForEach{Get-ADUser $_.User}
$Users | ForEach{Set-ADUser -mail ($_.samaccountname+"#"+$Mailbox+".com")}
}

Related

Remove #{} from output and place in command

I am trying to figure out how to remove the #{} from the output of the $list coming from the CSV file.
I am trying to run the Command "Complete-DomainJoin -Identity $line -DagNumber 023" for each line inside the CSV file
#Complete-DomainJoin (MultipleObjects)
$csv = Import-Csv C:\Temp\DEPTComputers023DEPTComputers023.csv
foreach ($line in $csv) {
Complete-DomainJoin -Identity $line -DagNumber 023
}
When I run this powershell script it errors out because its placing the line item from the CSV into the command as #{xxxx}, from reading around it seems the common way to fix the issue is by adding -ExpandProperty variable but I have not found where to place it or I am not understanding how to implement it correctly.
If everything is in the first column with no header row, then you can try this:
# Name the first column
$csv = Import-Csv C:\Temp\DEPTComputers023DEPTComputers023.csv -Header 'ComputerName'
foreach ($row in $csv) {
# use the ComputerName property
Complete-DomainJoin -Identity $row.ComputerName -DagNumber 023
}
If there aren't any commas in the file at all, then just replace Import-Csv with Get-Content
the #{property=value} syntax means that your object has sub-properties, instead of being a single string

How to seperate CSV values within a CSV into new rows in PowerShell

I'm receiving an automated report from a system that cannot be modified as a CSV. I am using PowerShell to split the CSV into multiple files and parse out the specific data needed. The CSV contains columns that may contain no data, 1 value, or multiple values that are comma separated within the CSV file itself.
Example(UPDATED FOR CLARITY):
"Group","Members"
"Event","362403"
"Risk","324542, 340668, 292196"
"Approval","AA-334454, 344366, 323570, 322827, 360225, 358850, 345935"
"ITS","345935, 358850"
"Services",""
I want the data to have one entry per line like this (UPDATED FOR CLARITY):
"Group","Members"
"Event","362403"
"Risk","324542"
"Risk","340668"
"Risk","292196"
#etc.
I've tried splitting the data and I just get an unknown number of columns at the end.
I tried a foreach loop, but can't seem to get it right (pseudocode below):
Import-CSV $Groups
ForEach ($line in $Groups){
If($_.'Members'.count -gt 1, add-content "$_.Group,$_.Members[2]",)}
I appreciate any help you can provide. I've searched all the stackexchange posts and used Google but haven't been able to find something that addresses this exact issue.
Import-Csv .\input.csv | ForEach-Object {
ForEach ($Member in ($_.Members -Split ',')) {
[PSCustomObject]#{Group = $_.Group; Member = $Member.Trim()}
}
} | Export-Csv .\output.csv -NoTypeInformation
# Get the raw text contents
$CsvContents = Get-Content "\path\to\file.csv"
# Convert it to a table object
$CsvData = ConvertFrom-CSV -InputObject $CsvContents
# Iterate through the records in the table
ForEach ($Record in $CsvData) {
# Create array from the members values at commas & trim whitespace
$Record.Members -Split "," | % {
$MemberCount = $_.Trim()
# Check if the count is greater than 1
if($MemberCount -gt 1) {
# Create our output string
$OutputString = "$($Record.Group), $MemberCount"
# Write our output string to a file
Add-Content -Path "\path\to\output.txt" -Value $OutputString
}
}
}
This should work, you had the right idea but I think you may have been encountering some syntax issues. Let me know if you have questions :)
Revised the code as per your updated question,
$List = Import-Csv "\path\to\input.csv"
foreach ($row in $List) {
$Group = $row.Group
$Members = $row.Members -split ","
# Process for each value in Members
foreach ($MemberValue in $Members) {
# PS v3 and above
$Group + "," + $MemberValue | Export-Csv "\path\to\output.csv" -NoTypeInformation -Append
# PS v2
# $Group + "," + $MemberValue | Out-File "\path\to\output.csv" -Append
}
}

Powershell replace text once per line

I have a Powershell script that I am trying to work out part of it, so the text input to this is listing the user group they are part of. This PS script is supposed to replace the group with the groups that I am assigning them in active directory(I am limited to only changing groups in active directory). My issue is that when it reaches HR and replaces it, it will then proceed to contine and replace all the new but it all so replaces the HR in CHRL, so my groups look nuts right now. But I am looking it over and it doesn't do it with every line. But for gilchrist it will put something in there for the HR in the name. Is there anything can I do to keep it for changing or am I going to have to change my HR to Human Resources? Thanks for the help.
$lookupTable = #{
'Admin' = 'W_CHRL_ADMIN_GS,M_CHRL_ADMIN_UD,M_CHRL_SITE_GS'
'Security' = 'W_CHRL_SECURITY_GS,M_CHRL_SITE_GS'
'HR' = 'M_CHRL_HR_UD,W_CHRL_HR_GS,M_CHRL_SITE_GS'
$original_file = 'c:\tmp\test.txt'
$destination_file = 'c:\tmp\test2.txt'
Get-Content -Path $original_file | ForEach-Object {
$line = $_
$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line = $line -replace $_.Key, $_.Value
}
}
$line
} | Set-Content -Path $destination_file
Get-Content $destination_file
test.txt:
user,group
john.smith,Admin
joanha.smith,HR
john.gilchrist,security
aaron.r.smith,admin
abby.doe,secuity
abigail.doe,admin
Your input appears to be in CSV format (though note that your sample rows have trailing spaces, which you'd have to deal with, if they're part of your actual data).
Therefore, use Import-Csv and Export-Csv to read / rewrite your data, which allows a more concise and convenient solution:
Import-Csv test.txt |
Select-Object user, #{ Name='group'; Expression = { $lookupTable[$_.group] } } |
Export-Csv -NoTypeInformation -Encoding Utf8 test2.txt
Import-Csv reads the CSV file as a collection of custom objects whose properties correspond to the CSV column values; that is, each object has a .user and .name property in your case.
$_.group therefore robustly reports the abstract group name only, which you can directly pass to your lookup hashtable; Select-Object is used to pass the original .user value through, and to replace the original .group value with the lookup result, using a calculated property.
Export-Csv re-converts the custom objects to a CSV file:
-NoTypeInformation suppresses the (usually useless) data-type-information line at the top of the output file
-Encoding Utf8 was added to prevent potential data loss, because it is ASCII encoding that is used by default.
Note that Export-Csv blindly double-quotes all field values, whether they need it or not; that said, CSV readers should be able to deal with that (and Import-Csv certainly does).
As for what you tried:
The -replace operator replaces all occurrences of a given regex (regular expression) in the input.
Your regexes amounts to looking for (case-insensitive) substrings, which explains why HR matches both the HR group name and substring hr in username gilchrist.
A simple workaround would be to add assertions to your regex so that the substrings only match where you want them; e.g.: ,HR$ would only match after a , at the end of a line ($).
However, your approach of enumerating the hashtable keys for each input CSV row is inefficient, and you're better off splitting off the group name and doing a straight lookup based on it:
# Split the row into fields.
$fields = $line -split ','
# Update the group value (last field)
$fields[-1] = $lookupTable[$fields[-1]]
# Rebuild the line
$line = $fields -join ','
Note that you'd have to make an exception for the header row (e.g., test if the lookup result is empty and refrain from updating, if so).
Why don't you load your text file as a CSV file, using Import-CSV and use "," as a delimiter?
This will allow you to have a Powershell Object you can work on. and then export it as text o CSV. if I use your file & lookup table this code may help you :
$file = Import-Csv -Delimiter "," -Path "c:\ps\test.txt"
$lookupTable = #{
'Admin' = 'W_CHRL_ADMIN_GS,M_CHRL_ADMIN_UD,M_CHRL_SITE_GS'
'Security' = 'W_CHRL_SECURITY_GS,M_CHRL_SITE_GS'
'HR' = 'M_CHRL_HR_UD,W_CHRL_HR_GS,M_CHRL_SITE_GS'}
foreach ($i in $file) {
#Compare and replace
...
}
Export-CSV $file -Delimiter ","
You can then iterate over $file and compare and replace. you can also Export-CSV after you're done.

Powershell Import-csv with return character

I tried the following to turn a text file into a document by leveraging import-csv where each item in the original document was a new line
Sample file.txt
James Cameron
Kirk Cobain
Linda Johnson
Code:
$array = import-csv file.txt | ConvertFrom-Csv -Delim `r
foreach ($Data in $array)
{
if (sls $Data Master.txt -quiet)
{Add-Content file.txt $Data}
}
It never created the document
Import-Csv takes a CSV and outputs PSCustomObjects. It's intended for when the file has a header row, and it reads that as the properties of the objects. e.g.
FirstName,LastName
James,Cameron
Kirk,Cobain
# ->
#{FirstName='James';LastName='Cameron'}
#{FirstName='Kirk';LastName='Cobain'}
etc.
If your file has no header row, it will take the first row and then ruin everything else afterwards. You need to provide the -Header 'h1','h2',... parameter to fix that. So you could use -Header Name, but your data only has one property, so there's not much benefit.
ConvertFrom-Csv is intended to do the same thing, but from CSV data in a variable instead of a file. They don't chain together usefully. It will try, but what you end up with is...
A single object, with a property called '#{James=Kirk}' and a value of '#{James=Linda}', where 'James' was taken from line 1 as a column header, and the weird syntax is from forcing those objects through a second conversion.
It's not at all clear why you are reading in from file.txt and adding to file.txt. But since you don't have a CSV, there's no benefit from using the CSV cmdlets.
$lines = Get-Content file.txt
$master = Get-Content master.txt
foreach ($line in $lines)
{
if ($master -contains $line)
{
Add-Content file2.txt $line
}
}
or just
gc file.txt |? { sls $_ master.txt -quiet } | set-content file2.txt
Auto-generated PS help links from my codeblock (if available):
gc is an alias for Get-Content (in module Microsoft.PowerShell.Management)
? is an alias for Where-Object
sls is an alias for Select-String (in module Microsoft.PowerShell.Utility)
Set-Content (in module Microsoft.PowerShell.Management)

Removing a single line from CSV

I have a CSV with several rows and two columns: "Name" and "Information".
I'm looping through the CSV and checking each line for a condition, if the condition is met I'd like to remove the line:
for ($i=0; $i -le $CSV.length; $i++)
{
if ($CSV.name == "Fred")
{
#remove $CSV[$i] -- that one line; both "Name" and "Information"
}
}
I've seen solutions that use Get-Content or Import-Csv and temporary file(s) but I haven't wrapped my head around it and figure there must be an easier way.
Regardless, any help is appreciated!
I would read the CSV using the Import-CSV cmdlet and use the Where-Object cmdlet to filter the entries. Finally, write the CSV back using the Export-CSV cmdlet:
$csv = import-csv 'YourPath.csv'
$csv | Where Name -ne 'Fred' | Export-Csv 'YourPath.csv' -NoTypeInformation