Files
IVANTI/reporting–dashboard-with-pswritehtml/SuiviMigrationWindows.ps1
2025-10-31 08:38:13 +01:00

767 lines
25 KiB
PowerShell

############################################### SQL ################################################################
$Exporthtml = "C:\Exploitation\WSUS\default.htm"
$configPath = "C:\Scripts\config.json"
############################################### SQL ################################################################
$config = Get-Content $configPath | ConvertFrom-Json
$ServerSQL = $config.SQL.Server
$database = $config.SQL.Database
$user = $config.SQL.Username
$password = $config.SQL.Password
# Convertir le mot de passe en SecureString
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($user, $securePassword)
$PassSQL = $creds.GetNetworkCredential().Password
Import-Module -Name PSwriteHTML
#. ([scriptblock]::Create((Invoke-WebRequest -Uri "https://raw.githubusercontent.com/DavidWuibaille/Repository/main/Function/DashboardEPM.ps1" -UseBasicParsing).Content))
# ----------------------------------------------------------------------------------------------------------------------
function Get-SqlData($connection, $query) {
$command = $connection.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
$table = New-Object System.Data.DataTable
$table.Load($result)
return $table
}
function Connect-SQLDatabase {
param (
[Parameter(Mandatory = $true)]
[string]$Server,
[Parameter(Mandatory = $true)]
[string]$Database,
[Parameter(Mandatory = $true)]
[string]$User,
[Parameter(Mandatory = $true)]
[string]$Password
)
try {
# Construction de la chaîne de connexion
$connectionString = "Server=$Server;uid=$User;pwd=$Password;Database=$Database;Integrated Security=False;"
# Création de l'objet connexion SQL
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
# Ouverture de la connexion
$connection.Open()
Write-Host "------ Connected to Database: $Database on Server: $Server"
# Retourne l'objet connexion
return $connection
} catch {
# Gestion des erreurs
Write-Host "------ Error connecting to Database: $Database on Server: $Server"
Write-Host "------ Error details: $_"
return $null
}
}
function Get-ApplicationData {
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection,
[Parameter(Mandatory = $true)]
[string]$AppFilter
)
# Initialiser la collection pour stocker les données
$ApplicationData = @()
# Définir la requête SQL avec le filtre dynamique
$query = @"
SELECT DISTINCT A0.DISPLAYNAME, A1.SUITENAME, A1.VERSION
FROM Computer A0 (nolock)
LEFT OUTER JOIN AppSoftwareSuites A1 (nolock) ON A0.Computer_Idn = A1.Computer_Idn
WHERE (A1.SUITENAME LIKE N'%$AppFilter%')
ORDER BY A0.DISPLAYNAME
"@
# Exécuter la requête et charger les résultats
$table = Get-SqlData -Connection $Connection -Query $query
# Parcourir les résultats et remplir la collection
foreach ($element in $table) {
$ApplicationData += [PSCustomObject]@{
'APPLICATION' = $element.SUITENAME
'VERSION' = $element.VERSION
'DEVICENAME' = $element.DISPLAYNAME
}
}
# Retourner la collection
return $ApplicationData
}
function Get-BitlockerDetails {
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection
)
# Initialiser la collection pour stocker les données
$BitlockerDetails = @()
# Définir la requête SQL avec le filtre dynamique pour le système d'exploitation
$query = @"
SELECT DISTINCT A0.DISPLAYNAME, A2.SECUREBOOTENABLED, A2.UEFIENABLED, A3.CONVERSIONSTATUS,
A4.TPMENABLE, A4.TPMVERSION, A5.MODEL
FROM Computer A0 (nolock)
LEFT OUTER JOIN Operating_System A1 (nolock) ON A0.Computer_Idn = A1.Computer_Idn
LEFT OUTER JOIN BIOS A2 (nolock) ON A0.Computer_Idn = A2.Computer_Idn
LEFT OUTER JOIN BitLocker A3 (nolock) ON A0.Computer_Idn = A3.Computer_Idn
LEFT OUTER JOIN TPMSystem A4 (nolock) ON A0.Computer_Idn = A4.Computer_Idn
LEFT OUTER JOIN CompSystem A5 (nolock) ON A0.Computer_Idn = A5.Computer_Idn
WHERE
A0.Computer_Idn NOT IN (
SELECT Computer_Idn FROM Computer WHERE TYPE LIKE N'%Server%'
)
ORDER BY A0.DISPLAYNAME
"@
# Exécuter la requête et charger les résultats
$table = Get-SqlData -Connection $Connection -Query $query
# Parcourir les résultats et remplir la collection
foreach ($element in $table) {
$BitlockerDetails += [PSCustomObject]@{
'DEVICENAME' = $element.DISPLAYNAME
'BitLocker' = if ([string]::IsNullOrEmpty($element.CONVERSIONSTATUS)) { "NoData" } else { $element.CONVERSIONSTATUS }
'SECURE Boot' = $element.SECUREBOOTENABLED
'UEFI' = $element.UEFIENABLED
'TPM' = $element.TPMENABLE
'TPM Version' = $element.TPMVERSION
'Model' = $element.MODEL
}
}
# Retourner la collection d'objets
return $BitlockerDetails
}
function Get-WindowsDetails {
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection
)
$WindowsDetails = @()
$SkippedDevices = @()
$query = @"
SELECT DISTINCT
A0.DISPLAYNAME,
A1.OSTYPE,
A2.CURRENTBUILD,
A2.UBR
FROM
Computer A0 (NOLOCK)
LEFT OUTER JOIN Operating_System A1 (NOLOCK) ON A0.Computer_Idn = A1.Computer_Idn
LEFT OUTER JOIN OSNT A2 (NOLOCK) ON A0.Computer_Idn = A2.Computer_Idn
WHERE
A0.Computer_Idn NOT IN (
SELECT Computer_Idn FROM Computer WHERE TYPE LIKE N'%Server%'
)
ORDER BY
A0.DISPLAYNAME
"@
# Exécute la requête SQL
$table = Get-SqlData -Connection $Connection -Query $query
foreach ($element in $table) {
$versionCode = Clean-WindowsVersion -OSType $element.OSTYPE -Build $element.CURRENTBUILD
$ubrValue = if ([string]::IsNullOrWhiteSpace($element.UBR)) {
$null
} else {
try {
[int]$element.UBR
} catch {
$null
}
}
if ($null -ne $ubrValue) {
$WindowsDetails += [PSCustomObject]@{
DEVICENAME = $element.DISPLAYNAME
VERSION = "$versionCode.$ubrValue"
}
} else {
$SkippedDevices += $element.DISPLAYNAME
}
}
if ($SkippedDevices.Count -gt 0) {
Write-Warning "Les machines suivantes n'ont pas de valeur UBR valide et ont été ignorées :"
$SkippedDevices | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow }
}
return $WindowsDetails | Sort-Object VERSION
}
function Close-SQLConnection {
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection
)
try {
# Vérifier si la connexion est ouverte
if ($Connection.State -eq [System.Data.ConnectionState]::Open) {
$Connection.Close()
Write-Host "------ Connection closed successfully."
} else {
Write-Host "------ Connection is already closed or not initialized."
}
} catch {
# Gestion des erreurs
Write-Host "------ Error closing the connection."
Write-Host "------ Error details: $_"
}
}
function Clean-WindowsVersion {
param (
[string]$OSType,
[string]$Build
)
# Hashtable principale
$buildMap = @{
# Windows 11
'22621' = 'w11_22H2'
'22631' = 'w11_23H2'
# Windows 10 - Standard
'19041' = 'w10_2004'
'19042' = 'w10_20H2'
'19043' = 'w10_21H1'
'19044' = 'w10_22H2' # Peut être override via OSTYPE
'19045' = 'w10_22H2'
# Windows 10 - LTSC / LTSB
'17763' = 'w10_LTSC2019'
'14393' = 'w10_LTSB2016'
# Windows 8.x / 7
'9600' = 'w81'
'9200' = 'w8'
'7601' = 'w7_sp1'
# Windows Server
'17763_srv' = 'ws2019'
'20348' = 'ws2022'
'25398' = 'ws2025'
}
# Gestion spéciale de certains builds ambigus
if ($Build -eq '19044' -and $OSType -like '*LTSC 2021*') {
return 'w10_LTSC2021'
}
if ($Build -eq '17763' -and $OSType -like '*Server*') {
return $buildMap['17763_srv']
}
if ($Build -eq '14393' -and $OSType -like '*Server*') {
return 'ws2016'
}
if ($buildMap.ContainsKey($Build)) {
return $buildMap[$Build]
}
# Fallback dynamique
if ($OSType -like '*Server*') {
return "$Build"
} else {
return "$Build"
}
}
function Clean-OSType {
param (
[string]$OSType
)
if (-not $OSType) { return "Unknown OS" }
$os = $OSType
# Nettoyage de base
$os = $os -replace ',\s*64-bit', ''
$os = $os -replace 'Microsoft\s+', ''
$os = $os -replace '\s+Edition', ''
# Patterns à détecter
$patterns = @(
@{ Pattern = 'Windows\s+10\s+Enterprise\s+2016\s+LTSB'; Replacement = 'Windows 10 LTSB 2016' },
@{ Pattern = 'Windows\s+10\s+Enterprise\s+LTSC\s+2019'; Replacement = 'Windows 10 LTSC 2019' },
@{ Pattern = 'Windows\s+10\s+Enterprise\s+LTSC\s+2021'; Replacement = 'Windows 10 LTSC 2021' },
@{ Pattern = 'Windows\s+10\s+Enterprise'; Replacement = 'Windows 10 Enterprise' },
@{ Pattern = 'Windows\s+11\s+Enterprise'; Replacement = 'Windows 11 Enterprise' },
@{ Pattern = 'Windows\s+10\s+IoT\s+Enterprise'; Replacement = 'Windows 10 IoT Enterprise' },
@{ Pattern = 'Windows\s+Server\s+(\d+)\s+Datacenter'; Replacement = 'Windows Server $1 Datacenter' },
@{ Pattern = 'Windows\s+Server\s+(\d+)\s+Standard'; Replacement = 'Windows Server $1 Standard' }
)
foreach ($p in $patterns) {
if ($os -match $p.Pattern) {
return ($os -replace $p.Pattern, $p.Replacement)
}
}
# Fallback : retourne nom nettoyé
return $os
}
function Get-WorkstationModels {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection
)
$workstationModels = New-Object System.Collections.Generic.List[object]
$query = @"
SELECT DISTINCT
A0.DISPLAYNAME,
A1.MODEL,
A2.OSTYPE
FROM
Computer A0 WITH (NOLOCK)
LEFT JOIN CompSystem A1 WITH (NOLOCK) ON A0.Computer_Idn = A1.Computer_Idn
LEFT JOIN Operating_System A2 WITH (NOLOCK) ON A0.Computer_Idn = A2.Computer_Idn
ORDER BY
A0.DISPLAYNAME
"@
Write-Verbose "Executing SQL query to retrieve workstation models."
$table = Get-SqlData -Connection $Connection -Query $query
foreach ($row in $table) {
$modelValue = if ([string]::IsNullOrWhiteSpace($row.MODEL) -or $row.MODEL -eq 'Default string') {
'No Data'
} else {
$row.MODEL
}
$cleanOS = Clean-OSType -OSType $row.OSTYPE
$workstationModels.Add([PSCustomObject]@{
DEVICENAME = $row.DISPLAYNAME
MODEL = $modelValue
OS = $cleanOS
})
}
return $workstationModels
}
function Get-WorkstationManufacturers {
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection
)
# Initialiser la collection pour stocker les données
$ManufacturerData = @()
# Définir la requête SQL
$query = @"
SELECT DISTINCT A0.DISPLAYNAME, A1.MANUFACTURER
FROM Computer A0 (nolock)
LEFT OUTER JOIN CompSystem A1 (nolock) ON A0.Computer_Idn = A1.Computer_Idn
WHERE (A0.Computer_Idn NOT IN (
SELECT Computer_Idn
FROM Computer
WHERE TYPE LIKE N'%Server%'
))
ORDER BY A0.DISPLAYNAME
"@
# Exécuter la requête et charger les résultats
$table = Get-SqlData -Connection $Connection -Query $query
# Parcourir les résultats et remplir la collection
foreach ($element in $table) {
$ManufacturerData += [PSCustomObject]@{
'DEVICENAME' = $element.DISPLAYNAME
'MANUFACTURER' = if ([string]::IsNullOrEmpty($element.MANUFACTURER)) { "NoData" } else { $element.MANUFACTURER }
}
}
# Retourner la collection d'objets
return $ManufacturerData
}
function Get-EnvironmentVariables {
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection,
[Parameter(Mandatory = $true)]
[string]$VariableName
)
# Initialiser la collection pour stocker les données
$EnvironmentVariables = @()
# Définir la requête SQL avec le filtre pour le nom de la variable
$query = @"
SELECT DISTINCT A0.DISPLAYNAME, A1.VALUESTRING
FROM Computer A0 (nolock)
LEFT OUTER JOIN EnvironSettings A1 (nolock) ON A0.Computer_Idn = A1.Computer_Idn
WHERE (A0.Computer_Idn NOT IN (
SELECT Computer_Idn
FROM Computer
WHERE TYPE LIKE N'%Server%'
))
AND A1.NAME = N'$VariableName'
ORDER BY A0.DISPLAYNAME
"@
# Exécuter la requête et charger les résultats
$table = Get-SqlData -Connection $Connection -Query $query
# Parcourir les résultats et remplir la collection
foreach ($element in $table) {
$EnvironmentVariables += [PSCustomObject]@{
'DEVICENAME' = $element.DISPLAYNAME
'VALUE' = $element.VALUESTRING
}
}
# Retourner la collection d'objets
return $EnvironmentVariables
}
function Get-HardwareScanDay {
param (
[Parameter(Mandatory = $true)]
[System.Data.SqlClient.SqlConnection]$Connection
)
# Initialiser la collection pour stocker les données
$HardwareScanData = @()
# Définir la requête SQL
$query = @"
SELECT DISTINCT A0.DISPLAYNAME, A0.HWLASTSCANDATE
FROM Computer A0 (nolock)
WHERE (A0.Computer_Idn NOT IN (
SELECT Computer_Idn
FROM Computer
WHERE TYPE LIKE N'%Server%'
))
ORDER BY A0.DISPLAYNAME
"@
# Exécuter la requête et charger les résultats
$table = Get-SqlData -Connection $Connection -Query $query
# Parcourir les résultats et remplir la collection
foreach ($element in $table) {
$scanDate = $element.HWLASTSCANDATE
$daysDifference = (Get-Date) - [datetime]$scanDate
# Classifier la date selon les intervalles
$category = switch ($true) {
($daysDifference.TotalDays -le 7) { "<7 jours" }
($daysDifference.TotalDays -le 14) { "<14 jours" }
($daysDifference.TotalDays -le 30) { "<30 jours" }
($daysDifference.TotalDays -le 90) { "<90 jours" }
default { ">90 jours" }
}
$HardwareScanData += [PSCustomObject]@{
'DEVICENAME' = $element.DISPLAYNAME
'SCAN_CATEGORY' = $category
}
}
# Retourner la collection d'objets
return $HardwareScanData
}
#$Connection = Connect-SQLDatabase -Server $ServerSQL -Database $database -User $user -Password $PassSQL
#$Application1 = Get-ApplicationData -Connection $Connection -AppFilter $ApplicationFilter1
#$Application2 = Get-ApplicationData -Connection $Connection -AppFilter $ApplicationFilter2
#$BitlockerDetails = Get-BitlockerDetails -Connection $Connection
#$WindowsDetails = Get-WindowsDetails -Connection $Connection
#$WorkstationModels = Get-WorkstationModels -Connection $Connection
#$WorkstationMakes = Get-WorkstationManufacturers -Connection $Connection
#$Variable1 = Get-EnvironmentVariables -Connection $Connection -VariableName $VariableFilter1
#$HardwareScanDay = Get-HardwareScanDay -Connection $Connection
#$WindowsgroupesVersion = $WindowsDetails | Group-Object -Property VERSION
#$BitlockerStatus = $BitlockerDetails | Group-Object -Property Bitlocker
#$Modelscount = $WorkstationModels | Group-Object -Property MODEL
#$Makesount = $WorkstationMakes | Group-Object -Property MANUFACTURER
#$ScanDaycount = HardwareScanDay | Group-Object -Property SCAN_CATEGORY
#Close-SQLConnection -Connection $Connection
# ----------------------------------------------------------------------------------------------------------------------
$Connection = Connect-SQLDatabase -Server $ServerSQL -Database $database -User $user -Password $PassSQL
#$Application1 = Get-ApplicationData -Connection $Connection -AppFilter $ApplicationFilter1
#$Application2 = Get-ApplicationData -Connection $Connection -AppFilter $ApplicationFilter2
#$BitlockerDetails = Get-BitlockerDetails -Connection $Connection
$WindowsDetails = Get-WindowsDetails -Connection $Connection
$WorkstationModels = Get-WorkstationModels -Connection $Connection
#$WorkstationMakes = Get-WorkstationManufacturers -Connection $Connection
#$Variable1 = Get-EnvironmentVariables -Connection $Connection -VariableName $VariableFilter1
#$HardwareScanDay = Get-HardwareScanDay -Connection $Connection
$WindowsgroupesVersion = $WindowsDetails | Group-Object -Property VERSION
#$BitlockerStatus = $BitlockerDetails | Group-Object -Property Bitlocker
$Modelscount = $WorkstationModels | Group-Object -Property MODEL
$Makesount = $WorkstationMakes | Group-Object -Property MANUFACTURER
#$ScanDaycount = HardwareScanDay | Group-Object -Property SCAN_CATEGORY
Close-SQLConnection -Connection $Connection
$WindowsMajorMap = $WindowsDetails | ForEach-Object {
$major = ($_.VERSION -split '\.')[0]
[PSCustomObject]@{
MAJORVERSION = $major
DEVICENAME = $_.DEVICENAME
}
}
# Sauvegarde quotidienne des versions Windows
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$historyFile = Join-Path -Path $scriptPath -ChildPath "windows_versions_history.csv"
$today = (Get-Date -Format "yyyy-MM-dd")
$todayData = $WindowsgroupesVersion | ForEach-Object {
[PSCustomObject]@{
Date = $today
Version = $_.Name
Count = $_.Count
}
}
$yesterday = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
$raw = Import-Csv -Path $historyFile
$cleaned = foreach ($entry in $raw) {
$date = $entry.Date
$version = $entry.Version
$count = [int]$entry.Count
$parts = $version -split '\.'
$build = $parts[0]
$ubr = $parts[1]
if ($ubr -match '^\d+$') {
$cleanCode = Clean-WindowsVersion -OSType '' -Build $build
[PSCustomObject]@{
Date = $date
Version = "$cleanCode.$ubr"
Count = $count
}
}
}
# Réécriture propre du CSV (overwrite)
$cleaned | Export-Csv -Path $historyFile -NoTypeInformation -Encoding UTF8
# Génération des courbes temporelles
$rawHistory = Import-Csv -Path $historyFile
# Nettoyage et enrichissement des lignes
$historyParsed = foreach ($entry in $rawHistory) {
$date = $entry.Date
$rawVer = $entry.Version
$count = [int]$entry.Count
# Découper Version brute : "17763.7009" => Build + UBR
$parts = $rawVer -split '\.'
$build = $parts[0]
$ubr = if ($parts[1] -match '^\d+$') { [int]$parts[1] } else { $null }
# Appeler Clean-WindowsVersion pour retrouver le VersionCode
$versionCode = Clean-WindowsVersion -OSType '' -Build $build
[PSCustomObject]@{
Date = $date
VersionCode = $versionCode
UBR = $ubr
Count = $count
}
}
# Reconstruire version finale sans tag .old
$history = foreach ($row in $historyParsed) {
[PSCustomObject]@{
Date = $row.Date
Version = "$($row.VersionCode).$($row.UBR)"
Count = $row.Count
}
}
# Assurer que Count est bien un entier (au cas où CSV l'ait converti en string)
$history | ForEach-Object { $_.Count = [int]$_.Count }
$versions = $history.Version | Sort-Object -Unique
$dates = $history.Date | Sort-Object -Unique
# Courbe pour chaque version complète
$lines = foreach ($version in $versions) {
$valeurs = foreach ($date in $dates) {
$sum = ($history | Where-Object { $_.Date -eq $date -and $_.Version -eq $version } | Measure-Object -Property Count -Sum).Sum
if (-not $sum) { $sum = 0 }
$sum
}
[PSCustomObject]@{
Version = $version
Values = $valeurs
}
}
# Ajouter colonne MajorVersion à partir de Version
$historyGrouped = $history | ForEach-Object {
$_ | Add-Member -NotePropertyName "MajorVersion" -NotePropertyValue ($_.Version -split '\.')[0] -Force
$_
}
# Liste des versions majeures uniques
$majorVersions = $historyGrouped.MajorVersion | Sort-Object -Unique
# Courbes pour les versions majeures
$linesMajor = foreach ($version in $majorVersions) {
$valeurs = foreach ($date in $dates) {
$sum = ($historyGrouped | Where-Object {
$_.Date -eq $date -and $_.MajorVersion -eq $version
} | Measure-Object -Property Count -Sum).Sum
if (-not $sum) { $sum = 0 }
$sum
}
$displayName = $version
[PSCustomObject]@{
Version = $displayName
Values = $valeurs
}
}
# Rapport HTML
New-HTML -TitleText 'Dashboard' {
# Onglet Windows - Donut chart actuel
New-HTMLTab -Name 'Windows' {
New-HTMLSection -HeaderText 'Windows Versions Over Time' {
New-HTMLPanel {
New-HTMLChart -Title "Windows Versions (Daily Evolution)" -TitleAlignment center {
New-ChartAxisX -Names $dates
foreach ($line in $lines) {
New-ChartLine -Name $line.Version -Value $line.Values
}
}
}
}
New-HTMLSection -HeaderText 'Windows Version' {
New-HTMLPanel {
New-HTMLChart -Title "Version" {
New-ChartToolbar -Download
New-ChartEvent -DataTableID 'WindowsOS' -ColumnID 1
foreach ($groupe in $WindowsgroupesVersion) {
New-ChartDonut -Name $($groupe.Name) -Value $($groupe.Count)
}
}
}
New-HTMLPanel {
New-HTMLTable -DataTable $WindowsDetails -DataTableID 'WindowsOS' -HideFooter
}
}
}
# Onglet Windows major
New-HTMLTab -Name 'Windows major' {
New-HTMLSection -HeaderText 'Windows Versions Over Time (Grouped by Major Version)' {
New-HTMLPanel {
New-HTMLChart -Title "Windows Major Versions (Daily Evolution)" -TitleAlignment center {
New-ChartAxisX -Names $dates
foreach ($line in $linesMajor) {
New-ChartLine -Name $line.Version -Value $line.Values
}
}
}
}
New-HTMLSection -HeaderText 'Windows Versions' {
New-HTMLPanel {
New-HTMLChart -Title "Windows Major" {
New-ChartToolbar -Download
New-ChartEvent -DataTableID 'WindowsV' -ColumnID 0
foreach ($line in $linesMajor) {
$total = ($line.Values | Measure-Object -Sum).Sum
New-ChartDonut -Name $($line.Version) -Value $total
}
}
}
New-HTMLPanel {
New-HTMLTable -DataTable $WindowsMajorMap -DataTableID 'WindowsV' -HideFooter
}
}
}
# Onglet Hardware
New-HTMLTab -Name 'Model' {
New-HTMLSection -HeaderText 'Hardware' {
New-HTMLPanel {
New-HTMLChart -Title "Version" {
New-ChartToolbar -Download
New-ChartEvent -DataTableID 'Modelcount' -ColumnID 1
foreach ($groupe in $Modelscount) {
New-ChartDonut -Name $($groupe.Name) -Value $($groupe.Count)
}
}
New-HTMLPanel {
New-HTMLTable -DataTable $WorkstationModels -DataTableID 'Modelcount' -HideFooter
}
}
}
}
# Pied de page
New-HTMLFooter {
New-HTMLText -Text "Date of this report (GMT time): $(Get-Date)" -Color Blue -Alignment Center
}
} -FilePath $Exporthtml -Online