output will not format correctly - powershell

I am having some problems writing to a text file with powershell using the correct format. I am pulling some windows event logs and saving the output to a text file. When I issue the following 2 commands
$event = get-winevent -filterhashtable #{logname='ForwardedEvents'; id=4688}
$event | format-list >> C:\scripts\file01.txt
I get something like this saved to the text file with a bunch of white space and the line broken up because it is too long:
New Process Name:
C:\Windows\System32\SearchFilterHost.exe
Is there anyway to make the output look like this instead:
New Process Name: C:\Windows\System32\SearchFilterHost.exe
thanks

If you're only interested in the message content of the event, don't pipe the entire event object through Format-List - output the Message property directly instead:
$evt.Message >> C:\scripts\file01.txt

Related

PowerShell Log Function Readability

Currently my log function spits out the information in a single column and is hard to read. Is there a way to make it split up into different columns which each (DisplayName, PoolName, PoolSnapshot, and DesktopSVIVmSnapshot) and its respective information is put correctly?
function log ([string]$entry) {
Write-Output $entry | Out-File -Append "C:\logs\SNAPSHOT.csv"
}
Add-PSSnapin Quest.ActiveRoles.ADManagement
$date = Get-Date -Format "MM-dd-yyyy"
$time = Get-Date -Format "hh:mm:sstt"
# begin log
log $(Get-Date)
log "The below Desktops are not using the correct Snapshot."
if (#($DesktopExceptions).Count -lt 1) {
Write-Output "All desktops in $pool are currently using the correct snapshots." |
Out-File -Append "C:\logs\SNAPSHOT.csv"
} else {
Write-Output $DesktopExceptions |
Select-Object DisplayName,PoolName,PoolSnapshot,DesktopSVIVmSnapshot |
sort DisplayName |
Out-File -Append "C:\logs\SNAPSHOT.csv"
}
log $(Get-Date)
09/11/2017 12:16:17
DisplayName PoolName PoolSnapshot DesktopSVIVmSnapshot
----------- -------- ------------ --------------------
xxxc-13v xxxc-xxx /8-11-2017/09-07-2017 /8-11-2017
xxxc-15v xxxc-xxx /8-11-2017/09-07-2017 /8-11-2017
xxxc-1v xxxc-xxx /8-11-2017/09-07-2017 /8-11-2017
xxxc-20v xxxc-xxx /8-11-2017/09-07-2017 /8-11-2017
Note: I removed parts of the log for in the hopes to not make the post long.
CSV files require uniform lines: a header line with column names, followed by data lines containing column values.
By writing the output from Get-Date first - a single date/time string - followed by another single-string output, followed by multi-column output from your $DesktopExceptions | Select-Object ... call, you're by definition not creating a valid CSV file.
If you still want to create such a file:
log (Get-Date) # With a single command, you don't need $(...) - (...) will do.
log "The below Desktops are not using the correct Snapshot."
If ($DesktopExceptions) # a non-empty array / non-$null object
{
log ($DesktopExceptions |
Select-Object DisplayName,PoolName,PoolSnapshot,DesktopSVIVmSnapshot |
Sort-Object DisplayName |
ConvertTo-Csv -NoTypeInformation)
}
Else
{
log "All desktops in $pool are currently using the correct snapshots."
}
log (Get-Date)
By defining your log() function's parameter as type [string], you're effectively forcing stringification of whatever object you pass to it. This stringification is the same you get when you embed a variable reference or command inside "..." (string expansion / interpolation) - but it is not the same as what you get by default, when you print to the console.
Out-File, by contrast, does result in the same output you get when printing to the console, which, however, is a format for human consumption, not for machine parsing (as CSV is, for instance).
To get CSV-formatted output, you must either use Export-Csv - to write directly to a file - or ConvertTo-Csv- to get a string representation.
Also note that there's typically no reason to use Write-Output explicitly - any command / expression's output that is not explicitly assigned to a variable / redirected (to a file or $null) is implicitly sent to PowerShell's [success] output stream; e.g., Write-Output Get-Date is the same as just Get-Date.
It looks like you're just writing an object, and taking the default PowerShell formatter behavior.
A better thing to do is make your log only responsible for one thing - writing messages to a file (no formatting). Here's an example of what you might try:
function Write-LogMessage {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, HelpMessage = "The text-content to write to the log file.",
ValueFromPipeline = $true)]
[string]$Text
)
Process {
Write-Host -ForegroundColor Green $Text
}
}
Set-Alias log Write-LogMessage
Note: This example writes directly to the PowerShell console, but you would in practice need to direct output to a file (using Out-File or one of the redirection operators - see Get-Help about_Operators).
To use it, you would write something like this:
"This is a message that would be written" | Write-LogMessage
For your specific example, you could just format the message inline, and pipe it:
Write-Output $DesktopExceptions | Select-Object DisplayName,PoolName,PoolSnapshot,DesktopSVIVmSnapshot | sort DisplayName | ForEach-Object { "{0}: Host = {1}, Pool = {2}, Pool SN = {3}, SVIV Snapshot = {4}" -f (Get-Date), $_.DisplayName, $_.PoolName, $_.PoolSnapshot, $_.DesktopSVIVmSnapshot } | log
Note that you don't need the log statement: just add formatting before piping to the Out-File cmdlet, and you'll get what you're after.
Edit: The OP asked in the original post how to format columns (tabular output). To achieve this, you can use either the ConvertTo-Csv or Export-Csv cmdlets (generally, you would use the -NoTypeInformation switch parameter with these commands, to avoid the first line of the output being a type definition). An example of this is:
$DesktopExceptions | Select-Object DisplayName,PoolName,PoolSnapshot,DesktopSVIVmSnapshot | sort DisplayName | Export-Csv C:\Temp\Datum.csv -NoTypeInformation
As pointed out in another answer, using Write-Output is not required, because PowerShell automatically writes all output to the output stream unless otherwise directed (using file redirection, a redirection operator, or the Out-Null cmdlet).
Please read my answer as part solution and part advice.
The "problem" with PowerShell is that it doesn't capture only the output of your code. It will capture output from other scripts, modules and executables. In other words, any attempt to make logging behave like it's generated by e.g. C# with NLOG, has an inherent problem.
I looked into this subject myself for a complex continuous delivery pipeline I'm building. I understood that a structured log will not be 100% possible and therefore I accepted the purpose of PowerShell transcription (Start-Transcript). But still I wanted to avoid creating functions like Write-Log and if possible provide an enhanced output for all code that uses Write-Debug, Write-Verbose functionality.
I ended up creating XWrite PowerShell module which works very well, even to my own suprize. I use it because it enhances the produced trace message by the caller's name (cmdlet or script) and a timestamp. The caller's name helps a lot with troubleshooting and the timestamp I use to implicitly benchmark. here are a couple of example
DEBUG: Test-MyXWrite.ps1: Hello
DEBUG: Script: Test-MyXWrite.ps1: 20170804: 10:57:27.845: Hello
There are some limitations though. Any binary's code trace output will not be enhanced. Also if a cmllet refers explicitly to the Write-* using their full namespace it will not work. To capture line by line all trace and output requires some very deep into the .net types of PowerShell implementation hooking. There is a guy who has done this, but I don't want to get influence the PowerShell process's behavior that aggresively. And at this moment I believe that to be the role of the transcription.
If you like the idea, install the module from XWrite
At some point, I would like to extend the module with a redirection to telemetry services, but I've still not decided I want to do that, because I will not capture the above mentioned exceptions and other executable. It will just offer me visible progress as the script is executing.

Use an XmlWriter to fill the Powershell output stream line by line

It is possible to "open" the Powershell pipeline output stream as a System.IO.Stream or as TextWriter/XmlWriter?
Basically I want to convert some pipeline input strings or objects to Xml text using the services of XmlWriter like namespace handling and formatting capabilities. I want so send the result of conversion, incrementally, to the pipeline output, and not build the full output in one big string and send the whole output the the end.
If I'm understanding you correctly won't something really simple as this do what you want?
$XmlWriter = New-Object System.XMl.XmlTextWriter("c:\temp\output.xml",$Null)
Get-Content "file_with_input.txt" | foreach-object {[do your stuff with the XmlWriter]}
The lines from the input file will arrive one at a time and you can handle them as you please.

Strange output combining Tee-object and $(Get-Date)

I am trying to append output to a .txt file. My command is as follows:
ni C:\example\example.txt -type file -value "`n$(Get-Date)"|out-null
$CSVvariable | sort Property | Format-Table | Tee-Object -Append -FilePath C:\example\example.txt
When I run the command I get the following output in the .txt file:
《⼹㔰㈯㄰″㤱㐺㨹
(The table is being presented correctly.)
$(Get-Date) seems to become the above symbols. Anyone have an idea why ?
Thanks in advance.
You don't really want to use format-table inside a pipeline like this. The output of format-table is a collection of "formatting objects" that the host interprets and are pretty much incomprehensible. The last time I checked, they weren't even documented well.
If you really want the table formatting, you can try adding out-string to the pipeline before the tee-object, but at that point you'll have a collection of strings, not "objects".
That's a separate issue from why you got strange characters, but I'd try removing the format-table and see how the file looks.
What is your CultuerInfo?
([System.Threading.Thread]::CurrentThread.CurrentCulture).DateTimeFormat
It seems like it might be set to something other than en-US.

Powershell: How to capture output from the host

I am using powershell to automate some tasks related to checking out/merging in TFS. When I call
tf get * /recurse
I get a bunch of data scrolling by about the files that are getting checked out. The last line generated by this command (assuming its success) is one telling the checkin number. I would like to parse this out so it can be used later on in my script.
I know that I can do something like
$getOutput = tf get * /recurse
but then the output is suppressed entirely and I want the output of that command to be scrolled in realtime. I would basically like to grab everything that just got sent to the output buffer.
Try something like this:
tf get * /recurse | tee-Object -Variable getOutput
The tee-object in PowerShell 2.0 allows you to pipe results to two sources. If you leave the second source empty, the results go to the console.
ls | tee-object -filePath directoryListing.txt
This will write the directory listing to both the console and a text file.

Output from external exe and my custom objects in powershell

(Sorry for strange title, haven't come up with anything better..)
Background
I use nunit-console to test my assemblies. It is called like this (simplified):
function Test-ByNunit {
param($assembly, $tempFile = 'c:\temp\nunit.xml')
& <path-to-nunit-console> $assembly /nologo /xml:$tempFile #othparam
}
Test-ByNunit c:\temp\myAssembly.dll
I have no problem with this, it works fine.
Problem
nunit-console should output its messages as so far. That means - if not captured, it should send them to screen, otherwise it could be stored in file (Test-ByNunit $dll | set-content path)
I'd like to return somehow information about each test-case that was run (the info is stored in the /xml file) in form of array of PSObject objects.
Question
Do you have any tip how to return the info and still leave nunit output its messages?
If I simply write it to output, the function will return array of strings (output from nunit-console) and array of my objects. Then redirection to output file will store my objects as well, but I'd like just display them in console window.
The only possibility that could work is to use [ref], but I'd like to avoid it.
(this is not only about nunit-console, but of course it is general question)
If I got the task right then Out-Host should help:
function Get-WithOutHost {
# external output is redirected to the host
cmd /c dir | Out-Host
# normal output to be reused later
Get-Process
}
# call
$result = Get-WithOutHost
# now $result holds the data to use, external output is on the screen
EDIT: of course this is not enough if external output should be reused, too, not just shown