PowerShell How to loop a script based on script array count - arrays

I'm new to PowerShell. I'm trying to pull a users name and place it in a file. I have two corresponding arrays, $title and $csvfile. $title[0] corresponds with $csvfile[0] and $title[1] to $csvfile[1] and so on. Is it possible to loop this script while increasing the index for both at the same time so that each index runs once but also in sync?
$title = #('jim' 'john' 'james')
$csvfile = #('jim.csv' 'john.csv' 'james.csv')
Get -ADUser -filter {(Title -like "$title") -and (Company -like "Location1")} | Export-Csv c:\temp\$csvfile
Problem 2
The $title array doesn't seem to be iterating one at a time. If I replace $title[$] with any of the listed array items it works. Funny thing is that it DOES create all my .csv files from the $csvfile array, they are just empty. I've done some looking on the web, not sure if my array items are to long or the quotations are not parsing right. Any help would be muchly appreciated.
$title = #(
'Director of Nursing'
'Assistant Director of Nursing'
'Activities Director'
)
$csvfile = #(
'DON'
'ADON'
'ACTIVITIES'
)
for($i=0; $i -lt $title.Count; $i++)
{
#Get-ADUser -Filter { (Title -like "Director of Nursing") -and (Company -like "location1") }| Export-Csv c:\temp\$($csvfile[$i]).csv"
Get-ADUser -filter { (Title -like "$($title[$i])") -and (Company -like "Location1") }| Export-Csv "c:\tempPath\$($csvfile[$i]).csv"
}

If I'm understanding this correctly. You'd like to append the user information from Get-Aduser to the corresponding csv of the users name you got it from? Such as, jim info goes to jim.csv, and so on for each one?
Seems like you're looking for the Foreach loop.
$title = #('jim','john','james')
#$csvfile = #('jim.csv' 'john.csv' 'james.csv')
Foreach($user in $title){
Get-ADUser -filter {(Title -like "$title") -and (Company -like "Location1")} | Export-Csv "c:\temp\$title.csv"}
A Foreach loop goes through a list of objects and performs the same action for every object, ending when it's finished with the last one. The list of objects is typically an array. When you run a loop over a list of objects, we say you're iterating over the list.
The Foreach loop can be used in 3 different ways: Foreach Statement, Foreach-Object cmdlet, or as a foreach() method.
What we're using here is the Foreach statement which is followed by parentheses that contain three elements, in order: a variable, the keyword in, and the object or array to iterate over. As it moves through list ($title-array in this case), Powershell will copy the object it's looking at into the Variable defining each item in the list, $user.
Note: because the variables is just a copy, you cannot directly change the item in the original list.
Please note as well, the items in an array are read separately by adding a comma to the end of each item in the list(if its not in a new line). In the code above, we're appending the same name you're iterating with to the csv file as well.
EDIT: Using for loop. . .
$title = #('jim','john','james')
$csvfile = #('CEO','CFO','CIO')
For($i=0; $i -lt $title.Count; $i++){
Get-ADUser -filter {(Title -like "$($title[$i])") -and (Company -like "Location1")} | Export-Csv "c:\temp\$($csvfile[$i]).csv"}
Matches the output like so:
jim - CEO.csv
john - CFO.csv
james - CIO.csv

Related

PowerShell ForEach Loop to Add UserPrincipalName and object ID to a file

I have a file of user emails. I need to gather their UPNs and object IDs.
Get-AzureADUser -Filter "PrimarySMTPAddress eq '$user.EmailAddress'"
I'm using this line to query AzureAD and it works perfectly. However when I place it in a loop I can't seem to get any data.
import-csv .\Book4.csv | ForEach-Object{
Get-AzureADUser -Filter "PrimarySMTPAddress eq '$_.EmailAddress'" | Select-Object UserPrincipalName, ObjectID
} | Export-Csv -Path .\Book4.csv -NoTypeInformation
Can anyone tell me where I'm going wrong. I thought this would be something simple but I've been stuck for an hour. The CSV file has three column headers: EmailAddress, UserPrincipalName, ObjectID
"PrimarySMTPAddress eq '$_.EmailAddress'" doesn't work as intended, because the attempted property access on variable $_, .EmailAddress, isn't effective:
Inside "...", an expandable string, you need $(...), the subexpression operator in order to access a variable's properties[1], call methods on it, or, more generally embed entire statements.
Therefore, use the following string instead:
"PrimarySMTPAddress eq '$($_.EmailAddress)'"
Also, you're mistakenly trying to read from and write back to the same file (.\Book4.csv) in the same pipeline, which doesn't work (without additional effort), as discussed in Allen Wu's helpful answer.
[1] Without enclosing in $(...), "...$_.EmailAddress..." cause $_ to be expanded by itself, whereas .EmailAddress is used verbatim. Compare Write-Output "$HOME.Length" to Write-Output "$($HOME.Length)", for instance.
#mklement0 is correct.
But another important thing is that we can't read from and write back to the same file in the same pipeline. Unless you make sure that the input file is read into memory, in full, before its lines are sent through the pipeline.
Use this:
$users = import-csv E:\temp\Book4.csv | ForEach-Object{
Get-AzureADUser -Filter "PrimarySMTPAddress eq '$($_.EmailAddress)'" | Select-Object UserPrincipalName, ObjectID
}
$users | Export-Csv -Path E:\temp\Book4.csv -NoTypeInformation

Extracting first and last names from txt file and using it to get username from AD and import it into csv

as a test and a way to get specific data from AD i am trying to get the data based on a txt file filled with names and last names of users. I worked out that using ConvertFrom-Stringcmdlet allows you to split names inside the txt file into two separate values, thus enabling you to use (in theory) Get-ADUser to find the user from AD and its attributes.
The code i was using so far is the following, with random changes here and there as i tried various options to make it work. I have been able to get the data i need by storing names under variables and then using the Get-ADUser cmdlet to pick them up from AD, i was even able to export that data into the CSV file. The issue is cant make it work when there is a text file filled with several entries.
Get-Content C:\temp\users.txt -encoding UTF8 |
ConvertFrom-String |
ForEach-Object {Get-ADUser -Filter {(givenName -Like '$($_.P1)') -and (sn -Like '$($_.P2)')} -Properties *} |
Select-object givenName, Surname, SamAccountName | Export-CSV C:\Temp\usersexport.csv -NoTypeInformation
Any help would be very appreciated.
You are using a sub-expression inside a non-expanding string. Check out Powershell's about_Quoting_Rules help topic for more detailed information. But, the short story is you need double quotes to expand something like $($_).
Something like:
Get-Content C:\temp\users.txt -encoding UTF8 |
ConvertFrom-String |
ForEach-Object {Get-ADUser -Filter "( givenName -like '$($_.P1)' ) -and (sn -like '$($_.P2)' )" -Properties *} |
Select-object givenName, Surname, SamAccountName | Export-CSV C:\Temp\usersexport.csv -NoTypeInformation
Notice that I quoted the entire filter. Technically Get-ADUser's -Filter parameter takes a string so this is acceptable and makes it a little easier to read.
Also note: you may need "*" to make this work correctly. Technically -like will work on exact matches, but it's more typically used when searching with wildcards. In your case using '*$($_.P1)*' might help get you past the issue.
If you are confident in the values especially considering you are also using the -and operator you might think about using -eq. However I'd be concerned as there's always a chance of common name collisions. For example in the US there could easily be 2 John Smiths in AD...
I'd also point out it's more expedient to use Import-Csv for this. When corrected your approach is working, but below is more readable and easier to write in the first place:
Example with -eq:
Import-Csv -Path C:\temp\users.txt -header "givenName","sn" -Delimiter " " |
ForEach-Object{
$GivenName = $_.givenName
$LastName = $_.sn
ForEach-Object { Get-ADUser -Filter "(givenName -eq '$GivenName') -and (sn -eq '$LastName')" -Properties * }
} |
Select-Object givenName, Surname, SamAccountName |
Export-CSV C:\Temp\usersexport.csv -NoTypeInformation
Example with -like and Wildcards:
Import-Csv -Path C:\temp\users.txt -header "givenName","sn" -Delimiter " " |
ForEach-Object{
$GivenName = $_.givenName
$LastName = $_.sn
ForEach-Object { Get-ADUser -Filter "(givenName -like '*$GivenName*') -and (sn -like '*$LastName*')" -Properties * }
} |
Select-Object givenName, Surname, SamAccountName |
Export-CSV C:\Temp\usersexport.csv -NoTypeInformation
You also may want to avoid -Properties * and instead define a more exact set of properties to retrieve. In bigger jobs that's going to make a performance difference.
Oh and, you usually don't need to specify the encoding for Get-Content it's very good at figuring that out on it's own.
In PowerShell Core the default value for the parameter is UTF8NoBOM. NoBOM just means there's no "Byte Order Mark" declaring the encoding and so it shouldn't have any issues with a UTF8 file. In Windows PowerShell 5.1 you may need to specify as you have.
It looks like this is a learning exercise for you, let me know if this helps.

Using arrays with where-object to filter multiple strings out of a csv

I am trying to filter out lines out of a csv that contain any of the values in an array.
Using this post as reference:
Use -notlike to filter out multiple strings in PowerShell
I managed to get it working with this format:
Import-Csv "$LocalPath\Stripped1Acct$abbrMonth$Year.csv" |
where {$_."SubmitterName" -notlike "*${Report2a}*"
-and $_."SubmitterName" -notlike "*${Report2b}*"
-and $_."SubmitterName" -notlike "*${Report2c}*"} |
Export-Csv "$LocalPath\Stripped2Acct$abbrMonth$Year.csv" -NoTypeInformation
Eventually I plan to rewrite the script so it will pull the exclusion list from a text file generated by an end user. In order to do that, I'll have to have it access values in an array. I tried doing that with the following syntax, but it didn't work as intended:
Import-Csv "$LocalPath\Stripped1Acct$abbrMonth$Year.csv" |
where {$_."SubmitterName" -notlike "*${Report2[0]}*"
-and $_."SubmitterName" -notlike "*${Report2[1]}*"
-and $_."SubmitterName" -notlike "*${Report2[2]}*"} |
Export-Csv "$LocalPath\Stripped2Acct$abbrMonth$Year.csv" -NoTypeInformation
I have a feeling it's just an syntax issue, but after playing around with it for far too long, I've run out of ideas.
I have a feeling it's a syntax issue
This is a syntax issue. The ${Name} syntax is used primarily for names that contain odd characters, like ${A ~*Strange*~ Variable Name}. It's not an expression though, so you can't index into it with [0] inside the braces; that would be taken as a literal part of the variable name.
Instead you can use a sub-expression $(...) to do this:
"*$($Report2[0])*"
As an alternative approach, I might convert your whole array into a single regular expression and then use the -match (or -notmatch) operator:
$regex = $Report2.ForEach({ [RegEx]::Escape($_) }) -join '|'
Import-Csv "$LocalPath\Stripped1Acct$abbrMonth$Year.csv" |
where {$_."SubmitterName" -notmatch $regex} |
Export-Csv "$LocalPath\Stripped2Acct$abbrMonth$Year.csv" -NoTypeInformation
This takes the $Report2 array, then builds an array of the same values, but escaped for Regular Expressions (so that any special characters are matched literally), and then builds a regex that looks like:
Item1|Item2|Item3
In RegEx, a pipe is alternation, so it looks for a match of Item1 or Item2, etc. Regex finds it anywhere in the string so it doesn't need a wildcard character the way that -like does.
So with that built to pre-contain all items in your array, then you can use -notmatch to achieve the same thing, and you don't have to hardcode a bunch of indices.
you can use contains too like this
short version
[string[]]$listexludevalue=Get-Content "C:\temp\exludevalue.txt"
Import-Csv "$LocalPath\Stripped1Acct$abbrMonth$Year.csv" | %{$valcontain=$true; $col=$_.Owner; $listexludevalue.ForEach({$valcontain=$valcontain -and !$col.Contains($valuetotest)}); if ($valcontain) {$_} } | Export-Csv "$LocalPath\Stripped2Acct$abbrMonth$Year.csv" -NoTypeInformation
detailed version :
$listexludevalue=Get-Content "C:\temp\exludevalue.txt"
Import-Csv "$LocalPath\Stripped1Acct$abbrMonth$Year.csv" |
% {
$valcontain=$true
foreach ($valuetotest in $listexludevalue) {$valcontain=$valcontain -and !$_.SubmitterName.Contains($valuetotest)}
if ($valcontain) {$_}
} | Export-Csv "$LocalPath\Stripped2Acct$abbrMonth$Year.csv" -NoTypeInformation

Comparing Arrays in Powershell

There is probably a simple way to do this, but I've been hitting my head against a wall for hours at this point. I'm trying to grab several user attributes out of AD, compare two of those attributes, and then modify them based on the differences. However since some users have null values for either their office or department fields which causes compare-object to fail, I have those going into other arrays with a -replace to get rid of the nulls, so my variables look like this:
$UserData = Get-ADuser -filter * -properties physicaldeliveryofficename,department | select samaccountname,physicaldeliveryofficename,department
$Offices = $UserData.physicaldeliveryofficename -replace "^$","N/A"
$Departments = $UserData.department -replace "^$","N/A"
So far so good, but when I loop through to compare values, I start to run into trouble. Looping through the users like this seems to be comparing every element to every other element:
Foreach ($user in $UserData.samaccountname) {
Compare-object $offices $departments -includeqeual}
While not having a loop and using compare-object by itself gives accurate results, but then I'd need a loop to check for matches anyway.
Assuming I just want to determine which users have matching office and department fields (and based off that do a pretty simple Set-ADUser command), how would I go about comparing the values without checking every element against every other element?
Your ForEach loop won't work properly because even though you are going through each user account, you are always comparing the same collection of offices and departments. I wrote this that might give you better results and saves the compare results as part of an object so you can see the user account as well.
Get-ADuser -Filter * -properties physicaldeliveryofficename,department | ForEach {
$Offices = $_.physicaldeliveryofficename -replace "^$","N/A"
$Departments = $_.department -replace "^$","N/A"
$Results = Compare-object $offices $departments -IncludeEqual
[pscustomobject]#{
User = $_.samaccountname
compare = $Results
}
}

Powershell: Using -notcontains to compare arrays doesn't find non-matching values

PS noob here (as will be obvious shortly) but trying hard to get better. In my exchange 2010 environment I import and export huge numbers of .pst files. Many will randomly fail to queue up and once they're not in the queue it's very tedious to sort through the source files to determine which ones need to be run again so I'm trying to write a script to do it.
first I run a dir on the list of pst files and fill a variable with the associated aliases of the accounts:
$vInputlist = dir $vPath -Filter *.pst |%{ get-mailbox -Identity $_.basename| select alias}
Then I fill a variable with the aliases of all the files/accounts that successfully queued:
$vBatch = foreach ($a in (Get-MailboxImportRequest -BatchName $vBatchname)) {get-mailbox $a.mailbox | select alias}
Then I compare the two arrays to see which files I need to queue up again:
foreach($should in $vInputlist){if ($vBatch -notcontains $should){Write-Host $should ""}}
It seems simple enough yet the values in the arrays never match, or not match, as the case may be. I've tried both -contains and -notcontains. I have put in a few sanity checks along the way like exporting the variables to the screen and/or to csv files and the data looks fine.
For instance, when $vInputlist is first filled I send it to the screen and it looks like this:
Alias
MapiEnableTester1.psiloveyou.com
MapiEnableTester2.psiloveyou.com
MapiEnableTester3.psiloveyou.com
MapiEnableTester4.psiloveyou.com
Yet that last line of code I displayed above (..write-host $should,"") will output this:
#{Alias=MapiEnableTester1.psiloveyou.com}
#{Alias=MapiEnableTester2.psiloveyou.com}
#{Alias=MapiEnableTester3.psiloveyou.com}
#{Alias=MapiEnableTester4.psiloveyou.com}
(those all display as a column, not sure why they won't show that way here)
I've tried declaring the arrays like this, $vInputlist = #()
I've tried instead of searching for the alias just cleaning .pst off off the $_.basename using .replace
I've searched on comparing arrays til I'm blue in the fingers and I don't think my comparison is wrong, I believe that somehow no matter how I fill these variables I am corrupting or changing the data so that seemingly matching data simply doesn't.
Any help would be greatly appreciated. TIA
Using -contains to compare objects aren't easy because the objects are never identical even though they have the same property with the same value. When you use select alias you get an array of pscustomobjects with the property alias.
Try using the -expand parameter in select, like
select -expand alias
Using -expand will extract the value of the alias property, and your lists will be two arrays of strings instead, which can be compared using -contains and -notcontains.
UPDATE I've added a sample to show you what happends with your code.
#I'm creating objects that are EQUAL to the ones you have in your code
#This will simulate the objects that get through the "$vbatch -notcontains $should" test
PS > $arr = #()
PS > $arr += New-Object psobject -Property #{ Alias="MapiEnableTester1.psiloveyou.com" }
PS > $arr += New-Object psobject -Property #{ Alias="MapiEnableTester2.psiloveyou.com" }
PS > $arr += New-Object psobject -Property #{ Alias="MapiEnableTester3.psiloveyou.com" }
PS > $arr | ForEach-Object { Write-Host $_ }
#{Alias=MapiEnableTester1.psiloveyou.com}
#{Alias=MapiEnableTester2.psiloveyou.com}
#{Alias=MapiEnableTester3.psiloveyou.com}
#Now this is what you will get if you use "... | select -expand alias" instead of "... | select alias"
PS > $arrWithExpand = $arr | select -expand alias
PS > $arrWithExpand | ForEach-Object { Write-Host $_ }
MapiEnableTester1.psiloveyou.com
MapiEnableTester2.psiloveyou.com
MapiEnableTester3.psiloveyou.com

Resources