Home > Software engineering >  Append SQL result to HTML table via Powershell
Append SQL result to HTML table via Powershell

Time:07-02

I have multiple databases from multiple SQL Servers.

I am trying a output SQL data to an HTML table using Powershell from these databases/SQL Servers, then send a mail. The script I made works, but it will output multiple tables for each result in the html report. What I want is to append the data to the same table (Only 1 table containing all data). Struggling a bit on how to concatenate the data to only 1 table.



#Set flag to 0 for email notification (if any)
$alert= 0


$List = Get-Content -Path "C:\Users\testadmin\Documents\Log\serverlist.txt"


Foreach($Server in $ServerList){

$SQL = ($Server -split '=')[0] 
$ = ($Server -split '=')[1]



$Query = "select host, Service, Status from Table with(nolock)"

$Value = Invoke-Sqlcmd -ServerInstance $SQL -Database $DB -Query $Query 




foreach($mylocal in $Value ){

    $hostname = $mylocal.host
    $Service= $mylocal.Service
    $Status = $mylocal.Status 



         if($Status -ne 'Running'){
         
         $alert= 1

         $Body  = "<head> 
          <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>

          <style>
            TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
            TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #6495ED;}
            TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
            
            table.center {
            margin-left: auto; 
            margin-right: auto;
            }
          </style>

          <title>Warning</title>
          </head> 
          <body> 

          <h3>Apps are Stopped !</h3>

          <h4>Timestamp: $(Get-Date)</h4>
          
        <table>

          <tr>
            <th>SQL</th>
            <th>DB</th>
            <th>Service Status</th>
          </tr>

          <tr>
            <td>$SQL</td>
            <td>$DB</td>
            <td>$Status</td>
          </tr>

          </table>
          </body>
          " 
         } 


         else{
            Write-Host ("no alert") 
         }
  }  

}

  if($alert-eq '1'){

          $Mail = @{
            From = '[email protected]' 
            To = '[email protected]'
            Subject = 'Warning!!'
            smtpserver = 'myrelay.test.com'
            Body = $Body
            BodyAsHtml = $true
          } 


          Send-MailMessage @Mail
        } 





CodePudding user response:

You need to create your base html (the one containing the head / body / table title / table headers) outside of the loop. In the loop, you create only the table cells.

From there, you build your complete HTML.

Example


# Just some data... I used calculated field to have the same field name as you.
$Value = Get-Service | Select @{'Name' = 'host' ; 'Expression' = { "localhost" } },
@{'Name' = 'Service' ; 'Expression' = { $_.ServiceName } }, Status


$HtmlTemplate = @{
    Base_Date_TableLoop          = @'
<head> 
          <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>

          <style>
            TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
            TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #6495ED;}
            TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
            
            table.center {
            margin-left: auto; 
            margin-right: auto;
            }
          </style>

          <title>Warning</title>
          </head> 
          <body> 

          <h3>Apps are Stopped !</h3>

          <h4>Timestamp: {0:Date}</h4>
          
        <table>

            <tr>
                <th>SQL</th>
                <th>DB</th>
                <th>Service Status</th>
            </tr>

          {0:TableLoop}

          </table>
          </body>
'@
    TableLoop_SQL_DB_Status = @'
        <tr>
            <td>{0}</td>
            <td>{1}</td>
            <td>{2}</td>
        </tr>
'@
}

 $Date = Get-Date
$Table = [System.Text.StringBuilder]::new()




foreach ($mylocal in $Value ) {

    $hostname = $mylocal.host
    $Service = $mylocal.Service
    $Status = $mylocal.Status

    $Table.AppendLine(($HtmlTemplate.TableLoop_SQL_DB_Status -f $hostname,$Service,$Status)) | Out-Null

}

$HtmlBody = $HtmlTemplate.Base_Date_TableLoop.Replace('{0:Date}',$Date).Replace('{0:TableLoop}',$Table.ToString())

$HtmlBody | Out-File 'SomeReport.html' 

Design choices

I like to have all my html elements in a single variable. In the previous example, I used $HtmlTemplate to store all the pieces. These pieces are not meant to be edited directly but serves as my building blocks.

For the elements themselves, I personally like to have something along the lines of Base_Date_TableLoop where each underscores delimit something I need to replace so it is easier later on to do the replace without having to think about what goes where.

Additional notes

I used a mix of '{0}' with the -f for the loop piece and {0:TableLoop} with .replace for the base html piece. This is because the base html contains brackets {} used for the format operator, which would need to be doubled down for the -f operator to ignore them. Since I don't want to arbitrarily modify the html, I use the .replace .Net method, which allow me to replace the things I need without having to modify the html completely. Also, .Replace is used here instead of -Replace because the latter is a Regex operator, and I don't want that.

  • Related