Powershell CSV Output Issue - powershell

Can anyone assist my with explanation as to why I get System.String[] as my Gateway output in the CSV file but it works fine in the ISE view/window.
$testcomputers = Get-Content -Path 'C:\Users2\101.txt'
$exportLocation = 'C:\Users2\PingResults.csv'
# Test connection to each computer before getting the inventory info
foreach ($computer in $testcomputers) {
if (Test-Connection -ComputerName $computer -Quiet -count 1){
Add-Content -value $computer -path C:\Users2\OnlineComputers.txt
}else{
Add-Content -value $computer -path C:\Users2\OfflineComputers.txt
}
}
#Specify the list of PC names in the line below
$ArrComputers = Get-Content -Path 'C:\Users2\OnlineComputers.txt'
Clear-Host
foreach ($Computer in $ArrComputers)
{
$computerSystem = get-wmiobject Win32_ComputerSystem -Computer $Computer
$computerBIOS = get-wmiobject Win32_BIOS -Computer $Computer
$computerOS = get-wmiobject Win32_OperatingSystem -Computer $Computer
$computerCPU = get-wmiobject Win32_Processor -Computer $Computer
$computerHDD = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter drivetype=3
$computerGateway = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Computer $Computer -Filter “IPEnabled=TRUE”
write-host "System Information for: " $computerSystem.Name -BackgroundColor DarkCyan
"-------------------------------------------------------"
"Manufacturer: " + $computerSystem.Manufacturer
"Model: " + $computerSystem.Model
"Serial Number: " + $computerBIOS.SerialNumber
"CPU: " + $computerCPU.Name
"HDD Capacity: " + "{0:N2}" -f ($computerHDD.Size/1GB) + "GB"
"HDD Space: " + "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size) + " Free (" + "{0:N2}" -f ($computerHDD.FreeSpace/1GB) + "GB)"
"RAM: " + "{0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB) + "GB"
"Operating System: " + $computerOS.caption + ", Service Pack: " + $computerOS.ServicePackMajorVersion
"User logged In: " + $computerSystem.UserName
"Last Reboot: " + $computerOS.ConvertToDateTime($computerOS.LastBootUpTime)
"Gateway: " + $computerGateway.DefaultIPGateway
""
"-------------------------------------------------------"
#Build the CSV file
$csvObject = New-Object PSObject -property #{
'PCName' = $computerSystem.Name
'Manufacturer' = $computerSystem.Manufacturer
'Model' = $computerSystem.Model
'Service Tag' = $computerBIOS.SerialNumber
'RAM' = "{0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB)
'HDDSize' = "{0:N2}" -f ($computerHDD.Size/1GB)
'HDDFree' = "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size)
'CPU' = $computerCPU.Name
'OS' = $computerOS.caption
'SP' = $computerOS.ServicePackMajorVersion
'User' = $computerSystem.UserName
'Last_Reboot' = $computerOS.ConvertToDateTime($computerOS.LastBootUpTime)
'Gateway' = $computerGateway.DefaultIPGateway
}
#Export the fields you want from above in the specified order
$csvObject | Select PCName, Manufacturer, Model, OS, SerialNumber, CPU, Ram, User, Last_Reboot, HDDSize, HDDFree, Gateway | Export-Csv 'C:\Users2\results.csv' -NoTypeInformation -Append
}
Here is the ISE output
System Information for: WS101161
-------------------------------------------------------
Manufacturer: Dell Inc.
Model: OptiPlex 5070
Serial Number: 7HF8T13
CPU: Intel(R) Core(TM) i5-9500 CPU # 3.00GHz
HDD Capacity: 237.96GB
HDD Space: 67.87% Free (161.51GB)
RAM: 15.79GB
Operating System: Microsoft Windows 10 Pro, Service Pack: 0
User logged In:
Last Reboot: 04/28/2022 17:40:52
Gateway: 10.170.1.250
-------------------------------------------------------
And here is the CSV output (User being empty is okay, nobody is signed into this PC at the moment)
"PCName","Manufacturer","Model","OS","SerialNumber","CPU","RAM","User","Last_Reboot","HDDSize","HDDFree","Gateway"
"WS101161","Dell Inc.","OptiPlex 5070","Microsoft Windows 10 Pro",,"Intel(R) Core(TM) i5-9500 CPU # 3.00GHz","15.79",,"4/28/2022 5:40:52 PM","237.96","67.87%","System.String[]"
Thank you for the assistance!
asdasdasd

The main issue that answers your question, the property DefaultIPGateway of the Win32_NetworkAdapterConfiguration class is of the type string[]:
DefaultIPGateway
Data type: string array
You need to convert it to string so that Export-Csv can handle it:
PS /> #{ Gateway = $computerGateway[0].DefaultIPGateway } | ConvertTo-Csv
"Gateway"
"System.String[]"
PS /> #{ Gateway = [string] $computerGateway[0].DefaultIPGateway } | ConvertTo-Csv
"Gateway"
"192.168.0.1"
It is also worth noting that $computerGateway may return an array of IPs in which case you should join them:
$computerGateway.DefaultIPGateway -join ', '
Aside from that, there are other issues with your code, for example all Division operations on $computerHDD:
$computerHDD.Size / 1GB
Will throw this exception if the host has more than one Physical Disk:
InvalidOperation: Method invocation failed because [System.Object[]] does not contain a method named 'op_Division'.
Test-NetConnection is not reliable to tell you if you can or cannot connect to a remote host, there may be a firewall blocking ICMP packets and you may have been able to still connect it since the protocol being used in your code is WinRM (WinRM HTTP uses port 5985 and WinRM HTTPS uses port 5986).
Your code can also be invoked in parallel instead of going one host at a time, Invoke-Command is the best alternative as Lee_Dailey mentions in his helpful comment.
Sending information to the console (Write-Host) will only slow your script down, I've removed all instances of console output.
Appending to a file with Export-Csv -Append on each iteration of your loop will also slow your script down, the more Disk I/O the slower the code will be, it is better to collect all output first in memory and then writing to a file once.
Lastly, Get-WmiObject should be replaced by Get-CimInstance. All the WMI Cmdlets are no longer in circulation in newer versions of PowerShell (PowerShell Core). And the CIM Cmdlets have been available since PowerShell 3. No reason to use WMI over CIM.
With all that being said, this is how I would approach your script:
$unavaibleHosts = [System.Collections.Generic.List[string]]::new()
$session = foreach($computer in Get-Content -Path 'C:\Users2\101.txt') {
try {
New-PSSession $computer -ErrorAction Stop
}
catch {
$unavaibleHosts.Add($computer)
}
}
Invoke-Command -Session $session -HideComputerName -ScriptBlock {
$computerSystem = Get-CimInstance Win32_ComputerSystem
$computerBIOS = Get-CimInstance Win32_BIOS
$computerOS = Get-CimInstance Win32_OperatingSystem
$computerCPU = Get-CimInstance Win32_Processor
$computerHDD = Get-CimInstance Win32_LogicalDisk -Filter drivetype=3
$computerGateway = Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE
$hddSize = $computerHDD.ForEach{ "[{0} {1:N2}]" -f $_.DeviceID, ($_.Size / 1GB) } -join ' | '
$hddFree = $computerHDD.ForEach{ "[{0} {1:P2}]" -f $_.DeviceID, ($_.FreeSpace / $_.Size) } -join ' | '
# output from these 2 would look like this:
# HDDSize : [C: 236.76] | [D: 931.51]
# HDDFree : [C: 73.30%] | [D: 81.43%]
[pscustomobject]#{
'PCName' = $computerSystem.Name
'Manufacturer' = $computerSystem.Manufacturer
'Model' = $computerSystem.Model
'Service Tag' = $computerBIOS.SerialNumber
'RAM' = "{0:N2}" -f ($computerSystem.TotalPhysicalMemory / 1GB)
'HDDSize' = $hddSize
'HDDFree' = $hddFree
'CPU' = $computerCPU.Name
'OS' = $computerOS.caption
'SP' = $computerOS.ServicePackMajorVersion
'User' = $computerSystem.UserName
'Last_Reboot' = $computerOS.LastBootUpTime
'Gateway' = $computerGateway.DefaultIPGateway -join ','
}
} | Select-Object * -ExcludeProperty RunspaceId | Export-Csv 'C:\Users2\results.csv' -NoTypeInformation
Remove-PSSession $session
$unavaibleHosts # => Is here for you to troubleshoot the failed connections.

# https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-networkadapterconfiguration
# DefaultIPGateway
# Data type: string array
# Access type: Read-only
# Qualifiers: MappingStrings ("Win32Registry|System\\CurrentControlSet\\Services|Parameters|DefaultGateway")
# Array of IP addresses of default gateways that the computer system uses.
# Example: "192.168.12.1 192.168.46.1"
# Your case
$computerGateway = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Computer "DESKTOP-9EPNRSP" -Filter "IPEnabled=TRUE"
$csvObject = New-Object PSObject -property #{
'Gateway' = $computerGateway.DefaultIPGateway
}
$csvObject.Gateway.GetType().FullName
# Convert to String
$computerGateway = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Computer "DESKTOP-9EPNRSP" -Filter "IPEnabled=TRUE"
$csvObject = New-Object PSObject -property #{
'Gateway' = "$($computerGateway.DefaultIPGateway)"
}
$csvObject.Gateway.GetType().FullName
#Take first element
$computerGateway = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Computer "DESKTOP-9EPNRSP" -Filter "IPEnabled=TRUE"
$csvObject = New-Object PSObject -property #{
'Gateway' = $computerGateway.DefaultIPGateway[0]
}
$csvObject.Gateway.GetType().FullName
#Generate with comma sepparated - █ Recommended █
$computerGateway = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Computer "DESKTOP-9EPNRSP" -Filter "IPEnabled=TRUE"
$csvObject = New-Object PSObject -property #{
'Gateway' = [string]::Join(",", $computerGateway.DefaultIPGateway)
}
$csvObject.Gateway.GetType().FullName
Output
System.String[]
System.String
System.String
System.String

Related

Retrieving system information from a remote system(s) using powershell - script provided

I am trying to create a script that pulls relevant system information from the a client workstation and provides the following:
Pings workstations to see if online/offline
Displays the requested information on the screen
Dumps requested data to a file
The reason why I am doing a write host and dumping to a file, I want to be able to have anyone in our service desk run it, and have a file copy if we need to retain the data for historical purposes. I am having trouble writing the information to the file, plain and simple I don't know how to do that (sorry :D) here is my code, any help that you are able to provide would be greatly appreciated. Thank you!
$Computers = "COMPUTERNAME"
Clear-Host
#Sets the array for the input of any string
$Outputmessage = #()
#Runs "foreach" loop statement and sets the computer variables, and performs WMI queries based on WMI Providers
foreach ($Computer in $Computers) {
#Perform ping test on workstation prior to checking system information
$pingtest = Test-Connection -ComputerName $Computer -Quiet -Count 1 -ErrorAction SilentlyContinue
if ($pingtest) {
#Runs WMI queries to get computer information to output on to the screen
$computerSystem = get-wmiobject Win32_ComputerSystem -Computer $Computer
$computerBIOS = get-wmiobject Win32_BIOS -Computer $Computer
$computerOS = get-wmiobject Win32_OperatingSystem -Computer $Computer
$computerCPU = get-wmiobject Win32_Processor -Computer $Computer
$computerHDD = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Filter drivetype=3
$computerDiskType = Get-WmiObject Win32_Diskdrive | Where { $_.Model } | Select caption
$computerDisplayCount = (Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorBasicDisplayParams | where { $_.Active -like "True" }).Active.Count
#Displays on screen the system information requested translated to the below
"-------------------------------------------------------"
write-host "System Information for: " $computerSystem.Name -BackgroundColor DarkCyan
""
"Manufacturer: " + $computerSystem.Manufacturer
"Model: " + $computerSystem.Model
"Asset Tag: " + $computerBIOS.SerialNumber
"CPU: " + $computerCPU.Name
"HDD Capacity: " + "{0:N2}" -f ($computerHDD.Size / 1GB) + "GB"
"HDD Space: " + "{0:P2}" -f ($computerHDD.FreeSpace / $computerHDD.Size) + " Free (" + "{0:N2}" -f ($computerHDD.FreeSpace / 1GB) + "GB)"
"Disk Type: " + $computerDiskType.caption
"RAM: " + "{0:N2}" -f ($computerSystem.TotalPhysicalMemory / 1GB) + "GB"
"Operating System: " + $computerOS.caption + ", Service Pack: " + $computerOS.ServicePackMajorVersion
"Connected Monitors: " + $computerDisplayCount
"User logged In: " + $computerSystem.UserName
""
"-------------------------------------------------------"
$Result = $computerSystem.Name, $computerBIOS.Model, $computerOS.SerialNumber, $computerCPU.Name, $computerHDD.Size, $computerDiskType.caption, $computerDisplayCount, $computerSystem.UserName
}
else {
$OutputMessage += "$Computer - OFFLINE"
}
}
$Outputmessage + $Result | Out-File -FilePath "FILE PATH" -Encoding utf8 -Append
Here is the output what is displayed in the screen
-------------------------------------------------------
System Information for: COMPUTERNAME
Manufacturer: Manufacturer
Model: System model
Asset Tag: serial number
CPU: CPU information
HDD Capacity: drive capacity
HDD Space: Free space
Disk Type: Drive type
RAM: Memory amount
Operating System: OS Version
Connected Monitors: Number of connected monitors
User logged In: DOMAIN\USERNAME
-------------------------------------------------------
Thank you so much for your assistance
I would take the route of using a PSCustomObject to better organize your data. First and foremost, there are a couple of issues in your code where you don't reference the remote computer, but rather it defaults to yours relaying your information, not theirs. Another point to be made is connectivity. Just because you can ping a machine, it doesn't mean you can connect to it.
With all that's said above, given that this will be used by others, I would create a re-usable function in this case:
Function Get-SystemInfo {
[cmdletbinding()]
Param (
[parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerName = $env:COMPUTERNAME
)
Begin
{
$PSCustomObject = [PSCustomObject]#{
ComputerName = $null
Status = $null
Manufacturer = $null
Model = $null
AssetTag = $null
CPU = $null
HDDCapacity = $null
HDDSpace = $null
DiskType = $null
RAM = $null
OperatingSystem = $null
ConnectedMonitors = $null
UserloggedIn = $null
}
}
Process
{
foreach ($computer in $ComputerName)
{
try {
$PSCustomObject.ComputerName = $computer
$cimSession = New-CimSession -ComputerName $computer -ErrorAction 'Stop'
$PSCustomObject.Status = 'Online - Connected'
$computerSystem = Get-CimInstance -ClassName 'Win32_ComputerSystem' -CimSession $cimSession -Property 'Manufacturer','Model','TotalPhysicalMemory ','UserName'
$PSCustomObject.Manufacturer = $computerSystem.Manufacturer
$PSCustomObject.Model = $computerSystem.Model
$PSCustomObject.RAM = "{0:N2}" -f ($computerSystem.TotalPhysicalMemory / 1GB) + "GB"
$PSCustomObject.UserloggedIn = $computerSystem.UserName
$computerBIOS = Get-CimInstance -ClassName 'Win32_BIOS' -CimSession $cimSession
$PSCustomObject.AssetTag = $computerBIOS.SerialNumber
$computerOS = Get-CimInstance -ClassName 'Win32_OperatingSystem' -CimSession $cimSession
$PSCustomObject.OperatingSystem = $computerOS.caption + ", Service Pack: " + $computerOS.ServicePackMajorVersion
$computerCPU = Get-CimInstance -ClassName 'Win32_Processor' -CimSession $cimSession
$PSCustomObject.CPU = $computerCPU.Name
$computerHDD = Get-CimInstance -ClassName 'Win32_LogicalDisk' -CimSession $cimSession -Filter 'drivetype=3'
$PSCustomObject.HDDCapacity = "{0:N2}" -f ($computerHDD.Size / 1GB) + "GB"
$PSCustomObject.HDDSpace = "{0:P2}" -f ($computerHDD.FreeSpace / $computerHDD.Size) + " Free (" + "{0:N2}" -f ($computerHDD.FreeSpace / 1GB) + "GB)"
$computerDiskType = Get-CimInstance -ClassName 'Win32_Diskdrive' -CimSession $cimSession | Where-Object -FilterScript { $_.Model }
$PSCustomObject.DiskType = $computerDiskType.caption
$computerDisplayCount = (
Get-CimInstance -Namespace 'root\wmi' -ClassName 'WmiMonitorBasicDisplayParams' -CimSession $cimSession |
Where-Object -FilterScript { $_.Active -like "True" }
).Active.Count
$PSCustomObject.ConnectedMonitors = $computerDisplayCount
}
catch {
$PSCustomObject.Status = if (Test-Connection -ComputerName $computer -Count 1) { 'Online - Unable to Connect' } else { 'offline' }
$PSCustomObject.Manufacturer = $null
$PSCustomObject.Model = $null
$PSCustomObject.AssetTag = $null
$PSCustomObject.CPU = $null
$PSCustomObject.HDDCapacity = $null
$PSCustomObject.HDDSpace = $null
$PSCustomObject.DiskType = $null
$PSCustomObject.RAM = $null
$PSCustomObject.OperatingSystem = $null
$PSCustomObject.ConnectedMonitors = $null
$PSCustomObject.UserloggedIn = $null
}
finally {
$PSCustomObject
if ($cimSession)
{
Remove-CimSession -CimSession $cimSession -ErrorAction 'SilentlyContinue'
}
}
}
}
End { }
}
A function becomes much more handy when typing Get-SystemInfo -ComputerName 'ComputerOne','ComputerTwo', rather than modifying the $computers variable each time. From here you can pipe to Out-File, Export-Csv or any other export cmdlet of your choosing.

How to export information about the monitor to a csv file using PowerShell?

I am new to PowerShell and am struggling to write a script to export the UserFriendlyName (see the code below) for three monitors. Here is what I have so far:
$monitors = Get-WmiObject -Namespace root\wmi -Class wmiMonitorID
Get-CimInstance -Namespace root\wmi -ClassName wmimonitorid -ComputerName $ComputerName |
foreach {
$Object = New-Object PSObject -Property #{
MonitorName = ($monitors.UserFriendlyName -notmatch '^0$' | foreach {[char]$_}) -join ""
MonitorSerial = ($monitors.serialnumberid -notmatch '^0$' | foreach {[char]$_}) -join ""
}
}
$Object | Select MonitorName,MonitorSerial
$Object | Export-Csv -append -force /Computer.csv -NoTypeInformation
The result that I am getting:
MonitorName MonitorSerial
----------- -------------
27B1DELL P2717HDELL P2717H GUHJBHA018695YKNFG6CQAGLLYKNFG71KAPTL
I would like to have each monitor name and serial number under their own column (Monitor 1, Monitor 2, Monitor 3 and the same for serial number) but the values are together. Any help is much appreciated.
I am hoping to have the above incorporated with this:
$computerSystem = Get-CimInstance CIM_ComputerSystem
$computerBIOS = Get-CimInstance CIM_BIOSElement
$computerOS = Get-CimInstance CIM_OperatingSystem
$computerCPU = Get-CimInstance CIM_Processor
$computerHDD = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID = 'C:'"
Get-CimInstance -Namespace root\wmi -ClassName wmimonitorid -ComputerName $ComputerName |
foreach {
$Object = New-Object PSObject -Property #{
"Computer Name" = $computerSystem.Name
"Operating System" = $computerOS.caption + ", Service Pack: " + $computerOS.ServicePackMajorVersion
"Manufacturer" = $computerSystem.Manufacturer
"Model" = $computerSystem.Model
"Serial Number" = $computerBIOS.SerialNumber
"CPU" = $computerCPU.Name
"HDD Capacity" = "{0:N2}" -f ($computerHDD.Size/1GB) + "GB"
"RAM" = "{0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB) + "GB"
"User logged In" = $computerSystem.UserName
}
}
$Object | Export-Csv -append -force /Computer.csv -NoTypeInformation
Something does not seem right with your code. You should be adding each monitor to an array or a list.
$monitors = Get-WmiObject -Namespace root\wmi -Class wmiMonitorID
$allMonitors = #()
Get-CimInstance -Namespace root\wmi -ClassName wmimonitorid -ComputerName $ComputerName |
foreach {
$Object = [PSCustomObject]#{
MonitorName = ($monitors.UserFriendlyName -notmatch '^0$' | foreach {[char]$_}) -join ""
MonitorSerial = ($monitors.serialnumberid -notmatch '^0$' | foreach {[char]$_}) -join ""
}
$allMonitors += $Object
}
$allMonitors | Select MonitorName,MonitorSerial
$allMonitors | Export-Csv -append -force /Computer.csv -NoTypeInformation

powershell computer information script

I need to create a powershell script you can double-click on (64-bit computer) and it will output to a .txt file in the same location as the powershell script to generate information on:
Computer name/model/serial no.
C drive size/available disk space on the C drive
Which version operating system the computer is running
Who is currently logged onto the computer
So far (but it's not quite working) I've got:
$computerSystem = get-wmiobject Win32_ComputerSystem
$computerOS = get-wmiobject Win32_OperatingSystem
$computerHDD = Get-WmiObject Win32_LogicalDisk -Filter drivetype=3
$txtObject = New-Object PSObject -property #{
'PCName' = $computerSystem.Name
'Model' = $computerSystem.Model
'SerialNumber' = $computerBIOS.SerialNumber
'HDDSize' = "{0:N2}" -f ($computerHDD.Size/1GB)
'HDDFree' = "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size)
'OS' = $computerOS.caption
'SP' = $computerOS.ServicePackMajorVersion
'User' = $computerSystem.UserName
}
$txtObject | Select PCName, Model, SerialNumber, HDDSize, HDDFree, OS, SP, User | Get-Process | Out-File 'system-info.txt' -NoTypeInformation -Append
$PSScriptRoot = current location where your script is launched, so if you specify it in the save path like Out-File "$PSScriptRoot\system-info.txt", it will be saved at the same location as the script
Get-Process can't be used at this position
NoTypeInformation does not exist as a parameter of Out-File
$computerSystem = get-wmiobject Win32_ComputerSystem
$computerOS = get-wmiobject Win32_OperatingSystem
$computerHDD = Get-WmiObject Win32_LogicalDisk -Filter drivetype=3
$txtObject = New-Object PSObject -property #{
'PCName' = $computerSystem.Name
'Model' = $computerSystem.Model
'SerialNumber' = $computerBIOS.SerialNumber
'HDDSize' = "{0:N2}" -f ($computerHDD.Size/1GB)
'HDDFree' = "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size)
'OS' = $computerOS.caption
'SP' = $computerOS.ServicePackMajorVersion
'User' = $computerSystem.UserName
}
$txtObject | Select-Object PCName, Model, SerialNumber, HDDSize, HDDFree, OS, SP, User | Out-File "$PSScriptRoot\system-info.txt" -Append

Create txt file from AD and query servers listed to get system information and export to csv

I'm trying to create a list of computer systems in AD, which I would like to save to a text file, and then using the text file retrieve the system information, i.e make, model, manufacturer, serial number etc.
Rather than try and tackle this all in one go I thought I'd do the query system information first, but the first problem is I can read contents from text file, but it only displays the information from first server then stops and secondly I've set it to (or tried to set it to) export to CSV, but it creates the CSV file but no information on the CSV file. Also at some point I'm going to need to sort headers too.
New-Item -ItemType Directory -Force -Path C:\Computer_Details
set-location C:\Computer_Details
$results = ForEach ($Computersystem in $Computer)
{
$Computer = Get-Content -path C:\computers.txt
$computerSystem = Get-CimInstance CIM_ComputerSystem
$computerBIOS = Get-CimInstance CIM_BIOSElement
$computerOS = Get-CimInstance CIM_OperatingSystem
$computerCPU = Get-CimInstance CIM_Processor
$computerHDD = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID = 'C:'"}
Write-Host "System Information for: " $computerSystem.Name -BackgroundColor DarkCyan
"Manufacturer: " + $computerSystem.Manufacturer
"Model: " + $computerSystem.Model
"Serial Number: " + $computerBIOS.SerialNumber
"CPU: " + $computerCPU.Name
"HDD Capacity: " + "{0:N2}" -f ($computerHDD.Size/1GB) + "GB"
"HDD Space: " + "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size) + " Free (" + "{0:N2}" -f ($computerHDD.FreeSpace/1GB) + "GB)"
"RAM: " + "{0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB) + "GB"
"Operating System: " + $computerOS.caption + ", Service Pack: " + $computerOS.ServicePackMajorVersion
"User logged In: " + $computerSystem.UserName
"Last Reboot: " + $computerOS.LastBootUpTime
$results | Export-Csv ComputerDetails.csv
Any help would be greatly appreciated and probably should mention I'm fairly new to PowerShell, but guessing you'll work that out reading the above :)
You need to define $Computer outside the foreach loop. In addition to that, you'd want to gather all the system information strings per computer inside the loop:
New-Item -ItemType Directory -Force -Path C:\Computer_Details
set-location C:\Computer_Details
# Get computer names from file
$Computers = Get-Content -path C:\computers.txt
# Loop over each computer name
$results = foreach($Computer in $Computers)
{
# Set up a remote session to the machine in question
try{
$ComputerSession = New-CimSession -ComputerName $Computer
# Fetch all the information from it
$computerSystem = Get-CimInstance CIM_ComputerSystem -CimSession $ComputerSession
$computerBIOS = Get-CimInstance CIM_BIOSElement -CimSession $ComputerSession
$computerOS = Get-CimInstance CIM_OperatingSystem -CimSession $ComputerSession
$computerCPU = Get-CimInstance CIM_Processor -CimSession $ComputerSession
$computerHDD = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID = 'C:'" -CimSession $ComputerSession
$Sucess = $true
} catch {
Write-Warning "Unable to fetch information from $Computer"
$Success = $false
}
if($Sucess){
# Create a new object containing all the details you want
New-Object psobject -Property #{
"ComputerName" = $Computer
"Manufacturer" = $computerSystem.Manufacturer
"Model" = $computerSystem.Model
"Serial Number" = $computerBIOS.SerialNumber
"CPU" = $computerCPU.Name
"HDD Capacity" = "{0:N2}GB" -f ($computerHDD.Size/1GB)
"HDD Space" = "{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size)
"HDD Free" = "{0:N2}GB" -f ($computerHDD.FreeSpace/1GB)
"RAM" = "{0:N2}GB" -f ($computerSystem.TotalPhysicalMemory/1GB)
"Operating System" = "{0}, Service Pack: {1}" -f $computerOS.caption,$computerOS.ServicePackMajorVersion
"User logged In" = $computerSystem.UserName
"Last Reboot" = $computerOS.LastBootUpTime
}
} else {
New-Object psobject -Property #{
"ComputerName" = $Computer
}
}
}
# $results is now an array of the objects created above, export it to a CSV file!
$results | Export-Csv ComputerDetails.csv
You should not use Write-Host as a method to produce data, it's only good for debugging purposes. Instead output a single string inside your foreach-loop as a result, this way it will be properly gathered in your results variable. Next, you want to iterate through more than a single computer, but instead you get the only file there is, c:\computers.txt and more, you don't query Get-CIMInstance against any remote computer identified by its name. To resolve: First, get the computer name out of computers.txt content, one by one, then execute a series of remote requests to CIM instances on that computer by providing -ComputerName as an argument to Get-CIMInstance. And finally, collect the output as a single string (this is preferred to simplify further parsing, if there is any) and use it as an output of your script block.
$computers=get-content -path C:\computers.txt
$results = ForEach ($Computer in $Computers)
{
try {
# remote computer might not be accessible!
$computerSystem = Get-CimInstance CIM_ComputerSystem -computername $computer
$computerBIOS = Get-CimInstance CIM_BIOSElement -computername $computer
$computerOS = Get-CimInstance CIM_OperatingSystem -computername $computer
$computerCPU = Get-CimInstance CIM_Processor -computername $computer
$computerHDD = Get-CimInstance Win32_LogicalDisk -computername $computer -Filter "DeviceID = 'C:'"}
# once you've collected the data, prepare output string
$output="System Information for: $($computerSystem.Name)`r`n"
$output+="Manufacturer: $($computerSystem.Manufacturer)`r`n"
$output+="Model: $($computerSystem.Model)`r`n"
$output+="Serial Number: $($computerBIOS.SerialNumber)`r`n"
$output+="CPU: $($computerCPU.Name)`r`n"
$output+="HDD Capacity: $("{0:N2}" -f ($computerHDD.Size/1GB)) GB`r`n"
$output+="HDD Space: $("{0:P2}" -f ($computerHDD.FreeSpace/$computerHDD.Size)) Free ($({0:N2}" -f ($computerHDD.FreeSpace/1GB)) GB)"
$output+="RAM: $({0:N2}" -f ($computerSystem.TotalPhysicalMemory/1GB)) GB`r`n"
$output+="Operating System: $($computerOS.caption), Service Pack: $($computerOS.ServicePackMajorVersion)`r`n"
$output+="User logged In: $($computerSystem.UserName)`r`n"
$output+="Last Reboot: $($computerOS.LastBootUpTime)`r`n"
} catch {
$output="$computer is not accessible!"
}
# once built, output the string into the variable
$output
}
$results | Out-File ComputerDetails.txt -Encoding UTF8
Note, I have used a "$(expression)" construction everywhere in the strings, it simplifies syntax of building a single string out of many expressions' results.

How to make script run remotelly on computers?

I have written a script that is to collect hardware and software information from a forrest/domain. I've read several posts about running a PS-script from a computer on a server, but I want to do the opposite.
How do you know that a script is "remotely accesible".
I've seen this command beeing used:
Invoke-Command -ComputerName {serverName} –ScriptBlock { commands }
Is there any other alternatives than computername? I'm thinking that this is not exclussive as several computers can have the same name..
This is my script:
try{
$ConfirmPreference="none"
$error.clear()
$erroractionpreference = "silentlycontinue"
#Gets the date of this day, used as name for XML-file later.
$a = get-date -uformat "%Y_%m_%d"
#Saves computername to compname variable(HELPER)
$compname = gc env:computername
#Gets the path to directory for all saved files and folders
$scriptpath = Split-Path -parent $myinvocation.MyCommand.Definition
#PC Serial Number, is used as name for directory containing XML files for this computer.
$serialnr = gwmi win32_bios | select -Expand serialnumber
#Creates a folder with the name of the computers hardware serialnumber if it does not exist.
if(!(Test-Path -path $scriptpath\$serialnr)) {
New-item -path $scriptpath -name $serialnr -type directory
}
#Username
$username = gc env:username
#System Info
gwmi -computer $compname Win32_ComputerSystem | ForEach {$siname = $_.Name; $simanufacturer = $_.Manufacturer; $simodel = $_.Model}
#Graphic card
$gpuname = gwmi win32_VideoController | select -Expand Name
#Processor Info
gwmi -computer $compname Win32_Processor | ForEach-Object {$cpuname = $_.Name; $cpumanufacturer = $_.Manufacturer; $cpucores = $_.NumberOfCores; $cpuaddresswidth = $_.AddressWidth}
#Memory
$totalmem = 0
$memsticks = gwmi -Class win32_physicalmemory
foreach ($stick in $memsticks) { $totalmem += $stick.capacity }
$totalmem = [String]$($totalmem / 1gb) + " GB"
#Drive
$totalspace = 0
$totalsize = gwmi -Class win32_logicaldisk
foreach($size in $totalsize) { $totalspace += $size.size }
$totalspace = "{0:N2}" -f ($totalspace/1Gb) + " GB"
#Install time for windows OS
$utctime = get-wmiobject win32_OperatingSystem | select-object -expandproperty installDate
$installtime = [System.Management.ManagementDateTimeConverter]::ToDateTime($utctime);
$installtime = Get-Date $installtime -uformat "%d/%m/%Y %X"
#--------#
#XML-form
#--------#
try{
$erroractionpreference = "stop"
$template = "<computer version='1.0'>
<hardware>
<serialnumber>$serialnr</serialnumber>
<systeminfo>
<name>$siname</name>
<manufacturer>$simanufacturer</manufacturer>
<model>$simodel</model>
</systeminfo>
<drive>
<size>$totalspace</size>
</drive>
<memory>
<size>$totalmem</size>
</memory>
<gpu>
<name>$gpuname</name>
</gpu>
<cpu>
<name>$cpuname</name>
<manufacturer>$cpumanufacturer</manufacturer>
<id>cpuid</id>
<numberofcores>$cpucores</numberofcores>
<addresswidth>$cpuaddresswidth</addresswidth>
</cpu>
</hardware>
<software>
<user>
<name>$username</name>
</user>
<osinfo>
<caption></caption>
<installdate>$installtime</installdate>
<servicepack></servicepack>
</osinfo>
</software>
</computer>"
$template | out-File -force $ScriptPath\$serialnr\$a.xml
$systemroot = [System.Environment]::SystemDirectory
$xml = New-Object xml
$xml.Load("$ScriptPath\$serialnr\$a.xml")
}catch{
}
#OSInfo, software
$newosinfo = (#($xml.computer.software.osinfo)[0])
Get-WmiObject -computer $compname Win32_OperatingSystem |
ForEach-Object {
$newosinfo = $newosinfo.clone()
[String] $bitversion = $_.osarchitecture
$newosinfo.caption = [String]$_.caption + "" + $_.osarchitecture
$newosinfo.servicepack = $_.csdversion
$xml.computer.software.AppendChild($newosinfo) > $null
}
$xml.computer.software.osinfo | where-object {$_.caption -eq ""} | foreach-object {$xml.computer.software.RemoveChild($_)}
#-------Save and get content--------
$xml.Save("$scriptpath\$serialnr\$a.xml")
#$new = Get-Content $scriptpath\$serialnr\$a.xml
#-----------------------------------
if(!$?){
"An error has occured"
}
}catch{
[system.exception]
"Error in script: system exception"
}finally{
}
For the -ComputerName parameter, you can use NETBIOS name, IP address, or fully-qualified domain name. For more details, see Invoke-Command on TechNet.
Seems like your script is "only" saving the data on the machine it is running, you will probably want it to return something in order to be useful with Invoke-Command.