Output onscreen very different than output in file

by brittfam at 2012-09-26 11:57:41

Hello,

I am having difficulty formatting a text file (That could also be convertto-html) from a script that I wrote to query my ADCS server for expiring certificates within the next x days.

The premis is this:

I am querying my ADCS servers with an external command "certutil" within a POSH script. The query defaults to 31 days but have parameters that can be changed if desired.
I am then using send-mailmessage to mail the results of the query to a distribution list.

If I run the command without the send-mailmessage and output to scrennthe formatting is as I want it. If however I run the full command, I receive the email and the data but it is all garbled together, no longer resembling anything like I want it. The same is true if I pipe the output to a text file or convertto-html. If however, I open the file with, say, Notepad++ the formatting is correct again. I think it has somthing to do with the way the line endings are interpreted. I am not sure.

Can anyone help me with figuring this out? I do not even know where to look for the answer or how to formulate the question in google. Incidentally, I have tried Google but have not found a good answer.

Thanks in advance for any help.

BB.
by DonJ at 2012-09-26 12:05:39
Can you clarify "garbled?" Is it just a matter of character encoding, perhaps linefeed characters not working out right? That’s kind of a normal thing; the encoding PowerShell uses isn’t the default one Notepad uses. Try opening the file in Notepad using a different encoding (it’s an option on the File Open dialog).
by DonJ at 2012-09-26 12:23:16
BTW… if you’re using ConvertTo-HTML, it shouldn’t matter what the file looks like in Notepad; what it looks like in a Web browser would be the only thing that mattered. But ConvertTo-HTML won’t generate the best results with the output from an external command. You’ll get something, but it won’t necessarily be pretty.

Also, note that Out-File has several output encoding options. If the default one isn’t working for you, try one of the others.
by brittfam at 2012-09-26 12:49:35
By "garbled", I mean that the test is jumbled together without the line breaks and columns that were displayed in the on-screen iteration of running the command. I tried the open the file in notepad (Windows native version) in the various encoding formats and that did not change the way the txt was displayed. If I turn off WordWrap it appears as one line. Using Notepad++ I used EOL conversion to Windows format [CR LF] and NotePad displays it correctly. (I can display the linefeeds in NotePad++) So it seems that I need to do ??somthing?? to convert the lines to CR LF, I think. How would I go about doing that. My goal it to include this as the body of the Send-mailmessage. I can include my script if that would help.

BB
by brittfam at 2012-09-26 12:51:27
Also, I am not as concerned with the convertto-html as I am with sending the output as the body of the send-mailmessage. Is encoding the same as converting the Line feeds?
by brittfam at 2012-09-26 14:23:30
Below is a copy of my script. Names have been changed.

I am storing the results of my certutil query in $certresult. I am using the values in $certresult as the body of the mail message. When the message is delivered, the text is word wrapped. so the columns and line separations that I expect from the on-screen display do not show. If I take the variable $certresult and redirect it to a file and open it in Notepad I need to change the EOL to Windows style (CR LF). However if I create a variable $a = get-content c:\certresults.txt even before changing the EOL settings, and display $a in the cli, it is formatted correctly. I am at a loss of what to do or even how to search in Google. Help is much appreciate.

<#
.SYNOPSIS
Retrieves Certificates expiring in 31 days by default. The CA and number of days can be changed form the default
by using the -config and -int parameters.
.DESCRIPTION
This script queries the Active directory Certificate Services Issuing CA 1
for any expiring certificates within the next 31 days by default. The CA and number of days can be changed
by specifying the -config and -int parameters.
.PARAMETER config
The name of the certificate server to be queried. Name should be in the form of "ServerName\CA Common Name"
The default queries the Issuing CA 1
.PARAMETER int
Number of days. Used in the query to determine the certificates that will expire within the specified number of days.
Use integers like: 1,2, 15, 30, etc.
.EXAMPLE
./get-test
This will get the expiring certificates from the default CA within the next 31 days starting
from the time the script is executed.
.EXAMPLE
./get-test -config ServerName\CA Common Name
This will get the expiring certificates from the specified CA for the next 31 days starting
from the time the script is executed
.EXAMPLE
./get-test -int 15
This will query the default CA for certificates that will expire in the specified number of day as
indicated by the -int parameter.
.EXAMPLE
./get-test -config ServerName\CA Common Name -int 15
This will query the specified CA as indicated by the -config parameter for the specified number
of days as indicated by the -int parameter.
.EXAMPLE
.\get-test -verbose
This will display a message on screen indicating the CA name and number of days being queried
for verification. You may use the -verbose with any of the previous examples to view which CA
and number of days that are being queried.
#>


[cmdletBinding()]
param(
[string]$Config="Machine\Issuing CA 1",
[int]$Num=31
)

$Now = Get-Date -UFormat %x
$Date = $Now.date
$Days = $Num
$31days = ((Get-Date).adddays($Num))| Get-Date -UFormat %x
$CA = $Config
Write-host "Querying $config for certificates expiring within the next $Num days"
$certresult = (certutil.exe -config "$CA" -view -restrict "NotAfter>=$Now,NotAfter<=$31days" -out "commonName,NotAfter")
send-mailmessage -to "some.one@vdomain.com" -from "noreply@domain.com" -subject "this is a test" -body ("$certresult") -smtp "srfs.domain.com"
by DonJ at 2012-09-26 15:05:52
I think you’re more or less on the same problem as viewtopic.php?f=2&t=395. However, there may be a few things you don’t realize.

When PowerShell captures the stdout of an external command, it parses it not as one big hunk of text, but as a collection of String objects. You could probably look at $certresult.count to see that.

When you ask the shell to output $certresult, it uses its default mechanism for displaying collections, which on-screen makes them look like a big chunk of text. But, as you’re seeing, the character encoding isn’t always compatible with stuff like Notepad or e-mail clients. It’s mainly the CRLF thing, which that thread I referenced was also discussing. Take a look at that other thread, where Kirk was discussing how he deals with the situation. You’re welcome to continue that thread if you feel you’re having basically the same issue, but don’t see the solution you need.
by brittfam at 2012-10-09 12:50:01
I have figured out the problem. By piping $certresult to out-string, the formatting is corrected. I am posting my completed script for anyone eals to use or improve upon.

# Written by Brian Britt #
# of Vanderbilt University #
# Directory Services Specialist #
# Please feel free to use and improve giving credit where credit is due. #

<#
.SYNOPSIS
Retrieves Certificates expiring in 31 days by default. The CA and number of days can be changed form the default
by using the -config and -int parameters.
.DESCRIPTION
This script queries the Active directory Certificate Services Issuing CA 1
for any expiring certificates within the next 31 days by default. The CA and number of days can be changed
by specifying the -config and -int parameters.
.PARAMETER config
The name of the certificate server to be queried. Name should be in the form of "ServerName\CA Common Name"
The default queries the Issuing CA 1
.PARAMETER int
Number of days. Used in the query to determine the certificates that will expire within the specified number of days.
Use integers like: 1,2, 15, 30, etc.
.EXAMPLE
./get-test
This will get the expiring certificates from the default CA within the next 31 days starting
from the time the script is executed.
.EXAMPLE
./get-test -config ServerName\CA Common Name
This will get the expiring certificates from the specified CA for the next 31 days starting
from the time the script is executed
.EXAMPLE
./get-test -int 15
This will query the default CA for certificates that will expire in the specified number of day as
indicated by the -int parameter.
.EXAMPLE
./get-test -config ServerName\CA Common Name -int 15
This will query the specified CA as indicated by the -config parameter for the specified number
of days as indicated by the -int parameter.
.EXAMPLE
.\get-test -verbose
This will display a message on screen indicating the CA name and number of days being queried
for verification. You may use the -verbose with any of the previous examples to view which CA
and number of days that are being queried.
#>


[cmdletBinding()]
param(
[string]$Config=(‘hostname1\Issuing CA 1’,‘hostname2\Issuing CA 2’),
[int]$Num=31
)
# Variables used in the script
$Now = Get-Date -UFormat %x
$Date = $Now.date
$Days = $Num
$31days = ((Get-Date).adddays($Num))| Get-Date -UFormat %x
$CA = $Config

# Script block. Takes the contents of $Config and processes each within the for loop. Each value for the parameter is processed before moving to the next value n the parameter.
# The "|" obejct "out-string" in $certresult is used to format the text. Without it, the text would be a single line and unreadable in the email message.

foreach ($c in $ca) {
write-host $c
Write-host ""
$adcsnames = ($c | foreach-object {$_.split("&quot;)[0]})
$Source = ($adcsnames + ‘@emaildomain.com’)
$certresult = (certutil -config "$c" -view -restrict "NotAfter>=$Now,NotAfter<=$31days" -out "NotAfter,certificatetemplate,commonName,Request.SubmittedWhen,Request.RequesterName,Request.CallerName,Request.Disposition,Request.EMail,Request.orgunit") | out-string
write-host "$certresult"
# Sends an email to a recipient or shared mailbox containing the list of expired certificates and the requested schema fields.
send-mailmessage -to "certificate.expirations@emaildomain.com", "notifications@emaildomain.com" -from "$source" -subject "$c Certificate Expirations in the next $num days" -body "Querying $c for Expiring Certificates in $num days nn $certresult" -smtp "smtp.domain.com"
}