Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## Timestamp and uptime restoration (unreleased)

- Restore Convert-LogonTimestamp and Get-OsUpTime; export twenty-two supported commands.
- Preserve local DateTime and default date formatting; add UTC and pipeline conversion.
- Treat zero logon timestamps as no recorded logon; reject invalid FILETIME values.
- Use invariant string formatting and document replicated AD timestamp semantics.
- Preserve uptime days/TimeSpan outputs; support local uptime on Windows, Linux and macOS.
- Replace remote WMI with CIM, use the server clock, support explicit/prompted credentials and clean up sessions.
- Propagate errors without changing caller preferences; document remote Windows requirements.
- Add boundary, culture, local uptime and mocked CIM regression tests.

## Email and URL validation restoration (unreleased)

- Restore Test-IsEmail and Test-IsUrl; export twenty supported commands.
Expand Down
2 changes: 2 additions & 0 deletions IT-ToolBox.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
'Test-IsDate'
'Test-IsEmail'
'Test-IsUrl'
'Convert-LogonTimestamp'
'Get-OsUpTime'
'New-StringEncryption'
'New-StringDecryption'
'New-RandomString'
Expand Down
2 changes: 2 additions & 0 deletions IT-ToolBox.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Export-ModuleMember -Function @(
'Test-IsDate'
'Test-IsEmail'
'Test-IsUrl'
'Convert-LogonTimestamp'
'Get-OsUpTime'
'New-StringEncryption'
'New-StringDecryption'
'New-RandomString'
Expand Down
40 changes: 40 additions & 0 deletions Private/Get-ITToolBoxRemoteUptime.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
function Get-ITToolBoxRemoteUptime {
param([string]$ComputerName, [pscredential]$Credential)

if (-not (Get-Command Get-CimInstance -ErrorAction SilentlyContinue)) {
throw [System.PlatformNotSupportedException]::new('Remote uptime requires Windows CIM cmdlets; run this query from PowerShell on Windows.')
}
$query = @{
Namespace = 'root/cimv2'
ClassName = 'Win32_OperatingSystem'
Property = @('LastBootUpTime', 'LocalDateTime')
ErrorAction = 'Stop'
}
$session = $null
$queryFailed = $false
try {
if ($null -ne $Credential) {
$session = New-CimSession -ComputerName $ComputerName -Credential $Credential -ErrorAction Stop
$query.CimSession = $session
}
else { $query.ComputerName = $ComputerName }
$os = Get-CimInstance @query
if ($os.LastBootUpTime -isnot [datetime] -or $os.LocalDateTime -isnot [datetime]) {
throw 'The remote OS did not return valid LastBootUpTime and LocalDateTime values.'
}
return ($os.LocalDateTime.ToUniversalTime() - $os.LastBootUpTime.ToUniversalTime())
}
catch {
$queryFailed = $true
throw
}
finally {
if ($null -ne $session) {
try { Remove-CimSession -CimSession $session -ErrorAction Stop }
catch {
if (-not $queryFailed) { throw }
Write-Warning ('CIM session cleanup also failed: {0}' -f $_.Exception.Message)
}
}
}
}
47 changes: 47 additions & 0 deletions Public/Convert-LogonTimestamp.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
function Convert-LogonTimestamp {
<#
.SYNOPSIS
Converts an AD logon FILETIME into a DateTime or formatted string.
.DESCRIPTION
Accepts an unsigned decimal string or an Int64 FILETIME value, with pipeline
support. Returns local DateTime by default; -Utc returns UTC instead. Zero
means no recorded logon and produces no output. Invalid or out-of-range input
throws. This converts the value only; lastLogonTimestamp is replicated and is
not a precise real-time record of a user's most recent authentication.
.PARAMETER StringOutput
Returns text instead of DateTime. Formatting uses invariant culture.
.PARAMETER DateFormat
.NET date format; specifying this also selects string output. Defaults to yyyy-MM-dd.
.EXAMPLE
Convert-LogonTimestamp -TimeStamp 133801632000000000 -Utc
#>
[CmdletBinding(DefaultParameterSetName = 'Default')]
[OutputType([datetime], ParameterSetName = 'Default')]
[OutputType([string], ParameterSetName = 'StringOutput')]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string]$TimeStamp,
[Parameter(ParameterSetName = 'StringOutput')]
[switch]$StringOutput,
[Parameter(ParameterSetName = 'StringOutput')]
[ValidateNotNullOrEmpty()]
[string]$DateFormat = 'yyyy-MM-dd',
[switch]$Utc
)

process {
[long]$fileTime = 0
if (-not [long]::TryParse($TimeStamp, [System.Globalization.NumberStyles]::None,
[System.Globalization.CultureInfo]::InvariantCulture, [ref]$fileTime)) {
throw [System.FormatException]::new('TimeStamp must be an unsigned decimal Int64 FILETIME value.')
}
if ($fileTime -eq 0) { return }
$date = [datetime]::FromFileTimeUtc($fileTime)
if (-not $Utc) { $date = $date.ToLocalTime() }
if ($PSCmdlet.ParameterSetName -eq 'StringOutput') {
return $date.ToString($DateFormat, [System.Globalization.CultureInfo]::InvariantCulture)
}
return $date
}
}
55 changes: 55 additions & 0 deletions Public/Get-OsUpTime.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
function Get-OsUpTime {
<#
.SYNOPSIS
Returns local OS uptime or a remote Windows computer's uptime.
.DESCRIPTION
Uses Get-Uptime locally on Windows, Linux and macOS. Remote queries require
Windows CIM cmdlets and a reachable Windows CIM endpoint. Remote uptime is
computed from the server's LocalDateTime and LastBootUpTime, avoiding use of
the client's clock. Failures terminate; any session created here is disposed.
.PARAMETER FullOutput
Returns TimeSpan. Without this switch, returns whole elapsed days as Int32.
.PARAMETER Credential
Explicit credentials for a remote computer, without an interactive prompt.
.PARAMETER Credentials
Compatibility switch: prompts with Get-Credential for a remote computer.
.EXAMPLE
Get-OsUpTime -FullOutput
.EXAMPLE
Get-OsUpTime -ComputerName server01 -Credential $credential -FullOutput
#>
[CmdletBinding(DefaultParameterSetName = 'LocalMachine')]
[OutputType([int], [timespan])]
param(
[Parameter(ParameterSetName = 'RemoteMachine', Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ComputerName,
[switch]$FullOutput,
[Parameter(ParameterSetName = 'RemoteMachine')]
[ValidateNotNull()]
[pscredential]$Credential,
[Parameter(ParameterSetName = 'RemoteMachine')]
[switch]$Credentials
)

if ($Credentials -and $PSBoundParameters.ContainsKey('Credential')) {
throw 'Use either -Credential or -Credentials, not both.'
}
if ($PSCmdlet.ParameterSetName -eq 'RemoteMachine') {
if ([string]::IsNullOrWhiteSpace($ComputerName)) { throw 'ComputerName cannot be whitespace.' }
if ($Credentials) {
$promptedCredential = Get-Credential -Message 'Please specify alternate credentials to be used:' -ErrorAction Stop
if ($null -eq $promptedCredential) { throw 'No credential was supplied.' }
$Credential = $promptedCredential
}
$uptime = Get-ITToolBoxRemoteUptime -ComputerName $ComputerName -Credential $Credential
}
else {
$uptime = Get-Uptime -ErrorAction Stop
}
if ($uptime -isnot [timespan] -or $uptime -lt [timespan]::Zero) {
throw 'The uptime source did not return a nonnegative TimeSpan.'
}
if ($FullOutput) { return $uptime }
return [int]$uptime.Days
}
44 changes: 40 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ The foundation imports without WinSCP, GnuPG, Active Directory or Exchange depen
| Test-IsDate | Culture-aware date validation with optional exact format |
| Test-IsEmail | Common bare email-address syntax, including IDN domains |
| Test-IsUrl | Absolute HTTP/HTTPS/FTP/FTPS URL syntax |
| Convert-LogonTimestamp | AD FILETIME conversion with local/UTC output |
| Get-OsUpTime | Local OS uptime and remote Windows CIM queries |
| New-StringEncryption | Passphrase-based AES-256-GCM string encryption |
| New-StringDecryption | Authenticate and decrypt the versioned string format |
| New-RandomString | Secure random selection from the historical alphabet |
Expand Down Expand Up @@ -60,10 +62,10 @@ Redaction is opt-in and does not guarantee detection of every secret.
- SCP and GnuPG wrappers and bundled WinSCP binaries are removed. Separate modules
will own file transfer and OpenPGP; no replacement is bundled here.
- `Legacy/` retains string encryption, Exchange and script-context helpers for reference.
- `Staging/v3/` retains 7 candidate commands pending tests and compatibility fixes.
These cover logon timestamps, uptime, report chains, character removal,
distinguished names, user principal names and registry values. They are not currently exported.
- Only the twenty listed commands are exported. Private helpers, variables and aliases
- `Staging/v3/` retains 5 candidate commands pending tests and compatibility fixes.
These cover report chains, character removal, distinguished names,
user principal names and registry values. They are not currently exported.
- Only the twenty-two listed commands are exported. Private helpers, variables and aliases
are not exported. Existing calls to other v2 commands require the v2 release until
those commands return to the supported API.
- The module GUID and Git history are preserved.
Expand Down Expand Up @@ -246,3 +248,37 @@ Use `-LegacyFormat` only when comparing with old delimiter-free decimal output.
The hash algorithm remains SHA-256; the default representation intentionally changes.
Both hash commands use UTF-8 without a BOM and do not normalize text. Neither is
a password-storage function or an authentication mechanism.

## Logon timestamps and OS uptime

`Convert-LogonTimestamp` accepts an unsigned decimal FILETIME string or Int64 value
and supports pipeline input. Local DateTime output remains the default; use `-Utc`
for UTC. `-StringOutput` defaults to `yyyy-MM-dd`, and `-DateFormat` also selects
string output. String formatting uses invariant culture. Invalid values throw.
Zero means no recorded logon and produces no output rather than a date in 1601.
The command converts a supplied value only: AD's replicated `lastLogonTimestamp`
is not an exact record of the user's latest authentication.

```powershell
Convert-LogonTimestamp -TimeStamp $user.lastLogonTimestamp -Utc
Convert-LogonTimestamp -TimeStamp $user.lastLogonTimestamp -Utc -DateFormat 'yyyy-MM-dd HH:mm:ss'
Get-OsUpTime -FullOutput
Get-OsUpTime -ComputerName 'server01' -Credential $credential -FullOutput
```

`Get-OsUpTime` preserves whole elapsed days as Int32 by default and TimeSpan with
`-FullOutput`. Local uptime delegates to PowerShell's built-in `Get-Uptime` on
Windows, Linux and macOS. Remote uptime requires Windows CIM cmdlets and a
reachable Windows CIM endpoint; no remote Linux/macOS support is implied.

Remote queries replace removed `Get-WmiObject` calls with `Get-CimInstance`.
They compute uptime from the server's `LocalDateTime` and `LastBootUpTime`, not
the client's clock. With `-Credential`, a temporary CIM session is created and
cleanup is attempted in `finally`. The historical `-Credentials` switch prompts
with `Get-Credential`; either credential option requires `-ComputerName`. Errors
terminate rather than returning warnings and no value. A failed cleanup is reported
without replacing an already-failed query's error. The caller's error preference
is not changed.

Tests cover actual local uptime and mocked remote query/session behavior. Remote
Windows connectivity and authentication have not been integration-tested.
94 changes: 0 additions & 94 deletions Staging/v3/Convert-LogonTimestamp.ps1

This file was deleted.

Loading
Loading