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

[table “” not found /]

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.

[table “” not found /]

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.

    • L Swiski says

      Paul – IN the real world you never want to go beyond the NETBIOS limitation. Especially in an enterprise / real world environment. As soon as you integrate Active Directory or other services, you will see that this limitation is held over in many other core services.

      I was extremely surprised to see how much is still linked in to the netbios libraries. Just did a project w/ AD Cloud Connector and Azure Active Directory Cloud Edition – even in these and 2019 Windows Datacenter builds there are hooks still to the 15 character limitation.

      So the short of it is, just plan that this will be here for ever from MSSQL Server AG’s / Cluster objects in AD to other simple things, this limitation is tied in from a very low level and isn’t going away anytime soon. Just name your systems 15 chars and get over it.

      Good luck !=]

  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. 🙂

Trackbacks

  1. […] while ago I wrote a blogentry about different ways of obtaining the hostname. When I wrote that one I thought wonder how the different way of accessing the member values […]

Leave a Reply to Paul Wiegmans Cancel reply