Getting the computername in Powershell

Update 2015-07-03:

So I got two comments from Paul Wiegmans. These were mainly that the functions delivered different hostname vs netbios version. And that I had missed a good function. And I’m so used to that since windows limits the Netbios computername to 16 characters, where the last one is a reserved character so I forgot to test for longer versions.

Updated blogpost with all the glory:

Last weekend my company and a couple of customers had an event in the Swedish village of Åre. To cut to the chase we had both speakers from Knowledge factory, TrueSec and Microsoft at the event. And during Bruce Payette‘s presentation I noticed that he used hostname instead of $env:computername as I and other use. So I talked a little with him about it, and decided to write a blog entry about it. So we discussed a couple of options mostly using $env, the .NET method and hostname.exe. I also decided to test the speed of a couple of ways. Lets start with the speeds and go from there.

What are the main players in this drama then?

  1. The environment variable
  2. hostname process way
  3. WMI/CIM way
  4. .NET MachineName
  5. Kernel32 function call
  6. .NET GetHostName function

1. The environment variable

The first and most simple solution for most coders are to use the $env:computername. This is a fast and efficient way of doing it. But it has its drawbacks. Every part of $env can be written over by a command in your script. But it is fast end efficient.

2. hostname process way

Windows ships with the same binary that linux systems has, just write hostname and the system will return the name of the computer. This requires the creating of a process and not just a read of information. And as you will see gives a great penalty.

3. The way of WMI

As most things there is a way of doing it using WMI. WMI has been preinstalled since Windows 2000 and as a download prior to that. On my system there is 1107 classes in the default namespace of root\cimv2. WMI is also available when running WinPE by using the commandline tool wimc. WMI uses CIM to access the data, but since WMI the more famous..

3b. Way of CIM

You could also use Get-CIMInstance  to access Common Information Model, which the WMI way also does. So this is just cutting out the middleman.

4. .NET MachineName

You could use .NET which is easily accessed through powershell. Infact all you need it to enclose the class within [brackets] and start calling stuff. This is the fast and good solution.

5. Kernel32.dll request

I wrote another post about this because it got a bit bigger than this small area. But the short story, could make the call kernel32.dll and ask for the computername. Its more complicated then needed and doesn’t improve the speed very much or at all. So I would say don’t use it.

6. .NET GetHostName function

This one was suggested by Paul. It uses the system.net.dns class and its gethostname function.

Different results

As Paul also specified, I didn’t really care enough about computernames longer than 15 characters, since the different approaches gives different results. So this is a table of what the different

Method Result
The environment variable NetBIOS computername
hostname process way Full computername
WMI/CIM way NetBIOS computername
.NET MachineName NetBIOS computername
Kernel32 function call NetBIOS computername
.NET GetHostName function Full computername

Speedtests

So I ran through each method 10000 times and wrote down the average time it took. Some ways (kernel32) is really biased against as it doesn’t remember the system.runtime between tries.

Method Avg Ticks
.NET MachineName 148
$Env 402
.Net GetHostName 538
Kernel32 3306
WMI 91361
hostname 93548
CIM 99398

I was planing a diagram here, but since I only had small or large values it looked bad. So use your imagination.

How to test for your self.

The conclusion

So in all future scripts I know what I want, I want to use .NET. It is simple fast and secure. That the solution is mostly built-in is a good part of my reasoning to. I would like to have a Get-ComputerName but I can simplify that part myself, atleast until we get a cmdlet from Microsoft.

Simplifying it:

Just wrapping [system.environment]::MachineName to something more elegant and easy.

Comments

  1. I enjoyed this thank you for writing it!

  2. Very nice post, except it is not complete. These 5 methods do not return the same thing. 4 of these methods do not return the computername. Instead they return the NetBIOS name which is limited to a length of 15 characters. When the name is longer dan 15 character, only hostname returns the actual computername.

    for example when the computername is “WAADEVOLAPPARAAT”

    the following code:

    $env:computername
    hostname.exe
    Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty name
    (Get-CIMInstance CIM_ComputerSystem).Name
    [system.environment]::MachineName

    return this:

    WAARDEVOLAPPARA
    WAARDEVOLAPPARAAT
    WAARDEVOLAPPARA
    WAARDEVOLAPPARA
    WAARDEVOLAPPARA

    You see here, if you don’t care about NetBIOS names, only hostname.exe returns the true computername.

  3. One more thing. A nice and faster equivalent of running the external utility “hostname.exe” is :

    [System.Net.Dns]::GetHostName()

  4. Actually, they don’t even return the same value if you are using Container in Windows Server 2016:

    PS:
    Write-Host ‘$env:computername:’ $env:computername
    $hostname = hostname
    Write-Host ‘hostname.exe:’ $hostname
    $w32compsys = Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty name
    Write-Host ‘Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty name:’ $w32compsys
    Write-Host ‘(Get-CIMInstance CIM_ComputerSystem).Name:’ (Get-CIMInstance CIM_ComputerSystem).Name
    $machinename = [system.environment]::MachineName
    Write-Host ‘[system.environment]::MachineName:’ $machinename
    $dnsname = [System.Net.Dns]::GetHostName()
    Write-Host ‘[System.Net.Dns]::GetHostName():’ $dnsname

    Result:
    $env:computername: WIN-E2ABUUD2V1V
    hostname.exe: Docker01-Gui
    Get-WMIObject Win32_ComputerSystem | Select-Object -ExpandProperty name: WIN-E2ABUUD2V1V
    (Get-CIMInstance CIM_ComputerSystem).Name: WIN-E2ABUUD2V1V
    [system.environment]::MachineName: WIN-E2ABUUD2V1V
    [System.Net.Dns]::GetHostName(): Docker01-Gui

    The environment is:
    The Container Host’s Computer name is Docker01-Gui
    The Container inside this host is WIN-E2ABUUD2V1V

    So it actually targets to different purpose, use it carefully. 🙂

Leave a Reply