In PowerShell if you need to determine the process or OS architecture, you should theoretically be able to leverage the .NET APIs to find it out. And in fact this works in PowerShell Core:

> [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
X64
> [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture
X64

But Windows PowerShell is inconsistent. The above syntax works on one of my two Windows machines, but the other acts as if the properties do not even exist:

> [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
> [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture
> [System.Runtime.InteropServices.RuntimeInformation] | gm -static

   TypeName: System.Runtime.InteropServices.RuntimeInformation

Name            MemberType Definition
----            ---------- ----------
Equals          Method     static bool Equals(System.Object objA, System.Object objB)
IsOSPlatform    Method     static bool IsOSPlatform(System.Runtime.InteropServices.OSPlatform platform)
new             Method     System.Runtime.InteropServices.RuntimeInformation new()
ReferenceEquals Method     static bool ReferenceEquals(System.Object objA, System.Object objB)
OSDescription   Property   static string OSDescription {get;}

So Windows PowerShell is hiding some of the properties on that type. I know it’s hiding it because [System.Runtime.InteropServices.RuntimeInformation].GetType().Assembly reveals the path to the assembly the type comes from and it’s just standard mscorlib.dll, which if I open in ILSpy does indeed have the properties I need. I can even circumvent the filter that Windows PowerShell must have inside by being slightly fancier:

> [type]::gettype('System.Runtime.InteropServices.RuntimeInformation').getproperties() | select name

Name
----
FrameworkDescription
OSDescription
OSArchitecture
ProcessArchitecture

Well how about that! The properties are there in the Windows PowerShell process.

It turns out that I had two RuntimeInformation classes in my process. PSReadLine (a PowerShell plugin I use) defines one, with the same namespace, but an incomplete set of properties. Go figure. To make the script work reliably, you just have to add an assembly name qualifier:

> [System.Runtime.InteropServices.RuntimeInformation,mscorlib]::OSArchitecture.ToString().ToLower()

It turns out that this syntax works on both Windows machines, in both Windows PowerShell and PowerShell Core. It works in PowerShell Core on Mac and Linux as well.

Note that although PowerShell Core runs on .NET 6+, which doesn’t define RuntimeInformation in mscorlib any more, .NET 6 does include an mscorlib type forwarding assembly, so pretending like the type is in mscorlib actually makes it work everywhere.