Display computer information for all computers in AD - powershell

Hi i've got a script that gets computer information. I have placed defined the different get values within a variable. And then created a hashtable where i call upon different properties within the variables. This part works fine for a single computer.
I want to get these information for all computer within the domain, but don't know how?
I've been thinking that you could define a variable that contains all AD computers, and then use a foreach statement to cycle through them, but can't get it to work.
How can i output this information information for all computers within the AD, anybody got any pointers?
Thanks in advance
$System =Get-WmiObject WIN32_ComputerSystem
$OS = Get-CimInstance WIN32_OperatingSystem
$Core = Get-WmiObject win32_processor
$GPU = Get-WmiObject WIN32_VideoController
$Disk = get-WmiObject win32_logicaldisk
$ADComps = Get-ADComputer -Filter *
foreach($i in $ADComps){
$hashtable = #{
CPU_Cores = $Core.NumberOfCores
CPU_Model = $Core.Caption
Ram = $System.TotalPhysicalMemory
GPU = $GPU.Caption
OSD = $OS.InstallDate
OS = $OS.Caption
Model = $System.Model
Computer_Producent = $System.Manufacturer
Computer_Name = $System.Name
}
}

Get-WmiObject and Get-CimInstance only queries the WMI service on one machine at a time, so you need to repeat the calls for each machine:
$ADComps = Get-ADComputer -Filter *
foreach($Comp in $ADComps){
$System = Get-WmiObject WIN32_ComputerSystem -ComputerName $Comp.DNSHostName
$OS = Get-CimInstance WIN32_OperatingSystem -ComputerName $Comp.DNSHostName
$Core = Get-WmiObject win32_processor -ComputerName $Comp.DNSHostName
$GPU = Get-WmiObject WIN32_VideoController -ComputerName $Comp.DNSHostName
$Disk = get-WmiObject win32_logicaldisk -ComputerName $Comp.DNSHostName
$hashtable = #{
CPU_Cores = $Core.NumberOfCores
CPU_Model = $Core.Caption
Ram = $System.TotalPhysicalMemory
GPU = $GPU.Caption
OSD = $OS.InstallDate
OS = $OS.Caption
Model = $System.Model
Computer_Producent = $System.Manufacturer
Computer_Name = $System.Name
}
}
Get-WmiObject defaults to querying to remote machine using RPC, if that's blocked in the network you might want to use Invoke-Command to run the queries on the remote machine and then return the resulting object:
$ADComps = Get-ADComputer -Filter *
foreach($Comp in $ADComps){
Invoke-Command -ComputerName $Comp.DNSHostName {
$System = Get-WmiObject WIN32_ComputerSystem
$OS = Get-CimInstance WIN32_OperatingSystem
$Core = Get-WmiObject win32_processor
$GPU = Get-WmiObject WIN32_VideoController
$Disk = get-WmiObject win32_logicaldisk
$hashtable = #{
CPU_Cores = $Core.NumberOfCores
CPU_Model = $Core.Caption
Ram = $System.TotalPhysicalMemory
GPU = $GPU.Caption
OSD = $OS.InstallDate
OS = $OS.Caption
Model = $System.Model
Computer_Producent = $System.Manufacturer
Computer_Name = $System.Name
}
return [pscustomobject]$hashtable
}
}

Related

Powershell script in columns instead of rows

I was trying to write a script which would use a text file with hostnames and needs to generate a file with the extra data. I can only manage to get it in rows instead of columns:
Model:
Bios Version:
TPM OEM Ver:
User logged In:
I would also like to get the emailaddress from the logged on user. I was thinking to use get-aduser. Could I add another foreach after the current code, using the column with the usernames? How would I do this?
All help is greatly appreciated!
My code is:
$output = foreach ($hostname in Get-Content C:\temp\hostnames.txt)
{
$computerinfo = get-ciminstance -computername $hostname Win32_ComputerSystem
$computerBIOS = get-ciminstance -computername $hostname Win32_BIOS
$tpm = Get-ciminstance -class Win32_Tpm -namespace root\CIMV2\Security\MicrosoftTpm -ComputerName $hostname
"Hostname: " + $computerinfo.name
"Model: " + $computerinfo.Model
"Bios Version: " + $computerBIOS.smbiosbiosversion
"TPM OEM Ver: " + $tpm.ManufacturerVersion
"User logged In: " + $computerinfo.UserName
}
$output | out-file 'C:\Temp\hostnames3.txt' -append
You should use the CSV file format instead of plain text for structured data. That makes it easier to use them for further steps if needed.
$output =
foreach ($hostname in Get-Content C:\temp\hostnames.txt) {
$computerinfo = Get-CimInstance -ComputerName $hostname -ClassName Win32_ComputerSystem
$computerBIOS = Get-CimInstance -ComputerName $hostname -ClassName Win32_BIOS
$tpm = Get-CimInstance -Namespace root\CIMV2\Security\MicrosoftTpm -ComputerName $hostname -ClassName Win32_Tpm
[PSCustomObject]#{
Hostname = $computerinfo.name
Model = $computerinfo.Model
Bios_Version = $computerBIOS.smbiosbiosversion
TPM_OEM_Ver = $tpm.ManufacturerVersion
User_logged_In = $computerinfo.UserName
}
}
$output |
Export-Csv -Path 'C:\Temp\hostnames3.csv' -NoTypeInformation
In my experience the UserName property of the CIM-Class Win32_ComputerSystem is unreliable to determine the logged on user. I usually use good old quser.exe like this:
$UserQuery = ( C:\Windows\System32\quser.exe /server:$hostname 2> null)
if ($UserQuery) {
$UserQuery[1].Trim() -match "^(\S+)\s+.*((\d{2}\.){2}\d{4}\s+\d{2}:\d{2})" | Out-Null
$LoggedOnUser = $Matches[1]
$LogonTime = Get-Date -Date $Matches[2]
}
Then you can use $LoggedOnUser and $LogonTime to include it in your output object if you like.
Of course you can include a additional AD query for more information about the logged on user.

Single connection for multiple Get-WmiObject calls

The script below successfully obtains the manufacturer, model, serial number, and operating system from each computer I provide in hostnames.txt. However, it is slow because it must connect to WMI on each computer three times.
$OS = Get-WmiObject Win32_OperatingSystem -ComputerName $Computer
$CS = Get-WmiObject Win32_ComputerSystem -ComputerName $Computer
$BIOS = Get-WmiObject Win32_Bios -ComputerName $Computer
Using PowerShell, how can I connect to the remote-computer's WMI once and execute the three queries using the same connection?
$Array = #() ## Create Array to hold the Data
$Computers = Get-Content -Path .\hostnames.txt
foreach ($Computer in $Computers)
{
$Result = "" | Select HostPS,Mfg,Model,Serial,OS
$Result.HostPS = $Computer
$ErrorActionPreference = "SilentlyContinue" ## Don't output errors for offline computers
$OS = Get-WmiObject Win32_OperatingSystem -ComputerName $Computer
$CS = Get-WmiObject Win32_ComputerSystem -ComputerName $Computer
$BIOS = Get-WmiObject Win32_Bios -ComputerName $Computer
$ErrorActionPreference = "Continue"
$Result.Mfg = $CS.Manufacturer
$Result.Model = $CS.Model
$Result.Serial = $BIOS.SerialNumber
$Result.OS = $OS.Caption
$Array += $Result ## Add the data to the array
}
$Array | Export-Csv file.csv -NoTypeInformation
You can use CIM (which has a session option), more on CIM vs WMI ("WMI is the Microsoft implementation of CIM for the Windows platform")
$CIMSession = New-CimSession -ComputerName $RemoteComputer
Get-CimInstance win32_OperatingSystem -CimSession $CIMSession -Property Caption
Get-CimInstance Win32_ComputerSystem -CimSession $CIMSession -Property Manufacturer,Model
Get-CimInstance Win32_Bios -CimSession $CIMSession -Property SerialNumber
for me, the usual way to do that is to use Invoke-Command to run a script block on each target system. if you tell it to ignore errors, it will run in parallel [on the target systems] and return your data from the responders. to get the non-responders, compare the input list with the results. [grin]
here's a demo of the idea ...
#requires -RunAsAdministrator
# fake reading in a list of computer names
# in real life, use Get-Content or (Get-ADComputer).Name
$ComputerList = #'
Localhost
BetterNotBeThere
127.0.0.1
10.0.0.1
::1
'#.Split("`n").Trim("`r")
$IC_ScriptBlock = {
$CIM_ComputerSystem = Get-CimInstance -ClassName CIM_ComputerSystem
$CIM_BIOSElement = Get-CimInstance -ClassName CIM_BIOSElement
$CIM_OperatingSystem = Get-CimInstance -ClassName CIM_OperatingSystem
$CIM_Processor = Get-CimInstance -ClassName CIM_Processor
$CIM_LogicalDisk = Get-CimInstance -ClassName CIM_LogicalDisk |
Where-Object {$_.Name -eq $CIM_OperatingSystem.SystemDrive}
[PSCustomObject]#{
LocalComputerName = $env:COMPUTERNAME
Manufacturer = $CIM_ComputerSystem.Manufacturer
Model = $CIM_ComputerSystem.Model
SerialNumber = $CIM_BIOSElement.SerialNumber
CPU = $CIM_Processor.Name
RAM_GB = '{0:N2}' -f ($CIM_ComputerSystem.TotalPhysicalMemory / 1GB)
SysDrive_Capacity_GB = '{0:N2}' -f ($CIM_LogicalDisk.Size / 1GB)
SysDrive_FreeSpace_GB ='{0:N2}' -f ($CIM_LogicalDisk.FreeSpace / 1GB)
SysDrive_FreeSpace_Pct = '{0:N0}' -f ($CIM_LogicalDisk.FreeSpace / $CIM_LogicalDisk.Size * 100)
OperatingSystem_Name = $CIM_OperatingSystem.Caption
OperatingSystem_Version = $CIM_OperatingSystem.Version
OperatingSystem_BuildNumber = $CIM_OperatingSystem.BuildNumber
OperatingSystem_ServicePack = $CIM_OperatingSystem.ServicePackMajorVersion
CurrentUser = $CIM_ComputerSystem.UserName
LastBootUpTime = $CIM_OperatingSystem.LastBootUpTime
UpTime_Days = '{0:N2}' -f ([datetime]::Now - $CIM_OperatingSystem.LastBootUpTime).Days
}
}
$IC_Params = #{
ComputerName = $ComputerList
ScriptBlock = $IC_ScriptBlock
ErrorAction = 'SilentlyContinue'
}
$RespondingSystems = Invoke-Command #IC_Params
$NOT_RespondingSystems = $ComputerList.Where({
# these two variants are needed to deal with an ipv6 localhost address
"[$_]" -notin $RespondingSystems.PSComputerName -and
$_ -notin $RespondingSystems.PSComputerName
})
$RespondingSystems
$NOT_RespondingSystems
one item from the $RespondingSystems list ...
LocalComputerName : [MySystemName]
Manufacturer : System manufacturer
Model : System Product Name
SerialNumber : System Serial Number
CPU : AMD Phenom(tm) II X4 945 Processor
RAM_GB : 8.00
SysDrive_Capacity_GB : 931.41
SysDrive_FreeSpace_GB : 735.15
SysDrive_FreeSpace_Pct : 79
OperatingSystem_Name : Microsoft Windows 7 Professional
OperatingSystem_Version : 6.1.7601
OperatingSystem_BuildNumber : 7601
OperatingSystem_ServicePack : 1
CurrentUser : [MySystemName]\[MyUserName]
LastBootUpTime : 2018-10-19 7:01:51 PM
UpTime_Days : 7.00
PSComputerName : [::1]
RunspaceId : e17c2741-ba8b-4fbb-b3db-9c7fd0d84f0d
the $NOT_RespondingSystems list ...
BetterNotBeThere
10.0.0.1

get-process product version remote computer

If I locally do this I get all the information:
get-process | select-object name,fileversion,company
However, if I do it on a remote computer I only get the process name and all the other fields are blank. Does anyone know why or how to get the same information. I am using a domain admin credential so I should have access to that information.
get-process -computername xcomp123 | select-object name,fileversion,company
You can try this solution:
$Computername = 'Remotehost'
$Session = New-CimSession -ComputerName $Computername
$process = Get-CimInstance -ClassName Win32_Process -CimSession $Session
$col = New-Object System.Collections.ArrayList
foreach ($n in $process){
$exePath = $null
$ExeInfo = $null
$exePath = $n.ExecutablePath -Replace '\\','\\'
$ExeInfo = Get-CimInstance -ClassName Cim_DataFile -Filter "Name = '$exePath'" -ErrorAction SilentlyContinue
[void]$col.add([PSCustomObject]#{
Name = $n.name
FileVersion = $ExeInfo.Version
Company = $ExeInfo.Manufacturer
PSComputername = $n.PSComputername
})
}
Remove-Cimsession $session
$col
Update:
I reduced the code to check for one process only. I assert the referencefile having the same name, as the process on the client computers. You might change that for your needs.
You can specify multiple computers at $computername, so you do not have to run the code over and over again.
#region Reference file
$RefFile = Get-item "\\x123\c$\program files\prog\winagent\file.exe"
#endregion
#region remote file
[string[]]$Computername = 'Remotehost1', 'Remotehost2'
$Processname = $RefFile.Name
foreach ($n in $Computername) {
$Session = New-CimSession -ComputerName $n
$process = Get-CimInstance -ClassName Win32_Process -CimSession $Session -Filter "name = '$Processname'"
$exePath = $process.ExecutablePath -Replace '\\', '\\'
$ExeInfo = Get-CimInstance -ClassName Cim_DataFile -Filter "Name = '$exePath'" -ErrorAction SilentlyContinue
[PSCustomObject]#{
Name = $Processname
FileVersion = $ExeInfo.Version
Company = $ExeInfo.Manufacturer
PSComputername = $n
}
Remove-Cimsession $session
}
#endregion

Sort and format in email

I'm new to Powershell and I'm trying to send the output by email of the following piece of code, if I send it to the console, it is formatted nicely, but when I pipeline it into an email, the retrieved objects are not lined up.
$bios = Get-WmiObject Win32_BIOS -ComputerName localhost
$os = Get-WmiObject `Win32_OperatingSystem -ComputerName localhost
$Proc = Get-WmiObject Win32_processor -ComputerName localhost | Select-Object -First 1
$memory = Get-WmiObject Win32_physicalmemory -ComputerName localhost
$system = Get-WmiObject Win32_ComputerSystem -ComputerName localhost
$Systeminfo = New-Object PSObject -Property #{
'ComputerName' = $proc.SystemName;
'Manufacturer' = $bios.Manufacturer;
'Model' = $system.Model;
'BIOS Version' = $bios.Version;
'Serial Number' = $bios.SerialNumber;
'Number of Processors ' = $system.NumberOfProcessors;
'Processor Name' = $proc.name;
'Logical Processor' = $system.NumberOfLogicalProcessors;
'Speed (MHZ)' = $proc.CurrentClockSpeed;
'RAM (GB)' = $system.TotalPhysicalMemory / 1GB -as [int];
'Used RAM slots' = $memory.count;
'OSVersion' = $os.Caption
}
$Systeminfo = $Systeminfo | Out-String
Also, is there a way to rearrange the order they appear, for example, I would like the computername to be first in the array, but it appears in the middle?
If you have PowerShell 3.0 you could create the object using [ordered]
$Systeminfo = [pscustomobject][ordered] #{
'ComputerName' = $proc.SystemName;
'Manufacturer' = $bios.Manufacturer;
'Model' = $system.Model;
'BIOS Version' = $bios.Version;
'Serial Number' = $bios.SerialNumber;
'Number of Processors ' = $system.NumberOfProcessors;
'Processor Name' = $proc.name;
'Logical Processor' = $system.NumberOfLogicalProcessors;
'Speed (MHZ)' = $proc.CurrentClockSpeed;
'RAM (GB)' = $system.TotalPhysicalMemory / 1GB -as [int];
'Used RAM slots' = $memory.count;
'OSVersion' = $os.Caption
}
That should output the properties in the order they were defined. Else you would need to use a select statement to order the properties that you want.
$Systeminfo | Select-Object ComputerName,Manufacturer,Model,"BIOS Version","Serial Number","Number of Processors","Processor Name","Logical Processor","Speed (MHZ)","RAM (GB)","Used RAM slots",OSVersion

Displaying array properties for custom PSObjects

I'm pretty new to PowerShell, so to learn the ropes better, I'm working on a Powershell function to return some basic overview information on computers in our network. I've gotten just about everything that I'm looking for, but I don't know how to display all results for arrays returned by the WMI queries for things like hard disks or MAC addresses.
For example, right now I'm using the WMI query "DHCPEnabled = TRUE" to detect active NICs and retrieve their MAC addresses - but on a laptop, it's theoretically possible that query could return both a wired and wireless NIC.
The output of this command would then display the custom PSObject that I create, but in the resultant PSObject, the property MACAddress will display blank. The results are there, and I could get to them via the pipeline or a Select-Object, but I don't know how to save them for a report or otherwise "prettify" them.
Here's the working function I have now, which makes an awful assumption that the first result returned is the only one I care about. Again, in this example, this is mostly a concern for hard disks and for MAC addresses, but I'd like to understand the concept behind it for future reference.
Function Get-PCInfo
{
[CmdletBinding()]
param(
[Parameter(Mandatory = $true,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias("CName")]
[string[]] $ComputerName
)
foreach($cName in $ComputerName)
{
Write-Verbose "Testing connection to $cName"
If (Test-Connection -ComputerName $cName -BufferSize 16 -Quiet)
{
Write-Verbose "Connection successful."
Write-Verbose "Obtaining WMI objects from $cName"
$cs = Get-WMIObject -Class Win32_ComputerSystem -ComputerName $cName
$csp = Get-WMIObject -Class Win32_ComputerSystemProduct -ComputerName $cName
$os = Get-WMIObject -Class Win32_OperatingSystem -ComputerName $cName
$bios = Get-WMIObject -Class Win32_BIOS -ComputerName $cName
$cpu = Get-WMIObject -Class Win32_Processor -ComputerName $cName
$hdd = Get-WMIObject -Class Win32_LogicalDisk -Filter 'DeviceID = "C:"' -ComputerName $cName
$network = Get-WMIObject -Class Win32_NetworkAdapterConfiguration -Filter 'DHCPEnabled = True' -ComputerName $cName
if ($hdd -is [System.array])
{
Write-Verbose "Multiple hard drives detected; using first result"
$hddResult = $hdd[0]
} else {
Write-Verbose "Single hard drive detected"
$hddResult = $hdd
}
if ($network -is [System.array])
{
Write-Verbose "Multiple network cards detected; using first result"
$networkResult = $network[0]
} else {
Write-Verbose "Single network card detected"
$networkResult = $network
}
Write-Verbose "Creating output table"
$props = #{'Name' = $cs.Name;
'OSVersion' = $os.Version;
'ServicePack' = $os.ServicePackMajorVersion;
'HardDiskSize' = $hddResult.Size;
'SerialNumber' = $bios.serialNumber;
'Model' = $cs.Model;
'Manufacturer' = $cs.Manufacturer;
'Processor' = $cpu.Name;
'RAM' = $cs.TotalPhysicalMemory;
'MACAddress' = $networkResult.MACAddress}
Write-Verbose "Creating output object from table"
$result = New-Object -TypeName PSObject -Property $props
Write-Verbose "Outputting result"
$resultArray += #($result)
} else {
Write-Verbose "Connection failure"
$resultArray += #($null)
}
}
Write-Output $resultArray
}
Here's an example run, for some more clarity. The data is fake, but this is the format of the result:
PS> Get-PCInfo localhost
SerialNumber : 12345
MACAddress :
RAM : 4203204608
Manufacturer : Computers, Inc.
Processor : Intel(R) Core(TM) i5-2400 CPU # 3.10GHz
HardDiskSize : 500105736192
OSVersion : 6.2.9200
Name : PC1
Model: : Super Awesome Computer
ServicePack : 0
I'd like to send this to ConvertTo-HTML or something to make a nice-looking report, but because MACAddress is blank, I can't make anything nice out of it. What I'd like to see is something like this:
SerialNumber : 12345
MACAddress[0] : 00-11-22-33-44-55
MACAddress[1] : 88-99-AA-BB-CC-DD
...
HardDiskSize[0]: 500105736192
HardDiskSize[1]: 500105736192
I'm not quite sure I understand? It depends on how you want them to output. You can do it in many ways. An example for HDDs and MAC addresses:
....
'HardDiskSize' = ($hdd | % { "HDD $($_.DeviceID) - $($_.Size)" }) -join "`n"
....
'MACAddress' = ($networkResult | Select-Object -ExpandProperty MACAddress) -join "`n"
}
You can try this (untested). Copy and paste the edited parts back:
$hdd = #(Get-WMIObject -Class Win32_LogicalDisk -Filter 'DeviceID = "C:"' -ComputerName $cName)
$network = #(Get-WMIObject -Class Win32_NetworkAdapterConfiguration -Filter 'DHCPEnabled = True' -ComputerName $cName)
$props = #{'Name' = $cs.Name;
'OSVersion' = $os.Version;
'ServicePack' = $os.ServicePackMajorVersion;
'SerialNumber' = $bios.serialNumber;
'Model' = $cs.Model;
'Manufacturer' = $cs.Manufacturer;
'Processor' = $cpu.Name;
'RAM' = $cs.TotalPhysicalMemory;
Write-Verbose "Creating output object from table"
$result = New-Object -TypeName PSObject -Property $props
# Add MAC addresses
for ($i = 0; $i -lt $network.Count; $i++) {
Add-Member -InputObject $result -MemberType NoteProperty -Name "MACAddress[$i]" -Value $network[$i].MACAddress
}
# Add HDDs
for ($i = 0; $i -lt $hdd.Count; $i++) {
Add-Member -InputObject $result -MemberType NoteProperty -Name "HardDiskSize[$i]" -Value $hdd[$i].Size
}