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
Related
My overall goal is to allow the user to have a gui pop-up which lets them select any txt file of choice and to use that file they selected to run the code below the function. As of right now, in a txt file that contains 50 targets, it only runs through the first PC name and completes the code.
What can I do to fix the issue?
function Get-Content($initialDirectory) {
[void] [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
if ($initialDirectory) { $OpenFileDialog.initialDirectory = $initialDirectory }
$OpenFileDialog.filter = 'All files (*.*)|*.*'
[void] $OpenFileDialog.ShowDialog()
return $OpenFileDialog.FileName
}
($FilePermissions = Get-Content C:\)
###########################################################################
write-host "Please wait while gathering information..."
$counter = 0
foreach ($computernames in $FilePermissions)
{
Write-host "Processing $computernames ($counter/$($FilePermissions.count))"
IF (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computernames -Quiet)
{
$Computersystem = Get-WMIObject Win32_ComputerSystem -ComputerName $computernames -AsJob
$videocontroller = Get-WmiObject win32_videocontroller -ComputerName $computernames -AsJob
$nvidiacontroller = Get-WmiObject win32_videocontroller -ComputerName $computernames -AsJob
$bioscontroller1 = Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $computernames
$bioscontroller2 = Get-CimInstance -ClassName Win32_BIOS -ComputerName $computernames
$userlogon = Get-CimInstance -ClassName Win32_ComputerSystem -Property UserName -ComputerName $computernames
Wait-Job -Job $Computersystem,$videocontroller -Timeout 18 | out-Null
$computersystem_output = Receive-Job $Computersystem
$videocontroller_output = Receive-Job $videocontroller | ? {$_.name -ilike "*Intel*"}
$nvidiavideocontroller_output = Receive-Job $nvidiacontroller | ? {$_.name -ilike "*NVIDIA*"}
# Creating spreadsheet headers
$newrow = [Pscustomobject] #{
Host_name = $computersystem_output.name
Model_Name = $bioscontroller1.Model
Serial_Number = $bioscontroller2.SerialNumber
BIOS_Version = $bioscontroller2.SMBIOSBIOSVersion
Last_User_Logon = $userlogon.UserName
Intel_Card = $videocontroller_output.name
IntelGraphics_Version = $videocontroller_output.DriverVersion
Nvidia_Card = $nvidiavideocontroller_output.name
NvidiaGraphics_Version = $nvidiavideocontroller_output.DriverVersion
}
$newrow | export-csv -path c:\HostnameData.csv -Append -NoTypeInformation
Remove-Job -job $Computersystem,$videocontroller,$nvidiacontroller -Force
$counter++
}
Else
{
write-Host "The remote computer " $computernames " is Offline"
}
}
My Powershell Script below wants to retrieve multiple registry values, from a set of Windows Services (200+), from multiple machines (up to 8 machines). Since I'm retrieving quite a lot of info, I decided to use New-PSSession.
But whenever it gets into the scriptblock of the foreach loop, it executes it on the incorrect machine and thus eventually errors out saying
The Error:
Cannot find path 'HKLM:\system\currentcontrolset\services\SomeServiceName' because it does not exist.
+ CategoryInfo : ObjectNotFound: (HKLM:\system\cu...ces\SomeServiceName:String) [Get-ItemProperty], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemPropertyCommand
+ PSComputerName : WEB002
The Powershell Script:
$MyCustomServiceArray = #()
$c1 = 0
$s = New-PSSession -ComputerName WEB001, WEB002
$MyCustomServices = Invoke-Command -session $s -scriptblock {Get-WmiObject -Class win32_service |
Where-Object -FilterScript {$_.Displayname -like "*Special Service for Me*"} |
Select PSComputerName, Name, DisplayName, State, StartMode, StartName, PathName |
Sort DisplayName}
foreach($MyCustomService in $MyCustomServices)
{
$MyCustomServiceName = $MyCustomService.Name
$RegistryPathForService = "hklm:\system\currentcontrolset\services\$MyCustomServiceName"
$c1 += 1
Write-Host "$c1. $MyCustomServiceName`n"
$StartUpParameter = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).startupparameter}
if ($StartUpParameter -eq $null) {$StartUpParameter = ""}
$DatabaseServerName = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).servername}
if ($DatabaseServerName -eq $null) {$DatabaseServerName = ""}
$DatabaseName = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).database}
if ($DatabaseName -eq $null) {$DatabaseName = ""}
$NetType = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).nettype}
if ($NetType -eq $null) {$NetType = ""}
$ObjectCacheSize = Invoke-Command -session $s -scriptblock {(Get-ItemProperty $using:RegistryPathForService).objectcache}
if ($ObjectCacheSize -eq $null) {$ObjectCacheSize = 0}
$Row = "" | Select HostName,ServiceName,StartUpParameter,ServiceDisplayName,Status,StartUpType, `
DatabaseServerName,DatabaseName,NetType,ObjectCacheSize, `
LogOnName,PathName
$Row.HostName = $MyCustomService.PSComputerName
$Row.ServiceName = $MyCustomService.Name
$Row.StartUpParameter = $StartUpParameter
$Row.ServiceDisplayName = $MyCustomService.DisplayName
$Row.Status = $MyCustomService.State
$Row.StartUpType = $MyCustomService.StartMode
$Row.DatabaseServerName = $DatabaseServerName
$Row.DatabaseName = $DatabaseName
$Row.NetType = $NetType
$Row.ObjectCacheSize = $ObjectCacheSize
$Row.LogOnName = $MyCustomService.StartName
$Row.PathName = $MyCustomService.PathName
$MyCustomServiceArray += $Row
}
$MyCustomServiceArray | Export-Csv -NoTypeInformation -Path "C:\Users\GaryTheBrave\Documents\AllMyCustomServices.csv"
Is there a way to loop through these Multiple Machines in the correct order?
The Solution was, as Lee_Daily hinted, Looping through sessions. Thanks Lee_Daily!!!
Here is the modified Powershell Script that works perfectly
$MyCustomServiceArray = #()
$c1 = 0
$sessions = New-PSSession -ComputerName WEB001, WEB002
foreach($session in $sessions)
{
$MyCustomServices = Invoke-Command -session $session -scriptblock {Get-WmiObject -Class win32_service |
Where-Object -FilterScript {$_.Displayname -like "*Special Service for Me*"} |
Select PSComputerName, Name, DisplayName, State, StartMode, StartName, PathName |
Sort DisplayName}
foreach($MyCustomService in $MyCustomServices)
{
$MyCustomServiceName = $MyCustomService.Name
$RegistryPathForService = "hklm:\system\currentcontrolset\services\$MyCustomServiceName"
$c1 += 1
Write-Host "$c1. $MyCustomServiceName`n"
$StartUpParameter = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).startupparameter}
if ($StartUpParameter -eq $null) {$StartUpParameter = ""}
$DatabaseServerName = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).servername}
if ($DatabaseServerName -eq $null) {$DatabaseServerName = ""}
$DatabaseName = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).database}
if ($DatabaseName -eq $null) {$DatabaseName = ""}
$NetType = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).nettype}
if ($NetType -eq $null) {$NetType = ""}
$ObjectCacheSize = Invoke-Command -session $session -scriptblock {(Get-ItemProperty $using:RegistryPathForService).objectcache}
if ($ObjectCacheSize -eq $null) {$ObjectCacheSize = 0}
$Row = "" | Select HostName,ServiceName,StartUpParameter,ServiceDisplayName,Status,StartUpType, `
DatabaseServerName,DatabaseName,NetType,ObjectCacheSize, `
LogOnName,PathName
$Row.HostName = $MyCustomService.PSComputerName
$Row.ServiceName = $MyCustomService.Name
$Row.StartUpParameter = $StartUpParameter
$Row.ServiceDisplayName = $MyCustomService.DisplayName
$Row.Status = $MyCustomService.State
$Row.StartUpType = $MyCustomService.StartMode
$Row.DatabaseServerName = $DatabaseServerName
$Row.DatabaseName = $DatabaseName
$Row.NetType = $NetType
$Row.ObjectCacheSize = $ObjectCacheSize
$Row.LogOnName = $MyCustomService.StartName
$Row.PathName = $MyCustomService.PathName
$MyCustomServiceArray += $Row
}
}
$MyCustomServiceArray | Export-Csv -NoTypeInformation -Path "C:\Users\GaryTheBrave\Documents\AllMyCustomServices.csv"
I got this to work locally however on a remote system it doesn't show the path but only the drive letter. My goal is to get it to show the drive path of the remote host.
Also sometimes it doesn't show all the drive that are mapped to the remote computer and I don't know why.
I have tried changing Win32_LogicalDisk to MappedLogicalDisk but it just results to no information.
$DISK = Get-WmiObject -computer $compname Win32_LogicalDisk
foreach ($device in $DISK){
Write-Host "Drive: " $device.name
Write-Host "Path: " $device.ProviderName
""
}
Pause
CheckHost
Try one of these examples:
This one...
$ComputerName = "ServerName"
gwmi win32_mappedlogicaldisk -ComputerName $ComputerName |
select SystemName, Name, ProviderName, SessionID |
foreach {
$disk = $_
$user = gwmi Win32_LoggedOnUser -ComputerName $ComputerName |
where { ($_.Dependent.split("=")[-1] -replace '"') -eq $disk.SessionID} |
foreach {$_.Antecedent.split("=")[-1] -replace '"'}
$disk | select Name, ProviderName, #{n = "MappedTo"; e = {$user} }
}
Or this one
function Get-MappedDrives($ComputerName)
{
$output = #()
if(Test-Connection -ComputerName $ComputerName -Count 1 -Quiet)
{
$Hive = [long]$HIVE_HKU = 2147483651
$sessions = Get-WmiObject -ComputerName $ComputerName -Class win32_process |
?{$_.name -eq "explorer.exe"}
if($sessions)
{
foreach($explorer in $sessions)
{
$sid = ($explorer.GetOwnerSid()).sid
$owner = $explorer.GetOwner()
$RegProv = get-WmiObject -List -Namespace "root\default" -ComputerName $ComputerName |
Where-Object {$_.Name -eq "StdRegProv"}
$DriveList = $RegProv.EnumKey($Hive, "$($sid)\Network")
if($DriveList.sNames.count -gt 0)
{
foreach($drive in $DriveList.sNames)
{
$output += "$($drive)`t$(($RegProv.GetStringValue($Hive, "$($sid)\Network\$($drive)",
"RemotePath")).sValue)`t$($owner.Domain)`t$($owner.user)`t$($ComputerName)"
}
}
else{write-debug "No mapped drives on $($ComputerName)"}
}
}
else{write-debug "explorer.exe not running on $($ComputerName)"}
}
else{write-debug "Can't connect to $($ComputerName)"}
return $output
}
<#
#Enable if you want to see the write-debug messages
$DebugPreference = "Continue"
$list = "Server01", "Server02"
$report = $(foreach($ComputerName in $list){Get-MappedDrives $ComputerName}) |
ConvertFrom-Csv -Delimiter `t -Header Drive, Path, Domain, User, Computer
#>
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
I'm looking for a way to to have a choice of a list or a single computername in a foreach loop.
If the user enters in a single computername I want the script to execute for that one computername
but if that user wants to use a path to a list of computers how could I replace $computername with the path that user wants?
function Get-OSInfo {
[CmdletBinding()]
param (
#[Parameter(ValueFromPipeline=$True,
# ValueFromPipelineByPropertyName=$True)]
[string]$computername,
[string]$errorlog = 'c:\errors.txt',
[switch]$logerrors
)
PROCESS {
foreach ($computer in $computername) {
Try {
$os = Get-WmiObject -EA Stop –Class Win32_OperatingSystem –ComputerName $computer
$cs = Get-WmiObject -EA Stop –Class Win32_ComputerSystem –ComputerName $computer
$bios = Get-WmiObject -EA Stop –Class Win32_BIOS –ComputerName $computer
$cpu = Get-WmiObject -EA Stop -class Win32_processor -ComputerName $computer
$props = #{'ComputerName'=$computer;
'OSVersion'=$os.version;
'SPVersion'=$os.servicepackmajorversion;
'OSBuild'=$os.buildnumber;
'OSArchitecture'=$os.osarchitecture;
'Manufacturer'=$cs.manufacturer;
'Model'=$cs.model;
'BIOSSerial'=$bios.serialnumber
'CPU Count'=$CPU.Count
'Memory'= [Math]::round(($cs.TotalPhysicalMemory/1gb),2)
'CPU Speed'= $CPU.MaxClockSpeed[0]}
$obj = New-Object -TypeName PSOBject -Property $props
$obj.PSObject.TypeNames.Insert(0,'Get-OS.OSInfo')
#Write-Output $obj
$obj | Export-Csv c:\test4.csv -Append
} Catch {
if ($logerrors) {
$computer | Out-File $errorlog -append
}
Write-Warning "$computer failed"
}
}
}
}
Change the type of the $ComputerName parameter to a string array instead of just a single string:
param(
[string[]]$ComputerName,
[string]$errorlog = 'c:\errors.txt',
[switch]$logerrors
)
Notice the [] after the type name, this denotes an array of strings, rather than a single string.
Now you can do:
PS C:\> $computers = Get-Content C:\computers.txt
PS C:\> Get-OSInfo -ComputerName $computers
If you'd like to be able to specify a path to a file containing the target computers as the argument to the function, you can use multiple parameter sets:
[CmdletBinding(DefaultParameterSetName='ByName')]
param(
[Parameter(ParameterSetName='ByName',ValueFromPipeline)]
[string[]]$ComputerName,
[Parameter(ParameterSetName='ByFile')]
[string]$InputFile
)
begin {
if($PSCmdlet.ParameterSetName -eq 'ByFile'){
try{
$ComputerName = Get-Content -LiteralPath $InputFile
}
catch{
throw
return
}
}
}
process {
foreach($Computer in $ComputerName){
# Work with $Computer here...
}
}