PowerShell Scripting Help - Generate a System Uptime Report that will send an daily html email blast to my team : < 30 days critcal systems as RED, 21 to 29 days as Yellow, and 0 to 20 days GREEN

Hi PowerShell Gurus,

Happy New Year Guys!

Once again, I need your help.

The company I work for has tasked me with generating a daily report, html preferred sent via email, of the Uptime of Windows Servers in our environment.

1. Query only active servers in our domains (Get-AdComputer -Filter {OperatingSystem -like "*windows*server*"} but I am having issues with the syntax of only active\enabled systems)
2. Generate an "Uptime Report\Date of Report" in an excel-like html format with systems that have been up longer than 30 days as red, systems that will need a reboot soon (21 to 29 days) yellow, and systems that are still within the 0 to 20 days window, as green
3. Email a list of users, daily, with this report so they can remediate these systems during our scheduled maintenance window

I found the system uptime code on the internet:

<#
.SYNOPSIS
Outputs the last bootup time and uptime for one or more computers.

.DESCRIPTION
Outputs the last bootup time and uptime for one or more computers.

.PARAMETER ComputerName
One or more computer names. The default is the current computer. Wildcards are not supported.

.PARAMETER Credential
Specifies credentials that have permission to connect to the remote computer. This parameter is ignored for the current computer.

.OUTPUTS
PSObjects containing the computer name, the last bootup time, and the uptime.
#>

[CmdletBinding()]
param(
  [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
    $ComputerName,
  [System.Management.Automation.PSCredential]
    $Credential
)

begin {
  function Out-Object {
    param(
      [System.Collections.Hashtable[]] $hashData
    )
    $order = @()
    $result = @{}
    $hashData | ForEach-Object {
      $order += ($_.Keys -as [Array])[0]
      $result += $_
    }
    New-Object PSObject -Property $result | Select-Object $order
  }

  function Format-TimeSpan {
    process {
      "{0:00} d {1:00} h {2:00} m {3:00} s" -f $_.Days,$_.Hours,$_.Minutes,$_.Seconds
    }
  }

  function Get-Uptime {
    param(
      $computerName,
      $credential
    )
    # In case pipeline input contains ComputerName property
    if ( $computerName.ComputerName ) {
      $computerName = $computerName.ComputerName
    }
    if ( (-not $computerName) -or ($computerName -eq ".") ) {
      $computerName = [Net.Dns]::GetHostName()
    }
    $params = @{
      "Class" = "Win32_OperatingSystem"
      "ComputerName" = $computerName
      "Namespace" = "root\CIMV2"
    }
    if ( $credential ) {
      # Ignore -Credential for current computer
      if ( $computerName -ne [Net.Dns]::GetHostName() ) {
        $params.Add("Credential", $credential)
      }
    }
    try {
      $wmiOS = Get-WmiObject @params -ErrorAction Stop
    }
    catch {
      Write-Error -Exception (New-Object $_.Exception.GetType().FullName `
        ("Cannot connect to the computer '$computerName' due to the following error: '$($_.Exception.Message)'",
        $_.Exception))
      return
    }
    $lastBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($wmiOS.LastBootUpTime)
    Out-Object `
      @{"ComputerName" = $computerName},
      @{"LastBootTime" = $lastBootTime},
      @{"Uptime"       = (Get-Date) - $lastBootTime | Format-TimeSpan}
  }
}

process {
  if ( $ComputerName ) {
    foreach ( $computerNameItem in $ComputerName ) {
      Get-Uptime $computerNameItem $Credential
    }
  }
  else {
    Get-Uptime "."
  }
}

Open in new window


I have crafted this from online research and trial and error:

Get-ADComputer -Filter {(OperatingSystem -Like “*Windows*Server*”)} | Where {$_.Enabled -eq $true} | Select-Object -ExpandProperty Name | Sort-object | .\Get-Uptime.ps1

Open in new window


These are my results (Beginning Stages) - still receiving RPC errors:

PS G:\Status Report Script> Get-ADComputer -Filter {OperatingSystem -like "*windows*server*"}|Where {$_.Enabled -eq $true}|Select-Object -ExpandProperty Name | Sort-Object | .\Get-Uptime.ps1

ComputerName                                         LastBootTime                                         Uptime                                              
------------                                         ------------                                         ------                                              
*********                                           12/17/2018 5:35:38 AM                                15 d 14 h 35 m 24 s                                 
Get-Uptime : Cannot connect to the computer '***********' due to the following error: 'The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)'
At G:\Status Report Script\Get-Uptime.ps1:91 char:7
+       Get-Uptime $computerNameItem $Credential
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Get-Uptime
 
Get-Uptime : Cannot connect to the computer '**********' due to the following error: 'The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)'
At G:\Status Report Script\Get-Uptime.ps1:91 char:7
+       Get-Uptime $computerNameItem $Credential
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Get-Uptime
 
'**********'                                         12/26/2018 12:38:04 PM                               06 d 07 h 32 m 59 s                                 
Get-Uptime : Cannot connect to the computer '**********' due to the following error: 'The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)'
At G:\Status Report Script\Get-Uptime.ps1:91 char:7
+       Get-Uptime $computerNameItem $Credential
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Get-Uptime
 
Get-Uptime : Cannot connect to the computer '**********' due to the following error: 'The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)'
At G:\Status Report Script\Get-Uptime.ps1:91 char:7
+       Get-Uptime $computerNameItem $Credential
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Get-Uptime
 
 '**********'                                        12/20/2018 6:08:48 AM                                12 d 14 h 02 m 36 s                                 
 '**********'                                        12/20/2018 6:07:51 AM                                12 d 14 h 03 m 33 s                                 
Get-Uptime : Cannot connect to the computer '**********' due to the following error: 'The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)'
At G:\Status Report Script\Get-Uptime.ps1:91 char:7
+       Get-Uptime $computerNameItem $Credential
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Get-Uptime

Open in new window


So, I am asking the experts what do I need to do to put this all together?  I have an SMTP server that is accessible, sent messages to my work account today, but I am not sure how to set this script up as I need it and the business is wanting it.  :(

Thanks guys!
Mark RoddySystem AdministratorAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

CoralonCommented:
You're probably better off using a Test-Connection (which is a ping) instead of Enabled/Disabled.   That will help avoid the RPC not available message (not completely, but it will help a lot).

I did some playing with that WMI call, and the date time isn't easily parsed as a datetime object,  From what I can see you have to break it out to convert it to datetime object.  Since it's a very straightforward string, it'd be easy to parse it out manually to get a date time object.  Are all your machines in a single timezone?  (that matters if they are going to be contacted from a different timezone).

I reworked it, but left some of it to do... (primarily building the CSS rules for the HTML body at line 41)..   I'm sure there will be suggestions to optimize this, but it should get you going..   The main thing is that the ComputerName parameter will take multiple entries, and it will then process them.  So, you could use your Get-ADComputer line and pipe it directly to this function, or gather the names into a single variable and use that as the input to -ComputerName, etc..

[CmdletBinding()]
param(
    [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string[]]$ComputerName,

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [System.Management.Automation.PSCredential]$Credential = (Get-Credential),

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]$SMTPServer = 'smtp.domain.com', 

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]$SMTPTo = @('address1@domain.com', 'address2@domain.com'),

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]$SMTPFrom = 'ServerUptimeReport@domain.com',

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]$SMTPSubject = 'Server Uptime Report',

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [int]$SMTPPort = 25
)

$Now = [datetime]::Now 
$GreenMinUpDays = 0
$GreenMaxUpDays = 20
$YellowMinUpDays = 21
$YellowMaxUpDays = 29
$RedMinUpDays = 30

$HTML = [System.Collections.ArrayList]::new()
$Tab = "`t"
$CRLF = "`r`n"

$Null = $HTML.Add( ('<HTML>') )
$Null = $HTML.Add( ('{0}<HEAD>' -f $Tab) )
$Null = $HTML.Add( ('{0}<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">' -f $Tab) )
$Null = $HTML.Add( ('{0}<title>Crimson Editor Setup</title>' -f $Tab) )
$Null = $HTML.Add( ('{0}<style>' -f $tab) )
$Null = $HTML.Add( ('put in CSS rules here.. 1 per line'))
$Null = $HTML.Add( ('{0}</style>' -f $tab) )
$Null = $HTML.Add( ('{0}<body>' -f $tab) )
$Null = $HTML.Add( ('{0}{0}<table>' -f $Tab ) )
$Null = $HTML.Add( ('{0}{0}{0}<th note="put in table header row, etc. here">') )

foreach ($Computer in $ComputerName)
{
    if (Test-Connection -ComputerName $Computer -Quiet -Count 2)
    {
        try
        {
            $wmiObject = Get-WmiObject -Class Win32_OperatingSystem -Property LastBootUpTime -ComputerName $Computer -Credential $Credential
            $UptimeString = $wmiObject.LastBootUpTime
            $LastBootYear = $UptimeString.SubString(0,4)
            $LastBootMonth = $UptimeString.SubString(4,2)
            $LastBootDay = $UptimeString.SubString(6,2)
            $LastBootHour = $UptimeString.SubString(8,2)
            $LastBootMinute = $UptimeString.SubString(10,2)
            $LastBootSecond = $UptimeString.SubString(12,2)
            $LastBootTime = Get-Date -Year $LastBootYear -Month $LastBootMonth -Day $LastBootDay -Hour $LastBootHour -Minute $LastBootMinute -Second $LastBootSecond
            $UptimeSpan = New-TimeSpan -Start $LastBootTime -End $Now
            $UptimeDays = [int]($UptimeSpan.TotalDays)
    
            if ($UptimeDays -ge $GreenMinUpDays -and $UptimeDays -le $GreenMaxUpDays)
            {
                $BootState = 'Green'
            }
            elseif ($UptimeDays -ge $YellowMinUpDays -and $UptimeDays -le $YellowMaxUpDays)
            {
                $BootState = 'Yellow'
            }
            elseif ($UptimeDays -ge $RedMinUpDays)
            {
                $BootState = 'Red'
                $UpTimeDays = -1
            }
    
        }
        catch 
        {
            Write-Error -Exception (New-Object $_.Exception.GetType().FullName, ('Cannot connect to the computer ({0}) due to the following error: {1}' -f $Computer, $_.Exception.Message), $_.Exception)
            $BootState = 'FAILED'
        }
    }
    else 
    {
        Write-Warning -Message ('{0} is not reachable' -f $Computer)
        $BootState = 'UNREACHABLE'
        $UptimeDays = -1

    }

    $Null = $HTML.Add( ('{0}{0}{0}<tr>' -f $Tab) )
    $Null = $HTML.Add( ('{0}{0}{0}{0}<td class="{1}">{1}</td>' -f $Tab, $BootState, $Computer) )
    $null = $HTML.Add( ('{0}{0}{0}{0}<td class="{1}">{2}</td>' -f $Tab, $BootState, $UptimeDays) )
    $Null = $HTML.Add( ('{0}{0}{0}</tr>' -f $Tab) )
}

$Null = $HTML.Add( ('{0}{0}</table>' -f $Tab ) )
$Null = $HTML.Add( ('{0}</body>' -f $tab) )
$Null = $HTML.Add( ('</html>' -f $tab) )

Send-MailMessage -To $SMTPTo -From $SMTPFrom -SmtpServer $SMTPServer -Port $SMTPPort -Body ($HTML -join $CRLF) -BodyAsHtml

Open in new window


More error checking could be added, or the html could be dumped to a file and sent as an attachment, etc.. but it should be a good start..
Éric MoreauSenior .Net ConsultantCommented:
for the RPC error, I strongly suspect the firewall. Have a look at http://techgenix.com/troubleshoot-rpc-server-unavailable/
Mark RoddySystem AdministratorAuthor Commented:
Hi Guys,

Thank you both for your input.

Coralon: Thanks a ton for writing that up for me!  I will test it and let you know how it is working out for me!

Eric:  I think your assumptions are correct.  There are some firewall rules that need to be implemented to get WRM and RPC calls enabled.  Thanks for that article.  I will get with our network guy to make the necessary changes.

I will report back soon!

Mark
Maximize Customer Retention with Superior Service

The IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy for valuable how-to assets including sample agreements, checklists, flowcharts, and more to help build customer satisfaction and retention.

CoralonCommented:
Just don't forget... the HTML sections need some work.. (I ran out of time to work it out, since I'm not much of an HTML guy).. :-)

Coralon
Mark RoddySystem AdministratorAuthor Commented:
Hi Guys,

Unfortunately I am not a html guru myself.  I ran it again today, tweaking your script a little bit, but it was throwing errors rendering the html pages.

I will post some more tomorrow.  Getting ready to go to bed.

Mark
CoralonCommented:
Yeah it would.. there's a lot more that needs to be done for this.  I'm actually using my own answer here as a template for a very similar thing here at work.. once I get this worked out, I'll try to remember to post the sanitized version here..  

I'm glad to help here if you'll post the errors.  

Coralon
Mark RoddySystem AdministratorAuthor Commented:
Hi Coralon,

Thanks for the assist! Still receiving RPC errors when trying to enumerate the found systems BUT I am still having issues with writing to the html array.  Here is my current code:

[CmdletBinding()]
param(
    #[parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    #[string[]]$ComputerName = (Get-ADComputer -Filter {(OperatingSystem -Like “*Windows*Server*”)} | Where {$_.Enabled -eq $true} | Select-Object -ExpandProperty Name),

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [System.Management.Automation.PSCredential]$Credential = (Get-Credential),

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]$SMTPServer = '***.***.***.***', 

    #[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    #[string]$SMTPTo = @('****@***.***','***@***.***'),

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]$SMTPFrom = 'ServerUptimeReport@***.***',

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [string]$SMTPSubject = 'Server Uptime Report',

    [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
    [int]$SMTPPort = 25
)
$ComputerName = Get-ADComputer -Filter {OperatingSystem -like "Windows Server*"} -Properties * | Select-Object -ExpandProperty Name
$SMTPto = "***@***.***","***@***.***" 
$Now = [datetime]::Now 
$GreenMinUpDays = 0
$GreenMaxUpDays = 20
$YellowMinUpDays = 21
$YellowMaxUpDays = 29
$RedMinUpDays = 30

$HTML = New-Object -TypeName System.Collections.ArrayList[string]
$Tab = "`t"
$CRLF = "`r`n"

 if($files -ne $null){
$Null = $HTML.Add( ('<HTML>') )
$Null = $HTML.Add( ('{0}<HEAD>' -f $Tab) )
$Null = $HTML.Add( ('{0}<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">' -f $Tab) )
$Null = $HTML.Add( ('{0}<title>Crimson Editor Setup</title>' -f $Tab) )
$Null = $HTML.Add( ('{0}<style>' -f $tab) )
$Null = $HTML.Add( ('put in CSS rules here.. 1 per line'))
$Null = $HTML.Add( ('{0}</style>' -f $tab) )
$Null = $HTML.Add( ('{0}<body>' -f $tab) )
$Null = $HTML.Add( ('{0}{0}<table>' -f $Tab ) )
$Null = $HTML.Add( ('{0}{0}{0}<th note="put in table header row, etc. here">') )}

foreach ($Computer in $ComputerName)
{
    if (Test-Connection -ComputerName $Computer -Quiet -Count 2)
    {
        try
        {
            $wmiObject = Get-WmiObject -Class Win32_OperatingSystem -Property LastBootUpTime -ComputerName $Computer -Credential $Credential
            $UptimeString = $wmiObject.LastBootUpTime
            $LastBootYear = $UptimeString.SubString(0,4)
            $LastBootMonth = $UptimeString.SubString(4,2)
            $LastBootDay = $UptimeString.SubString(6,2)
            $LastBootHour = $UptimeString.SubString(8,2)
            $LastBootMinute = $UptimeString.SubString(10,2)
            $LastBootSecond = $UptimeString.SubString(12,2)
            $LastBootTime = Get-Date -Year $LastBootYear -Month $LastBootMonth -Day $LastBootDay -Hour $LastBootHour -Minute 
            $LastBootMinute -Second $LastBootSecond
            $UptimeSpan = New-TimeSpan -Start $LastBootTime -End $Now
            $UptimeDays = [int]($UptimeSpan.TotalDays)
    
            if ($UptimeDays -ge $GreenMinUpDays -and $UptimeDays -le $GreenMaxUpDays)
            {
                $BootState = 'Green'
            }
            elseif ($UptimeDays -ge $YellowMinUpDays -and $UptimeDays -le $YellowMaxUpDays)
            {
                $BootState = 'Yellow'
            }
            elseif ($UptimeDays -ge $RedMinUpDays)
            {
                $BootState = 'Red'
                $UpTimeDays = -1
            }
    
        }
        catch 
        {
            Write-Error -Exception (New-Object $_.Exception.GetType().FullName, ('Cannot connect to the computer ({0}) due to the following error: {1}' -f $Computer, $_.Exception.Message), $_.Exception)
            $BootState = 'FAILED'
        }
    }
    else 
    {
        Write-Warning -Message ('{0} is not reachable' -f $Computer)
        $BootState = 'UNREACHABLE'
        $UptimeDays = -1

    }
    if($files -ne $null){
    $Null = $HTML.Add( ('{0}{0}{0}<tr>' -f $Tab) )
    $Null = $HTML.Add( ('{0}{0}{0}{0}<td class="{1}">{1}</td>' -f $Tab, $BootState, $Computer) )
    $null = $HTML.Add( ('{0}{0}{0}{0}<td class="{1}">{2}</td>' -f $Tab, $BootState, $UptimeDays) )
    $Null = $HTML.Add( ('{0}{0}{0}</tr>' -f $Tab) )
}
if($files -ne $null){
$Null = $HTML.Add( ('{0}{0}</table>' -f $Tab ) )
$Null = $HTML.Add( ('{0}</body>' -f $tab) )
$Null = $HTML.Add( ('</html>' -f $tab) )}
}

Send-MailMessage -To $SMTPTo -From $SMTPFrom -SmtpServer $SMTPServer -Port $SMTPPort -Body ($HTML -join $CRLF) -BodyAsHtml

Open in new window

I was receiving an error with the original $HTML array.  So I changed it and now I am receiving:

New-Object : Cannot find type [System.Collections.ArrayList[string]]: verify that the assembly containing this type is loaded.
At line:33 char:9
+ $HTML = New-Object -TypeName System.Collections.ArrayList[string]
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

Open in new window

RPC errors are still popping up when systems report back they are active. I ran the Test-WSMan on a few of them and they reported back clean.  No issues BUT I do not know if it is erroring out on systems that were erroring out on before due to firewall issues  

Windows PowerShell
Copyright (C) 2014 Microsoft Corporation. All rights reserved.

PS C:\Windows\system32> Test-WSMan -computername **************

wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 0.0.0 SP: 0.0 Stack: 3.0

It is properly flagging the systems that are inactive BUT still in AD (We need to cleanup a bit) - https://www.screencast.com/t/RshqwuMYxU

RPC Errors below...

Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At line:55 char:26
+             $wmiObject = Get-WmiObject -Class Win32_OperatingSystem -Property La ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], COMException
    + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
 
New-Object : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'ComObject'. Specified method is not supported.
At line:84 char:48
+             Write-Error -Exception (New-Object $_.Exception.GetType().FullName,  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [New-Object], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.NewObjectCommand

Open in new window

And of course, it can't find anything in the array so it errors out on the sending the email..

Send-MailMessage : Cannot validate argument on parameter 'Body'. The argument is null or empty. Provide an argument that is not null or empty, and then try the 
command again.
At line:107 char:92
+ ... SMTPPort -Body ($HTML -join $CRLF) -BodyAsHtml
+                    ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Send-MailMessage], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.SendMailMessage

Open in new window

CoralonCommented:
I will look at this more tomorrow.. but what exactly did you change about the ArrayList? (I'll need to dig in deeper)..

Coralon
CoralonCommented:
I did a lot of reworking on my own script today.. and I reworked most of the HTML pieces..
What I ended up doing was creating here strings for all the html major pieces, and added functions for each of them.  Here's a pseudo code type of layout of the changes I made:

Add-Type -AssemblyName System.Web
$HTMLBody = @'
<html>
    <head>
        <style>
        </style>
    </head>
    <body>
        <div>
            <table>
'@

$HTMLRow = @'
                 <tr>
                        <th class="_CLASS_">_HEADER1_</th>
                        <td class="_CLASS_">_data1</td>
                 </tr>
'@

$HTMLClose = @'
            </table>
       </div>
    </body>
</html>

$OutputList = New-Object -Type System.Collections.ArrayList
function New-HTML 
{
    $OutputList.Add($HTMLBody)
}

function New-HTMLRow
{
    param(
        [string]$HeaderString,
        [string]$DataString
    )

     $HeaderString = [system.web.httputility]::HTMLEncode($HeaderString)
     $DataString = [system.web.httputility]::HTMLEncode($HeaderString)
     $TempData = $HTMLRow.Replace('_HEADER1_', $HeaderString)
     $TempData = $TempData.Replace('_DATA1_', $DataString)
    $HTMLBody.Add($TempData)
}

#And more functions for each of the HTML snippets, and then inside the function, just calling the function with the correct data.

Open in new window


When the script is done, then you just output the $OutputString ($OutputString.ToString()) or if you want a file, you output it to a file.. like $OutputString -join "`r`n" | out-file c:\Temp.html

Coralon
Mark RoddySystem AdministratorAuthor Commented:
Hi Coralon,

I will let you know my results.  Thank you so much for your help.

Mark
Mark RoddySystem AdministratorAuthor Commented:
Hi Coralon,

What parts do I need to replace in the original script you gave me?

Can you post a full example, if it needs to be sanitized that is ok by me!

Thank you,

Mark
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Powershell

From novice to tech pro — start learning today.