How to Build a Windows Server Inventory Report for Free with PowerShell

Powershell logo

PowerShell is used by many server administrators. Of course, one of the most used tasks is the ability to create scripts and functions to inventory your servers and understand what is in your environment.

Although there are many ways to achieve this, with varying levels of complexity, we will create a fairly simple but effective Windows Server inventory report in this article.

Preconditions

This article will be practical. If you intend to follow, first make sure that the following conditions are in place:

Work on a Windows 10 PC joined to an Active Directory (AD) domain
Have the ActiveDirectory PowerShell module installed from the RSAT toolbox.
Have permission to query AD computer accounts
Can execute remote WMI / CIM requests on remote computers
To have PowerShell Remote Access available on remote computers

Server recovery

The servers themselves are the basis of the script we build. You can write them individually in a read text file or in a table in the script itself, but using PowerShell, we can do better. To make the script more dynamic and not have to modify it each time a new server is added, we can use Active Directory (AD) to extract the list of IT objects in a given organizational unit (OU).

Below, we use the ActiveDirectory module, available in the RSAT toolbox, to query the Servers organizational unit and retrieve all the IT objects via Get-ADComputer.

Import-Module ActiveDirectory

$ OU = ‘OU = servers, DC = domain, DC = local’

$ Params = @ {
“SearchBase” = $ OR
“Filter” = ‘*’
}

$ Servers = Get-ADComputer @Params

At this point, we could have filtered only the name property to fill the $ servers variable, but it is often very useful to have the whole returned object for later use.

Determine the data to be collected

Now that we have our servers, we need to determine what exactly we need to collect from each server. One of the reasons why it may be important to keep the entire AD object is to combine this data with data directly from the server itself to get a bigger picture of your environment.

In practice, what does something like this look like? Let us list some of the properties that would be very useful to know.

Server values

Server host name
Free disk space
Memory
Network connections

AD values

Last password set
last connection
DNS host name

Retrieving information from the server

How do we collect this information from our list of returned servers? Since we have a list of servers, we will have to browse the $ Servers item and the request. Starting with a simple Foreach-Object loop below, we can create a custom object to hold our values.

$ Servers | Foreach-Object {
[PSCustomObject]@ {
“ServerHostName” = $ _. Last name
“Description” = $ _. Description
“FreeDiskSpace” = $ Null
“TotalMemory” = $ Null
“NetworkConnections” = $ Null
“PasswordLastSet” = $ _. PwdLastSet
“LastLogon” = $ _. LastLogon
“DNSHostName” = $ _. DNSHostName
“CreationDate” = $ _. WhenCreated
}
}

As you can see, by saving the entire Active Directory object when the computers are first recovered, we can fill in a wide range of information. Unfortunately, this is not all the information we need.

To get information from each server, we will use a familiar interface for many server administrators, which is the Windows Management Instrumentation (WMI) interface. You may notice that the cmdlets used below come from the Common Information Model (CIM) interface, of which WMI is Microsoft’s implementation of this standard.

Get free disk space

By using the available WMI class of Win32_LogicalDisk, we can get all the available disks and their free space. When we run the Get-CimInstance -ClassName Win32_LogicalDisk command for the first time, you may notice that it is not exactly readable in its default output.

The second problem here is that we have more than one drive returned. I would like to know each of these disks and how much free space is available in GB. Let’s modify the code to perform transformations and improve it.

$ Disks = Get-CimInstance -ClassName Win32_LogicalDisk

$ DisksResult = $ Disks | Foreach-Object {
[PSCustomObject]@ {
“Drive” = $ _. DeviceID
“FreeSpace” = [Math]:: Round (($ _. FreeSpace / 1GB), 2)
}
}

$ DisksResult

After running the commands, our output is much cleaner and can now be used in our script.

But what if we want to alert on a low disk space condition? It would be good to extend it slightly to define an indicator on each reader meeting this condition. By comparing the free space to the total available space, we can see if it is less than 10% or 10 GB. The reason for the condition -or is that on very large disks, 10% can still be very generous , so setting an absolute limit is useful.

$ Disks = Get-CimInstance -ClassName Win32_LogicalDisk

$ DisksResult = $ Disks | Foreach-Object {
$ FreeSpace = [Math]:: Round (($ _. FreeSpace / 1GB), 2)
$ TotalSpace = [Math]:: Round (($ _. Size / 1 Go), 2)

If (($ FreeSpace / $ TotalSpace -LT 0.10) -Or $ FreeSpace -LT 10) {
$ LowDiskSpace = $ True
} Other {
$ LowDiskSpace = $ False
}

[PSCustomObject]@ {
“Drive” = $ _. DeviceID
“FreeSpace” = $ FreeSpace
“LowDiskSpace” = $ LowDiskSpace
}
}

$ DisksResult

As you can see now, we have a large set of information to record with our servers.

Comment% 20à% 20Construire% 20a% 20Windows% 20Server% 20Inventory% 20Report% 20for / Untitled% 202.png

Get available memory

It is handy to know how much RAM is allocated to each server, especially in a virtual machine environment. If you find that some are over-provisioned, you can save valuable resources by properly sizing the servers. Fortunately, it is much easier to recover.

Using the WMI Win32_PhysicalMemory class, we can add all the returned Capacity properties to get the total memory.

(Get-CimInstance -ClassName Win32_PhysicalMemory | Measure-Object -Property Capacity -Sum) .Sum / 1GB

Get all network connections

Finally, we want to get all the network connections together. It is useful to know if a certain server has several interfaces to worry about. Using a slightly different mechanism this time, we use the Get-NetAdapter cmdlet, but since this has no ComputerName parameter, we will use PS Remoting to invoke it locally on the target server and return the results to our script.

$ NetworkConnections = Invoke-Command -ComputerName $ _. DnsHostName -ScriptBlock {
Get-NetAdapter -Physical | Object name, status, link speed
}

Our output will look like the one below and we can then save it in our script.

Keep in mind that for Invoke-Command to work, PS Remoting will need to be configured on the target servers.

Putting it all together

Now that we have all the pieces, let’s put it all together. The final script is below and combines all the code to create a custom output object with exactly what we want to report.

Import-Module ActiveDirectory

$ OU = ‘OU = servers, DC = domain, DC = local’

$ Params = @ {
“SearchBase” = $ OR
“Filter” = ‘*’
}

$ Servers = Get-ADComputer @Params

$ Servers | Foreach-Object {
$ Disks = Get-CimInstance -ComputerName $ _. DnsHostName -ClassName Win32_LogicalDisk

$ DisksResult = $ Disks | Foreach-Object {
[PSCustomObject]@ {
“Drive” = $ _. DeviceID
“FreeSpace” = [Math]:: Round (($ _. FreeSpace / 1GB), 2)
}
}

$ NetworkConnections = Invoke-Command -ComputerName $ _. DnsHostName -ScriptBlock {
Get-NetAdapter -Physical | Object name, status, link speed
}

[PSCustomObject]@ {
“ServerHostName” = $ _. Last name
“Description” = $ _. Description
“FreeDiskSpace” = $ DisksResult
“TotalMemory” = ((Get-CimInstance -ComputerName $ _. DnsHostName -ClassName Win32_PhysicalMemory | Measure-Object -Property Capacity -Sum) .Sum / 1GB)
“NetworkConnections” = $ NetworkConnections
“PasswordLastSet” = $ _. PwdLastSet
“LastLogon” = $ _. LastLogon
“DNSHostName” = $ _. DNSHostName
“CreationDate” = $ _. WhenCreated
}
}

Conclusion

What we have demonstrated here is just the tip of the iceberg in terms of what can be built for an inventory report. There are many other useful properties that you can add to this report. Going further, you can embed it in an HTML page, schedule a task to run this week, or even wrap it in other tools such as Ansible.

PowerShell makes it easy to gather all the information you need in one place. Once you have analyzed your environment and determined what you need to know, create the report in PowerShell to help sustain your ability to audit your environment.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.