Private/Code.ps1
|
# What a code means. # # A status or an error code is never shown to a Technician bare. The code stays - it is # what gets looked up - and what it means stands beside it. These are the decoders the # Kinds share: the error numbers of Windows, HTTP statuses, the sign-in statuses of # Windows and of Microsoft Entra ID. A code none of them knows comes back with nothing, # and is then shown as it is: that is a gap to close and not a reason to hide the code. # # All pure. Where a table here is not Microsoft's own documentation, it says where it is from. function ConvertTo-CodeNumber { <# .SYNOPSIS A code as a number, however it was written: 1067, '0x5', 'C000006D'. $null for what is none. Pure. #> [CmdletBinding()] [OutputType([long])] param([AllowNull()]$Code, [switch]$Hex) $text = "$Code".Trim() if (-not $text) { return $null } $number = [long]0 if ($text -match '^(?i)0x([0-9a-f]+)$') { if ([long]::TryParse($Matches[1], [Globalization.NumberStyles]::HexNumber, [cultureinfo]::InvariantCulture, [ref]$number)) { return $number } return $null } if ($Hex -and $text -match '^(?i)[0-9a-f]{8}$') { if ([long]::TryParse($text, [Globalization.NumberStyles]::HexNumber, [cultureinfo]::InvariantCulture, [ref]$number)) { return $number } } if ([long]::TryParse($text, [Globalization.NumberStyles]::Integer, [cultureinfo]::InvariantCulture, [ref]$number)) { return $number } $null } function Get-Win32ErrorText { <# .SYNOPSIS What Windows itself says of one of its error numbers, in the language Windows runs in. Nothing for zero and for a number it has no words for. Pure. .DESCRIPTION The exit code of a service, the error of a Group Policy run, the low word of an HRESULT that begins 0x8007. Windows carries the sentences; a number it does not know comes back as "Unknown error (0x...)", which is no meaning and is dropped. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Code) $number = ConvertTo-CodeNumber -Code $Code if ($null -eq $number -or $number -le 0 -or $number -gt [int]::MaxValue) { return '' } try { $message = "$((New-Object System.ComponentModel.Win32Exception ([int]$number)).Message)".Trim() if ($message -match '(?i)^(Unknown error|Unbekannter Fehler)' -or $message -match '\(0x[0-9a-f]+\)$') { return '' } $message } catch { '' } } function Get-HttpStatusText { <# .SYNOPSIS What an HTTP status means, in a Technician's words. Nothing for one this has no word for and whose class says nothing either. Pure. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Status) $number = ConvertTo-CodeNumber -Code $Status if ($null -eq $number) { return '' } switch ([int]$number) { 200 { return Get-Text 'Value.Code.Http.200' } 204 { return Get-Text 'Value.Code.Http.204' } 301 { return Get-Text 'Value.Code.Http.301' } 302 { return Get-Text 'Value.Code.Http.302' } 307 { return Get-Text 'Value.Code.Http.302' } 400 { return Get-Text 'Value.Code.Http.400' } 401 { return Get-Text 'Value.Code.Http.401' } 403 { return Get-Text 'Value.Code.Http.403' } 404 { return Get-Text 'Value.Code.Http.404' } 407 { return Get-Text 'Value.Code.Http.407' } 408 { return Get-Text 'Value.Code.Http.408' } 429 { return Get-Text 'Value.Code.Http.429' } 500 { return Get-Text 'Value.Code.Http.500' } 502 { return Get-Text 'Value.Code.Http.502' } 503 { return Get-Text 'Value.Code.Http.503' } 504 { return Get-Text 'Value.Code.Http.504' } } if ($number -ge 300 -and $number -lt 400) { return Get-Text 'Value.Code.Http.3xx' } if ($number -ge 500 -and $number -lt 600) { return Get-Text 'Value.Code.Http.5xx' } '' } function Format-CodeWithMeaning { <# .SYNOPSIS A code with what it means behind it; the code alone where nothing is known. Pure. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()][AllowEmptyString()][string]$Code, [AllowNull()][AllowEmptyString()][string]$Meaning) if (-not "$Code".Trim()) { return '' } if (-not "$Meaning".Trim()) { return "$Code".Trim() } (Get-Text 'Value.Code.WithMeaning') -f "$Code".Trim(), "$Meaning".Trim() } function Format-HttpStatus { <# .SYNOPSIS An HTTP status with what it means behind it. Pure. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Status) Format-CodeWithMeaning -Code "$Status" -Meaning (Get-HttpStatusText -Status $Status) } function Get-NtStatusText { <# .SYNOPSIS What a sign-in status of Windows (NTSTATUS) means. Nothing for one not known here. Pure. .DESCRIPTION The ones a sign-in ends in, as Microsoft documents them for logon events (event 4625, "Status" and "Sub Status"), and the ones Microsoft's article on troubleshooting the primary refresh token names for the "Attempt Status" of dsregcmd /status: the network was not there, or the logon session was gone. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Code) $number = ConvertTo-CodeNumber -Code $Code -Hex if ($null -eq $number) { return '' } switch ('{0:X8}' -f ($number -band 0xFFFFFFFFL)) { 'C000006D' { return Get-Text 'Value.Code.NtStatus.LogonFailure' } 'C000006A' { return Get-Text 'Value.Code.NtStatus.WrongPassword' } 'C0000064' { return Get-Text 'Value.Code.NtStatus.NoSuchUser' } 'C0000234' { return Get-Text 'Value.Code.NtStatus.LockedOut' } 'C0000072' { return Get-Text 'Value.Code.NtStatus.Disabled' } 'C0000071' { return Get-Text 'Value.Code.NtStatus.PasswordExpired' } 'C0000224' { return Get-Text 'Value.Code.NtStatus.PasswordMustChange' } 'C0000193' { return Get-Text 'Value.Code.NtStatus.AccountExpired' } 'C000006F' { return Get-Text 'Value.Code.NtStatus.LogonHours' } 'C0000070' { return Get-Text 'Value.Code.NtStatus.Workstation' } 'C000005E' { return Get-Text 'Value.Code.NtStatus.NoLogonServers' } 'C0000133' { return Get-Text 'Value.Code.NtStatus.TimeDifference' } 'C000015B' { return Get-Text 'Value.Code.NtStatus.LogonType' } 'C000005F' { return Get-Text 'Value.Code.NtStatus.NoLogonSession' } 'C00000BE' { return Get-Text 'Value.Code.NtStatus.BadNetworkPath' } 'C00000C4' { return Get-Text 'Value.Code.NtStatus.UnexpectedNetworkError' } 'C00000D0' { return Get-Text 'Value.Code.NtStatus.RequestNotAccepted' } 'C000023C' { return Get-Text 'Value.Code.NtStatus.NetworkUnreachable' } } '' } function Get-HResultText { <# .SYNOPSIS What an HRESULT means: a Win32 error it wraps, a sign-in status of Windows, or one of the sign-in broker's own. Nothing for one not known here. Pure. .DESCRIPTION 0x8007nnnn is the Win32 error nnnn. 0xCAAnnnnn are the codes of the Microsoft account and work account broker (AAD/WAM) that Office logs; the ones named here are those Microsoft's troubleshooting articles for Office sign-in name, and 0xCAA82EEn carry a WinHTTP error in their low word (12002 timeout, 12007 name not resolved, 12029 cannot connect, 12030 connection aborted, 12175 no secure connection). The same WinHTTP errors wrapped as 0x80072nnn are named here too: Windows keeps their sentences in winhttp.dll and not where Get-Win32ErrorText reads, so it has none for them. The errors of AppX deployment (0x80073CFn) need no table: they are Win32 errors Windows has words for. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Code) $number = ConvertTo-CodeNumber -Code $Code -Hex if ($null -eq $number) { return '' } $hex = '{0:X8}' -f ($number -band 0xFFFFFFFFL) switch ($hex) { '80072EE2' { return Get-Text 'Value.Code.WinHttp.Timeout' } '80072EE7' { return Get-Text 'Value.Code.WinHttp.NameNotResolved' } '80072EFD' { return Get-Text 'Value.Code.WinHttp.CannotConnect' } '80072EFE' { return Get-Text 'Value.Code.WinHttp.ConnectionAborted' } '80072F8F' { return Get-Text 'Value.Code.WinHttp.SecureFailure' } 'CAA82EFE' { return Get-Text 'Value.Code.WinHttp.ConnectionAborted' } 'CAA82F8F' { return Get-Text 'Value.Code.WinHttp.SecureFailure' } } if ($hex.StartsWith('8007')) { return Get-Win32ErrorText -Code ($number -band 0xFFFF) } switch ($hex) { 'CAA20003' { return Get-Text 'Value.Code.Wam.InvalidGrant' } 'CAA2000C' { return Get-Text 'Value.Code.Wam.InteractionRequired' } 'CAA70004' { return Get-Text 'Value.Code.Wam.ServerNotFound' } 'CAA70007' { return Get-Text 'Value.Code.Wam.ServerNotResponding' } 'CAA82EE2' { return Get-Text 'Value.Code.Wam.Timeout' } 'CAA82EE7' { return Get-Text 'Value.Code.Wam.NameNotResolved' } 'CAA82EFD' { return Get-Text 'Value.Code.Wam.CannotConnect' } } # One of the broker's own that is not named above: at least whose code it is. A # Technician who knows that it is the sign-in, and not the network or the program, # knows where to look and what to search for. if ($hex.StartsWith('CAA')) { return Get-Text 'Value.Code.Wam.Other' } Get-NtStatusText -Code $number } function Get-AadStsText { <# .SYNOPSIS What an error number of Microsoft Entra ID (AADSTS) means. Nothing for one not known here. Pure. .DESCRIPTION The ones a user's sign-in ends in, as Microsoft documents them under "Microsoft Entra authentication and authorization error codes". #> [CmdletBinding()] [OutputType([string])] param([AllowNull()]$Code) $number = ConvertTo-CodeNumber -Code ("$Code" -replace '(?i)^\s*AADSTS', '') if ($null -eq $number) { return '' } switch ([long]$number) { 50020 { return Get-Text 'Value.Code.Aad.50020' } 50034 { return Get-Text 'Value.Code.Aad.50034' } 50053 { return Get-Text 'Value.Code.Aad.50053' } 50055 { return Get-Text 'Value.Code.Aad.50055' } 50057 { return Get-Text 'Value.Code.Aad.50057' } 50058 { return Get-Text 'Value.Code.Aad.50058' } 50074 { return Get-Text 'Value.Code.Aad.50076' } 50076 { return Get-Text 'Value.Code.Aad.50076' } 50079 { return Get-Text 'Value.Code.Aad.50079' } 50097 { return Get-Text 'Value.Code.Aad.50097' } 50105 { return Get-Text 'Value.Code.Aad.50105' } 50126 { return Get-Text 'Value.Code.Aad.50126' } 50133 { return Get-Text 'Value.Code.Aad.50133' } 50155 { return Get-Text 'Value.Code.Aad.50155' } 50158 { return Get-Text 'Value.Code.Aad.50158' } 50173 { return Get-Text 'Value.Code.Aad.50173' } 53000 { return Get-Text 'Value.Code.Aad.53000' } 53001 { return Get-Text 'Value.Code.Aad.53001' } 53003 { return Get-Text 'Value.Code.Aad.53003' } 65001 { return Get-Text 'Value.Code.Aad.65001' } 70008 { return Get-Text 'Value.Code.Aad.70008' } 70043 { return Get-Text 'Value.Code.Aad.70043' } 70044 { return Get-Text 'Value.Code.Aad.70043' } 135011 { return Get-Text 'Value.Code.Aad.135011' } 700016 { return Get-Text 'Value.Code.Aad.700016' } 700082 { return Get-Text 'Value.Code.Aad.700082' } } '' } function Get-OAuthErrorText { <# .SYNOPSIS What an OAuth error word of Microsoft Entra ID means (invalid_grant and the like). Nothing for one not known here. Pure. .DESCRIPTION The words the token endpoint answers with, as Microsoft documents them for the OAuth 2.0 authorization code flow ("Error codes for token endpoint errors"). The AAD log and dsregcmd /status carry them beside the AADSTS number, which says more. #> [CmdletBinding()] [OutputType([string])] param([AllowNull()][AllowEmptyString()][string]$Code) switch ("$Code".Trim().ToLowerInvariant()) { 'invalid_grant' { return Get-Text 'Value.Code.Wam.InvalidGrant' } 'interaction_required' { return Get-Text 'Value.Code.Wam.InteractionRequired' } 'consent_required' { return Get-Text 'Value.Code.Aad.65001' } 'invalid_client' { return Get-Text 'Value.Code.OAuth.InvalidClient' } 'unauthorized_client' { return Get-Text 'Value.Code.OAuth.UnauthorizedClient' } 'temporarily_unavailable' { return Get-Text 'Value.Code.OAuth.TemporarilyUnavailable' } } '' } |