How do I pass a parameter down the pipeline to a function that doesn't accept pipeline input? - powershell

I've got a text file that contains a list of user identities. I want to pass each member on that list to Get-csMeetingMigrationStatus and get back UserPrincipalName, Status and LastMessage. From there I want to output to gridview as an eyes on test for migration status.
I'd like to do this in one pipeline but can't work it out. I know that Get-csMeetingMigrationStatus does not take pipeline input for its parameter -identity so I have to use another means of passing that parameter to the function. I've done things like this before and am wondering if the fact that the list of users is an import from a text file rather than an array created from scratch is the cause.
I needed to get this working asap so thought I'd just put it all in a foreach loop as below. I know the example below doesn't give me all I want regards output but even the sample below fails.
$UserDetails = Get-Content -path c:\test.txt
foreach ($ud in $UserDetails) {
Get-csMeetingMigrationStatus -identity $ud | Select-Object Userprincipalname, status, lastmessage
}
When I run the above the return for the first identity is the property headers for my connect-microsoftteams connection.
Subsequent iterations return a blank line. For each iteration of the foreach loop, if I copy and paste the command in the foreach statement into the terminal window and run it, the correct information for each user is displayed. I don't understand how that can be.
Ultimately I'd like to push all this down a single pipeline and output to gridview.
If anyone can assist with either problem above I'd appreciate it as I've tried sorting this for a good number of hours now and can't find a solution.
Thanks

Use the ForEach-Object cmdlet:
Get-Content -path c:\test.txt |ForEach-Object {
Get-CsMeetingMigrationStatus -identity $_ | Select-Object Userprincipalname, status, lastmessage
} |Out-GridView
Nesting the pipeline inside ForEach-Object means that we can supply another downstream cmdlet (in this case Out-GridView) with whatever output is produced by it (unlike with the foreach(){} loop statement)

Related

Issues getting get-adcomputer to recognize variable in filter

Below is the code I am working with. I have verified that the initial import-csv is working as it should be, and if I change out the variable object for a concrete object, the script works as it should. It just seems to not recognize/use the variable the way that it should.
$CSVOutput = "C:\temp\output.csv"
$Output = foreach($u in $userlastname)
{
Get-ADComputer -Filter {Description -Like '*$u*'} -properties Description | Select Name, Description
}
$Output | Export-Csv $CSVOutput
If I replace the $u in the filter with one of the values from the $userlastname variable, it works, but it just runs the search with the set value as many times as it runs the foreach loop. I am expecting to see several different computer objects that have the different values from $userlastname in their description. Currently it returns nothing, as if it found no values that matched in the description field.
While it’s technically possible to use a scriptblock as a filter in the ADCommands, it isn’t recommended - use a string instead:
Get-ADComputer -Filter "Description -like '*$($u.name)*'" -Properties ...
Using a string will solve your variable substitution issue.
ETA: Comments indicated that you were getting #{Name=User} as the expansion for $u in the filter expression. This is because $u was a structured [PSCustomObject], and you were looking for a single field from that object. The easiest way to get the value of the desired field of the object is simply to use the PowerShell evaluation construct, as given in the edited answer.

Is there a way to export the variable in a ForEach loop to my CSV output on each line

I am attempting to run a script against our AD that spits out the AD Users in a group, for all the groups in a list. Essentially attempting to audit the groups to find the users. I have a functioning script, except I have no way to determine when the output of one group ends, and where the output of the next begins.
I have tried looking for previous examples, but nothing fits exactly the method I am using, and with me just dipping my toes into powershell I have not been able to combine other examples with my own.
$groups = Get-Content "C:\Folder\File_with_lines_of_ADGroup_Names.txt"
foreach ($group in $groups) { Get-ADGroupMember -Identity "$group" | Where-Object { $_.ObjectClass -eq "user" } | Get-ADUser -Property Description,DisplayName | Select Name,DisplayName,Description,$group | Export-csv -append -force -path "C:\Folder\File_of_outputs.csv" -NoTypeInformation }
Right now the problem lies with getting the $group variable to be exported along with the Name, DisplayName, and Description of each user returned. This is the best way I can think of to tag each user's group and keep all the results in a single file. However, only the first line of results works which is the HEADERS of the CSV, and everything after it is either listed as "Microsoft.ActiveDirectory.Management.ADPropertyValueCollection"or simply blank after the first group of results.
Hoping someone can show me how to easily add my variable $group to the output for each user found for filtering/pivoting purposes.
Thanks and let me know if you have questions.
I believe what you are after are calculated properties on your select statement.
Select Name,DisplayName,Description,$group
Should Probably be something like
Select Name,DisplayName,Description,#{n='Group'; e={$group};}
See also https://serverfault.com/questions/890559/powershell-calculated-properties-for-multiple-values

Powershell pipeline objects from import-csv headaches

We have a big audit coming up, and we want to be sure that all termed employees' AD computer accounts have been deactivated. I have a .csv that has a unique identifier that we use - our schema includes that ID in the middle of the ADcomputer Name property. I am having enormous trouble correctly piping the imported csv objects and their property to the filter. I think. I'm not entirely sure. This is something like what I've been trying.
Import-Csv termname.csv | ForEach-Object
{ Get-ADComputer -Filter { Name -Contains "$($_.id)" } } |
Export-Csv termcomp.csv -NoTypeInformation
This "script" pulled a total of no content, while I KNOW that when I test
Get-ADComputer -Filter { Name -Contains $id }
where $id is that unique ID, I get hits on AD Computer objects. Likewise, testing this block
Import-Csv termname.csv | ForEach-Object { Get-ADComputer { Name -Contains $_.id }
PowerShell's error is:
..."Property 'id' not found in object of type: 'System.Management.Automation.PSCustomObject'.
I think that I am misapprehending what precisely is an "object" in this case. Is the whole imported array an object, with a property of .id? Or is each row its own object, with a .id property? And, either way, why has PowerShell such strong objections to a pipeline object, which is, after all, its primary job?
Is there an entirely better way to go about this whole process? Am I somehow overthinking the whole mess?
UPDATE
As requested, sample contents of the csv file. Taking the suggestion to use wildcards instead, I've put the wildcards into the .csv itself, which, again, may not be the most elegant solution. But as long as it works! Here's the sample, copied from Notepad++
id
*rstarr56*
*jlennon58*
*pmcartney74*
*gharrison68*
So it looks like your CSV file does not have an "ID" heading in it, causing that not to be an available property, easy fix there is to confirm the contents of the CSV file, if everything looks correct try running Import-Csv termname.csv | ForEach-Object { Write-Host $_.id } to confirm that the property is coming across correctly. However you will also have trouble using -Contains here as that operator is meant to check if an array contains a particular value, you'll need to use Name -Like "*$($_.id)*"
After hours of digging, I found an answer here:
Import-CSV and Foreach to use Get-ADUser
Thanks especially to Mike for some great thinking about -Like and how to test the success of the code.
The line that ended up working was:
Import-Csv termname.csv | ForEach-Object {
Get-ADComputer -Filter "Name -Like '*$($_.id)*'"
} | Export-Csv termout.csv
Apparently, Powershell hates {{}}.
Thanks again, all!

Differences between | and $

Can anyone explain to me the differences I'm seeing in either using a | to pipe one command to another or using $ to 'pipe' it a different way (sorry not sure if the use $ is actually considering piping).
So… this works:
Get-Mailbox -ResultSize Unlimited |
where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" } |
Get-MailboxPermission
Which is great, however because I want to place another where command after the Get-MailboxPermission which doesn't work above I then tried to use this:
$Mailbox = Get-Mailbox -ResultSize Unlimited |
where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" }
Get-MailboxStatistics -Identity $Mailbox |
where { $_.IsInherited.tostring() -eq "False" }
It causes me to get this error:
Cannot process argument transformation on parameter 'Identity'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "Microsoft.Exchange.Configuration.Tasks.GeneralMailboxOrMailUserIdParameter".
Surely using | or $ is the same in the sense that it pipes through the results to the next command or am I completely wrong?
I don't have an exchange shell here to test but I guess I can still explain the basics and point you in the right direction.
The pipe | is used to redirect output from one command to another command. $ in Powershell is the character which defines that the character sequence right behind it is either a variable (e.g. $Mailbox as an example for a normal variable or $_ as an example for a variable that holds data that has been piped through from a previous command) or an expression. An example for an expression one is $(4+5).
Or in a more frequently used example:
PS C:\Users\Administrator> $file = (get-childitem)[0]
PS C:\Users\Administrator> write-output "The fullname of $file is $($file.fullname)"
The fullname of .ssh is C:\Users\Administrator\.ssh
In that example it is actually necessary to use an expression, because variable detection inside a string doesn't recognize dots as separator between variable and a variable member (fullname is a member of $file).
If it's not clear to you why there is a point and what members are, you should probably look into object oriented programming a bit because Powershell is object oriented through and through.
In your 2nd example you just save everything that's returned by your Get-Mailbox command in the $Mailbox variable. The $Mailbox variable is available as long as you don't delete it or leave its scope (in this case, the powershell session). You can actually use the variable as input for multiple commands without losing its data.
When using the pipe, the data returned by your first command is only accessible for the command behind the pipe and after that it's gone.
That's probably the difference you're interested in.
As for your actual problem: Powershell tells you that it's not expecting to be handed a variable of type System.Collections.ArrayList, which is what Get-Mailbox returns. The technet help is unclear as to what Get-Mailbox specificly returns, but I strongly guess it's an ArrayList of Mailbox-Objects. You can check it like this:
$Mailbox.GetType()
$Mailbox[0].GetType() # gets the type of the first object in $Mailbox
To fix your code, you need to loop over what's been returned by Get-Mailbox. Try this:
$Mailboxes = Get-Mailbox -ResultSize Unlimited | where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" }
$Mailboxes | ForEach-Object { Get-MailboxStatistics -Identity $_ }
The ForEach-Object cmdlet loops over an array or a list and works on each item individually.
Your first example works so far because Powershell has been made smarter about piped data a few versions ago (See paragraph about 'Member Enumeration'). It's actually ForEach-ing over the passed in data.
Follow up links:
The $_ variable in powershell
Powershell is an object oriented language
Sorry to have to say this, but you're completely wrong. Pipelines and variables are two entirely different things.
The pipe (|) connects the output of one cmdlet to the input of another cmdlet. List output is processed one item at a time, i.e. the second cmdlet receives each list item separately.
If you collect a list of items in a variable ($mailbox) and call a cmdlet with that variable you're passing the list object instead of individual list items. That only works if the cmdlet parameter accepts array input.
The pipe operator | i used to flow the output of one command into the input of another command.
The dollar symbolc, $ is used to denote that the name following it is a variable, and has nothing to do with piping data between cmdlets. The where cmdlet create a $_ variable for use within its expression.

using powershell and pipeing output od Select-Object to access selected columns

I have the power shell below that selectes certain fields
dir -Path E:\scripts\br\test | Get-FileMetaData | Select-Object name, Comments, Path, Rating
what i want to do is utilize Name,Comments,Path,Rating in further Pipes $_.name etc dosnt work
If I understand your question correctly, you want to do something with the output of Select-Object, but you want to do it in a pipeline.
To do this, you need to pass the output down the pipeline into a Cmdlet that accepts pipeline input (such as ForEach-Object). If the next operation in the pipeline does not accept pipeline input, you will have to set the output to a variable and access the information through the variable,
Using ForEach-Object
In this method, you will be processing each object individually. This will be similar to the first option in Method 1 (that is, dealing with individual items in the collection of items returned by Select-Object).
dir | Get-FileMetaData | Select-Object Name,Comments,Path,Rating | ForEach-Object {
# Do stuff with $_
# Note that $_ is a single item in the collection returned by Select-Object
}
The variable method is included in case your next Cmdlet does not accept pipeline input.
Using Variable
In this method, you will treat $tempVariable as an array and you can operate on each item. If need be, you can actually access each column individually, getting everything at once.
$tempVariable = dir | Get-FileMetaData | Select-Object Name,Comments,Path,Rating
# Do stuff with each Name by using $tempVariable[i].Name, etc.
# Or do stuff with all Names by using $tempVariable.Name, etc.