Statusimo.psm1

function Get-FileName { 
    <#
    .SYNOPSIS
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Extension
    Parameter description
 
    .PARAMETER Temporary
    Parameter description
 
    .PARAMETER TemporaryFileOnly
    Parameter description
 
    .EXAMPLE
    Get-FileName -Temporary
    Output: 3ymsxvav.tmp
 
    .EXAMPLE
 
    Get-FileName -Temporary
    Output: C:\Users\pklys\AppData\Local\Temp\tmpD74C.tmp
 
    .EXAMPLE
 
    Get-FileName -Temporary -Extension 'xlsx'
    Output: C:\Users\pklys\AppData\Local\Temp\tmp45B6.xlsx
 
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param([string] $Extension = 'tmp',
        [switch] $Temporary,
        [switch] $TemporaryFileOnly)
    if ($Temporary) { return "$($([System.IO.Path]::GetTempFileName()).Replace('.tmp','')).$Extension" }
    if ($TemporaryFileOnly) { return "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).$Extension" }
}
function ConvertFrom-Color { 
    [alias('Convert-FromColor')]
    [CmdletBinding()]
    param ([ValidateScript( { if ($($_ -in $Script:RGBColors.Keys -or $_ -match "^#([A-Fa-f0-9]{6})$" -or $_ -eq "") -eq $false) { throw "The Input value is not a valid colorname nor an valid color hex code." } else { $true } })]
        [alias('Colors')][string[]] $Color,
        [switch] $AsDecimal)
    $Colors = foreach ($C in $Color) {
        $Value = $Script:RGBColors."$C"
        if ($C -match "^#([A-Fa-f0-9]{6})$") { return $C }
        if ($null -eq $Value) { return }
        $HexValue = Convert-Color -RGB $Value
        Write-Verbose "Convert-FromColor - Color Name: $C Value: $Value HexValue: $HexValue"
        if ($AsDecimal) { [Convert]::ToInt64($HexValue, 16) } else { "#$($HexValue)" }
    }
    $Colors
}
function Convert-Color { 
    <#
    .Synopsis
    This color converter gives you the hexadecimal values of your RGB colors and vice versa (RGB to HEX)
    .Description
    This color converter gives you the hexadecimal values of your RGB colors and vice versa (RGB to HEX). Use it to convert your colors and prepare your graphics and HTML web pages.
    .Parameter RBG
    Enter the Red Green Blue value comma separated. Red: 51 Green: 51 Blue: 204 for example needs to be entered as 51,51,204
    .Parameter HEX
    Enter the Hex value to be converted. Do not use the '#' symbol. (Ex: 3333CC converts to Red: 51 Green: 51 Blue: 204)
    .Example
    .\convert-color -hex FFFFFF
    Converts hex value FFFFFF to RGB
 
    .Example
    .\convert-color -RGB 123,200,255
    Converts Red = 123 Green = 200 Blue = 255 to Hex value
 
    #>

    param([Parameter(ParameterSetName = "RGB", Position = 0)]
        [ValidateScript( { $_ -match '^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$' })]
        $RGB,
        [Parameter(ParameterSetName = "HEX", Position = 0)]
        [ValidateScript( { $_ -match '[A-Fa-f0-9]{6}' })]
        [string]
        $HEX)
    switch ($PsCmdlet.ParameterSetName) {
        "RGB" {
            if ($null -eq $RGB[2]) { Write-Error "Value missing. Please enter all three values seperated by comma." }
            $red = [convert]::Tostring($RGB[0], 16)
            $green = [convert]::Tostring($RGB[1], 16)
            $blue = [convert]::Tostring($RGB[2], 16)
            if ($red.Length -eq 1) { $red = '0' + $red }
            if ($green.Length -eq 1) { $green = '0' + $green }
            if ($blue.Length -eq 1) { $blue = '0' + $blue }
            Write-Output $red$green$blue
        }
        "HEX" {
            $red = $HEX.Remove(2, 4)
            $Green = $HEX.Remove(4, 2)
            $Green = $Green.remove(0, 2)
            $Blue = $hex.Remove(0, 4)
            $Red = [convert]::ToInt32($red, 16)
            $Green = [convert]::ToInt32($green, 16)
            $Blue = [convert]::ToInt32($blue, 16)
            Write-Output $red, $Green, $blue
        }
    }
}
function Get-StatusimoData {
    [cmdletbinding()]
    param(
        [string] $FolderPath      
    )
    if ($FolderPath -ne '' -and (Test-Path -LiteralPath $FolderPath)) {
        $Files = Get-ChildItem -LiteralPath $FolderPath    
        foreach ($File in $Files) {       
            $Output = Get-Content -LiteralPath $File.FullName | ConvertFrom-Json      
            $TimeZoneBias = (Get-CimInstance -ClassName Win32_TimeZone).Bias
            $Output.PSObject.Properties | Where-Object { $_.Name -like 'Date*' } | ForEach-Object {
                if ($_.Value -is [DateTime]) {
                    $Output.($_.Name) = $_.Value.AddMinutes($TimeZoneBias)
                }
            }
            Add-Member -InputObject $Output -Name 'FullName' -Value $File.FullName -MemberType NoteProperty -Force
            $Output
        }
    } 
}
function New-StatusimoEvent {
    [alias('New-StatusimoIncident')]
    [cmdletbinding()]
    param(
        [DateTime] $Date = (Get-Date),
        [string] $Service,
        [ValidateSet('Operational', 'Partial Degradation', 'Down')][string] $Status,
        [string] $Title,
        [string] $Overview,
        [alias('IncidentsPath', 'EventsPath')][string] $FolderPath
    )
    $FileNameService = $Service -replace '[^a-zA-Z]', '_'
    $FileNameData = $Date.ToString("yyyy-MM-dd_HH_MM_ss")
    $FileNameEnd = Get-FileName -Extension 'json' -TemporaryFileOnly
    $FileName = $FileNameService + '_' + $FileNameData + '_' + $FileNameEnd
    $FilePath = [IO.Path]::Combine($FolderPath, $FileName)
    if ($Maintenance) {

    } else {
        $Incident = [ordered] @{
            Date     = $Date
            Service  = $Service
            Status   = $Status
            Title    = $Title
            Overview = $Overview
        }
        $Output = $Incident | ConvertTo-Json
        $Output | Set-Content -LiteralPath $FilePath
    }
}
function New-StatusimoMaintenance {
    [cmdletbinding()]
    param(
        [DateTime] $DateStart,
        [DateTime] $DateEnd,
        [string] $Service,
        [ValidateSet('Operational', 'Partial Degradation', 'Down')][string] $Status,
        [string] $Title,
        [string] $Overview,
        [string] $FolderPath
    )
    $FileNameService = $Service -replace '[^a-zA-Z]', '_'
    $FileNameData = $DateStart.ToString("yyyy-MM-dd_HH_MM_ss")
    $FileNameEnd = Get-FileName -Extension 'json' -TemporaryFileOnly
    $FileName = $FileNameService + '_' + $FileNameData + '_' + $FileNameEnd
    $FilePath = [IO.Path]::Combine($FolderPath, $FileName)

    $Maintenance = [ordered] @{
        DateStart = $DateStart
        DateEnd   = $DateEnd
        Service   = $Service
        Status    = $Status
        Title     = $Title
        Overview  = $Overview
    }
    $Output = $Maintenance | ConvertTo-Json
    $Output | Set-Content -LiteralPath $FilePath
    
}
function New-StatusimoPage {
    [cmdletbinding()]
    param(
        [string] $FilePath,
        [alias('Incident', 'Incidents', 'IncidentPath')][string] $IncidentsPath,
        [alias('Maintenance', 'MaintenancePath')][string] $MaintenancesPath,
        [int] $AutoRefresh,
        [switch] $Online
    )
    New-HTML -TitleText 'Services Status' -Online:$Online -AutoRefresh $AutoRefresh -FilePath $FilePath {

        $Today = Get-Date
        $Incidents = Get-StatusimoData -FolderPath $IncidentsPath | Sort-Object -Property Date, Title -Descending
        $Maintenances = Get-StatusimoData -FolderPath $MaintenancesPath | Sort-Object -Property DateEnd, DateStart, Title -Descending

        $IncidentTypes = $Incidents.Service | Sort-Object -Unique

        New-HTMLSection -Invisible {
            New-HTMLContainer -Width '900px' -Margin 'auto' {
                $Statuses = foreach ($Type in $IncidentTypes) {
                    $Incidents | Where-Object { $_.Service -eq $Type } | Select-Object -First 1 -ExpandProperty Status
                }
                if ($Statuses -notcontains 'Partial Degradation' -and $Statuses -notcontains 'Down') {
                    New-HTMLToast -TextHeader 'Information' -Text 'Everything is running smoothly!' -BarColorLeft Blue -IconSolid info-circle -IconColor Blue
                } else {
                    New-HTMLToast -TextHeader 'Warning' -Text "Some systems are affected. We're hard at work fixing." -BarColorLeft Orange -IconSolid exclamation-circle -IconColor Orange
                }

                New-HTMLHeading -Heading h1 -HeadingText 'Current Status' -Color Black

                New-HTMLStatus {

                    foreach ($Type in $IncidentTypes) {
                        $IncidentStatus = $Incidents | Where-Object { $_.Service -eq $Type } | Select-Object -First 1
                        if ($IncidentStatus.Status -eq 'Operational') {
                            $Icon = 'Good'
                            $Percentage = '100%'
                        } elseif ($IncidentStatus.Status -eq 'Partial Degradation') {
                            $Icon = 'Bad'
                            $Percentage = '30%'
                        } elseif ($IncidentStatus.Status -eq 'Down') {
                            $Icon = 'Dead'
                            $Percentage = '0%'
                        }

                        New-HTMLStatusItem -ServiceName $IncidentStatus.Service -ServiceStatus $IncidentStatus.Status -Icon $Icon -Percentage $Percentage
                    }
                }

                New-HTMLHeading -Heading h1 -HeadingText 'Scheduled Maintenance' -Color Black

                foreach ($Maintenance in $Maintenances) {
                    $Title = "$($Maintenance.Title) ($($Maintenance.DateStart) - $($Maintenance.DateEnd))"
                    if ($Today -ge $Maintenance.DateStart -and $Today -le $Maintenance.DateEnd) {
                        New-HTMLToast -TextHeader $Title  -Text $Maintenance.Overview -BarColorLeft Orange -IconSolid exclamation-circle -IconColor Orange
                    } elseif ($Today -le $Maintenance.DateStart) {
                        New-HTMLToast -TextHeader $Title  -Text $Maintenance.Overview -BarColorLeft Blue -IconSolid info-circle
                    } else {
                        New-HTMLToast -TextHeader $Title -Text $Maintenance.Overview -BarColorLeft Green -IconSolid check-circle -IconColor Green
                    }
                }

                New-HTMLHeading -Heading h1 -HeadingText 'Incidents per day' -Color Black

                $Data = foreach ($Element in 30..0) {
                    $Date = (Get-Date).AddDays(-$Element).Date
                    [Array] $IncidentsPerDay = $Incidents | Where-Object { ($_.Status -eq 'Partial Degradation' -or $_.Status -eq 'Down') -and $_.Date.Date -eq $Date }
                    $IncidentsPerDay.Count
                }
                $DataCategories = foreach ($Element in 30..0) {
                    (Get-Date).AddDays(-$Element).ToShortDateString()
                }
                New-HTMLChart -Title 'Incidents per day' -TitleAlignment left {
                    New-ChartCategory -Name $DataCategories
                    New-ChartLine -Name 'Incidents' -Value $Data
                }

                New-HTMLHeading -Heading h1 -HeadingText 'Timeline' -Color Black

                New-HTMLTimeline {
                    foreach ($Incident in $Incidents) {
                        New-HTMLTimelineItem -HeadingText $Incident.Title -Text $Incident.Overview -Date $Incident.Date -Color Black
                    }
                }
            }
        }
    }
}
function Remove-StatusimoMaintenance {
    [cmdletbinding()]
    param(
        [alias('Maintenance', 'MaintenancePath')][string] $MaintenancesPath,
        [int] $DaysOld
    )

    $DateOld = (Get-Date).AddDays($DaysOld)

    $Maintenances = Get-StatusimoData -FolderPath $MaintenancesPath | Where-Object { $_.DateEnd -lt $DateOld }
    foreach ($Maintenance in $Maintenances) {
        Remove-Item -LiteralPath $Maintenance.FullName -Confirm:$false
    }
}



Export-ModuleMember -Function @('New-StatusimoEvent', 'New-StatusimoMaintenance', 'New-StatusimoPage', 'Remove-StatusimoMaintenance') -Alias @('New-StatusimoIncident')