Recursive html report (made with jagged array of psobject)
When you print, header and footer will repeat each page
Requis: google chrome (for printing)
At the moment it scan for windows code and office code and put it in the report
#########################################################################
$title01 = "report psobject html xml jagged recursive colored"
#########################################################################
cls
# this recursive script will:
# take a psobject with sub psobjects that contain data about a report
# put the main psobject in a html table, all sub psobject are replaced with a text
# color any html cell we want to warn about certains things (errors, warnings) in main html/xml hybrid object and all sub html/xml objects
# convert back the main html/xml table into an html report
# insert back all sub html/xml tables (once they were colored)
# generate final html report, printable in google chrome only, with a table format and sheet format compatible for print with header and footer
#########################
# parameters
#########################
# scan office keys
#$computers01 = @("mav03", "mav06", "mav09", "mav10", "mav14", "mav16", "mav18", "mav19")
$computers_local01 = @("localhost","caca")
###################################
# requis
###################################
### Enable-PSRemoting
#powershell.exe -command Enable-PSRemoting -SkipNetworkProfileCheck -force
#Enable-PSRemoting -SkipNetworkProfileCheck -force
#Test-WSMan -ComputerName <IP or host name>
# on HOST ########################################## tooo!
# Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*'
#############################################################################
# color for html report if this string is found, html td color is changed
#############################################################################
# possible colors are defined in $style01
[string[]]$error_message_arr = "WARNING"
[string[]]$error_color_arr = "yellow" # yellow
[string[]]$error_message_Arr+= "ERROR"
[string[]]$error_color_arr+= "red" # red
[string[]]$error_message_arr += "cacasss"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_arr += "00:00:00"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_arr += "inprogress"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_arr += "pending"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_Arr+= "failed"
[string[]]$error_color_arr+= "red" # red
[string[]]$error_message_Arr+= "true"
[string[]]$error_color_arr+= "red" # red
[string[]]$error_message_Arr+= "unknow"
[string[]]$error_color_arr+= "red" # red
###############################
# error management object
###############################
$error01 = New-Object PsObject
$error01 | Add-Member NoteProperty -Name number -value ''
$error01 | Add-Member NoteProperty -Name description -value ''
$error01 | Add-Member NoteProperty -Name error02 -value ''
#######################
# logfile
#######################
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$scriptname = split-path -leaf $MyInvocation.MyCommand.Definition
$Logfilename = $scriptname + "_log.txt"
$logfile = $scriptPath + "\" + $Logfilename
$logall = 1
$msg01 = "START " + $title01
write-host $msg01 -ForegroundColor Green
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile}catch{}}
#######################################################################
# get domain name to check for hyper-v or esx vmware on each client
#######################################################################
# (Get-WmiObject Win32_ComputerSystem).Domain
$domain01 = (Get-WmiObject Win32_ComputerSystem).Domain
#################################
# function color xml html table
#################################
function color_xml_table([xml]$ttable)
{
for($trcnt02=1; $trcnt02 -le $ttable.table.tr.count; $trcnt02++)
{
for($tdcnt02=1; $tdcnt02 -le $ttable.table.tr[$trcnt02].td.Count; $tdcnt02++)
{
#write-host "td" $ttable.table.tr[$trcnt02].td.Count
# having 1 item reverse y and x in a table
if($ttable.table.tr[$trcnt02].td.Count -eq 1)
{
# cannot index element if there is only one
$item03 = $ttable.table.tr[$trcnt02].td
}
else
{
$item03 = $ttable.table.tr[$trcnt02].td[$tdcnt02]
}
for($errorcnt = 0; $errorcnt -lt $error_message_arr.count; $errorcnt++)
{
$error_message = $error_message_arr[$errorcnt]
if($item03 -like "*$error_message*")
{
#write-host "changed color" $error_color_arr[$errorcnt] -ForegroundColor Yellow
# having 1 item reverse y and x in a table
if($ttable.table.tr[$trcnt02].td.Count -eq 1)
{
$ttable.table.tr[$trcnt02].ChildNodes.SetAttribute('class',$error_color_arr[$errorcnt])
}
else
{
try
{
$ttable.table.tr[$trcnt02].ChildNodes[$tdcnt02].SetAttribute('class',$error_color_arr[$errorcnt])
}
catch{}
}
}
}
}
}
return $ttable
}
function ConvertTo-ProductKey
{
<#
.SYNOPSIS
Converts registry key value to windows product key.
.DESCRIPTION
Converts registry key value to windows product key. Specifically the following keys:
SOFTWARE\Microsoft\Windows NT\CurrentVersion\DigitalProductId
SOFTWARE\Microsoft\Windows NT\CurrentVersion\DigitalProductId4
.PARAMETER Registry
Either DigitalProductId or DigitalProductId4 (as described in the description)
.NOTES
Author: Zachary Loeber
Original Author: Boe Prox
Version: 1.0
- Took the registry setting retrieval portion from Boe's original script and converted it
to this basic conversion function. This is to be used in conjunction with my other
function, get-remoteregistryinformation
.EXAMPLE
PS > $reg_ProductKey = "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
PS > $a = Get-RemoteRegistryInformation -Key $reg_ProductKey -AsObject
PS > ConvertTo-ProductKey $a.DigitalProductId
XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
PS > ConvertTo-ProductKey $a.DigitalProductId4 -x64
XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
Description
-----------
Retrieves the product key information from the local machine and converts it to a readible format.
#>
[cmdletbinding()]
param (
[parameter(Mandatory=$True,Position=0)]
$Registry,
[parameter()]
[Switch]$x64
)
begin {
$map="BCDFGHJKMPQRTVWXY2346789"
}
process {
$ProductKey = ""
$prodkey = $Registry[0x34..0x42]
for ($i = 24; $i -ge 0; $i--)
{
$r = 0
for ($j = 14; $j -ge 0; $j--)
{
$r = ($r * 256) -bxor $prodkey[$j]
$prodkey[$j] = [math]::Floor([double]($r/24))
$r = $r % 24
}
$ProductKey = $map[$r] + $ProductKey
if (($i % 5) -eq 0 -and $i -ne 0)
{
$ProductKey = "-" + $ProductKey
}
}
$ProductKey
}
}
###############################################
# get all programs installed on the computer
###############################################
function getallprograms01($vm_name01)
{
$result01 = @()
$msg01 = "PROGRAM (getting 64 bits programs) pssession opened"
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
$remote_command01 = "try {Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate, @{Name = 'type'; Expression = {`"64 bits`"}} } catch {throw `$_}"
$remote_command_block01 = [scriptblock]::Create($remote_command01)
$job_program64 = Invoke-Command -asjob -Session $s2 -ScriptBlock $remote_command_block01 -ErrorAction stop | Get-Job
$results01 = $job_program64| Wait-Job -timeout $jobtimeout01 | Receive-Job
$job_program64 | remove-job
$msg01 = "PROGRAM (getting 32 bits programs) pssession opened"
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
$remote_command01 = "try {Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate, @{Name = 'type'; Expression = {`"32 bits`"}} } catch {throw `$_}"
$remote_command_block01 = [scriptblock]::Create($remote_command01)
$job_program32 = Invoke-Command -asjob -Session $s2 -ScriptBlock $remote_command_block01 -ErrorAction stop | Get-Job
$results01 += $job_program32 | Wait-Job -timeout $jobtimeout01 | Receive-Job
$job_program32 | remove-job
return $result01
}
#########################################
# clean up bad caracter in psobject
#########################################
function psobject_cleanup01($psobject01)
{
#write-host "cleaning"
# get properties names of this object (columns names)
$properties = $null
$properties = $psobject01 | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
foreach($line in $psobject01)
{
for($elecnt02 = 0; $elecnt02 -lt $properties.count; $elecnt02++)
{
# when encountering a replaced sub object, we dont care if there is an error because it's a string that will be replaced later anyway by a table
try{$line.($properties[$elecnt02]) = $line.($properties[$elecnt02]).tostring()}catch{}
try{$line.($properties[$elecnt02]) = $line.($properties[$elecnt02]) -replace '[^a-zA-Z0-9 ,.!"/$%?&()_+=-]', ''}catch{}
$line.($properties[$elecnt02]) = $line.($properties[$elecnt02]) -replace '[^a-zA-Z0-9 ,.!"/$%?&()_+=-]', ''
}
}
return $psobject01
}
function report_object_replacements01
{
param($object01)
# will detect object within array
# substitute them a text
# at final step, with a xml manipulation, replace all text with a html table so the report can have subtables
$global:recurse_level01 = 0
$global:item_to_replace_index01 = 0
$global:replace_arr01 = @()
#convert to html
#$object01 = $args[0]
$file10 = $scriptPath + "\" + $scriptname + ".htm"
$msg01 = "Type level 0: " + $object01.GetType().Name
#write-host $msg01
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
if($object01.GetType().Name -eq "Object[]")
{
$object_total = 0
for($elecnt = 0; $elecnt -lt $object01.count; $elecnt++)
{
#$elecnt
$msg01 = ""
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
if($object01[$elecnt].GetType().Name -eq "Object[]")
{
$object_total++
$msg01 = "Object, calling recurse from main loop"
write-host $msg01 -ForegroundColor Yellow
$object01[$elecnt] = report_object_replacements_recursive01($object01[$elecnt])
}
else
{
#write-host "cleaning: " $object01[$elecnt] -ForegroundColor Magenta
$object01[$elecnt] = psobject_cleanup01($object01[$elecnt])
# this is not an object, we do not recurse
#$msg01 = "not an object"; write-host $msg01 -ForegroundColor Green
}
}
#if($object_total -eq 0)
#{
# write-host "cleanup non-recursive"
# $object01 = psobject_cleanup01($object01)
#}
}
else
{
write-host "ERROR report_html_with_subtables The first param is not an object[]" -ForegroundColor Red
}
$htmlfile01 = $object01 | ConvertTo-Html -Head $header
$htmlfile01 | Out-File $file10
#write-host "type: " $htmlfile01.GetType().Name
#write-host $file10
return $file10
}
function report_object_replacements_recursive01($object02)
{
$msg01 = "In recursive level: " + $global:recurse_level01
write-host $msg01
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
if($object02.GetType().Name -eq "Object[]")
{
$global:recurse_level01++
$object_total = 0
for($elecnt01 = 0; $elecnt01 -lt $object02.count; $elecnt01++)
{
if($object02[$elecnt01].GetType().Name -eq "Object[]")
{
$object_total++
$msg01 = "Object, calling recurse from recurse"
write-host $msg01 -ForegroundColor Yellow
#$global:replace_arr01+= $object02[$elecnt01]
$object02[$elecnt01] = report_object_replacements_recursive01($object02[$elecnt01])
#$object02[$elecnt01] = psobject_cleanup01($object02[$elecnt01])
}
else
{
#write-host "cleaning recursive: " $object02[$elecnt01] -ForegroundColor Magenta
$object02[$elecnt01] = psobject_cleanup01($object02[$elecnt01])
#$msg01 = "not an object"; write-host $msg01 -ForegroundColor Green
#$object02[$elecnt01] = psobject_cleanup01($object02[$elecnt01])
}
}
$replacement_text01 = "replace with" + $global:item_to_replace_index01
$replacement01 = New-Object PsObject
$replacement01 | Add-Member NoteProperty -Name id -value $replacement_text01
$msg01 = "Replacing sub table with replacement text: " + $replacement_text01
#write-host $msg01 -ForegroundColor Cyan
$global:replace_arr01+= ,$object02
$object02 = $replacement01
$global:item_to_replace_index01++
#write-host $object02 -ForegroundColor Magenta
}
else
{
$global:recurse_level01--
write-host "not an object RECURSE CALLED FOR NOTHING" -ForegroundColor Green
}
return $object02
}
function replace_sub_table_xml($xml02)
{
for($trcnt=0;$trcnt -lt $xml02.table.tr.count;$trcnt++)
{
################################################################
# change color if certain keywords are found in html cell
################################################################
for($tdcnt=0;$tdcnt -lt $xml02.table.tr[$trcnt].td.Count;$tdcnt++)
{
# cannot index element if there is only one
if($xml02.table.tr[$trcnt].td.Count -eq 1)
{
# cannot index element if there is only one
$item02 = $xml02.table.tr[$trcnt].td
}
else
{
$item02 = $xml02.table.tr[$trcnt].td[$tdcnt]
}
####################
# color item
####################
for($errorcnt = 0; $errorcnt -lt $error_message_arr.count; $errorcnt++)
{
$error_message = $error_message_arr[$errorcnt]
if($item02 -like "*$error_message*")
{
#write-host "changed color" $error_color_arr[$errorcnt] -ForegroundColor Yellow
# cannot index element if there is only one
if($xml02.table.tr[$trcnt].td.Count -eq 1)
{
$xml02.table.tr[$trcnt].ChildNodes.SetAttribute('class',$error_color_arr[$errorcnt])
}
else
{
try
{
$xml02.table.tr[$trcnt].ChildNodes[$tdcnt].SetAttribute('class',$error_color_arr[$errorcnt])
}
catch{}
}
}
}
if($item02 -like "*replace with*")
{
#write-host "....... xml" -ForegroundColor Red
$replace_index01 = $item02.split("replace with")[$item02.split("replace with").GetUpperBound(0)]
#write-host "replace index01: " $replace_index01 -ForegroundColor Blue
#write-host "replacement: " $global:replace_arr01[$replace_index01]
#$test = $global:replace_arr01[$replace_index01] | ConvertTo-Html -fragment
[xml]$dummyxml = $global:replace_arr01[$replace_index01] | ConvertTo-Html -fragment
[xml]$dummyxml = replace_sub_table_xml_recurse([xml]$dummyxml)
# RECURSE HERE if there is a sub text for replacement
$xml02.table.tr[$trcnt].ChildNodes[$tdcnt].innertext = ""
$imported = $xml02.ImportNode($dummyxml.DocumentElement, $true)
### prod (sub table in column 1)
#$xml02.table.tr[$trcnt].ChildNodes[$tdcnt+1].AppendChild($imported) | out-null
### test (sub table in last column)
$xml02.table.tr[$trcnt].ChildNodes[($xml02.table.tr[$trcnt].td.Count)-1].AppendChild($imported) | out-null
}
}
}
return $xml02
}
function replace_sub_table_xml_recurse($xml01)
{
for($trcnt=0;$trcnt -lt $xml01.table.tr.count;$trcnt++)
{
#write-host "recurse xml" -ForegroundColor Red
################################################################
# change color if certain keywords are found in html cell
################################################################
for($tdcnt=0;$tdcnt -lt $xml01.table.tr[$trcnt].td.Count;$tdcnt++)
{
# cannot index element if there is only one
if($xml01.table.tr[$trcnt].td.Count -eq 1)
{
$item03 = $xml01.table.tr[$trcnt].td
}
else
{
$item03 = $xml01.table.tr[$trcnt].td[$tdcnt]
}
####################
# color item
####################
for($errorcnt = 0; $errorcnt -lt $error_message_arr.count; $errorcnt++)
{
$error_message = $error_message_arr[$errorcnt]
if($item03 -like "*$error_message*")
{
#write-host "changed color" $error_color_arr[$errorcnt] -ForegroundColor Yellow
# cannot index element if there is only one
if($xml01.table.tr[$trcnt].td.Count -eq 1)
{
$xml01.table.tr[$trcnt].ChildNodes.SetAttribute('class',$error_color_arr[$errorcnt])
}
else
{
try
{
$xml01.table.tr[$trcnt].ChildNodes[$tdcnt].SetAttribute('class',$error_color_arr[$errorcnt])
}
catch{}
}
}
}
if($item03 -like "*replace with*")
{
#write-host "recurse xml" -ForegroundColor Red
#write-host "recurse xml" -ForegroundColor Red
$replace_index01 = $item03.split("replace with")[$item03.split("replace with").GetUpperBound(0)]
#write-host "replace index01: " $replace_index01 -ForegroundColor Blue
#write-host "replacement: " $global:replace_arr01[$replace_index01]
#$test = $global:replace_arr01[$replace_index01] | ConvertTo-Html -fragment
[xml]$dummyxml = $global:replace_arr01[$replace_index01] | ConvertTo-Html -fragment
[xml]$dummyxml = replace_sub_table_xml_recurse([xml]$dummyxml)
# RECURSE HERE if there is a sub text for replacement
$xml01.table.tr[$trcnt].ChildNodes[$tdcnt].innertext = ""
$imported = $xml01.ImportNode($dummyxml.DocumentElement, $true)
#$xml01.table.tr[$trcnt].ChildNodes[$tdcnt+1].AppendChild($imported) | out-null
### test (sub table in last column)
$xml01.table.tr[$trcnt].ChildNodes[($xml01.table.tr[$trcnt].td.Count)-1].AppendChild($imported) | out-null
}
}
}
return $xml01
}
function papersize01($size01, $orientation01)
{
#write-host "orientation: " $orientation01 -ForegroundColor Yellow
$paper01 = New-Object PsObject
$paper01 | Add-Member NoteProperty -Name orientation -value ''
$paper01 | Add-Member NoteProperty -Name height -value ''
$paper01 | Add-Member NoteProperty -Name width -value ''
$paper01 | Add-Member NoteProperty -Name size -value ''
If($size01 -eq "ledger" -Or $size01 -eq "11x17" -Or $size01 -eq "tabloid" -Or $size01 -eq "tabloïd")
#Tabloid / US, B / ANSI B 11 x 17 279 x 432
{
$paper01.size = "tabloid"
If ($orientation01 -eq "portrait")
{
$paper01.orientation = "portrait"
$paper01.height01 = "432"
$paper01.width01 = "279"
}
Else
{
$paper01.orientation = "landscape"
$paper01.height = "279"
$paper01.width = "432"
}
}
ElseIf($size01 -eq "letter" -Or $size01 -eq "lettre" -Or $size01 -eq "us letter")
{
$paper01.size = "us letter"
If ($orientation01 -eq "portrait")
{
$paper01.orientation = "portrait"
$paper01.height = "297"
$paper01.width = "210"
}
Else
{
$paper01.orientation = "landscape"
$paper01.height = "210"
$paper01.width = "297"
}
}
ElseIf($size01 -eq "legal")
{
$paper01.size = "legal"
If ($orientation01 -eq "portrait")
{
$paper01.orientation = "portrait"
$paper01.height = "356"
$paper01.width = "210"
}
Else
{
$paper01.orientation = "landscape"
$paper01.height = "210"
$paper01.width = "356"
}
}
Else
{
$paper01.size = "us letter"
If( $orientation01 -eq "portrait")
{
$paper01.orientation = "portrait"
$paper01.height = "297"
$paper01.width = "210"
}
Else
{
$paper01.orientation = "landscape"
$paper01.height = "210"
$paper01.width = "297"
}
}
return $paper01
}
function web_report($wrp02)
{
########################
# web report
########################
$paper01 = papersize01 "letter" "portrait"
$paper01size = $paper01.size
$paper01orientation = $paper01.orientation
$paper01height = $paper01.height + "mm"
$paper01width = $paper01.width + "mm"
$pagesetup01 = @"
@page
{
margin: 0.2in 0.2in 0.2in 0in;
size:$paper01size $paper01orientation;
}
HTML body
{
height:$paper01height;width:$paper01width;margin: 0.2in 0in 0in 0.2in;
}
"@
$leftsidewidth01 = "15"
$rightsidewidth01 = "15"
[string]$middlewidth01 = 100 - $leftsidewidth01 - $rightsidewidth01
######################
# thead
######################
#$thead01 = @"
#<THEAD><TR><th>
#<table width=""100%"" BORDERCOLOR=""black"" border=1 CELLSPACING=1 cellpadding=2 style='border-collapse:collapse;border:none;$wrp02.header_title_line_style01'>"
#<tr>
#<td width=""" + $leftsidewidth01 + "%""><img style='"
#"@
$thead01 = ""
$thead01 = $thead01 + "<THEAD>" + "`n"
$thead01 = $thead01 + "<TR id=""t01""><th id=""t01"">" + "`n"
#=== table inside header only
#$thead01 = $thead01 + "<table id=""tr02"" width=""100%"" BORDERCOLOR=""black"" border=1 CELLSPACING=1 cellpadding=2 style='border-collapse:collapse;border:none;" + $wrp02.header_title_line_style01 + "'>" + "`n"
$thead01 = $thead01 + "<table id=""tr02"" width=""100%"" CELLSPACING=1 cellpadding=2 style='" + $wrp02.header_title_line_style01 + "'>" + "`n"
$thead01 = $thead01 + "<tr>" + "`n"
#=== left side image
$thead01 = $thead01 + "<td width=""" + $leftsidewidth01 + "%""><img style='"
If($wrp02.header_left_image_percenty01 -gt 0)
{
$thead01 = $thead01 + "height: " + $wrp02.header_left_image_percenty01 + "%;"
}
If($wrp02.header_left_image_percentx01 -gt 0)
{
$thead01 = $thead01 + "width: " + $wrp02.header_left_image_percentx01 + "%;"
}
$thead01 = $thead01 + "object-fit: contain' "
$thead01 = $thead01 + "src=""" + $wrp02.header_left_image_path01 + """ ></td>" + "`n"
#=== title
$thead01 = $thead01 + "<td width=""" + $middlewidth01 + "%"">"
$thead01 = $thead01 + "<font size=""5"">" + $wrp02.header_title_line01 + "</font><br>"
#=== second title
$thead01 = $thead01 + "<font size=""3"">" + $wrp02.header_title_line02 + "</font></td>" + "`n"
#=== right side image
$thead01 = $thead01 + "<td width=""" + $rightsidewidth01 + "%""><img style='"
If($wrp02.header_right_image_percenty01 -gt 0)
{
$thead01 = $thead01 + "height: " + $wrp02.header_right_image_percenty01 + "%;"
}
If($wrp02.header_right_image_percentx01 -gt 0)
{
$thead01 = $thead01 + "width: " + $wrp02.header_right_image_percentx01 + "%;"
}
$thead01 = $thead01 + "Object-fit: contain' "
$thead01 = $thead01 + "src=""" + $wrp02.header_right_image_path01 + """></td>" + "`n"
$thead01 = $thead01 + "</tr>"
$thead01 = $thead01 + "</table>" + "`n" #=== header
$thead01 = $thead01 + "</th></TR>" + "`n"
$thead01 = $thead01 + "</THEAD>" + "`n"
############################
# tfoot
############################
$tfoot01 = ""
$tfoot01 = $tfoot01 + "<TFOOT style='" + $wrp02.footer_style01 + "'>`n"
$tfoot01 = $tfoot01 + "<TR id=""t01""><th id= ""t01"">"
$tfoot01 = $tfoot01 + "<table id=""t01"" width=""100%"" BORDERCOLOR=""black"" border=1 CELLSPACING=1 cellpadding=2 style='border-collapse:collapse;border:none;" + $wrp02.footer_table_style01 + "'>" + "`n"
#$tfoot01 = $tfoot01 + "<tr style = 'page-break-inside: avoid;'>"
$tfoot01 = $tfoot01 + "<td width=""25%"" style='text-align: center;'>" + $wrp02.footer_left01 + "</td>" + "`n"
$tfoot01 = $tfoot01 + "<td width=""50%"" style='text-align: center;'>" + $wrp02.footer_middle01 + "</td>" + "`n"
$tfoot01 = $tfoot01 + "<td width=""25%"" style='text-align: center;'>" + $wrp02.footer_right01 + "</td>" + "`n"
$tfoot01 = $tfoot01 + "</tr>"
$tfoot01 = $tfoot01 + "</table>"
$tfoot01 = $tfoot01 + "</th></TR>"
$tfoot01 = $tfoot01 + "</TFOOT>" + "`n"
############################
# html header
############################
# not used anymore
#$header = @"
#<style>$pagesetup01
#BODY{background-color:white;}
#TABLE{page-break-after: always;border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
#TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: LightBlue}
#TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: white}
#.green{background-color:#d5f2d5}
#.blue{background-color:#e0eaf1}
#.red{background-color:#ffd7de}
#.yellow{background-color:#ffff00}
#.orange{background-color:#ffa500}
#</style>
#"@
#tr02{border-bottom:1px solid black;border-right:1px solid black;background-color: LightBlue;border-width: 1px;padding: 5px;border-style: solid;border-color: black;}
#td02{border-bottom:1px solid black;border-right:1px solid black;background-color: LightBlue;border-width: 1px;padding: 5px;border-style: solid;border-color: black;}
#tr02{border-top: 1px solid #FFFFFF;padding: 5px;border-style: solid;border-color: black;background-color: LightBlue;border-width: 1px;border-spacing: -1px;}
#td02{border-top: 1px solid #FFFFFF;padding: 5px;border-style: solid;border-color: black;background-color: LightBlue;border-width: 1px;border-spacing: -1px;}
$style01 = @"
$pagesetup01
#t01{width: 100%; border-collapse:collapse;border:none;background-color: White}
BODY{background-color:white;}
TABLE{border-spacing:0;page-break-after: always;border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: LightBlue}
TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: White}
TR{page-break-inside: avoid;}
#tr02{padding: 5px;background-color: white;border-width: 1px;border-style: solid;border-color: black;position: relative;}
.green{background-color:#d5f2d5}
.blue{background-color:#e0eaf1}
.red{background-color:#ffd7de}
.yellow{background-color:#ffff00}
.orange{background-color:#ffa500}
"@
#<table id=""t01"" width=""100%"" BORDERCOLOR=""black"" border=0 CELLSPACING=1 cellpadding=2>
#$style01 = ""
$body = @"
<style>
$style01
</style>
<table id="t01" CELLSPACING=1 cellpadding=2>
$thead01
<tbody id="DATA">
<tr id="t01"><td id="t01">
$($wrp02.data)
</td></tr>
</tbody>
$tfoot01
</table>
"@
return $body
}
######################################################################################################################################################
# main
######################################################################################################################################################
########################################################################
# elevation Self-elevate the script (pop-up will ask for elevation)
########################################################################
if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator'))
{
if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000)
{
$CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments
Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine
Exit
}
}
$level0_arr = @()
$level1_arr = @()
$level2_arr = @()
###############
# level 0
###############
$level0_obj = New-Object PsObject
$level0_obj | Add-Member NoteProperty -Name id -value '1'
$level0_obj | Add-Member NoteProperty -Name test -value 'test1'
$level0_arr += $level0_obj.PSObject.Copy()
$level0_obj = New-Object PsObject
$level0_obj | Add-Member NoteProperty -Name id -value '2'
$level0_obj | Add-Member NoteProperty -Name test -value 'test2'
$level0_arr += $level0_obj.PSObject.Copy()
############# level 1
$level1_obj = New-Object PsObject
$level1_obj | Add-Member NoteProperty -Name id -value '3'
$level1_obj | Add-Member NoteProperty -Name test -value 'sub 0 test3'
$level1_arr += $level1_obj.PSObject.Copy()
$level1_obj = New-Object PsObject
$level1_obj | Add-Member NoteProperty -Name id -value '4'
$level1_obj | Add-Member NoteProperty -Name test -value 'sub 0 test4'
$level1_arr += $level1_obj.PSObject.Copy()
############# level 2
$level2_obj = New-Object PsObject
$level2_obj | Add-Member NoteProperty -Name id -value '5'
$level2_obj | Add-Member NoteProperty -Name test -value 'sub 1 test5'
$level2_arr += $level2_obj.PSObject.Copy()
$level2_obj = New-Object PsObject
$level2_obj | Add-Member NoteProperty -Name id -value '6'
$level2_obj | Add-Member NoteProperty -Name test -value 'sub 1 test6'
$level2_arr += $level2_obj.PSObject.Copy()
$level1_arr += ,$level2_arr
#############
$level0_arr +=,$level1_arr
######################################
# pssession parameters
######################################
[string]$dummy = $null
$jobtimeout01 = 3*60 #( 3 minutes)
$OpenTimeoutMSec01 = 5000
$OperationTimeoutMSec01 = 180000 # 3 minutes
$PSSessionOption01 = New-PSSessionOption -OpenTimeout $OpenTimeoutMSec01 -OperationTimeout $OperationTimeoutMSec01
$prod01 = 1
if($prod01 -eq 1)
{
####################################
# get windows key from bios
####################################
#$msg01 = "sub array value...: " + $level0_arr[2][2][1][0].test #= id 6 sub1 test 6
#$msg01 = "sub array value...: " + $level0_arr[2][0][0] #= id 6 sub1 test 6
#Format-Custom -InputObject $level0_arr
#write-host $msg01 -ForegroundColor Magenta
$level0_arr = @()
$id01 = 0
if ($domain01 -eq "workgroup")
{
# not in a domain, use a local login for pssession
$Username = "$vm_name01\adminlocal"
$Password = 'Lamaisondelapizza'
try
{
$SecureString = ConvertTo-SecureString -AsPlainText $Password -Force
}catch{}
# LOCAL login and password
$MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$SecureString
###################
$computers01 = $computers_local01
}
foreach($computer01 in $computers01)
{
########################################
# open pssession to ask each computer
########################################
$error01.number = 0
try
{
if($domain01 -eq "workgroup")
{
$s2 = New-PSSession -ComputerName $computer01 -SessionOption $PSSessionOption01 -Credential $MySecureCreds -erroraction stop
}
else
{
write-host "Pssession openning: " $computer01 -ForegroundColor Magenta
$s2 = New-PSSession -ComputerName $computer01 -SessionOption $PSSessionOption01 -erroraction stop
}
}
catch
{
#$error01.number = 1
#write-host "ERROR Pssession openning: " $computer01 -ForegroundColor red
}
if($s2 -ne $null)
{
#######################################
# oscaption (operation system name)
#######################################
$remote_command01 = "try {(Get-WMIObject Win32_OperatingSystem -erroraction stop).caption} catch {throw `$_}"
$remote_command_block01 = [scriptblock]::Create($remote_command01)
$job_caption = Invoke-Command -asjob -Session $s2 -ScriptBlock $remote_command_block01 -ErrorAction stop | Get-Job
$results01 = $job_caption | Wait-Job -timeout $jobtimeout01 | Receive-Job
$job_caption | remove-job
$oscaption = $results01
$level0_obj = New-Object PsObject
$level0_obj | Add-Member NoteProperty -Name id -value $id01
$level0_obj | Add-Member NoteProperty -Name computer_netbios_name -value $computer01
$level0_obj | Add-Member NoteProperty -Name type -value $oscaption
$level0_arr += $level0_obj.PSObject.Copy()
#################################
# licence windows from bios
#################################
$level1_obj = New-Object PsObject
$level1_obj | Add-Member NoteProperty -Name id -value '1'
$level1_obj | Add-Member NoteProperty -Name data -value "licences"
$level1_arr = @()
$level1_arr += $level1_obj.PSObject.Copy()
#################################
# licence windows from bios
#################################
$level2_obj = New-Object PsObject
$level2_obj | Add-Member NoteProperty -Name id -value 'windows from bios'
################################################
# bios windows code (or other codes in bios)
################################################
$remote_command01 = "try {(Get-WmiObject -query 'select * from SoftwareLicensingService').OA3xOriginalProductKey} catch {throw `$_}"
$remote_command_block01 = [scriptblock]::Create($remote_command01)
$job01 = Invoke-Command -asjob -Session $s2 -ScriptBlock $remote_command_block01 -ErrorAction stop | Get-Job
$results01 = $job01 | Wait-Job -timeout $jobtimeout01 | Receive-Job
$job01 | remove-job
$bios_keys = $results01
$level2_obj | Add-Member NoteProperty -Name data -value $bios_keys
$level2_arr = @()
$level2_arr += $level2_obj.PSObject.Copy()
####################################
# windows key from registry
####################################
$level2_obj = New-Object PsObject
$level2_obj | Add-Member NoteProperty -Name id -value 'windows from digitalid'
$path01 = "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion"
#$a = Get-RemoteRegistryInformation -Key $reg_ProductKey -AsObject
$remote_command01 = "try {get-ItemProperty -Path `"$path01`" -erroraction stop} catch {throw `$_}"
$remote_command_block01 = [scriptblock]::Create($remote_command01)
$job01 = Invoke-Command -asjob -Session $s2 -ScriptBlock $remote_command_block01 -ErrorAction stop | Get-Job
$results01 = $job01 | Wait-Job -timeout $jobtimeout01 | Receive-Job
$job01 | remove-job
$a= $null
$a = $results01
#$a = get-ItemProperty -Path $path01 -erroraction stop
$digital_id_key =""
$digital_id_key = ConvertTo-ProductKey $a.DigitalProductId
#ConvertTo-ProductKey $a.DigitalProductId4 -x64
$level2_obj | Add-Member NoteProperty -Name data -value $digital_id_key
$level2_arr += $level2_obj.PSObject.Copy()
###################################
# office find ospp.vbs to find key
###################################
# 15 = office 2016
[string[]]$office_all_ospp01 = "C:\Program Files\Microsoft Office\Office15\OSPP.VBS"
[string[]]$office_all_ospp01+= "C:\Program Files (x86)\Microsoft Office\Office15\OSPP.VBS"
[string[]]$office_all_ospp01+= "C:\Program Files\Microsoft Office\Office16\OSPP.VBS"
[string[]]$office_all_ospp01+= "C:\Program Files (x86)\Microsoft Office\Office16\OSPP.VBS"
$office_ospp_found01 = -1
for($osppcnt = 0; $osppcnt -lt $office_all_ospp01.count; $osppcnt++)
{
# check if ospp.vbs exist
$office_ospp01 = $office_all_ospp01[$osppcnt]
$msg01 = "Checking if path exist: " + $office_ospp01
write-host $msg01
$remote_command01 = "try {Test-Path `"$office_ospp01`"} catch {throw `$_}"
$remote_command_block01 = [scriptblock]::Create($remote_command01)
$job01 = Invoke-Command -asjob -Session $s2 -ScriptBlock $remote_command_block01 -ErrorAction stop | Get-Job
$results01 = $job01 | Wait-Job -timeout $jobtimeout01 | Receive-Job
#write-host "job status: " $job01.StatusMessage -ForegroundColor Yellow
$job01 | remove-job
#write-host "result...: " $results01
if ($results01 -eq $true)
{
$office_ospp_found01 = $osppcnt
}
}
$office_ospp02 = $office_all_ospp01[$office_ospp_found01]
if($office_ospp_found01 -ne -1)
{
##################################
# office key(s) last five digits
##################################
#$productKey=cscript "C:\Program Files (x86)\Microsoft Office\Office15\OSPP.VBS" /dstatus | Select-String -Pattern $regex -AllMatches | % { $_.Matches } | % {$_.Value}
#$productKey=cscript $office_ospp01 /dstatus
#c:\windows\system32\cscript.exe "C:\Program Files\Microsoft Office\Office16\OSPP.VBS" /dstatus
$remote_command01 = "try {c:\windows\system32\cscript.exe `"$office_ospp02`" /dstatus} catch {throw `$_}"
$remote_command_block01 = [scriptblock]::Create($remote_command01)
$job01 = Invoke-Command -asjob -Session $s2 -ScriptBlock $remote_command_block01 -ErrorAction stop | Get-Job
$results01 = ""
$results01 = $job01 | Wait-Job -timeout $jobtimeout01 | Receive-Job
#write-host "job status: " $job01.StatusMessage -ForegroundColor Yellow
$job01 | remove-job
#$job01
#break all
$alllines = $results01.split("`n")
$i=0
[string[]]$productid = @()
$skuid01 = @()
$licensename01 = @()
$keys01 = @()
foreach($line01 in $alllines)
{
#write-host $i " " $line01
#$i++
if($line01 -like "*PRODUCT ID*")
{
$line01
$productid += $line01.Split(" ")[$line01.Split(" ").GetUpperBound(0)]
}
if($line01 -like "*sku id*")
{
$line01
$skuid01 += $line01.Split(" ")[$line01.Split(" ").GetUpperBound(0)]
}
$tag01 = "LICENSE NAME: "
if($line01 -like "*$tag01*")
{
$line01
$licensename01 += $line01.replace($tag01, "")
}
if($line01 -like "*Last 5 characters of installed product key*")
{
$line01
$keys01 += $line01.Split(" ")[$line01.Split(" ").GetUpperBound(0)]
}
}
#$productid
#$skuid01
#$licensename01
#$key01
#$regex='\b([A-Z1-9]{5}$)\b'
#$office_key = $results01 | Select-String -Pattern $regex -AllMatches | % { $_.Matches } | % {$_.Value}
$level22_arr = @()
for($liccnt01=0; $liccnt01 -lt $keys01.count; $liccnt01++)
{
$level22_obj = New-Object PsObject
$level22_obj | Add-Member NoteProperty -Name id -value 'office'
$level22_obj | Add-Member NoteProperty -Name productid -value $productid[$liccnt01]
$level22_obj | Add-Member NoteProperty -Name skuid -value $skuid01[$liccnt01]
$level22_obj | Add-Member NoteProperty -Name licensename -value $licensename01[$liccnt01]
$level22_obj | Add-Member NoteProperty -Name data -value $keys01[$liccnt01]
$level22_arr += $level22_obj.PSObject.Copy()
}
}
else
{
$msg01 = "ERROR did not find ospp.vbs"
write-host $msg01 -ForegroundColor Red
$level22_arr = @()
$level22_obj = New-Object PsObject
$level22_obj | Add-Member NoteProperty -Name id -value 'office'
$level22_obj | Add-Member NoteProperty -Name ERROR -value $msg01
$level22_arr += $level22_obj.PSObject.Copy()
}
#$TEST = New-Object PsObject
#$test | Add-Member NoteProperty -Name id -value '1111'
#$test
######################################
# 1 line for each type of licences
######################################
$level1_arr += ,$level2_arr
$level1_arr += ,$level22_arr
$level0_arr += ,$level1_arr
#$level0_arr+="test"
#$level0_arr[1][1].data = "test"
#$level0_arr[1].data = "test" #$level1_obj #.PSObject.Copy()
#$msg01 = "sub array value...: " + $level0_arr[1].data
#write-host $msg01 -ForegroundColor Magenta
#$level0_arr[1][1] = "test"
#$level0_arr =,$level1_arr
} # pssession not null
else
{
$msg01 = "ERROR could not open a pssession on " + $computer01 + ". Try powershell enable-psremoting on the machine"
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
write-host $msg01 -ForegroundColor Red
$level0_obj = New-Object PsObject
$level0_obj | Add-Member NoteProperty -Name id -value $id01
$level0_obj | Add-Member NoteProperty -Name computer_netbios_name -value $computer01
$level0_obj | Add-Member NoteProperty -Name type -value $msg01
$level0_arr += $level0_obj.PSObject.Copy()
}
$id01++
try{$s2.close}catch{}
$s2 = $null
} # for each computer01 in computers01
#$level0_arr += ,$level1_obj
#$level0_arr = get-wmiobject -class win32_logicaldisk | select deviceid, freespace, size, volumename
} # prod01
##############################
# get installed programs
##############################
#$properties = @('DisplayName', 'DisplayVersion', 'Publisher', 'InstallDate', "EstimatedSize") #, 'DisplayVersion', 'Publisher', 'InstallDate'
#$level0_arr = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object $properties
#write-host "before" -ForegroundColor Yellow
####################################
# test content of jagged array
####################################
#$level0_arr[2][2][1] = id 6 sub1 test 6
###################################################################
# replace sub object with texts to be able to generate html table
###################################################################
$file01 = report_object_replacements01($level0_arr)
$msg01 = "Number of items to replace: " + $global:item_to_replace_index01 + " count: " + $global:replace_arr01.count
#write-host $msg01
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
# inspect elements
for($elecnt02 = 0; $elecnt02 -lt $global:replace_arr01.count; $elecnt02++)
{
$msg01 = "Ele: " + $elecnt02 + " " + $global:replace_arr01[$elecnt02]
#write-host $msg01
if ($logall -eq 1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
}
########################################
# main html table
########################################
[xml]$level0_arr_xml01 = $level0_arr | ConvertTo-Html -fragment
#############################################
# put back each object in a html sub table
#############################################
$level0_arr_xml01 = replace_sub_table_xml($level0_arr_xml01)
$body = ""
$htmlfile02 = ""
####################################################
# page setup for web print thead tfoot with logo
####################################################
$wrp02 = New-Object PsObject # stand for web report parameters
$wrp02 | Add-Member NoteProperty -Name header_title_line01 -value 'Audit microsoft licenses'
$wrp02 | Add-Member NoteProperty -Name header_title_line02 -value 'Rapport'
$wrp02 | Add-Member NoteProperty -Name paper01 -value 'letter'
$wrp02 | Add-Member NoteProperty -Name orientation01 -value 'portrait'
$wrp02 | Add-Member NoteProperty -Name header_title_line_style01 -value ''
$wrp02 | Add-Member NoteProperty -Name header_left_image_percentx01 -value '70'
$wrp02 | Add-Member NoteProperty -Name header_left_image_percenty01 -value '0'
#$wrp02 | Add-Member NoteProperty -Name header_left_image_path01 -value 'https://www.tremblaycie.com/wp-content/uploads/2017/11/cropped-tremblay-cie.png'
$wrp02 | Add-Member NoteProperty -Name header_left_image_path01 -value 'https://devicom365.sharepoint.com/sites/TableaudeBordDevicom/_api/GroupService/GetGroupImage?id=%279d43061d-8e7c-43dd-a94a-f9bea7433034%27&hash=637193715108549624'
$wrp02 | Add-Member NoteProperty -Name header_right_image_percentx01 -value '70'
$wrp02 | Add-Member NoteProperty -Name header_right_image_percenty01 -value '0'
$wrp02 | Add-Member NoteProperty -Name header_right_image_path01 -value 'https://devicom365.sharepoint.com/sites/TableaudeBordDevicom/_api/GroupService/GetGroupImage?id=%279d43061d-8e7c-43dd-a94a-f9bea7433034%27&hash=637193715108549624'
$wrp02 | Add-Member NoteProperty -Name footer_style01 -value ''
$wrp02 | Add-Member NoteProperty -Name footer_table_style01 -value ''
$wrp02 | Add-Member NoteProperty -Name footer_left01 -value ''
$wrp02 | Add-Member NoteProperty -Name footer_middle01 -value ''
$date01 = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$wrp02 | Add-Member NoteProperty -Name footer_right01 -value $date01
# object containing jagged objects
$wrp02 | Add-Member NoteProperty -Name data -value $level0_arr_xml01.innerxml
$body = web_report($wrp02)
$htmlfile02 = ConvertTo-Html -Body $body
$htmlfile02 | Out-File $file01
& $file01
# futur search jquery
#var allRows = $("tr");
#$("input#search").on("keydown keyup", function() {
# allRows.hide();
# $("tr:contains('" + $(this).val() + "')").show();
#});