Certificate expiry report
Review certificate renewal deadlines, named owners and practical next actions in one concise report.
- Static
- 100×30
- 27 lines
Show-ConsoleDemo certificate-expirydotnet new consolemonster --demo certificate-expiryFollow a certificate chain from connection to proof, then act on a clear trust verdict.
Demos Reports Showcase
Use it when operators need to see where certificate trust breaks and what to repair.
TLS verdict Follow the chain. Verify the trust. ╭─ Connection ledger ──────────────────────────────────────────────────────────────────────────────────────────────────╮ │ [01/05] ✓ Resolve DNS 203.0.113.42 │ │ [02/05] ✓ Connect :443 connected │ │ [03/05] ✓ TLS handshake TLS 1.3 │ │ [04/05] ✗ Validate chain expired intermediate │ │ [05/05] – Expiry and revocation skipped │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ Host legacy.example.test IP 203.0.113.42 Protocol TLS 1.3 Cipher AES_256_GCM_SHA384 Issuer Example Issuing CA Valid until 2027-04-15 (leaf) ┌──────────┐ :443 ┌────────────┐ TLS ┌────────┐ ✗ issuer ┌────────────────┐ anchor ┌────────┐ │ ✓ client │─────→│ ✓ host:443 │────→│ ✓ leaf │──── ✗ ──→│ ✗ intermediate │───────→│ – root │ └──────────┘ └────────────┘ └────────┘ └────────────────┘ └────────┘ │ └──→ ✗ expired 2026-09-30 ── Trust decision ────────────────────────────────────────────────────────────────────────────────────────────────────── ╔════════════════════════════════════════════════════ ✗ UNTRUSTED ═════════════════════════════════════════════════════╗ ║ Chain validation failed. ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ Cause Expired intermediate certificate. Proof Intermediate expired before 2026-10-25. Action Renew the intermediate; serve the full chain. Exit 1 · 184 ms
The whole program in one file, with its sample data at the top. It drew the recording above; both languages draw the same cells.
# A deterministic TLS chain story. Data and timings are fictional; no hosts are contacted.
param(
[ValidateSet('Healthy', 'ExpiredIntermediate')]
[string] $Scenario = 'Healthy'
)
Import-Module ConsoleMonster
<# Real data plugs in here: map the output of your TLS checker to these rows.
Keep connection results, certificate dates and revocation evidence separate.
A successful handshake alone does not establish trust. #>
$expired = $Scenario -eq 'ExpiredIntermediate'
$chainStatus = if ($expired) { 'Fail' } else { 'Pass' }
$verdict = if ($expired) { 'UNTRUSTED' } else { 'TRUSTED' }
$hostName = if ($expired) { 'legacy.example.test' } else { 'portal.example.test' }
$steps = @(
[pscustomobject] @{ Key = 'dns'; Label = 'Resolve DNS'; Status = 'Pass'; Detail = '203.0.113.42' }
[pscustomobject] @{ Key = 'connect'; Label = 'Connect :443'; Status = 'Pass'; Detail = 'connected' }
[pscustomobject] @{ Key = 'handshake'; Label = 'TLS handshake'; Status = 'Pass'; Detail = 'TLS 1.3' }
[pscustomobject] @{ Key = 'chain'; Label = 'Validate chain'; Status = $chainStatus; Detail = $(if ($expired) { 'expired intermediate' } else { 'verified' }) }
[pscustomobject] @{ Key = 'expiry'; Label = 'Expiry and revocation'; Status = $(if ($expired) { 'Skip' } else { 'Pass' }); Detail = $(if ($expired) { 'skipped' } else { 'valid / good' }) }
)
$context = @(
[pscustomobject] @{ Key = 'Host'; Value = $hostName }
[pscustomobject] @{ Key = 'IP'; Value = '203.0.113.42' }
[pscustomobject] @{ Key = 'Protocol'; Value = 'TLS 1.3' }
[pscustomobject] @{ Key = 'Cipher'; Value = 'AES_256_GCM_SHA384' }
[pscustomobject] @{ Key = 'Issuer'; Value = 'Example Issuing CA' }
[pscustomobject] @{ Key = 'Valid until'; Value = '2027-04-15 (leaf)' }
)
$nodes = @(
[pscustomobject] @{ Kind = 'Node'; Key = 'client'; Label = 'client'; Status = 'Pass' }
[pscustomobject] @{ Kind = 'Node'; Key = 'host'; Label = 'host:443'; Status = 'Pass' }
[pscustomobject] @{ Kind = 'Node'; Key = 'leaf'; Label = 'leaf'; Status = 'Pass' }
[pscustomobject] @{ Kind = 'Node'; Key = 'intermediate'; Label = 'intermediate'; Status = $chainStatus }
[pscustomobject] @{ Kind = 'Node'; Key = 'root'; Label = 'root'; Status = $(if ($expired) { 'Skip' } else { 'Pass' }) }
)
$edges = @(
[pscustomobject] @{ Kind = 'Edge'; Key = 'connect'; From = 'client'; To = 'host'; Label = ':443' }
[pscustomobject] @{ Kind = 'Edge'; Key = 'tls'; From = 'host'; To = 'leaf'; Label = 'TLS' }
[pscustomobject] @{ Kind = 'Edge'; Key = 'issuer'; From = 'leaf'; To = 'intermediate'; Label = 'issuer'; Status = $chainStatus; Cut = $expired }
[pscustomobject] @{ Kind = 'Edge'; Key = 'anchor'; From = 'intermediate'; To = 'root'; Label = 'anchor' }
)
$flow = @($nodes; $edges) | New-ConsoleFlow -WidgetId chain
if ($expired) {
$flow | Add-ConsoleItem {
New-ConsoleFlowBranch -From intermediate -Label 'expired 2026-09-30' -Status Fail
}
}
$proof = if ($expired) { 'Intermediate expired before 2026-10-25.' } else { 'All three certificates valid on 2026-10-25.' }
$action = if ($expired) { 'Renew the intermediate; serve the full chain.' } else { 'No repair needed; keep expiry monitoring on.' }
$exitCode = if ($expired) { 1 } else { 0 }
# demo: pause 1
New-ConsoleRows -Gap 1 {
'[bold accent]TLS verdict[/] [muted]Follow the chain. Verify the trust.[/]'
New-ConsolePanel -Title 'Connection ledger' {
$steps | New-ConsolePhases -StepCounter
}
New-ConsoleKeyValue -Columns 2 -KeyRole Accent -Content {
foreach ($pair in $context) {
New-ConsoleKeyValueItem -Key $pair.Key -Value $pair.Value
}
}
$flow
New-ConsoleRule -Title 'Trust decision'
$verdictOptions = @{
Title = $verdict
Verdict = $true
Status = $chainStatus
}
New-ConsolePanel @verdictOptions {
New-ConsoleText -Text $(if ($expired) { 'Chain validation failed.' } else { 'Chain validation passed.' })
}
New-ConsoleKeyValue -KeyRole $chainStatus -Content {
New-ConsoleKeyValueItem -Key 'Cause' -Value $(if ($expired) { 'Expired intermediate certificate.' } else { 'Trusted root and complete chain.' })
New-ConsoleKeyValueItem -Key 'Proof' -Value $proof
New-ConsoleKeyValueItem -Key 'Action' -Value $action
New-ConsoleKeyValueItem -Key 'Exit' -Value "$exitCode · 184 ms"
}
} | Show-Console
# demo: pause 6
// A deterministic TLS chain story. Data and timings are fictional; no hosts are contacted.
using ConsoleMonster;
using ConsoleMonster.Layout;
using ConsoleMonster.Styling;
using ConsoleMonster.Widgets;
string scenario = args.Length == 0 ? "Healthy" : args[0];
if (scenario != "Healthy" && scenario != "ExpiredIntermediate") throw new ArgumentException("Use Healthy or ExpiredIntermediate.");
bool expired = scenario == "ExpiredIntermediate";
StatusKind chainStatus = expired ? StatusKind.Fail : StatusKind.Pass;
string verdict = expired ? "UNTRUSTED" : "TRUSTED";
string hostName = expired ? "legacy.example.test" : "portal.example.test";
// Real data plugs in here: map your TLS checker's connection, date and revocation evidence
// to these records. A successful handshake alone does not establish trust.
Step[] steps = {
new("dns", "Resolve DNS", StatusKind.Pass, "203.0.113.42"),
new("connect", "Connect :443", StatusKind.Pass, "connected"),
new("handshake", "TLS handshake", StatusKind.Pass, "TLS 1.3"),
new("chain", "Validate chain", chainStatus, expired ? "expired intermediate" : "verified"),
new("expiry", "Expiry and revocation", expired ? StatusKind.Skip : StatusKind.Pass, expired ? "skipped" : "valid / good")
};
Pair[] context = {
new("Host", hostName), new("IP", "203.0.113.42"), new("Protocol", "TLS 1.3"),
new("Cipher", "AES_256_GCM_SHA384"), new("Issuer", "Example Issuing CA"), new("Valid until", "2027-04-15 (leaf)")
};
FlowNode[] nodes = {
new("client", "client", StatusKind.Pass), new("host", "host:443", StatusKind.Pass),
new("leaf", "leaf", StatusKind.Pass), new("intermediate", "intermediate", chainStatus),
new("root", "root", expired ? StatusKind.Skip : StatusKind.Pass)
};
FlowEdge[] edges = {
new("connect", "client", "host", ":443"), new("tls", "host", "leaf", "TLS"),
new("issuer", "leaf", "intermediate", "issuer", chainStatus, expired), new("anchor", "intermediate", "root", "anchor")
};
var flow = new Flow("chain");
foreach (FlowNode node in nodes) flow.Add(node);
foreach (FlowEdge edge in edges) flow.Add(edge);
if (expired) flow.WithBranch(new FlowBranch("intermediate", "expired 2026-09-30", Status: StatusKind.Fail));
var phases = new Phases { StepCounter = true };
foreach (Step step in steps) phases.Set(step.Key, step.Status, step.Label, step.Detail);
var facts = new KeyValueList { Columns = 2, KeyRole = ThemeToken.Accent };
foreach (Pair pair in context) facts.Add(pair.Key, pair.Value);
string proof = expired ? "Intermediate expired before 2026-10-25." : "All three certificates valid on 2026-10-25.";
string action = expired ? "Renew the intermediate; serve the full chain." : "No repair needed; keep expiry monitoring on.";
int exitCode = expired ? 1 : 0;
Terminal.Write(new Rows(
new TextBlock("[bold accent]TLS verdict[/] [muted]Follow the chain. Verify the trust.[/]"),
new Panel(phases, "Connection ledger"), facts, flow, new Rule("Trust decision"),
new Panel(new TextBlock(expired ? "Chain validation failed." : "Chain validation passed."), verdict) { Verdict = true, Status = chainStatus },
new KeyValueList { KeyRole = chainStatus.ToToken() }
.Add("Cause", expired ? "Expired intermediate certificate." : "Trusted root and complete chain.")
.Add("Proof", proof).Add("Action", action).Add("Exit", exitCode + " · 184 ms")) { Gap = 1 });
record Step(string Key, string Label, StatusKind Status, string Detail);
record Pair(string Key, string Value);
Three steps in PowerShell, four in C#. Every command copies with one click.
Once. Windows PowerShell 5.1 or PowerShell 7 on Windows, macOS or Linux.
Install-Module ConsoleMonster -Scope CurrentUserDownload it, or copy it from above into tls-verdict.ps1.
From the folder you saved it in.
./tls-verdict.ps1A plain console app; .NET 8 or later.
dotnet new console -n Tlsverdict; cd TlsverdictThe only package the program needs.
dotnet add package ConsoleMonsterDownload it, or copy the program from above.
dotnet runOr skip the copying. Every demo ships with the module and the project template.
Show-ConsoleDemo tls-verdictExport-ConsoleDemo tls-verdict -Path ./tls-verdict.ps1dotnet new install ConsoleMonster.Templatesdotnet new consolemonster --demo tls-verdict -n TlsverdictStart with these, then read the rest of the file.
Map your TLS checker's output to the connection and certificate rows.
Replace the simulated dates and revocation result with measured evidence.
Choose the action and exit code from your own trust policy.
Review certificate renewal deadlines, named owners and practical next actions in one concise report.
Show-ConsoleDemo certificate-expirydotnet new consolemonster --demo certificate-expiryShow storage pressure with threshold bars, a folder breakdown and a clear summary of remaining capacity.
Show-ConsoleDemo disk-usagedotnet new consolemonster --demo disk-usageCombine a query summary, wrapped DNS records, a delegation tree, mail checks and resolver latency.
Show-ConsoleDemo querydotnet new consolemonster --demo query