AppInfo
Display application-wide variables defined in the main server script and shared across runspaces.
Full source
File: pwsh/tutorial/examples/Assets/Pages/AppInfo.cshtml
@page
@model Kestrun.Razor.PwshKestrunModel
@using Microsoft.AspNetCore.Html
@functions {
public static IHtmlContent Fallback(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return new HtmlString("<span class=\"muted-value\">(not configured)</span>");
return new HtmlString(System.Net.WebUtility.HtmlEncode(value));
}
}
@{
ViewData["Title"] = "Application Info";
dynamic d = Model.Data ?? new
{
App = (object)null,
Flags = (IDictionary<string, object>)new Dictionary<string, object>(),
Motd = "",
Host = (object)null,
Environment = (object)null,
DotNet = (object)null,
Kestrel = (object)null,
Roslyn = (object)null,
OS = (object)null,
Process = (object)null,
PowerShell = (object)null
};
}
<div class="card">
<h1>Application Info</h1>
<p class="muted">Values defined in the main server script.</p>
</div>
@if (d.App != null)
{
<div class="card">
<h2>App</h2>
<ul>
<li><strong>Name:</strong> @d.App.Name</li>
<li><strong>Environment:</strong> @d.App.Environment</li>
<li><strong>Version:</strong> @d.App.Version</li>
<li><strong>Started (UTC):</strong> @d.App.StartedUtc</li>
</ul>
</div>
}
@if (d.Environment != null)
{
<div class="card">
<h2>Environment</h2>
<ul>
<li>
<strong>ASP.NET Core:</strong>
@Fallback((string)d.Environment.EnvironmentName)
</li>
<li>
<strong>Host application:</strong>
@Fallback((string)d.Environment.ApplicationName)
</li>
<li>
<strong>Content root:</strong>
@Fallback((string)d.Environment.ContentRootPath)
</li>
<li>
<strong>Web root:</strong>
@Fallback((string)d.Environment.WebRootPath)
</li>
<li>
<strong>Machine:</strong>
@d.Environment.MachineName
</li>
<li>
<strong>User:</strong>
@d.Environment.UserName
</li>
<li>
<strong>Web root provider:</strong>
@Fallback((string)d.Environment.WebRootFileProvider)
</li>
</ul>
</div>
}
@if (d.DotNet != null)
{
<div class="card">
<h2>.NET</h2>
<ul>
<li><strong>Runtime:</strong> @d.DotNet.Runtime</li>
<li><strong>Process arch:</strong> @d.DotNet.ProcessArch</li>
<li><strong>OS arch:</strong> @d.DotNet.OSArch</li>
</ul>
</div>
}
@if (d.Kestrel != null)
{
<div class="card">
<h2>Kestrel</h2>
<ul>
<li><strong>Version:</strong> @d.Kestrel.Version</li>
<li>
<strong>Assembly:</strong>
<div class="muted wrap">@d.Kestrel.Location</div>
</li>
</ul>
</div>
}
@if (d.Roslyn != null)
{
void Row(string label, dynamic asm)
{
<li>
<strong>@label:</strong>
@if (asm == null)
{
<span class="muted-value">(not loaded)</span>
}
else
{
<span>@asm.Version</span>
<div class="muted wrap">@asm.Location</div>
}
</li>
;
}
<div class="card">
<h2>Roslyn</h2>
<ul>
@{
Row("Microsoft.CodeAnalysis", d.Roslyn.Core);
Row("Microsoft.CodeAnalysis.CSharp", d.Roslyn.CSharp);
Row("Microsoft.CodeAnalysis.Scripting", d.Roslyn.Scripting);
Row("Microsoft.CodeAnalysis.CSharp.Scripting", d.Roslyn.CSharpScript);
}
</ul>
</div>
}
@if (d.PowerShell != null)
{
<div class="card">
<h2>PowerShell</h2>
<ul>
<li><strong>Edition:</strong> @d.PowerShell.Edition</li>
<li><strong>Version:</strong> @d.PowerShell.Version</li>
<li><strong>Platform:</strong> @d.PowerShell.Platform</li>
<li><strong>OS:</strong> @d.PowerShell.OS</li>
@if (!string.IsNullOrEmpty(d.PowerShell.RemotingProto))
{
<li><strong>Remoting protocol:</strong> @d.PowerShell.RemotingProto</li>
}
@if (!string.IsNullOrEmpty(d.PowerShell.Serialization))
{
<li><strong>Serialization:</strong> @d.PowerShell.Serialization</li>
}
</ul>
</div>
}
@if (d.OS != null)
{
<div class="card">
<h2>OS</h2>
<ul>
<li><strong>Description:</strong> @d.OS.Description</li>
<li><strong>Container:</strong> @d.OS.IsContainer</li>
</ul>
</div>
}
@if (d.Process != null)
{
<div class="card">
<h2>Process</h2>
<ul>
<li><strong>PID:</strong> @d.Process.PID</li>
<li><strong>Start time (UTC):</strong> @d.Process.StartTime</li>
</ul>
</div>
}
@if (d.Flags != null)
{
<div class="card">
<h2>Feature Flags</h2>
<ul>
@foreach (var k in d.Flags.Keys)
{
<li><strong>@k</strong>: @d.Flags[k]</li>
}
</ul>
</div>
}
@if (!string.IsNullOrEmpty(d.Motd))
{
<div class="card">
<h2>Message of the Day</h2>
<pre>@d.Motd</pre>
</div>
}
@if (d.Host != null)
{
<div class="card">
<h2>Host</h2>
<pre>@d.Host</pre>
</div>
}
File: pwsh/tutorial/examples/Assets/Pages/AppInfo.cshtml.ps1
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
param()
function Get-AsmInfo {
param([Parameter(Mandatory)][string]$TypeName)
try {
$t = [Type]::GetType($TypeName, $false)
if (-not $t) { return $null }
$asm = $t.Assembly
$name = $asm.GetName()
return [pscustomobject]@{
Name = $name.Name
Version = $name.Version.ToString()
Location = $asm.Location
}
} catch {
return $null
}
}
# These variables EXIST because they were defined
# before Enable-KrConfiguration in the main script
$Model = [pscustomobject]@{
App = $AppInfo
Flags = $FeatureFlags
Motd = $Motd
Host = (Expand-KrObject -InputObject $KrServer -PassThru)
}
# --- Extra runtime/environment info ---
# Best-effort access to IServiceProvider (depends on how KrServer is shaped)
try {
$webEnv = $Context.RequestServices.GetService([Microsoft.AspNetCore.Hosting.IWebHostEnvironment])
} catch {
$webEnv = $null
}
$proc = [System.Diagnostics.Process]::GetCurrentProcess()
$environment = if ($null -eq $webEnv) {
@{
EnvironmentName = 'Unknown'
ApplicationName = 'Unknown'
ContentRootPath = 'Unknown'
WebRootPath = 'Unknown'
WebRootFileProvider = 'Unknown'
}
} else {
@{
EnvironmentName = $webEnv.EnvironmentName
ApplicationName = $webEnv.ApplicationName
ContentRootPath = $webEnv.ContentRootPath
WebRootPath = $webEnv.WebRootPath
WebRootFileProvider = $webEnv.WebRootFileProvider.GetType().FullName
}
}
$environment.MachineName = [System.Environment]::MachineName
$environment.UserName = try { [System.Environment]::UserName } catch { 'Unknown' }
Expand-KrObject -InputObject $environment
$Model | Add-Member -Force -NotePropertyName Environment -NotePropertyValue ([pscustomobject]$environment)
$Model | Add-Member -Force -NotePropertyName DotNet -NotePropertyValue ([pscustomobject]@{
Runtime = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
ProcessArch = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture
OSArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
})
$Model | Add-Member -Force -NotePropertyName OS -NotePropertyValue ([pscustomobject]@{
Description = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
IsContainer = ([System.Environment]::GetEnvironmentVariable('DOTNET_RUNNING_IN_CONTAINER') -eq 'true')
})
$Model | Add-Member -Force -NotePropertyName Process -NotePropertyValue ([pscustomobject]@{
PID = $proc.Id
StartTime = try { $proc.StartTime.ToUniversalTime().ToString('o') } catch { $null } # ISO 8601 UTC
})
$kestrelAsm = [Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer].Assembly
$kestrelName = $kestrelAsm.GetName()
$Model | Add-Member -Force -NotePropertyName Kestrel -NotePropertyValue ([pscustomobject]@{
Version = $kestrelName.Version.ToString()
Location = $kestrelAsm.Location
})
$psVersion = $PSVersionTable
$Model | Add-Member -Force -NotePropertyName PowerShell -NotePropertyValue ([pscustomobject]@{
Edition = $psVersion.PSEdition
Version = $psVersion.PSVersion.ToString()
Compatible = ($psVersion.PSCompatibleVersions -join ', ')
RemotingProto = $psVersion.PSRemotingProtocolVersion?.ToString()
Serialization = $psVersion.SerializationVersion?.ToString()
OS = $psVersion.OS
Platform = $psVersion.Platform
})
# Roslyn assemblies (best-effort; some may be absent if you don't use scripting)
$roslyn = [ordered]@{
Core = Get-AsmInfo 'Microsoft.CodeAnalysis.SyntaxNode, Microsoft.CodeAnalysis'
CSharp = Get-AsmInfo 'Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode, Microsoft.CodeAnalysis.CSharp'
Scripting = Get-AsmInfo 'Microsoft.CodeAnalysis.Scripting.Script, Microsoft.CodeAnalysis.Scripting'
CSharpScript = Get-AsmInfo 'Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript, Microsoft.CodeAnalysis.CSharp.Scripting'
}
# Only add if at least one component exists
if ($roslyn.Values | Where-Object { $_ -ne $null } | Select-Object -First 1) {
$Model | Add-Member -Force -NotePropertyName Roslyn -NotePropertyValue ([pscustomobject]@{
Core = $roslyn.Core
CSharp = $roslyn.CSharp
Scripting = $roslyn.Scripting
CSharpScript = $roslyn.CSharpScript
})
}
Step-by-step
- Shared state: Define
$AppInfo,$FeatureFlags, and$Motdin the main server script beforeEnable-KrConfiguration. - Model: Read those variables in
AppInfo.cshtml.ps1. - Host dump: Use
Expand-KrObjectto create a readable snapshot of$KrServer. - View: Render App details, feature flags, MOTD, and the host dump.
Try it
curl -i http://127.0.0.1:5000/AppInfo
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| App section missing | $AppInfo not set or not in scope | Ensure $AppInfo is defined in the server script before Enable-KrConfiguration |
| Flags are empty | $FeatureFlags not set | Define $FeatureFlags in the server script and keep it in scope |
| Host dump fails | Expand-KrObject unavailable | Ensure the Kestrun module is imported and available to the Razor runspaces |