VMware Cloud Community
gerf0727
Enthusiast
Enthusiast
Jump to solution

Powershell/PowerCLI Dynamic Email Message Body with Datastores

Hello the following is related to this email but I am stuck in rounding the numbers

Powershell/PowerCLI Dynamic Email Message Body

I am getting an email with the following info:

Datastore HealthCheck vCenter

Datastore Space Available
Datastore                                UsedGB                                 Free GB                                 Perc Free
name1                                        273.30078125                         274.25                                    99%
name2                                        273.30078125                         274.25                                    99%
name3                                        268.466796875                         274.25                                    99%
name4                                        273.30078125                         274.25                                    99%

Three things that I am stuck: First, the UsedGB value, woud like to have it with only two decimal points, instead of having 273.30078125, nice to have 273.30. Second I am not getting the right percentage. Third: How do i sort the the Perf Free values from the lowest to the bigger percentage? Thanks for your help

Code:

$msg.Subject = "vCenter Datastore Health CompanyvCenter"  
    $array0 = @()
    $array1 = @()
    $array2 = @()
    start-sleep 1
    connect-VIServer $vcserver
    $array0 += Get-Datastore | Select-Object -ExpandProperty Name
    $array1 += Get-Datastore | Select-Object -ExpandProperty FreeSpaceGB
    $array2 += Get-Datastore | Select-Object -ExpandProperty CapacityGB
    $UsedSpace = [math]::round(($array2[$i] - $array1[$i]),2)
    $PercFree = [math]::Round((100 * $array1[$i] / $array2[$i]),0)
    $String0 = "$PercFree$Percent"
    $i = 0
    $j = 0
    # Header
    $msg.Body += "<FONT COLOR=black>Datastore HealthCheck CompanyvCenter</FONT><BR><BR>"
    # Datastore header
    $msg.Body += "<B><FONT COLOR=black>Datastore Space Available</FONT></B><BR>"
    $msg.Body += "<B><FONT COLOR=black>Datastore</FONT></B><B><FONT COLOR=black>                                             UsedGB</FONT></B>`
    <B><FONT COLOR=black>                                Free GB</FONT></B>`
    <B><FONT COLOR=black>                                Perc Free</FONT></B>"
    # Datasores
    0..($array0.Count-1) | %{
    $msg.Body += "<BR><FONT COLOR=Black>" + $array0[$_]
    $msg.Body += "</FONT>                                        <FONT COLOR=Black>" + [math]::round(($array2[$_] - $array1[$_]),2)
    #$msg.Body += "</FONT>                                        <FONT COLOR=Black>" + $array1[$_]
    $msg.Body += "</FONT>                         <FONT COLOR=Black>" + $array2[$_] + "</FONT>"
    #$msg.Body += "</FONT>                         <FONT COLOR=Black>" + [math]::round(($array2[$_] - $array1[$_]),2) + "</FONT>"
    $msg.Body += "                                     <FONT COLOR=Black>" + [math]::Round((100 * $array1[$i] / $array2[$i]),0)+"$Percent"+"</FONT><BR>"}
    $msg.Attachments.Add($att1)
    $msg.IsBodyHTML = $true
    $smtp.Send($msg)
    $att1.Dispose()
    Disconnect-VIServer $vcserver -Confirm:$false

Tags (2)
1 Solution

Accepted Solutions
fabiendibot
Enthusiast
Enthusiast
Jump to solution

#Here configure your paraneters

$SMTPServer = "Exchange"

$MailSubject = "vCenter Datastore Health CompanyvCenter"

$Email = "Enter@yourmail.com"

function Set-AlternatingCSSClasses {

    param(

        [string]$HTMLFragment,

        [string]$CSSEvenClass,

        [string]$CssOddClass

    )

    [xml]$xml = $HTMLFragment

    $table = $xml.SelectSingleNode('table')

    $classname = $CSSOddClass

    foreach ($tr in $table.tr) {

        if ($classname -eq $CSSEvenClass) { $classname = $CssOddClass }

        else { $classname = $CSSEvenClass }

        $class = $xml.CreateAttribute('class')

        $class.value = $classname

        $tr.attributes.append($class) | Out-null

    }

    $xml.innerxml | out-string

}

Function Report-Datastore {

    $output = @()

    Get-Datastore | % {

        $props = [ordered]@{'Name'=$_.Name;

                            'UsedSpace'=[math]::Round(($_.CapacityGB - $_.FreeSpaceGB),2);

                            'PercFree'=[math]::Round((100 * ($_.FreeSpaceGB/$_.CapacityGB)),0)}

        $output += New-Object -TypeName PSCUstomObject -Property $props

    }

    $output

}

$style = @"

<style>

body {

    color:#333333;

    font-family:Calibri,Tahoma;

    font-size: 10pt;

}

h1 {

    text-align:center;

}

h2 {

    border-top:1px solid #666666;

}

th {

    font-weight:bold;

    color:#eeeeee;

    background-color:#333333;

}

.odd  { background-color:#ffffff; }

.even { background-color:#dddddd; }

</style>

"@

#Connect to Vcenter

Connect-VIServer $vcserver

# Trnasform the Object in HTML code

$html_DS = Report-Datastore |

           Sort-Object PercFree |

           ConvertTo-HTML -Fragment |

           Out-String |

           Set-AlternatingCSSClasses -CSSEvenClass 'even' -CssOddClass 'odd'

$html_DS = "<h2>Datastores</h2>$html_DS"

$params = @{'Head'="<title>vCenter Datastore Health CompanyvCenter</title>$style";

            'PreContent'="<h1>Datastore HealthCheck CompanyvCenter</h1>";

            'PostContent'=$html_DS}

# Send email

Send-MailMessage -To $Email -Subject $MailSubject -BodyAsHtml -Body (ConvertTo-HTML @params) -SmtpServer $SMTPServer

# DIsconnect Vcenter

Disconnect-VIServer $vcserver -Confirm:$false

View solution in original post

Reply
0 Kudos
30 Replies
fabiendibot
Enthusiast
Enthusiast
Jump to solution

You can try this..; i can't test it at home, but it should work without few minors modifications Smiley Happy

function Set-AlternatingCSSClasses {

    param(

        [string]$HTMLFragment,

        [string]$CSSEvenClass,

        [string]$CssOddClass

    )

    [xml]$xml = $HTMLFragment

    $table = $xml.SelectSingleNode('table')

    $classname = $CSSOddClass

    foreach ($tr in $table.tr) {

        if ($classname -eq $CSSEvenClass) {

            $classname = $CssOddClass

        } else {

            $classname = $CSSEvenClass

        }

        $class = $xml.CreateAttribute('class')

        $class.value = $classname

        $tr.attributes.append($class) | Out-null

    }

    $xml.innerxml | out-string

}

Function Report-Datastore {

    $output = @()

    Get-Datastore | % {

    # | Select-Object -ExpandProperty Name

    #    $array1 += Get-Datastore | Select-Object -ExpandProperty FreeSpaceGB

    #    $array2 += Get-Datastore | Select-Object -ExpandProperty CapacityGB

        $props = [ordered]@{'Name'=$_.Name;

                            'UsedSpace'=[math]::Round(($_.CapacityGB - $_.FreeSpaceGB),2);

                            'PercFree'=[math]::Round((100 * ($_.FreeSpaceGB/$_.CapacityGB)),0)}

        $output += New-Object -TypeName PSCUstomObject -Property $props

    }

$output

}

$msg.Subject = "vCenter Datastore Health CompanyvCenter"

$style = @"

<style>

body {

    color:#333333;

    font-family:Calibri,Tahoma;

    font-size: 10pt;

}

h1 {

    text-align:center;

}

h2 {

    border-top:1px solid #666666;

}

th {

    font-weight:bold;

    color:#eeeeee;

    background-color:#333333;

}

.odd  { background-color:#ffffff; }

.even { background-color:#dddddd; }

</style>

"@

Connect-VIServer $vcserver

$html_DS = Report-Datastore |

           Sort-Object PercFree |

           ConvertTo-HTML -Fragment |

           Out-String |

           Set-AlternatingCSSClasses -CSSEvenClass 'even' -CssOddClass 'odd'

$html_DS = "<h2>Datastores</h2>$html_DS"

$params = @{'Head'="<title>vCenter Datastore Health CompanyvCenter</title>$style";

            'PreContent'="<h1>Datastore HealthCheck CompanyvCenter</h1>";

            'PostContent'=$html_DS}

$msg.Body = ConvertTo-HTML @params

$msg.Attachments.Add($att1)

$msg.IsBodyHTML = $true

$smtp.Send($msg)

$att1.Dispose()

Disconnect-VIServer $vcserver -Confirm:$false

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

Fabien,

Thank you very much. But, i am getting the following error:

The string starting:

At C:\scripts\VMware\DatastoreCheck.ps1:34 char:14

+     $style =  <<<< @"

is missing the terminator: "@.

At C:\scripts\VMware\DatastoreCheck.ps1:67 char:1

+  <<<<

    + CategoryInfo          : ParserError: (    <style>

    body...onfirm:$false

:String) [], ParseException

    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

The "@ has to be at the very start of the line in order  to work

here is a version without CSS...

Function Report-Datastore {

    $output = @()

    Get-Datastore | % {

        $props = [ordered]@{'Name'=$_.Name;

                            'UsedSpace'=[math]::Round(($_.CapacityGB - $_.FreeSpaceGB),2);

                            'PercFree'=[math]::Round((100 * ($_.FreeSpaceGB/$_.CapacityGB)),0)}

        $output += New-Object -TypeName PSCUstomObject -Property $props

    }

$output

}

$msg.Subject = "vCenter Datastore Health CompanyvCenter"

Connect-VIServer $vcserver

$html_DS = Report-Datastore |

           Sort-Object PercFree |

           ConvertTo-HTML -Fragment |

           Out-String

$html_DS = "<h2>Datastores</h2>$html_DS"

$params = @{'Head'="<title>vCenter Datastore Health CompanyvCenter</title>";

            'PreContent'="<h1>Datastore HealthCheck CompanyvCenter</h1>";

            'PostContent'=$html_DS}

$msg.Body = ConvertTo-HTML @params

$msg.Attachments.Add($att1)

$msg.IsBodyHTML = $true

$smtp.Send($msg)

$att1.Dispose()

Disconnect-VIServer $vcserver -Confirm:$false

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

nada...I got the following:

Property 'Subject' cannot be found on this object; make sure it exists and is settable.

At C:\scripts\VMware\DatastoreCheck.ps1:35 char:6

+ $msg. <<<< Subject = "vCenter Datastore Health CompanyvCenter"

    + CategoryInfo          : InvalidOperation: (Subject:String) [], RuntimeException

    + FullyQualifiedErrorId : PropertyNotFound

and bunch of messages like the following:

Unable to find type [ordered]: make sure that the assembly containing this type is loaded.

At C:\scripts\VMware\DatastoreCheck.ps1:17 char:27

+         $props = [ordered] <<<< @{'Name'=$_.Name;

    + CategoryInfo          : InvalidOperation: (ordered:String) [], RuntimeException

    + FullyQualifiedErrorId : TypeNotFound

New-Object : Cannot validate argument on parameter 'Property'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.

At C:\scripts\VMware\DatastoreCheck.ps1:23 char:65

+         $output += New-Object -TypeName PSCUstomObject -Property <<<<  $props

    + CategoryInfo          : InvalidData: (:) [New-Object], ParameterBindingValidationException

    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.NewObjectCommand

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

The property subject was the one you used before... i can't do anything about it. Remove [ordered] in the script, it seems you don't have powershell v3 😉

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

I fix the Subject think. And, I think I do have powershell v3. How do i know ?

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

You don't have, [ordered] is a new hastable format  😉 In PowerShell console user $PSVersionTable to know you version 😉

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

You were right. I just installed 3.

Name                      Value
----                      -----
WSManStackVersion         3.0
PSCompatibleVersions      {1.0, 2.0, 3.0}
SerializationVersion      1.1.0.1
BuildVersion              6.2.9200.16398
PSVersion                 3.0
CLRVersion                4.0.30319.1
PSRemotingProtocolVersion

2.2

Now I got the following errors: Exception calling "Add" with "1" argument(s): "Value cannot be null. Parameter name: item" At C:\scripts\VMware\DatastoreCheck.ps1:74 char:1 + $msg.Attachments.Add($att1) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~     + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException     + FullyQualifiedErrorId : ArgumentNullException You cannot call a method on a null-valued expression. At C:\scripts\VMware\DatastoreCheck.ps1:78 char:1 + $smtp.Send($msg) + ~~~~~~~~~~~~~~~~     + CategoryInfo          : InvalidOperation: (:) [], RuntimeException     + FullyQualifiedErrorId : InvokeMethodOnNull

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

it's your mail sender which is fucked. I presumed you had a working script bu i bet no. You should take a look look at Send-MailMessage

Send-MailMessage -To <yourmail> -Subject <yoursbuject> -Body (ConvertTo-HTML @params) -BodyAsHtml -SmtpServer <Your_SMTP_Server>

But ar first you should learn to use PowerShell/PowerCLi bascis in order to understand better your problems, the variable $msg isn't declared or builded anywhere in your script !

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

Hmm,  this is what I have:

clear-host

$vcserver = "vcenter"

$mailto = "Me"

$SMTPServer = "exchange"

$msg = new-object Net.Mail.MailMessage

Function Report-Datastore {

    $output = @()

    Get-Datastore | % {

        $props = [ordered]@{'Name'=$_.Name;

                            'UsedSpace'=[math]::Round(($_.CapacityGB - $_.FreeSpaceGB),2);

                            'PercFree'=[math]::Round((100 * ($_.FreeSpaceGB/$_.CapacityGB)),0)}

        $output += New-Object -TypeName PSCUstomObject -Property $props

    }

$output

}

$msg.Subject = "vCenter Datastore Health CompanyvCenter"

Connect-VIServer $vcserver

$html_DS = Report-Datastore |

           Sort-Object PercFree |

           ConvertTo-HTML -Fragment |

           Out-String

$html_DS = "<h2>Datastores</h2>$html_DS"

$params = @{'Head'="<title>vCenter Datastore Health CompanyvCenter</title>";

            'PreContent'="<h1>Datastore HealthCheck CompanyvCenter</h1>";

            'PostContent'=$html_DS}

$msg.Body = ConvertTo-HTML @params

#$msg.Attachments.Add($att1)

$msg.IsBodyHTML = $true

$smtp.Send($msg)

#$att1.Dispose()

Disconnect-VIServer $vcserver -Confirm:$false

Got the following error:

You cannot call a method on a null-valued expression.

At C:\scripts\VMware\DatastoreCheck.ps1:31 char:1

+ $smtp.Send($msg)

+ ~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : InvokeMethodOnNull

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

You use the $msg variable to build your .NET mail send object -> $msg = new-object Net.Mail.MailMessage Your declare the object with a wrong "method" This is more something like $emailFrom = somemail@bloh.com # $smtp.Send($msg) Before using this you have to declare this object $emailSmtpServer = "exchange" $emailSmtpServerPort = "587" $SMTP= New-Object System.Net.Mail.SmtpClient( $emailSmtpServer , $emailSmtpServerPort ) And if there are credentials required... $emailSmtpUser = "username" $emailSmtpPass = "password" $SMTPClient.Credentials = New-Object System.Net.NetworkCredential( $emailSmtpUser , $emailSmtpPass );

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

Hmm, I think I am lost in space. Now, I have the following:

Property 'Subject' cannot be found on this object; make sure it exists and is settable.

At C:\scripts\VMware\DatastoreCheck.ps1:17 char:1

+ $msg.Subject = "vCenter Datastore Health CompanyvCenter"

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : PropertyAssignmentException

Name                           Port  User

----                           ----  ----

vcenter.company.com         443   domain\domainadmin

Property 'Body' cannot be found on this object; make sure it exists and is settable.

At C:\scripts\VMware\DatastoreCheck.ps1:29 char:1

+ $msg.Body = ConvertTo-HTML @params

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : PropertyAssignmentException

Property 'IsBodyHTML' cannot be found on this object; make sure it exists and is settable.

At C:\scripts\VMware\DatastoreCheck.ps1:31 char:1

+ $msg.IsBodyHTML = $true

+ ~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : PropertyAssignmentException

You cannot call a method on a null-valued expression.

At C:\scripts\VMware\DatastoreCheck.ps1:32 char:1

+ $smtp.Send($msg)

+ ~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : InvokeMethodOnNull

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

I'll make a complet working script tomorrow at work 😉

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

Thank you sir. I will really appreciate.

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

#Here configure your paraneters

$SMTPServer = "Exchange"

$MailSubject = "vCenter Datastore Health CompanyvCenter"

$Email = "Enter@yourmail.com"

function Set-AlternatingCSSClasses {

    param(

        [string]$HTMLFragment,

        [string]$CSSEvenClass,

        [string]$CssOddClass

    )

    [xml]$xml = $HTMLFragment

    $table = $xml.SelectSingleNode('table')

    $classname = $CSSOddClass

    foreach ($tr in $table.tr) {

        if ($classname -eq $CSSEvenClass) { $classname = $CssOddClass }

        else { $classname = $CSSEvenClass }

        $class = $xml.CreateAttribute('class')

        $class.value = $classname

        $tr.attributes.append($class) | Out-null

    }

    $xml.innerxml | out-string

}

Function Report-Datastore {

    $output = @()

    Get-Datastore | % {

        $props = [ordered]@{'Name'=$_.Name;

                            'UsedSpace'=[math]::Round(($_.CapacityGB - $_.FreeSpaceGB),2);

                            'PercFree'=[math]::Round((100 * ($_.FreeSpaceGB/$_.CapacityGB)),0)}

        $output += New-Object -TypeName PSCUstomObject -Property $props

    }

    $output

}

$style = @"

<style>

body {

    color:#333333;

    font-family:Calibri,Tahoma;

    font-size: 10pt;

}

h1 {

    text-align:center;

}

h2 {

    border-top:1px solid #666666;

}

th {

    font-weight:bold;

    color:#eeeeee;

    background-color:#333333;

}

.odd  { background-color:#ffffff; }

.even { background-color:#dddddd; }

</style>

"@

#Connect to Vcenter

Connect-VIServer $vcserver

# Trnasform the Object in HTML code

$html_DS = Report-Datastore |

           Sort-Object PercFree |

           ConvertTo-HTML -Fragment |

           Out-String |

           Set-AlternatingCSSClasses -CSSEvenClass 'even' -CssOddClass 'odd'

$html_DS = "<h2>Datastores</h2>$html_DS"

$params = @{'Head'="<title>vCenter Datastore Health CompanyvCenter</title>$style";

            'PreContent'="<h1>Datastore HealthCheck CompanyvCenter</h1>";

            'PostContent'=$html_DS}

# Send email

Send-MailMessage -To $Email -Subject $MailSubject -BodyAsHtml -Body (ConvertTo-HTML @params) -SmtpServer $SMTPServer

# DIsconnect Vcenter

Disconnect-VIServer $vcserver -Confirm:$false

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

Smiley Sad Got the following:

Send-MailMessage : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Body'. Specified method is not supported.

At C:\scripts\VMware\DatastoreCheck.ps1:76 char:69

+ Send-MailMessage -To $Email -Subject $MailSubject -BodyAsHtml -Body (ConvertTo-H ...

+                                                                     ~~~~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (:) [Send-MailMessage], ParameterBindingException

    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.SendMailMessage

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

Send-MailMessage : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Body'. Specified method is not supported.

My bad i did'tn check the variable type used by -Body parameter...


Replace the Send mail part by this one, it should work (but not tested)

# Send email

$MailBody = ConvertTo-HTML @params | Out-String

$ReportingMail = "esx@lab.local"

Send-MailMessage -To $Email -From $ReportingMail -Subject $MailSubject -BodyAsHtml -Body $MailBody -SmtpServer $SMTPServer

Reply
0 Kudos
gerf0727
Enthusiast
Enthusiast
Jump to solution

Amost but nada Smiley SadCapture.PNG

Reply
0 Kudos
fabiendibot
Enthusiast
Enthusiast
Jump to solution

I will check, but did you try by yourself to understand the script and find where is the error ?

Reply
0 Kudos