
Continuing on security and CIS controls, from my other blog post describing monitoring of DHCP using ELK, one of the other important aspects is to know what is installed on the computers and devices connected to your network and by whom are these devices being used? If you already have Microsoft Configuration Manager or a similar product installed, generating inventory reports becomes a trivial task. You run a report and all information gathered by SCCM is available at your finger tips, however, not all SMB customers have SCCM or are using Intune. So what is the option for these customers? Do they have to invest in tools like ServiceNow, Nifty, Monday.com, SCCM, Ivanti or event Solarwinds? Well technically speaking yes, but not technically speaking no. If you are willing to spend some time and effort you can create quite good inventory solutions for Windows based machine. Like most of the times, this time too, it was a customer who needed this kind of a solution who was not willing to spend that much amount money on buying tools and then to spend money on configuring the tool to perform optimally according to their environment. Solution is to use tools already available in Windows to extract information and make it easily available for Admins. If you have worked with SCCM or just in general with Client and Server administration, chances are high that you are aware of what WMI is and what capabilities it has, in terms of providing information.
If you are not that familiar, just consider WMI as being a database containing almost all information about a machine, both software and hardware (simplified definition). Having this in mind, the goal was to make something that works on most of the machines without requiring too much reconfiguration of clients or deployment of agents. So what can a solution like this provide? You can browse the WMI store of a Client to view what information is available. The client in question was interested in the following classes or information from classes
- Computer general information
- Computer bios information
- Computer disk information
- Computer attached printer information
- Computer logged in user information
- Computer installed software information
- Computer installed patches information
- Network card information
Let us look at an inventory file created by one of the clients.
OS information
PC name=LTMP-1906
OS Type=Microsoft Windows 10 Pro
Installation date=03/26/2020 17:53:20
System started=07/21/2020 08:22:38
Operating system locale=0414
OS Build number=18363
Operating system SKU=PRODUCT_PROFESSIONAL
OS architecture=32-bits
OS Language=Norwegian Bokmål
OS product type=Workstation
OS serial number=00360-50230-00000-BBEHN
OS memory=3 GB
OS version=10.0.18363
PC information
Machine Vendor=Dell Inc.
Machine model=OptiPlex 990
PC user=DOMAIN\Jon Doe
Disk information
Disk=C:
Disk description=Local disk
Disk type=Local disk
Disk file system=NTFS
Volume name=OS,
Disk capacity= 454 GB
Free disk=408GB
Disk=D:
Disk description =CD-ROM-plate
Disk type=Compact disk
Disk filsystem=
Volumename=,
Disk capacity=0GB
Free disk=0GB
BIOS information
Serial number=453NB3P
Processor information
Processor name=Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz
Number core=4
Number logical Processors=4
Processor status=OK
PC Userinformation
Logged in User=DOMAIN\Jon Doe, Logged in through=Console/interactive
PC restart information
Needs restart=True
PC Share information
Share name=ADMIN$,Share description=Ekstern administrasjon, Share path=C:\WINDOWS
Installed programs
Application name=Windows-driverpakke – Dell Inc. PBADRV System (09/11/2009 1.0.1.6),Applikasjonsleverandør=Dell Inc.,Applikasjonsversjon=09/11/2009 1.0.1.6
Application name=Google Chrome,Applikasjonsleverandør=Google LLC,Applikasjonsversjon=84.0.4147.105
Application name=CyberLink PowerDVD 9.5,Applikasjonsleverandør=CyberLink Corp.,Applikasjonsversjon=9.5.1.4822
Application name=Microsoft Visual Studio 2010 Tools for Office Runtime (x86),Applikasjonsleverandør=Microsoft Corporation,Applikasjonsversjon=10.0.50903
Application name=Microsoft 365 Apps for enterprise – nb-no,Applikasjonsleverandør=Microsoft Corporation,Applikasjonsversjon=16.0.13001.20384
Application Application name=Dell Data Protection | Access | Drivers,Applikasjonsleverandør=Dell Inc.,Applikasjonsversjon=2.01.018
Application name=Intel(R) Management Engine Components,Applikasjonsleverandør=Intel Corporation,Applikasjonsversjon=7.0.0.1144
Application name=Roxio Creator Starter,Applikasjonsleverandør=Roxio,Applikasjonsversjon=12.1.77.0
Application name=Microsoft OneDrive,Applikasjonsleverandør=Microsoft Corporation,Applikasjonsversjon=20.124.0621.0006
Installert Windows Updateer
Type Update=Update,Update ID=KB4565633,Installationsdate=7/21/2020,Installert av=NT-Authority\SYSTEM,Service pack=,Updatestatus=
ID=KB4565554,Installationsdate=7/15/2020,Installert av=NT-Authority\SYSTEM,Service pack=,Updatestatus=
Type Update=Update,Update ID=KB4565483,Installationsdate=7/21/2020,Installert av=NT-Authority\SYSTEM,Service pack=,Updatestatus=
Local Printer information
Printername=OneNote (Desktop),Printer capabilities=Copies Color,Local=True,Network printer=False,Port name=nul:,Printer shared= False,Share name=,Printer status=Unknown,Printer last restarted=,Printer offline=False
PC Network card information
Network card description=Intel(R) 82579LM Gigabit Network Connection
DHCP active=True
Lease expiry=08/05/2020 12:07:58
IP leased=08/05/2020 10:07:58
DHCP server=10.10.10.1
DNS server=10.10.10.2 10.10.10.3
DNS registrering activ=True
IP adresse=10.2.3.115 fe80::88:884d:ecf6:7fc
MAC adresse=18:03:78:11:88:88
This information is gathered by each and every machine in the environment, and can be entered into a database or just to a file server depending on what resources you have available within your production environment. The customer in question chose to add the data to a file server share and wanted to develop it further by entering data to a DB and then retrieving it using a web service. I will be sharing the file server solution with you. Using this solution, a folder structure is created on you file server this is shown in the image below

Each folder contains a single log file containing all the details

The Powershell script can be packed into a EXE file or can be run directly from a Share. However, I would highly recommend you signing the file and then placing it on the file server if you choose to do so. The safest bet will be to keep the exection policy to signed and then rather create an EXE file of your Powershell script. Here is the script that is being utilized by the customer in question.
#Synopsis
#This script is used to only query machines that are part of this domain .
#Functions have been defined for each purpose
#The script uses secure protocols to the extent possible and avoids using old security standards
Function QueryOS ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber,Caption, CSNAME, InstallDate, Locale, OSArchitecture, OperatingSystemSKU, OSLanguage,TotalVisibleMemorySize, SerialNumber, Version, ProductType, LastBootUpTime -Authentication PacketPrivacy -Impersonation Impersonate;Return $OS
}
Function QueryBIOS ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$BIOS = Get-WmiObject -Class Win32_BIOS -Property SerialNumber -Authentication PacketPrivacy -Impersonation Impersonate;Return $BIOS
}
Function QueryPatches ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$Patches = get-wmiobject -class Win32_quickfixengineering -Property Description, HotfixID, InstalledOn, InstalledBy, ServicePackInEffect, Status ;Return $Patches
}
Function QueryPrinter ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$Printers = get-wmiobject -class Win32_Printer -Property CapabilityDescriptions, Caption, Local, Network,Shared, ShareName, Status, TimeOfLastReset,WorkOffline, PortName ;Return $Printers
}
Function QueryComputerSystem ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$ComputerSystem = get-wmiobject -class Win32_ComputerSystem -Property Name, HypervisorPresent, Manufacturer, Model, UserName ;Return $ComputerSystem
}
Function QueryUserAccounts ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$UserAccounts = get-wmiobject -class Win32_SystemUsers -Property PartComponent ;Return $UserAccounts
}
Function QueryDisk ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$Disks = get-wmiobject -class Win32_LogicalDisk -Property Caption, Description, DeviceID, DriveType, FileSystem, Freespace, MediaType, Name, Size, VolumeName ;Return $Disks
}
Function QueryLoggedOnUser ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
$LoggedOnUser = @{} ; $LoggedOnUser = get-wmiobject -class Win32_LoggedOnUser -Property Antecedent, Dependent ;Return $LoggedOnUser
}
Function QueryLoggedOnSession ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
$LoggedOnSession = @{}; $LoggedOnSession = get-wmiobject -class win32_LogonSession -Property LogonID,LogonType,StartTime ;Return $LoggedOnSession
}
Function QueryApps32bit ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$Apps32bit = get-wmiobject -class Win32_InstalledWin32Program -Property Name, Version, Vendor ;Return $Apps32bit
}
Function QueryProcessor ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
[Array]$Processor = get-wmiobject -class Win32_Processor -Property Name,NumberOfCores,NumberOfLogicalProcessors,Status ;Return $Processor
}
Function QueryNetworkAdapter ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
$NIC=@{};$NIC = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Property Description, DefaultIPGateway, DHCPEnabled, DHCPLeaseExpires, DHCPLeaseObtained, DHCPServer, DNSServerSearchOrder, FullDNSRegistrationEnabled, IPAddress, MACAddress -Filter "IPEnabled = True" ;Return $NIC;
}
#Local functions for Querying machine
Function ConvertWMIDate ([String]$WMIDate)
{
$WMIDate = [Management.ManagementDateTimeConverter]::ToDateTime($WMIDate)
Return $WMIDate
}
Function PerformDecode ([String]$CodeType,[Int]$Code)
{
Switch ($CodeType){
ProductType
{
if($Code -eq 1){$ProductType="Workstation";Return $ProductType}
elseif ($Code -eq 2){$ProductType="Domain Controller";Return $ProductType}
elseif ($Code -eq 3){$ProductType="Server";Return $ProductType}
else {$ProductType="Product code not defined in decode function! Error code 101!";Return $ProductType}
}
OSLanguage
{
#Write-host "Selected OSLanguage"
if($Code -eq 1030){$OSLanguage="Danish";Return $OSLanguage}
elseif ($Code -eq 1033){$OSLanguage="English USA";Return $OSLanguage}
elseif ($Code -eq 1035){$OSLanguage="Finish";Return $OSLanguage}
elseif ($Code -eq 1044){$OSLanguage="Norwegian Bokmål";Return $OSLanguage}
elseif ($Code -eq 1053){$OSLanguage="Swedish";Return $OSLanguage}
elseif ($Code -eq 2057){$OSLanguage="English UK";Return $OSLanguage}
elseif ($Code -eq 2068){$OSLanguage="Norwegian Nynorsk";Return $OSLanguage}
else {$OSLanguage="Language code not defined in decode function! Error code 101!";Return $OSLanguage}
}
OperatingSystemSKU
{
#Write-host "Selected OperatingSystemSKU"
if($Code -eq 0){$OperatingSystemSKU="PRODUCT_UNDEFINED";Return $OperatingSystemSKU}
elseif ($Code -eq 1){$OperatingSystemSKU="PRODUCT_ULTIMATE";Return $OperatingSystemSKU}
elseif ($Code -eq 2){$OperatingSystemSKU="PRODUCT_HOME_BASIC";Return $OperatingSystemSKU}
elseif ($Code -eq 3){$OperatingSystemSKU="PRODUCT_HOME_PREMIUM";Return $OperatingSystemSKU}
elseif ($Code -eq 4){$OperatingSystemSKU="PRODUCT_ENTERPRISE";Return $OperatingSystemSKU}
elseif ($Code -eq 5){$OperatingSystemSKU="PRODUCT_BUSINESS";Return $OperatingSystemSKU}
elseif ($Code -eq 7){$OperatingSystemSKU="PRODUCT_STANDARD_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 8){$OperatingSystemSKU="PRODUCT_DATACENTER_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 9){$OperatingSystemSKU="PRODUCT_SMALLBUSINESS_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 10){$OperatingSystemSKU="PRODUCT_ENTERPRISE_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 11){$OperatingSystemSKU="PRODUCT_STARTER";Return $OperatingSystemSKU}
elseif ($Code -eq 12){$OperatingSystemSKU="PRODUCT_DATACENTER_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 13){$OperatingSystemSKU="PRODUCT_STANDARD_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 14){$OperatingSystemSKU="PRODUCT_ENTERPRISE_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 17){$OperatingSystemSKU="PRODUCT_WEB_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 19){$OperatingSystemSKU="PRODUCT_HOME_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 20){$OperatingSystemSKU="PRODUCT_STORAGE_EXPRESS_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 21){$OperatingSystemSKU="PRODUCT_STORAGE_STANDARD_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 22){$OperatingSystemSKU="PRODUCT_STORAGE_WORKGROUP_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 23){$OperatingSystemSKU="PRODUCT_STORAGE_ENTERPRISE_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 24){$OperatingSystemSKU="PRODUCT_SERVER_FOR_SMALLBUSINESS";Return $OperatingSystemSKU}
elseif ($Code -eq 25){$OperatingSystemSKU="PRODUCT_SMALLBUSINESS_SERVER_PREMIUM";Return $OperatingSystemSKU}
elseif ($Code -eq 27){$OperatingSystemSKU="PRODUCT_ENTERPRISE_N";Return $OperatingSystemSKU}
elseif ($Code -eq 28){$OperatingSystemSKU="PRODUCT_ULTIMATE_N";Return $OperatingSystemSKU}
elseif ($Code -eq 29){$OperatingSystemSKU="PRODUCT_WEB_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 36){$OperatingSystemSKU="PRODUCT_STANDARD_SERVER_V";Return $OperatingSystemSKU}
elseif ($Code -eq 37){$OperatingSystemSKU="PRODUCT_DATACENTER_SERVER_V";Return $OperatingSystemSKU}
elseif ($Code -eq 38){$OperatingSystemSKU="PRODUCT_ENTERPRISE_SERVER_V";Return $OperatingSystemSKU}
elseif ($Code -eq 39){$OperatingSystemSKU="PRODUCT_DATACENTER_SERVER_CORE_V";Return $OperatingSystemSKU}
elseif ($Code -eq 40){$OperatingSystemSKU="PRODUCT_STANDARD_SERVER_CORE_V";Return $OperatingSystemSKU}
elseif ($Code -eq 41){$OperatingSystemSKU="PRODUCT_ENTERPRISE_SERVER_CORE_V";Return $OperatingSystemSKU}
elseif ($Code -eq 42){$OperatingSystemSKU="PRODUCT_HYPERV";Return $OperatingSystemSKU}
elseif ($Code -eq 43){$OperatingSystemSKU="PRODUCT_STORAGE_EXPRESS_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 44){$OperatingSystemSKU="PRODUCT_STORAGE_STANDARD_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 45){$OperatingSystemSKU="PRODUCT_STORAGE_WORKGROUP_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 46){$OperatingSystemSKU="PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 48){$OperatingSystemSKU="PRODUCT_PROFESSIONAL";Return $OperatingSystemSKU}
elseif ($Code -eq 49){$OperatingSystemSKU="PRODUCT_PROFESSIONAL_N";Return $OperatingSystemSKU}
elseif ($Code -eq 50){$OperatingSystemSKU="PRODUCT_SB_SOLUTION_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 63){$OperatingSystemSKU="PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 64){$OperatingSystemSKU="PRODUCT_CLUSTER_SERVER_V";Return $OperatingSystemSKU}
elseif ($Code -eq 97){$OperatingSystemSKU="PRODUCT_CORE_ARM";Return $OperatingSystemSKU}
elseif ($Code -eq 101){$OperatingSystemSKU="PRODUCT_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 103){$OperatingSystemSKU="PRODUCT_PROFESSIONAL_WMC";Return $OperatingSystemSKU}
elseif ($Code -eq 104){$OperatingSystemSKU="PRODUCT_MOBILE_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 123){$OperatingSystemSKU="PRODUCT_IOTUAP";Return $OperatingSystemSKU}
elseif ($Code -eq 143){$OperatingSystemSKU="PRODUCT_DATACENTER_NANO_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 144){$OperatingSystemSKU="PRODUCT_STANDARD_NANO_SERVER";Return $OperatingSystemSKU}
elseif ($Code -eq 147){$OperatingSystemSKU="PRODUCT_DATACENTER_WS_SERVER_CORE";Return $OperatingSystemSKU}
elseif ($Code -eq 148){$OperatingSystemSKU="PRODUCT_STANDARD_WS_SERVER_CORE";Return $OperatingSystemSKU}
else {$OperatingSystemSKU="SKU not defined in decode function! Error code 101!";Return $OperatingSystemSKU}
}
LogonType
{
if($Code -eq 0){$LogonType="System Logon";Return $LogonType}
elseif ($Code -eq 2){$LogonType="Console/interactive";Return $LogonType}
elseif ($Code -eq 3){$LogonType="Network/SMB Share";Return $LogonType}
elseif ($Code -eq 4){$LogonType="Batch/Scheduled tasks";Return $LogonType}
elseif ($Code -eq 5){$LogonType="Service";Return $LogonType}
elseif ($Code -eq 7){$LogonType="Unlock";Return $LogonType}
elseif ($Code -eq 8){$LogonType="Network Clear text!!!!";Return $LogonType}
elseif ($Code -eq 9){$LogonType="New Credentials";Return $LogonType}
elseif ($Code -eq 10){$LogonType="Remote interactive/RDP";Return $LogonType}
elseif ($Code -eq 11){$LogonType="Cached interactive/Cached credentials";Return $LogonType}
else {$LogonType="Logon type not defined in decode function! Error code 101!";Return $LogonType}
}
MediaType
{
if($Code -eq 0){$MediaType="Unknown media type";Return $MediaType}
elseif ($Code -eq 11){$MediaType="Removeable media";Return $MediaType}
elseif ($Code -eq 12){$MediaType="Fixed disk/drive";Return $MediaType}
else {$MediaType="Media type not defined in decode function! Error code 101!";Return $MediaType}
}
DriveType
{
if($Code -eq 0){$DriveType="Unknown drive type";Return $DriveType}
elseif ($Code -eq 1){$DriveType="No root directory";Return $DriveType}
elseif ($Code -eq 2){$DriveType="Removeable disk";Return $DriveType}
elseif ($Code -eq 3){$DriveType="Local disk";Return $DriveType}
elseif ($Code -eq 4){$DriveType="Network disk";Return $DriveType}
elseif ($Code -eq 5){$DriveType="Compact disk";Return $DriveType}
elseif ($Code -eq 6){$DriveType="RAM disk";Return $DriveType}
else {$DriveType="Drive type not defined in decode function! Error code 101!";Return $DriveType}
}
Default
{
Write-host "Undefined Value in decode switch function"
}
}
}
Function CreateLogFile ([String]$Name)
{
$PathVariable = "\\myfileserver.domain.local\InventoryData\"
$PathVariable = $PathVariable + $Name
$doesExists = $true
$doesExists = Test-Path $PathVariable
if($doesExists -eq $false){
New-Item -ItemType Directory -Path $PathVariable -Force
}
Else
{
#Write-host "Base folder already exists, we do not recreate it!"
}
# Now we create logfile with specified name
if(($Name -ne $null) -or ($Name -ne ""))
{
$Name = $Name + ".log"
New-Item -ItemType File -Path $PathVariable -Name $Name -Force
}
#Return logfile for specific computer
$Log = $PathVariable + "\" + $Name
Return $Log
}
Function WriteLogFile ([String]$LogFileName,[String]$LogFileText)
{
$DoesExist = Test-Path $LogFileName
if($DoesExist -eq $true)
{
Add-Content -Path $LogFileName -Value $LogFileText -Force -ErrorAction SilentlyContinue
}
else
{
#Logfile does not exist, let create it for you.
$TargetMachine = Get-ComputerInfo
$TargetMachine = $TargetMachine.CsName
CreateLogFile -Name $TargetMachine
}
}
Function checkRestartRequired ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials){
[boolean]$PendingRestart = $false
[boolean]$PendingRestart1 = $false
[boolean]$PendingRestart2 = $false
$PendingRestart1 = $RestartRequired = Get-ChildItem -Path 'REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\'
$RestartRequired = $RestartRequired.Name
foreach ($RestartRequired in $RestartRequired)
{
if($RestartRequired -match "Reboot")
{
$PendingRestart1 = $true
Return $PendingRestart1
}
}
$PendingRestart2 = $RestartRequired = Get-ChildItem -Path 'REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\'
$RestartRequired = $RestartRequired.Name
foreach ($RestartRequired in $RestartRequired)
{
if($RestartRequired -match "Reboot")
{
$PendingRestart1 = $true
Return $PendingRestart2
}
}
if(($PendingRestart1 -eq $true) -or ($PendingRestart2 -eq $true))
{
$PendingRestart = $true
}
return $PendingRestart
}
Function QueryComputerShares ([String]$Machine,[System.Management.Automation.PSCredential]$Credentials)
{
$Shares = @{}; $Shares = get-wmiobject -class win32_share -Property Name,Path,Description ;Return $Shares
}
#Main functions and and detailed implementation of script is below:
Try{
#Start with clear screen!
Clear
$WindowInstallPath = $env:windir
$ScriptStartDate = Get-Date
$TargetMachine = Get-ComputerInfo
$TargetMachine = $TargetMachine.CsName
$InventoryLogfile = CreateLogFile -Name $TargetMachine
$InventoryLogfile = $InventoryLogfile[0]
#Call for functions
$OSinfo = QueryOS -Machine $TargetMachine
$OSBuildNumber = $OSinfo.BuildNumber
$OSCaption = $OSinfo.Caption
$OSCSName = $OSinfo.CSName
$OSInstallDate = $OSinfo.InstallDate
$OSLastBootUpTime = $OSinfo.LastBootUpTime
$OSLocale = $OSinfo.Locale # 0409
$OSOperatingSystemSKU = $OSinfo.OperatingSystemSKU
$OSOSArchitecture = $OSinfo.OSArchitecture
$OSOSLanguage = $OSinfo.OSLanguage
$OSProductType = $OSinfo.ProductType
$OSSerialNumber = $OSinfo.SerialNumber
$OSTotalVisibleMemorySize = $OSinfo.TotalVisibleMemorySize
$OSVersion = $OSinfo.Version
#Formatting of OS information
$OSTotalVisibleMemorySize = $OSTotalVisibleMemorySize/1048576
$OSTotalVisibleMemorySize = [math]::Round($OSTotalVisibleMemorySize)
$OSLastBootUpTime = ConvertWMIDate -WMIDate $OSLastBootUpTime
#$OSLastBootUpTime = $OSLastBootUpTime.date
$OSInstallDate = ConvertWMIDate -WMIDate $OSInstallDate
#Conversion Product Code
$OSProductType = PerformDecode -CodeType "ProductType" -Code $OSProductType
#Conversion OSLanguage
$OSOSLanguage = PerformDecode -CodeType "OSLanguage" -Code $OSOSLanguage
#Conversion OperatingSystemSKU
$OSOperatingSystemSKU = PerformDecode -CodeType "OperatingSystemSKU" -Code $OSOperatingSystemSKU
$LogText = "PC navn=$OSCSName`n OS Type=$OSCaption`n Installasjonsdato=$OSInstallDate`n System startet=$OSLastBootUpTime`n Operativesystem lokale=$OSLocale`n OS Build nummer=$OSBuildNumber`n Operativsystem SKU=$OSOperatingSystemSKU`n OS arkitektur=$OSOSArchitecture`n OS språk=$OSOSLanguage`n OS produkttype=$OSProductType`n OS serienummer=$OSSerialNumber`n OS minne=$OSTotalVisibleMemorySize GB`n OS versjon=$OSVersion`n"
#Write-Host $OSBuildNumber,$OSCaption,$OSCSName,$OSInstallDate,$OSLastBootUpTime,$OSLocale,$OSOperatingSystemSKU,$OSOSArchitecture,$OSOSLanguage,$OSProductType,$OSSerialNumber,$OSTotalVisibleMemorySize,$OSVersion -ForegroundColor Green
#Write-Host $LogText
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
Remove-Variable OSinfo,OSBuildNumber,OSCaption,OSCSName,OSInstallDate,OSLastBootUpTime,OSLocale,OSOperatingSystemSKU,OSOSArchitecture,OSOSLanguage,OSProductType,OSSerialNumber,OSTotalVisibleMemorySize,OSVersion,OSBuildNumber,OSCaption,OSCSName,OSInstallDate,OSLastBootUpTime,OSLocale,OSOperatingSystemSKU,OSOSArchitecture,OSOSLanguage,OSProductType,OSSerialNumber,OSTotalVisibleMemorySize,OSVersion -ErrorAction SilentlyContinue
#
$ComputerSystemInfo = QueryComputerSystem -Machine $TargetMachine
$CSIHypervisorPresent = $ComputerSystemInfo.HypervisorPresent
$CSIManufacturer = $ComputerSystemInfo.Manufacturer
$CSIModel = $ComputerSystemInfo.Model
$CSIName = $ComputerSystemInfo.Name
$CSIUserName = $ComputerSystemInfo.UserName
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nPC informasjon"
#Write-Host "ComputerSystemInfo" -ForegroundColor Green
$LogText = " Maskin leverandør=$CSIManufacturer`n Maskin model=$CSIModel`n PC bruker=$CSIUserName"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
#Write-Host $CSIManufacturer, $CSIModel, $CSIName, $CSIUserName
#Disk information
$DiskInfo = QueryDisk -Machine $TargetMachine
if($DiskInfo.count -ge "1")
{
#Write-Host "Disk information" -ForegroundColor Green
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nDisk informasjon"
foreach($DiskInfo in $DiskInfo)
{
$DICaption = $DiskInfo.Caption
$DIDescription = $DiskInfo.Description
$DIDeviceID = $DiskInfo.DeviceID
$DIDriveType = $DiskInfo.DriveType
$DIDriveType = PerformDecode -Code $DIDriveType -CodeType "Drivetype"
$DIFileSystem = $DiskInfo.FileSystem
$DIFreeSpace = $DiskInfo.FreeSpace
$DIMediaType = $DiskInfo.MediaType
$DIMediaType = PerformDecode -Code $DIMediaType -CodeType "Mediatype"
$DIName = $DiskInfo.Name
$DISize = $DiskInfo.Size
$DIVolumeName = $DiskInfo.VolumeName
#Calculate free space
$Freespace = [math]::Round($DIFreeSpace/1gb)
$Size = [math]::Round($DISize/1gb)
$FreespaceValue = $Freespace.ToString()
$SizeValue = $Size.ToString()
$FreespaceValue = $FreespaceValue + "GB"
$SizeValue = $SizeValue + "GB"
$LogText = " Disk=$DICaption`n Disk beskrivelse=$DIDescription`n Disktype=$DIDriveType`n Disk filsystem=$DIFileSystem`n Volumenavn=$DIVolumeName,`n Disk kapasitet=$SizeValue`n Ledig disk=$FreespaceValue"
#Write-Host $LogText
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
#Write-Host $DICaption, $DIDescription, $DIDeviceID, $DIDriveType, $DIFileSystem, $DIName, $DISize, $DIVolumeName, $SizeValue, $FreespaceValue #$DIFreeSpace, $DIMediaType,
}
}
$BiosInfo = QueryBIOS -Machine $TargetMachine
$BiosInfoValue = $BiosInfo.SerialNumber
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nBIOS informasjon"
#write-host "BIOS Serialnumber:" -ForegroundColor Green
#write-host $BiosInfoValue
$LogText = " Serienummer=$BiosInfoValue"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
#
$ProcessorInfo = QueryProcessor -Machine $TargetMachine
#if($ProcessorInfo.count -ge "1")
# {
#Write-Host "Processor information" -ForegroundColor Green
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nProsessor informasjon"
foreach($ProcessorInfo in $ProcessorInfo)
{
$ProcName = $ProcessorInfo.Name
$ProcNumCores = $ProcessorInfo.NumberOfCores
$ProcNumLogicalProcs = $ProcessorInfo.NumberOfLogicalProcessors
$ProcStatus = $ProcessorInfo.Status
$LogText = " Prosessornavn=$ProcName`n Antall core=$ProcNumCores`n Antall logsike prosessorer=$ProcNumLogicalProcs`n Prosessor status=$ProcStatus"
#Write-Host $ProcName, $ProcNumCores,$ProcNumLogicalProcs, $ProcStatus
#Write-Host $LogText
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
}
# }
Remove-Variable BiosInfo, BiosInfoValue,ProcessorInfo, ProcName, ProcNumCores,ProcNumLogicalProcs, ProcStatus -ErrorAction SilentlyContinue
$LogonSessionInfo = QueryLoggedOnSession -Machine $TargetMachine
if($LogonSessionInfo.count -ge "1")
{
$LSIsessions = @{}
#Write-Host "Logonsession information" -ForegroundColor Green
foreach($LogonSessionInfo in $LogonSessionInfo)
{
$LSILogonID = $LogonSessionInfo.LogonId
$LSILogonType = $LogonSessionInfo.LogonType
$LSIStartTime = $LogonSessionInfo.StartTime
$LSIDecodedValue = PerformDecode -CodeType "LogonType" -Code $LSILogonType
$LSIFormatedDate = ConvertWMIDate -WMIDate $LSIStartTime
$LSIsessions.Add($LSILogonID,$LSIDecodedValue)
#Write-Host $LSILogonID, $LSIDecodedValue, $LSIFormatedDate #, $LSILogonType, $LSIStartTime
}
}
$UserInfo = QueryLoggedOnUser -Machine $TargetMachine
if($UserInfo.count -ge "1")
{
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nPC Brukerinformasjon"
#Write-Host "LoggedOnUser information" -ForegroundColor Green
$UsrSessions = @{}
foreach($UserInfo in $UserInfo)
{
$UsrIAntecedent = $UserInfo.Antecedent
$UsrIDependent = $UserInfo.Dependent
$UsrIAntecedent = $UsrIAntecedent.replace("\\.\root\cimv2:Win32_Account.Domain=","")
$UsrIDependent = $UsrIDependent.replace("\\.\root\cimv2:Win32_LogonSession.LogonId=","")
$UsrIAntecedent = $UsrIAntecedent.replace('"',"")
$UsrIAntecedent = $UsrIAntecedent.replace(",Name=","\")
$UsrIDependent = $UsrIDependent.replace('"',"")
$UsrSessions.Add($UsrIDependent,$UsrIAntecedent)
#Write-Host $UsrIAntecedent,$UsrIDependent
}
}
foreach( $h in $UsrSessions.GetEnumerator())
{
$currentValue = $h.Value
$currentKey = $h.Key
if($currentValue -match "DOMAIN")
{
$currentLogonMethod = $LSIsessions.Item($currentKey)
if($currentLogonMethod -match "Console")
{
#Write-Host $currentValue $currentLogonMethod -ForegroundColor Green
$LogText = " Pålogget bruker=$currentValue, Logget inn gjennom=$currentLogonMethod"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
}
else
{
#Write-Host $currentValue $currentLogonMethod -ForegroundColor Magenta
$LogText = " Andre pålogget brukere=$currentValue, Logget inn via=$currentLogonMethod"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
}
}
}
Remove-Variable LogonSessionInfo,LSILogonID, LSIDecodedValue, LSIFormatedDate, LSIsessions,UserInfo,UsrSessions,UsrIAntecedent,UsrIDependent,currentValue,currentLogonMethod -ErrorAction SilentlyContinue
$PendingRestart = checkRestartRequired -Machine $TargetMachine
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nPC restart informasjon"
$LogText = "Trenger omstart=$PendingRestart"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
#Write-Host "Pending restart information" -ForegroundColor Green
#write-host $PendingRestart
$Services = QueryDomainUserBasedServices -Machine $TargetMachine
<#
Must check, as only services running as domain user as returned
#>
$Shares = QueryComputerShares -Machine $TargetMachine
if($Shares.Count -ge "1")
{
#Write-Host "Shares information" -ForegroundColor Green
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nPC Share informasjon"
foreach($Shares in $Shares)
{
$SDescription = $Shares.Description
$SName = $Shares.Name
$Spath = $Shares.Path
#Write-Host $SName, $SDescription, $Spath
$LogText = "Sharenavn=$SName,Sharebeskrivelse=$SDescription,Sharesti=$Spath"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
}
}
Remove-Variable PendingRestart,Shares,SName, SDescription, Spath -ErrorAction SilentlyContinue
$AppsInfo = QueryApps32bit -Machine $TargetMachine
if($AppsInfo.count -ge "1")
{
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nInstallert programvare"
#Write-Host "Application information" -ForegroundColor Green
foreach($AppsInfo in $AppsInfo)
{
$AppName = $AppsInfo.Name
$AppVendor = $AppsInfo.Vendor
$AppVersion = $AppsInfo.Version
#Write-Host $AppName,$AppVendor,$AppVersion
$LogText = " Applikasjonsnavn=$AppName,Applikasjonsleverandør=$AppVendor,Applikasjonsversjon=$AppVersion"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
#WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
}
}
Remove-Variable AppsInfo,AppName,AppVendor,AppVersion -ErrorAction SilentlyContinue
$PatchInfo = QueryPatches -Machine $TargetMachine
if($PatchInfo.count -ge "1")
{
#Write-Host "Patch information" -ForegroundColor Green
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nInstallert Windows oppdateringer"
foreach($PatchInfo in $PatchInfo)
{
$HFDescription = $PatchInfo.Description
$HFHotfixID = $PatchInfo.HotfixID
$HFInstalledOn = $PatchInfo.InstalledOn
$HFInstalledBy = $PatchInfo.InstalledBy
$HFServicePackInEffect = $PatchInfo.ServicePackInEffect
$HFStatus = $PatchInfo.Status
$LogText = "Type Oppdatering=$HFDescription,Oppdatering ID=$HFHotfixID,Installasjonsdato=$HFInstalledOn,Installert av=$HFInstalledBy,Service pack=$HFServicePackInEffect,Oppdateringstatus=$HFStatus"
#Write-Host $HFDescription, $HFHotfixID, $HFInstalledOn, $HFInstalledBy, $HFServicePackInEffect, $HFStatus
#Write-Host $LogText
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
Start-Sleep -Seconds 1
}
}
Remove-Variable PatchInfo,HFDescription,HFHotfixID,HFInstalledOn, HFInstalledBy, HFServicePackInEffect,HFStatus -ErrorAction SilentlyContinue
$PrinterInfo = QueryPrinter -Machine $TargetMachine
if($PrinterInfo.count -ge "1")
{
#Write-Host "Printer information" -ForegroundColor Green
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nLokal skriverinformasjon"
foreach($PrinterInfo in $PrinterInfo)
{
$PrinterCapabilityDescriptions = $PrinterInfo.CapabilityDescriptions
$PrinterCaption = $PrinterInfo.Caption
$PrinterLocal = $PrinterInfo.Local
$PrinterNetwork = $PrinterInfo.Network
$PrinterPortName = $PrinterInfo.PortName
$PrinterShared = $PrinterInfo.Shared
$PrinterShareName = $PrinterInfo.ShareName
$PrinterStatus = $PrinterInfo.Status
$PrinterTimeOfLastReset = $PrinterInfo.TimeOfLastReset
$PrinterWorkOffline = $PrinterInfo.WorkOffline
$LogText = "Printernavn=$PrinterCaption,Printer kapabiliteter=$PrinterCapabilityDescriptions,Lokalskriver=$PrinterLocal,Nettverksskriver=$PrinterNetwork,Skriver portnavn=$PrinterPortName,Skriver delt=$PrinterShared,Skriver delingsnavn=$PrinterShareName,Skriverstatus=$PrinterStatus,Skriver sist resartet=$PrinterTimeOfLastReset,Skriver offline=$PrinterWorkOffline"
#Write-Host $PrinterCaption,$PrinterCapabilityDescriptions,$PrinterLocal,$PrinterNetwork,$PrinterPortName,$PrinterShared,$PrinterShareName,$PrinterStatus,$PrinterTimeOfLastReset,$PrinterWorkOffline
#Write-Host $LogText
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
}
}
Remove-Variable PrinterInfo,PrinterCaption,PrinterCapabilityDescriptions,PrinterLocal,PrinterNetwork,PrinterPortNamePrinterShared,PrinterShareName,PrinterStatus,PrinterTimeOfLastReset,PrinterWorkOffline -ErrorAction SilentlyContinue
$NICsInfo = QueryNetworkAdapter -Machine $TargetMachine
if($NICsInfo.count -ge "1")
{
#Write-Host "NIC information" -ForegroundColor Green
WriteLogFile -LogFileName $InventoryLogfile -LogFileText "`nPC Nettverksinformasjon"
foreach($NICsInfo in $NICsInfo)
{
$NicDescription = $NICsInfo.Description
$NicDHCPEnabled = $NICsInfo.DHCPEnabled
$NicDHCPLeaseExpires = $NICsInfo.DHCPLeaseExpires
$NicDHCPLeaseExpires = ConvertWMIDate -WMIDate $NicDHCPLeaseExpires
$NicDHCPLeaseObtained = $NICsInfo.DHCPLeaseObtained
$NicDHCPLeaseObtained = ConvertWMIDate -WMIDate $NicDHCPLeaseObtained
$NicDHCPServer = $NICsInfo.DHCPServer
$NicDNSServerSearchOrder = $NICsInfo.DNSServerSearchOrder
$NicFullDNSRegistrationEnabled = $NICsInfo.FullDNSRegistrationEnabled
$NicIPAddress = $NICsInfo.IPAddress
#$NicIPAddress = $NicIPAddress.ToString()
#$NicIPAddress = $NicIPAddress.split(" ")
#$NicIPAddress = $NicIPAddress[0]
$NicMACAddress = $NICsInfo.MACAddress
#write-host $NicDescription,$NicDHCPEnabled,$NicDHCPLeaseExpires,$NicDHCPLeaseObtained,$NicDHCPServer,$NicDNSServerSearchOrder,$NicFullDNSRegistrationEnabled,$NicIPAddress,$NicMACAddress
$LogText = " Nettverkskort beskrivelse=$NicDescription`n DHCP aktivert=$NicDHCPEnabled`n Lease utgår=$NicDHCPLeaseExpires`n IP adresse tildelt=$NicDHCPLeaseObtained`n DHCP server=$NicDHCPServer`n DNS server=$NicDNSServerSearchOrder`n DNS registrering aktiv=$NicFullDNSRegistrationEnabled`n IP adresse=$NicIPAddress`n MAC adresse=$NicMACAddress"
WriteLogFile -LogFileName $InventoryLogfile -LogFileText $LogText
}
}
Remove-Variable NICsInfo,NicDescription,NicDHCPEnabled,NicDHCPLeaseExpires,NicDHCPLeaseObtained,NicDHCPServer,NicDNSServerSearchOrder,NicFullDNSRegistrationEnabled,NicIPAddress,NicMACAddress -ErrorAction SilentlyContinue
#$ServiceAccountInfo = QueryDomainUserBasedServices -Machine $TargetMachine -Credentials $Creds
<# Currently not in use. Enlists all known users to machine.. Who has ever logged in local and domain accounts
$UserAccountInfo = QueryUserAccounts -Machine $TargetMachine -Credentials $Creds
#>
}
Catch
{}
Finally
{
#Write-Host "done!"
}
To specify your file server path you can update the line $PathVariable = “\myfileserver.domain.local\InventoryData\”.
Now that we have the file structure with inventory log files we can for example create an scheduled task or just manually run a script to send us valuable information for example a report showing all machines, their logged in user, disk space available etc. A sample report is shown in the image below

You have abundant possibilities in Powershell to create and format data. The script I have utilized to send the above email is as following
$fileserverpath = "\\myfileserver.domain.local\InventoryData"
$machineLogs = Get-ChildItem -Path $fileserverpath -Exclude "*.exe,*.config,*.log"
clear
$array = @()
foreach($machineLog in $machineLogs)
{
$currentMachineLog = $machineLog.fullname
$currentLogfile = Get-ChildItem $currentMachineLog -Force -ErrorAction SilentlyContinue -Exclude "*.exe,*.config"
$currentLogfilePath = $currentLogfile.FullName
$contentCurrentLog = Get-Content -Path $currentLogfilePath -Force -ErrorAction SilentlyContinue
Write-Host "Analyzing $currentMachineLog" -ForegroundColor Cyan
if(($currentLogfilePath -notmatch ".exe") -and ($currentLogfilePath -notmatch ".config"))
{
foreach($line in [System.IO.File]::ReadLines("$currentLogfilePath"))
{
if($line -match "PC navn=")
{
Write-Host $line -ForegroundColor Green
Add-Content -Path C:\temp\rapport.txt -Value $line -Force
$PC = $line
}
if($line -match "PC bruker=")
{
Write-Host $line -ForegroundColor Green
Add-Content -Path C:\temp\rapport.txt -Value $line -Force
$Bruker = $line
}
if(($line -match "Ledig disk=") -and ($line -notmatch "Ledig disk=0GB"))
{
Write-Host $line -ForegroundColor Green
Add-Content -Path C:\temp\rapport.txt -Value $line -Force
$Disk = $line
}
if(($line -match "serienummer=") -and ($line -notmatch "OS serienummer="))
{
Write-Host $line -ForegroundColor Green
Add-Content -Path C:\temp\rapport.txt -Value $line -Force
$Serienummer = $line
}
<#
if($line -match "bruker=")
{
Write-Host $line -ForegroundColor Green
Add-Content -Path C:\temp\rapport.txt -Value $line -Force
}
#>
if($line -match "IP adresse=")
{
Write-Host $line -ForegroundColor Green
Add-Content -Path C:\temp\rapport.txt -Value $line -Force
$IP = $line
}
if($line -match "MAC adresse=")
{
Write-Host $line -ForegroundColor Green
Add-Content -Path C:\temp\rapport.txt -Value $line -Force
$MAC = $line
}
}
$dataRow = "
</tr>
<td>$PC</td>
<td>$Bruker</td>
<td>$Disk</td>
<td>$Serienummer</td>
<td>$IP</td>
<td>$MAC</td>
</tr>
"
$inventoryReport += $dataRow
}
}
$report = "<html>
<style>
{font-family: Arial; font-size: 13pt;}
TABLE{border: 1px solid black; border-collapse: collapse; font-size:13pt;}
TH{border: 1px solid black; background: #dddddd; padding: 5px; color: #000000;}
TD{border: 1px solid black; padding: 5px; }
</style>
<h2>Computer inventory</h2>
<table>
<tr>
<th>PC</th>
<th>User</th>
<th>Free space</th>
<th>Serialnumber</th>
<th>IP</th>
<th>MAC</th>
</tr>
$InventoryReport
</table>
<tr>
"
Send-MailMessage -To yourmailaddress@zeglory.com -From send@zeglory.com -Subject "PC inventory data information" -BodyAsHtml $report -SmtpServer smtpserver.zeglory.com
You can update the script for your environment by updating $fileserverpath = “\myfileserver.domain.local\InventoryData” section. Furthermore, I am collecting data in a local file under C:\temp\rapport.txt, this is just for testing the outputs and can be ommited in your implementation of the system. Hope this helps any one out there trying to keep track of machines/clients in the production environment. Feel free to ask any questions, as always, the response is subject to availability.