HTML - Email Report

by TimBolton at 2013-04-09 10:27:23

This is the first "rough" draft of Get-Mobiledevices. I will add notes and some error catching soon.


<# Enter the UserID of the Requester and of the person you want the Mobile Report for. An email with the information will be sent out automatically to the Requester via their UserID and a BCC the Tech that is running this script to be able to see what they see. The person you are checking on will not get an email unless they are the requester.
#>

[CmdletBinding()]
param(
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[string]$Requester,

[Parameter(Mandatory=$True)]
[string]$UserID
)

PROCESS {

$Date = (get-date).ToString("MM-dd-yy")
$RequesterEmail=(Get-ADUser $Requester -Properties mail).Mail
$RequesterFirstName=(Get-ADUser $Requester -Properties GivenName).GivenName

$MyName = $env:username
$MyEmail = (Get-ADUSer $MyName -Properties mail).mail

# Get all AD properties of New User
$TheUser=(Get-ADUser $UserID -Properties *)
$TheUserName=$TheUser.DisplayName

# Email Settings
$SmtpServer = "Your Exchange Server Name Here"
$SmtpFrom = "$MyEmail"
$SmtpTo = "$RequesterEmail"
$SmtpBcc = New-Object system.Net.Mail.mailaddress "$MyEmail"
$MessageSubject = "Mobile report for $TheUserName "

$Message = New-Object System.Net.Mail.MailMessage $Smtpfrom, $Smtpto
# Add BCC
$Message.Bcc.Add($SmtpBcc)

$Message.Subject = $MessageSubject
$Message.IsBodyHTML = $true

#### HTML Output Formatting #######

$a = @"
<style>
body {
color:#333333;
font-family:Calibri,Tahoma;
font-size: 10pt;
}
TABLE {
border-width: 1px;
text-align: center;
border-style: solid;
border-color: black;
border-collapse: collapse;
}
th {
font-weight:bold;
border-width: 1px;
padding: 10px;
border-style: solid;
border-color: black;
color:#eeeeee;
background-color:#333333;
}
td {
font-weight:bold;
border-width: 1px;
padding: 10px;
border-style: solid;
border-color: black;
}
</style>
"@

# This is what will pull the information on the Mobile Devices being used by $UserID and will create the message body.
$Message.Body = Get-ActiveSyncDeviceStatistics -Mailbox $UserID | select DeviceType,DeviceModel,DeviceFriendlyName,DeviceOS,DeviceUserAgent,LastSyncAttemptTime,Lastsuccesssync,NumberOfFoldersSynced | ConvertTo-HTML -PreContent "<h2>Mobile Devices for $TheUserName</h2>","<h2>Date: $Date</h2>" -Head $a

$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)

}




What inspired this is… Recently there have been some updates to both iPhone/iPad and DROID devices that are used to access our Exchange 2010 activesync.

We have been having issues ranging from corrupt appointments, calendars being duplicated or deleted, activesync access errors etc.

The Desktop team (which does not have access to Exchange or Lync) will ask the end user

Q: "What mobile devices do you use?"
A: "This is the only one, I think…"

Which results in a phone call to me from the Desktop team asking me to verify the users info for them.

I would get the users userid, then run my simple script, then copy and paste it into an email and send it to the Desktop Tech.

90% of the time the issues are due to the user not remembering all of their devices in use and their response is going to be either -

1) Oh I forgot to reset my account info on my other phone.
2) Oh, I gave that phone to my kids when I bought this new one and the old one is still accessing my work email.

Now Mobile Security & Quarantining aside, I wanted to make something that would speed up this process for me.

That is what prompted me to write this, which is a combination of several examples via Blogs and Google searches that I have been able to make work.

However!

What I would LOVE to do, and have spent the last weekend attempting to do, is to follow the online guide here -

http://powershellbooks.com/ "Creating HTML Reports in Powershell" The basic (New-HTMLSystemReport.ps1) Non Enhanced HTML Report.ps1 is the script I am referring too.

- to make it look even better and to allow me to add additional information (if needed) in the future such as to their Lync settings and other Exchange bits.

Here is the code -

<#
.SYNOPSIS
Generates an HTML-based system report for one or more computers.
Each computer specified will result in a separate HTML file;
specify the -Path as a folder where you want the files written.
Note that existing files will be overwritten.
.PARAMETER ComputerName
One or more computer names or IP addresses to query.
.PARAMETER Path
The path of the folder where the files should be written.
.PARAMETER CssPath
The path and filename of the CSS template to use.
.EXAMPLE
.\New-HTMLSystemReport -ComputerName ONE,TWO <br> -Path C&#58;\Reports\ <br>#&gt;<br>&#91;CmdletBinding()&#93;<br>param(<br> &#91;Parameter(Mandatory=$True,<br> ValueFromPipeline=$True,<br> ValueFromPipelineByPropertyName=$True)&#93;<br> &#91;string&#91;&#93;&#93;$ComputerName,<br><br> &#91;Parameter(Mandatory=$True)&#93;<br> &#91;string&#93;$Path<br>)<br>PROCESS {<br><br>$style = @&quot;<br>&lt;style&gt;<br>body {<br> color&#58;#333333;<br> font-family&#58;Calibri,Tahoma;<br> font-size&#58; 10pt;<br>}<br>h1 {<br> text-align&#58;center;<br>}<br>h2 {<br> border-top&#58;1px solid #666666;<br>}<br><br>th {<br> font-weight&#58;bold;<br> color&#58;#eeeeee;<br> background-color&#58;#333333;<br>}<br>&#46;odd { background-color&#58;#ffffff; }<br>&#46;even { background-color&#58;#dddddd; }<br>&lt;/style&gt;<br>&quot;@<br><br>function Get-InfoOS {<br> &#91;CmdletBinding()&#93;<br> param(<br> &#91;Parameter(Mandatory=$True)&#93;&#91;string&#93;$ComputerName<br> )<br> $os = Get-WmiObject -class Win32_OperatingSystem -ComputerName $ComputerName<br> $props = @{'OSVersion'=$os&#46;version;<br> 'SPVersion'=$os&#46;servicepackmajorversion;<br> 'OSBuild'=$os&#46;buildnumber}<br> New-Object -TypeName PSObject -Property $props<br>}<br><br>function Get-InfoCompSystem {<br> &#91;CmdletBinding()&#93;<br> param(<br> &#91;Parameter(Mandatory=$True)&#93;&#91;string&#93;$ComputerName<br> )<br> $cs = Get-WmiObject -class Win32_ComputerSystem -ComputerName $ComputerName<br> $props = @{'Model'=$cs&#46;model;<br> 'Manufacturer'=$cs&#46;manufacturer;<br> 'RAM (GB)'=&quot;{0&#58;N2}&quot; -f ($cs&#46;totalphysicalmemory / 1GB);<br> 'Sockets'=$cs&#46;numberofprocessors;<br> 'Cores'=$cs&#46;numberoflogicalprocessors}<br> New-Object -TypeName PSObject -Property $props<br>}<br><br>function Get-InfoBadService {<br> &#91;CmdletBinding()&#93;<br> param(<br> &#91;Parameter(Mandatory=$True)&#93;&#91;string&#93;$ComputerName<br> )<br> $svcs = Get-WmiObject -class Win32_Service -ComputerName $ComputerName
-Filter "StartMode=‘Auto’ AND State<>‘Running’"
foreach ($svc in $svcs) {
$props = @{‘ServiceName’=$svc.name;
‘LogonAccount’=$svc.startname;
‘DisplayName’=$svc.displayname}
New-Object -TypeName PSObject -Property $props
}
}

function Get-InfoProc {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)][string]$ComputerName
)
$procs = Get-WmiObject -class Win32_Process -ComputerName $ComputerName
foreach ($proc in $procs) {
$props = @{‘ProcName’=$proc.name;
‘Executable’=$proc.ExecutablePath}
New-Object -TypeName PSObject -Property $props
}
}

function Get-InfoNIC {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)][string]$ComputerName
)
$nics = Get-WmiObject -class Win32_NetworkAdapter -ComputerName $ComputerName <br> -Filter &quot;PhysicalAdapter=True&quot;<br> foreach ($nic in $nics) { <br> $props = @{'NICName'=$nic&#46;servicename;<br> 'Speed'=$nic&#46;speed / 1MB -as &#91;int&#93;;<br> 'Manufacturer'=$nic&#46;manufacturer;<br> 'MACAddress'=$nic&#46;macaddress}<br> New-Object -TypeName PSObject -Property $props<br> }<br>}<br><br>function Set-AlternatingCSSClasses {<br> &#91;CmdletBinding()&#93;<br> param(<br> &#91;Parameter(Mandatory=$True,ValueFromPipeline=$True)&#93;<br> &#91;string&#93;$HTMLFragment,<br> <br> &#91;Parameter(Mandatory=$True)&#93;<br> &#91;string&#93;$CSSEvenClass,<br> <br> &#91;Parameter(Mandatory=$True)&#93;<br> &#91;string&#93;$CssOddClass<br> )<br> &#91;xml&#93;$xml = $HTMLFragment<br> $table = $xml&#46;SelectSingleNode('table')<br> $classname = $CSSOddClass<br> foreach ($tr in $table&#46;tr) {<br> if ($classname -eq $CSSEvenClass) {<br> $classname = $CssOddClass<br> } else {<br> $classname = $CSSEvenClass<br> }<br> $class = $xml&#46;CreateAttribute('class')<br> $class&#46;value = $classname<br> $tr&#46;attributes&#46;append($class) | Out-null<br> }<br> $xml&#46;innerxml | out-string<br>}<br><br>foreach ($computer in $computername) {<br> try {<br> $everything_ok = $true<br> Write-Verbose &quot;Checking connectivity to $computer&quot;<br> Get-WmiObject -class Win32_BIOS -ComputerName $Computer -EA Stop | Out-Null<br> } catch {<br> Write-Warning &quot;$computer failed&quot;<br> $everything_ok = $false<br> }<br><br> if ($everything_ok) {<br> $filepath = Join-Path -Path $Path -ChildPath &quot;$computer&#46;html&quot;<br> $html_os = Get-InfoOS -ComputerName $computer |<br> ConvertTo-HTML -As List -Fragment
-PreContent "<h2>OS</h2>" |
Out-String

$html_cs = Get-InfoCompSystem -ComputerName $computer |
ConvertTo-HTML -As List -Fragment `
-PreContent "<h2>Hardware</h2>" |
Out-String

$html_pr = Get-InfoProc -ComputerName $computer |
ConvertTo-HTML -Fragment |
Out-String |
Set-AlternatingCSSClasses -CSSEvenClass ‘even’ -CssOddClass ‘odd’
$html_pr = "<h2>Processes</h2>$html_pr"

$html_sv = Get-InfoBadService -ComputerName $computer |
ConvertTo-HTML -Fragment |
Out-String |
Set-AlternatingCSSClasses -CSSEvenClass ‘even’ -CssOddClass ‘odd’
$html_sv = "<h2>Check Services</h2>$html_sv"

$html_na = Get-InfoNIC -ComputerName $Computer |
ConvertTo-HTML -Fragment |
Out-String |
Set-AlternatingCSSClasses -CSSEvenClass ‘even’ -CssOddClass ‘odd’
$html_na = "<h2>NICs</h2>$html_na"

$params = @{‘Head’="<title>Report for $computer</title>$style";
‘PreContent’="<h1>System Report for $computer</h1>";
‘PostContent’=$html_os,$html_cs,$html_pr,$html_sv,$html_na}
ConvertTo-HTML @params | Out-File -FilePath $filepath
}
}

}



If I can get the basic HTML Structure correct then I want to set it up to email automatically like my original script.

I have tried several variations such as this called which is currently called TEST01.ps1

<#
Testing Get-Mobiledevices

#>

[CmdletBinding()]
param(
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[string]$Requester,

[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[string]$UserID,

[Parameter(Mandatory=$True)]
[string]$Path
)
PROCESS {

$Date = (get-date).ToString("MM-dd-yy")

$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>
"@

function Get-MD {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)][string]$UserID
)
$MD = Get-ActiveSyncDeviceStatistics -Mailbox $UserID

$Props = @{‘DeviceType’=$MD.DeviceType;
‘DeviceModel’=$MD.DeviceModel;
‘DeviceName’=$MD.DeviceFriendlyName;
‘DeviceOS’=$MD.DeviceOS;
‘DeviceUserAgent’=$MD.DeviceUserAgent;
‘LastSyncAttemptTime’=$MD.LastSyncAttemptTime;
‘LastSuccessSync’=$MD.Lastsuccesssync;
‘NumberOfFoldersSynced’=$MD.NumberOfFoldersSynced}
New-Object -TypeName PSObject -Property $props
}

function Set-AlternatingCSSClasses {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,ValueFromPipeline=$True)]
[string]$HTMLFragment,

[Parameter(Mandatory=$True)]
[string]$CSSEvenClass,

[Parameter(Mandatory=$True)]
[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
}

foreach ($User in $UserID) {
try {
$everything_ok = $true
Write-Verbose "Checking Devices for $User"
Get-Mailbox $User -EA Stop | Out-Null
} catch {
Write-Warning "$User failed"
$everything_ok = $false
}

if ($everything_ok) {
$filepath = Join-Path -Path $Path -ChildPath "$User.html"
$html_md = Get-MD -ActiveSyncDeviceStatistics $User |
ConvertTo-HTML -Fragment |
Out-String |
Set-AlternatingCSSClasses -CSSEvenClass ‘even’ -CssOddClass ‘odd’
$html_md = "<h2>Mobile Devices for $User Date: $Date </h2>$html_md"

$params = @{‘Head’="<title>Mobile Device Report for $User</title>$style";
‘PreContent’="<h1>Mobile Device Report for $User</h1>";
‘PostContent’=$html_md}
ConvertTo-HTML @params | Out-File -FilePath $filepath
}
}

}




I "THINK" that $html_md = Get-MD -ActiveSyncDeviceStatistics $User | is where I am stuck… I have tried -Mailbox as well with the same error -

Get-MD : A parameter cannot be found that matches parameter name ‘ActiveSyncDeviceStatistics’.
At C:\scripts\Modules\ebooks\HTMLReports\CreatingHTMLReports_Code\TEST01.ps1:118 char:27
+ $html_md = Get-MD -ActiveSyncDeviceStatistics $User |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:slight_smile: [Get-MD], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Get-MD



Again I would like to be able to send it out in an email like my code at the top. I would also like to follow Don’s examples so that I can expand on this if needed, thus adding Lync Settings, Exchange settings and even some AD groups etc…


Thank you in advance…
by mjolinor at 2013-04-09 10:40:03
Get-ActiveSyncDeviceStatistics wants an activesync device ID as input.
Try this:

$MD = Get-ActiveSyncDevice -Mailbox $UserID | Get-ActiveSyncDeviceStatistics
by TimBolton at 2013-04-09 11:30:17
Ok using -


$MD = Get-ActiveSyncDevice -Mailbox $UserID | Get-ActiveSyncDeviceStatistics
&

if ($everything_ok) {
$filepath = Join-Path -Path $Path -ChildPath "$User.html"
$html_md = Get-MD $User
ConvertTo-HTML -Fragment |
Out-String |
Set-AlternatingCSSClasses -CSSEvenClass ‘even’ -CssOddClass ‘odd’
$html_md = "<h2>Mobile Devices for $User Date: $Date </h2>$html_md"


The output has this info -

Mobile Device Report for tbolton

Mobile Devices for tbolton Date: 04-09-13

@{DeviceUserAgent=System.Object; DeviceOS=System.Object; DeviceModel=System.Object; DeviceType=System.Object; DeviceName=System.Object; NumberOfFoldersSynced=System.Object; LastSyncAttemptTime=System.Object; LastSuccessSync=System.Object}


If I use this -

$MD = Get-ActiveSyncDeviceStatistics -Mailbox $UserID &

$html_md = Get-MD $User

I do get some of the info -

Mobile Device Report for tbolton

Mobile Devices for tbolton Date: 04-09-13
@{DeviceUserAgent=MSFT-WP/8.0.10211; DeviceOS=Windows Phone 8.0.10211; DeviceModel=RM-820_nam_att_100; DeviceType=WP8; DeviceName=Lumia 920; NumberOfFoldersSynced=14; LastSyncAttemptTime=04/09/2013 18:21:00; LastSuccessSync=04/09/2013 18:21:01}


I just do not understand why replacing -ComputerName $computer with -Mailbox $User is not working…
by mjolinor at 2013-04-09 11:49:11
Too much copy and paste. Not enough get-help and grok.
by DonJ at 2013-04-10 00:26:47
Yeah, I think you’re just down to focusing on those specific commands, what they produce, and what input they want. I’m not an Exchange guy, so I’m not going to be a lot of help, but the way I’d troubleshoot this is more or less what you’re doing. Pull the commands out and run them individually. Get them working, get their output to be what you want. The plug them back into the script.

The HTML reporting book assumes you’re able to produce the data you want; it’s just helping make it pretty.
by TimBolton at 2013-04-10 07:55:11
"Stop and look at get-help about_functions_advanced_parameters" is where I am heading now. I think it is the data property itself and the way it is handled…?

Again I understand about 80% of what he is doing, the appending to the html is where I am getting lost as I have never seen it down that way…

I am also going to go back and re-read the eBook as well. I probably skipped right over my answer…
by DonJ at 2013-04-10 22:29:38
So, briefly, the idea with the HTML is to (a) pipe objects to ConvertTo-HTML using -Fragment, thus turning each set of objects into a standalone fragment. You then feed the fragments to ConvertTo-HTML (without -Fragment) to produce a complete, consolidated HTML page.

You can’t simply run objects to ConvertTo-HTML and append the content to an existing HTML page - that isn’t valid HTML. Instead, you produce a bunch of fragments (which are not complete pages) and then merge them together into a complete page. That’s why you see the examples using -Fragment several times, and then combining the fragments at the end.