Read-Host execution order - powershell

I'm trying to do the following:
Display a list from a multidimensional array:
$AssetList | Where-Object {$_.agenttype -eq "N"} | Select-Object number, hostname, agentstatus | Write-Output
Then read in a number from the user:
$Victim = Read-Host -Prompt 'Enter the number of the host you want rid of...'
What happens in reality is the Read-Host is displayed before the Write-Output. How can I flip it and reverse it?

I think the issue here is that you're using Write-Output (which writes to the output stream for your script/function). If you just want the data to be displayed on the console before the Read-Host cmdlet, try using Out-String | Write-Host. You can probably get away with just Out-String on its own.
Out-String
The Out-String cmdlet converts the objects that Windows PowerShell
manages into an array of strings. By default, Out-String accumulates
the strings and returns them as a single string, but you can use the
stream parameter to direct Out-String to return one string at a time.
This cmdlet lets you search and manipulate string output as you would
in traditional shells when object manipulation is less convenient.
https://technet.microsoft.com/en-us/library/hh849952.aspx

Related

PowerShell: get object properties in a readable table

I am trying to display StrongAuthenticationMethods from the azure object (user) in a more readable way inside of the script which will reset the MFA method.
When I call variable $mfa
$UserPname = Read-Host "Please enter e-mail address"
$AzureUser=Get-MsolUser -UserPrincipalName "$UserPname"
$methode = $AzureUser.StrongAuthenticationMethods
$mfa = $methode | Select-Object MethodType, IsDefault
$mfa
it gives me a nice table:
---------- ---------
PhoneAppOTP False
PhoneAppNotification True
When I try to write-host this variable:
Write-Host $mfa
It gives me:
Write-Host $mfa
#{MethodType=PhoneAppOTP; IsDefault=False} #{MethodType=PhoneAppNotification; IsDefault=
True}
How can I display this MethodType and IsDefault properties in the best readable way using
write-host?
Thanks for the information in advance!
Write-Host ($mfa | Format-Table | Out-String)
To print synchronous, richly formatted output to the host (display), use the Out-Host cmdlet rather than Write-Host: Out-Host uses PowerShell's rich formatting system, whereas Write-Host essentially performs .ToString() stringification, which often results in unhelpful output.
# Forces instant output to the display,
# but note that such output *cannot be captured*.
# Use ... | Format-Table | Out-Host to force tabular formatting,
# but with a custom object with 4 or fewer properties that isn't necessary.
$mfa | Out-Host
Judging by your later comments, the reason you want this is the well-known problem of the situational lack of synchronization between pipeline output and to-host output (as well as output to other streams), which can cause displayed output to print out of order.
# !! Read-Host executes FIRST
[pscustomobject] #{ print='me' }
Read-Host 'Press ENTER to continue'
# Workaround: Out-Host forces instant printing to the display,
# but note that such output then cannot be captured.
[pscustomobject] #{ print='me' } | Out-Host
Read-Host 'Press ENTER to continue'
The problem is limited to a very specific - albeit common - scenario: implicitly table-formatted output for types that do not have formatting data defined for them.
See this answer for a detailed explanation.
See GitHub issue #4594 for a discussion of this problematic behavior.

PowerShell 5.1 I am not getting the expected output in this simple example

$res = Invoke-Sqlcmd -Query "select * from customer" -ServerInstance "(localdb)\MSSQLLocalDB" -Database "Database1" -OutputAs DataTables
$res | Where-Object FirstName -eq "John"
$res.Where({$_.FirstName -eq "John"})
This is the output:
Id FirstName City
-- --------- ----
1 John Augusta
1 John Augusta
I was expecting this:
Id FirstName City
-- --------- ----
1 John Augusta
Id FirstName City
-- --------- ----
1 John Augusta
Building on the helpful comments:
The behavior is by design:
Objects output by a given script or a single interactively submitted command line are all sent to the success output stream of a single pipeline.
When output from a pipeline is neither captured nor redirected, PowerShell applies for-display output formatting to all output objects in the success output stream of a given pipeline, and if the first output object implicitly selects tabular formatting, all subsequent objects of the same type are formatted as part of the same table that is printed to the host (display, terminal).
Things get tricky if subsequent objects are of a different type as well as if they're also [pscustomobject] instances, but with different property sets - see this answer for more information.
If you want to format a given command's output individually, you have three basic options, all of which are suboptimal in case you also want to option to later programmatically process the output, not just format it for display:
Send the output directly to the host, using Out-Host:
$res | Where-Object FirstName -eq "John" | Out-Host
This bypasses the success output stream, meaning that this output cannot be captured or redirected.
Use a Format-* cmdlet such as Format-Table explicitly:
$res | Where-Object FirstName -eq "John" | Format-Table
This sends objects representing formatting instructions rather than the original objects to the success output stream, which the host (as the default output target) renders correctly, but these objects are meaningless for further programmatic processing.
Use Out-String (possibly preceded by a call to a Format-* cmdlet to select the kind of formatting and/or control the specifics of the formatting):
$res | Where-Object FirstName -eq "John" | Out-String
This sends a single, multi-line string to the success output stream that contains the same representation you would see in the host with Out-Host[1] and since strings always render as-is, you'll see the same host output; in programmatic processing, these strings are relatively more meaningful than the formatting-instruction objects output by Format-* cmdlets, but still amount to a loss of information compared to the original output objects.
[1] Unfortunately, Out-String always appends a trailing newline to this representation; this problematic behavior is the subject of GitHub issue #14444. As zett42 points out, using Out-String -Stream avoids this problem, albeit at the expense of sending the lines of the multi-line string representation individually to the output stream; To avoid that, you can use (... | Out-String -Stream) -join "`n"

Again with Powershell: I know how to create a file but I don't know how to write in it [duplicate]

I am creating a script and want to both use Write-Host and Write-Output
As I work I want a backup of information I pull from AD to also become attached to a .txt file.
This is more of a backup in case I miss a piece of information and need to go back and recreate a ticket. Anyways I have a sample of my script, form what I can tell it should be working. If someone with a bit more experience can take a look or point me in the right direction I would appreciate it. If I need to add any more of the script I can provide this. Thanks in Advance.
Import-Module activedirectory
$object = Get-ADUser $sid -Properties * | Select-Object EmailAddress
Write-Host Email: $object.EmailAddress
Write-Output ("Email: $object.EmailAddress") >> C:\psoutput\psoutput.txt -Append
This will create the .txt file of course but is also add other information such as:
Email: #{GivenName=myfirstname; Surname=mylastname; SamAccountName=myid; DisplayName=lastname, firstname - Contingent Worker; City=; EmailAddress=myemailaddress#mywork.com; EmployeeID=; Enabled=True; OfficePhone=; MobilePhone=(555) 555-5555; LockedOut=False; LockOutTime=0; AccountExpirationDate=05/09/2020 00:00:00; PasswordExpired=False; PasswordLastSet=12/03/2019 12:16:37}.EmailAddress
-Append
I am looking to have the output like the following...
name: username
email: user email address
phone: user phone number
etc...
All general information from Active Directory
Thanks again for the suggestions
Don't use write-output. Use (Get-ADUser $sid -properties mail).mail.
Like this:
Add-Content -Path "FilePath" -Value "Email: $((Get-ADUser $sid -properties mail).mail)"
Write-Output ("Email: $object.EmailAddress")
As an aside: No need for (...) here.
This doesn't do what you expect it to: it stringifies $object as a whole and then appends .EmailAddress verbatim; in order to embed an expression, such as accessing a property inside "..." (an expandable string), you need $(), the subexpression operator.
Write-Output "Email: $($object.EmailAddress)" >> C:\psoutput\psoutput.txt
See this answer for an overview of the syntax in PowerShell expandable strings.
Or, more simply, using PowerShell's implicit output behavior (use of Write-Output is rarely necessary):
"Email: $($object.EmailAddress)" >> C:\psoutput\psoutput.txt
>> C:\psoutput\psoutput.txt -Append
>> is effectively an alias for Out-File -Append (just like > is for just Out-File), so not only is there no need for -Append, it isn't interpreted by >>, which accepts only the filename operand.
Instead, -Append was interpreted by Write-Output, which is why it ended up literally in your output file.
Perhaps surprisingly, while a redirection such as >> C:\psoutput\psoutput.txt is typically placed last on the command line, that is not a syntactic requirement: other arguments may follow.
I am looking to have the output like the following..
It sounds like you want formatting as provided by the Format-List cmdlet:
$object | Format-List >> C:\psoutput\psoutput.txt
Note that > / >> / Out-File apply the default string formatting, i.e. the same representation that would by default display in the console.
By using an explicit Format-* cmdlet, you can control that formatting, but note two things about Out-File in general:
As you're outputting for-display formats, the resulting file may not be suitable for further programmatic processing.
To prevent truncation of values, you may have to pass a -Width argument to Out-File, control the enumeration length of nested properties with $FormatEnumerationLimit, and, in the case of Format-Table, specify -AutoSize.
You don't really need to use Write-Output at all. Try this to just get your string to your file:
("Email: " + $object.EmailAddress) >> C:\psoutput\psoutput.txt
You don't need to specify append because '>>' already does that for you

Variable from read-host not working in select-object

i encountered a problem when trying to build a small read-host script that pipes the result into an AD script:
when doing an AD search, i can select objects like that:
get-aduser exampleuser | select-object name,enabled
though when i enter
name,enabled
into the read-host that stores the input as a variable which later gets used in the script, it turns into this:
name,enabled
------------
{}
instead of this (when typing it manually)
get-adcomputer CTXTEST | select-object name,enabled
name enabled
---- -------
CTXTEST False
I assume that I overlook something rather simple, but I tried a lot of things and didnt find a solution through master Google.
Thats because the Select-Object cmdlet takes an array of strings, the Read-Host cmdlet only returns a singe string, even if the string contains a comma.
You can simply create an array by splitting the input on a comma. (I also use the regex to trim whitespaces):
$selectResult = (Read-Host "Which properties?") -split '\s*,\s*'

How do I remove newline from a PowerShell variable

I'm trying to do some processing logic - running some commands in parallel based on the tree configuration CSV file:
Operation;Parent;Enabled;Propagated;Job_ID;Status;Started;Finished
CA1;n/a;Y;N;;;;
PROD1;n/a;Y;N;;;Y;
CON1;CA1;N;N;;;Y;
CON2;CON1;N;N;;;Y;
I load the file into the variable and then I'm trying to find the next step which needs to be processed:
$Data = Import-Csv -delimiter ";" .\config.csv
$NextStep = $Data | Select-Object -first 1 | Where-Object {$_.Started -eq ""}
$NextStepText = $NextStep.Operation | ft -autosize | out-string
The problem is that it seems like $NextStep.Operation contains new line character. When I display it I get:
PS C:\temp\SalesForce> $NextStep.operation
CA1
PS C:\temp\SalesForce> $NextStep.Operation.Contains("`n")
False
Do you know what I'm doing wrong? I would like to display the content without the "dummy" new line character which is there even if contains method is saying it is not there.
Or please advise how to do it better. I'm still learning PowerShell; so far I just google the commands, and I'm trying to put it together.
The newline isn't in your data, it's being added by Out-String. Observe the output of the following (in particular, where you do and don't get the newline after CA1):
$Data = import-csv -delimiter ";" .\config.csv
$NextStep = $Data | select-object -first 1 | where-object {$_.Started -eq ""}
$NextStepText = $NextStep.Operation | ft -autosize | out-string
"hi"
$NextStepText
"hi"
$NextStep.Operation;
"hi"
$NextStep.Operation | ft -autosize
"hi"
You shouldn't be using Format-Table at that step (and Out-String is unnecessary in this script) if you intend to use $NextStepText for anything other than direct output later on. Consider Format-Table (or any of the Format-* cmdlets) the end of the line for usable data.
Why do you think that there is a new line character of some sort in there? If you are using the ISE then what you posted doesn't look like there is. It is normal to have a blank line between commands (in the v2/v3 ISE, not sure about v4), so what you posted would not indicate that it contains any new line characters.
You can always check the $NextStep.Operation.Length to see if it says 3 or 4. If there is a `n in there it'll show up in the length. For example (copied and pasted out of my v3 PS ISE):
PS C:\> $test = "Test`nTest2"
PS C:\> $test
Test
Test2
PS C:\> $test.Length
10
PS C:\>
That was to show that there is a new line character injected by following it with text, without any text following the new line character it looks like this:
PS C:\> $test = "Test`n"
PS C:\> $test
Test
PS C:\> $test.Length
5
PS C:\>
You'll notice that there are 2 blank lines after the text "Test" on the second command. The first is the line injected into the variable, and the second is the obligatory line that PS puts in to show separation between commands.
Out-String unexpectedly appends a trailing newline to the string it outputs.
This problematic behavior is discussed in GitHub issue #14444.
A simple demonstration:
# -> '42<newline>'
(42 | Out-String) -replace '\r?\n', '<newline>'
However, you neither need Format-Table nor Out-String in your code:
Format-* cmdlets output objects whose sole purpose is to provide formatting instructions to PowerShell's for-display output-formatting system. In short: only ever use Format-* cmdlets to format data for display, never for subsequent programmatic processing - see this answer for more information.
Out-String is capable of interpreting these formatting instructions, i.e. it does produce data - in the form of a single, multi-line string by default - that is the string representation of what would print to the display.
As such, the resulting string contains a representation for the human observer, not a structured text format suitable for programmatic processing.
In your case, Format-Table is applied to a string, which is pointless, because strings always render as themselves, in full (-AutoSize has no effect); piping to Out-String then in effect returns the original string with an (undesired) newline appended.
Therefore, use a simple variable assignment to store the property value of interest in a separate variable:
$NextStepText = $NextStep.Operation