When I run this:
Get-Process -name csrss -ComputerName (
Get-AdComputer -Filter {
(name -eq "gate") -or (name -eq "client")
} | Select-Object -ExpandProperty name
) | Select-Object Name,MachineName
or
Get-Process -name csrss -ComputerName gate,client | Select-Object Name,MachineName
I get selected processes from the two computers:
Name MachineName
---- -----------
csrss CLIENT
csrss GATE
csrss CLIENT
csrss GATE
But when I run this:
Get-AdComputer -Filter {(name -eq "gate") -or (name -eq "client")} |
Select-Object #{Name='computername';Expression={$_.name}} |
Get-Process -name csrss |
Select-Object name,machinename
I get this output:
Name MachineName
---- -----------
csrss GATE
csrss GATE
If i change name client to DC (it's a localhost) it shows processes only from DC. And it doesn't matter in which order I type the computer names (GATE first or DC first).
Even if I type this:
Get-AdComputer -Filter * |
Select-Object #{Name='computername';Expression={$_.name}} |
Get-Process -name csrss |
Select-Object name,machinename
I get output only from one machine (DC=localhost):
Name MachineName
---- -----------
csrss DC
csrss DC
Why does this happen? Why do I get processes only from one machine?
I can't get the logic, how Get-Process is getting ComputerName in the third case.
You are having difficulties because Get-ADComputer is returning two objects rather than one object with a two value array for computername. Here are four scenarios to illustrate the difference:
# One Object, two value property
[pscustomobject]#{computername="GATE","CLIENT"} |
Get-Process csrss
# Two Objects, one value property
[pscustomobject]#{computername="GATE"},[pscustomobject]#{computername="CLIENT"} |
Get-Process csrss
# Two Objects, one value property using a ForEach-Object
[pscustomobject]#{computername="GATE"},[pscustomobject]#{computername="CLIENT"} |
ForEach-Object { Get-Process csrss -Computername $_.Computername}
# Two Objects, one value property using a ForEach-Object with a nested pipe
[pscustomobject]#{computername="GATE"},[pscustomobject]#{computername="CLIENT"} |
ForEach-Object { $_ | Get-Process csrss}
Applying this concept to your code would look like this:
Get-AdComputer -Filter {(name -eq "gate") -or (name -eq "client")} |
Select-Object #{Name='computername';Expression={$_.name}} |
ForEachObject {
Get-Process -name csrss -Computername $_.Computername |
Select-Object name,machinename
}
Or alternatively using the pipeline inside the ForEach-Object
Get-AdComputer -Filter {(name -eq "gate") -or (name -eq "client")} |
Select-Object #{Name='computername';Expression={$_.name}} |
ForEachObject { $_ |
Get-Process -name csrss |
Select-Object name,machinename
}
Related
I am trying to get write a script where I can get all of the machine within my domains. here is what I found so far however I need to add additional information and still unable to get the correct information to get pull out. If someone can help me this will be great.
Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"' -Properties Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack,IPv4Address | Sort-Object -Property Operatingsystem | Select-Object -Property Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack, IPv4Address| ft -Wrap –Auto
I still need to be able to grab the MAC Address from all machines as well domains the machine belong to. and to make it worst I need to figure out how to export all of the data to CSV.
You will need to loop over the computers and get the MAC address individually inside the loop:
# Get-ADComputer returns these properties by default:
# DistinguishedName, GroupCategory, GroupScope, Name, ObjectClass, ObjectGUID, SamAccountName, SID
$props = 'OperatingSystem', 'OperatingSystemVersion', 'OperatingSystemServicePack', 'IPv4Address'
$result = Get-ADComputer -Filter "operatingsystem -like '*Windows server*' -and enabled -eq 'true'" -Properties $props |
Sort-Object OperatingSystem | ForEach-Object {
$mac = if ((Test-Connection -ComputerName $_.Name -Count 1 -Quiet)) {
# these alternative methods could return an array of MAC addresses
# get the MAC address using the IPv4Address of the computer
(Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='True'" -ComputerName $_.IPv4Address).MACAddress
# or use
# Invoke-Command -ComputerName $_.IPv4Address -ScriptBlock { (Get-NetAdapter | Where-Object {$_.Status -eq 'Up'}).MacAddress }
# or
# (& getmac.exe /s $_.IPv4Address /FO csv | ConvertFrom-Csv | Where-Object { $_.'Transport Name' -notmatch 'disconnected' }).'Physical Address'
}
else { $mac = 'Off-Line' }
# return the properties you want as object
$_ | Select-Object Name, OperatingSystem, OperatingSystemVersion, OperatingSystemServicePack, IPv4Address,
#{Name = 'MacAddress'; Expression = {#($mac)[0]}}
}
# output on screen
$result | Format-Table -AutoSize -Wrap
# or output to CSV file
$result | Export-Csv -Path 'X:\Wherever\ADComputers.csv' -NoTypeInformation -UseCulture
Active directory computer object doesn't contain the MAC address attribute , so you will not be able to get the info needed using active directory object only; but instead you can use the "IPv4Address" attribute of the AD computer object and query the DHCP server to find the machines MAC address and place the output data as "custompsobject" then export the result as C.V sheet.
Also if you have System center configuration manager "SCCM" you can query its database to generate a report with all needed data (Name,Operatingsystem, OperatingSystemVersion, OperatingSystemServicePack,IPv4Address and MAC address)
I am storing the following query value in a variable:
$unquotedPaths = Get-WmiObject -Class Win32_Service | Select-Object -Property Name,DisplayName,PathName,StartMode | Select-String "auto"
The problem starts when i print that variable becouse the variable takes from the query an object which is formed by hashtables like in this output:
PS C:\Users\pc> Get-WmiObject -Class Win32_Service | Select-Object -Property Name,DisplayName,PathName,StartMode | Select-String "auto"
#{Name=AGMService; DisplayName=Adobe Genuine Monitor Service; PathName="C:\Program Files (x86)\Common Files\Adobe\AdobeGCClient\AGMService.exe"; StartMode=Auto}
#{Name=AGSService; DisplayName=Adobe Genuine Software Integrity Service; PathName="C:\Program Files (x86)\Common Files\Adobe\AdobeGCClient\AGSService.exe"; StartMode=Auto}
#{Name=asComSvc; DisplayName=ASUS Com Service; PathName=C:\Program Files (x86)\ASUS\AXSP\1.01.02\atkexComSvc.exe; StartMode=Auto}
#{Name=AudioEndpointBuilder; DisplayName=Compilador de extremo de audio de Windows; PathName=C:\WINDOWS\System32\svchost.exe -k LocalSystemNetworkRestricted -p; StartMode=Auto}
How i can get and output like this:
Name DisplayName PathName Startmode
---------- ------------- ------------ ------------
ExampleName ExampleDisplayName C:\Example Auto
Select-String is meant to search and match patterns among strings and files, If you need to filter an object you can use Where-Object:
$unquotedPaths = Get-WmiObject -Class Win32_Service |
Where-Object StartMode -EQ Auto |
Select-Object -Property Name,DisplayName,PathName,StartMode
If the filtering required more complex logic you would need to change from Comparison Statement to Script Block, for example:
$unquotedPaths = Get-WmiObject -Class Win32_Service | Where-Object {
$_.StartMode -eq 'Auto' -and $_.State -eq 'Running'
} | Select-Object -Property Name,DisplayName,PathName,StartMode
This is probably a version issue, but I simply need to get the server name into the Format-Table PowerShell command.
$compArray = Get-Content C:\Users\Me\Documents\ServerList_All.txt
$Proc = foreach ($strComputer in $compArray) {
Get-WMIObject Win32_Service | Where-Object {
$_.Name -like 'SQL*' -or
$_.Name -like 'MSSQL*' -or
$_.Name -like 'OLAP*' -or
$_.Name -like 'MSDTS*' -or
$_.Name -like 'MSOLAP*' -or
$_.Name -like 'ReportServer*'
} | Sort-Object -Property Name | Format-Table $strComputer, Name, State
}
$Proc | Out-File C:\Users\ME\Documents\ServerStatus_All.txt
This works in PS v2:
| Sort-Object -Property Name | Format-Table Name, State
This does not, but does work in PS v3:
| Sort-Object -Property Name | Format-Table $strComputer, Name, State
Error:
Format-Table : Cannot convert System.Management.Automation.PSObject to one of the following types {System.String, System.Management.Automation.ScriptBlock}.
The only difference is the $strComputer variable. I am reading from a text file, and everything is beautiful in v3+.
No, I cannot upgrade to a newer PS version on the server I am running this from, sadly.
This should work for you in both...
$compArray = (Get-ADComputer -Filter *).Name
$Proc = foreach ($strComputer in $compArray) {
Get-WMIObject -Class Win32_Service -ComputerName $strComputer |
Where-Object {
$_.Name -like 'SQL*' -or
$_.Name -like 'MSSQL*' -or
$_.Name -like 'OLAP*' -or
$_.Name -like 'MSDTS*' -or
$_.Name -like 'MSOLAP*' -or
$_.Name -like 'ReportServer*'
} |
Select-Object -Property #{Name = 'Computer';Expression={$strComputer}}, Name, State |
Sort-Object -Property Name |
Format-Table -AutoSize
}
$Proc
# Results
Computer Name State
-------- ---- -----
LABSQL01 MSSQLFDLauncher Running
LABSQL01 MSSQLSERVER Running
LABSQL01 SQLBrowser Stopped
LABSQL01 SQLSERVERAGENT Running
LABSQL01 SQLTELEMETRY Running
LABSQL01 SQLWriter Running
Computer Name State
-------- ---- -----
LABWSM01 MSSQL$MICROSOFT##WID Stopped
Overall my goal is to get the VNC version for a list of remote computers along with the uninstall GUID so I can remotely uninstall VNC Viewer from certain computers. I have used the Get-WmiObject -Class Win32_Product but that is extremely slow.
I have the following script but in the results it includes the name of the select-object parameter.
$computers = Get-Content -Path "C:\Computers.txt"
$Results = #()
ForEach ($Computer in $Computers) {
$Results += New-Object PSObject -Property #{
"ComputerName" = $Computer
"Name" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object DisplayName
"DisplayVersion" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object DisplayVersion
"ModifyPath" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object ModifyPath
"Vendor" = Invoke-Command -ComputerName $Computer -ScriptBlock { Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* } `
| Where-Object -FilterScript {$_.DisplayName -like "VNC V*"} | select-object Publisher
}
}
$Results | Select-Object ComputerName,Name,DisplayVersion,ModifyPath,Vendor | Sort-Object ComputerName | Export-Csv C:\VNC.csv -notype ;
My results look like this:
ComputerName : ComputerName
Name : #{DisplayName=VNC Viewer 5.2.3}
DisplayVersion : #{DisplayVersion=5.2.3}
ModifyPath : #{ModifyPath=MsiExec.exe /I{18B1E36F-0DA3-4FDA-BC57-DD815B0DF3B2}}
Vendor : #{Publisher=RealVNC Ltd}
I would want it to look like this:
ComputerName : ComputerName
Name : VNC Viewer 5.2.3
DisplayVersion : 5.2.3
ModifyPath : MsiExec.exe /I{18B1E36F-0DA3-4FDA-BC57-DD815B0DF3B2}
Vendor : RealVNC Ltd
Is this possible or am I going about this script entirely wrong? I haven't figured out a way to run this Invoke-Command for multiple parameters and still output the results in individual columns any other way.
This script works but takes forever for 100's of computers:
if (Test-Path C:\VNCInstalled.csv) {Remove-Item C:\VNCInstalled.csv}
if (Test-Path C:\Computers.txt) {Remove-Item C:\Computers.txt}
$DirSearcher = New-Object System.DirectoryServices.DirectorySearcher([adsi]'')
$DirSearcher.Filter = '(&(objectClass=Computer)(!(cn=*esx*)) (!(cn=*slng*)) (!(cn=*dcen*)) )'
$DirSearcher.FindAll().GetEnumerator() | sort-object { $_.Properties.name } `
| ForEach-Object { $_.Properties.name }`
| Out-File -FilePath C:\Computers.txt
Get-Content -Path c:\Computers.txt `
| ForEach-Object {Get-WmiObject -Class Win32_Product -ComputerName $_} `
| Where-Object -FilterScript {$_.Name -like "VNC V*"} `
| select-object #{Name="ComputerName";Expression={$_.PSComputerName}},
Name,
#{Name="InstallLocation";Expression={$_.PackageCache}},
Vendor,
Version,
#{Name="GUID";Expression={$_.IdentifyingNumber}} `
| Sort-Object ComputerName `
| Export-CSV -path c:\VNCInstalled.csv -notype
Change all of your Select-Object commands to Select-Object
-ExpandProperty PropertyName, to discard the property name / column header.
This is the answer I gave three years ago and I think it was really a poor answer. Let me do a better job now.
Why your current code is slow
Your current code enumerates all machines from AD and puts them in a file called Computers.txt. Simple, and you do it fine.
Next up, your code performs this operation:
Get-Content -Path c:\Computers.txt |
ForEach-Object {Get-WmiObject -Class Win32_Product -ComputerName $_} `
| Where-Object -FilterScript {$_.Name -like "VNC V*"} [...]
This can be summarized as 'For each computer, request the full Win32_product table, and then after that, filter down to apps named VNC.' This is HUGELY performance impacting and for a few reasons.
Even on a fast modern computer, querying Win32_Product will take 30 seconds or more, because it returns every application installed. (on a new VM for me it took more than a minute with just a handful of apps installed!)
Querying Win32_Product also has this fun quirk which makes it take even longer, quoted from MSDN Documentation on the Win32_Product Class
Warning Win32_Product is not query optimized. Queries such as "select * from Win32_Product where (name like 'Sniffer%')" require WMI to use the MSI provider to enumerate all of the installed products and then parse the full list sequentially to handle the “where” clause. This process also initiates a consistency check of packages installed, verifying and repairing the install. With an account with only user privileges, as the user account may not have access to quite a few locations, may cause delay in application launch and an event 11708 stating an installation failure. For more information, see KB Article 794524.
So to summarize, querying Win32_Product is slow, AND it also triggers a consistency chceck on every app, AND we also have this query written to retrieve every single app before filtering. These add up to a process which probably takes ~3 minutes per pc, and will operate serially (one after the other) and take forever.
How to fix it
Software info can be retrieved reliably in two places:
If you have SCCM/ConfigMgr installed on your devices, it adds the Win32_AddRemoveProgram WMI Class you can query, which is a super fast version of Win32_Product
If not, we can always retrieve info from the registry.
Here's a short snippet to get applications like VLC installed on a computer (I don't have VNC like you, so I'm making due)
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-object DisplayName -like "VLC*" |Select-Object DisplayName, DisplayVersion, Publisher, InstallDate,UninstallString
DisplayName : VLC media player
DisplayVersion : 3.0.8
Publisher : VideoLAN
InstallDate :
UninstallString : "C:\Program Files (x86)\VideoLAN\VLC\uninstall.exe"
This operation is much faster, only 400 MS or so. Sadly we cannot get much faster using the registry as it has a very weird PowerShell provider that doesn't implement the -Filter parameter, so we do have to retrieve all programs and then filter to our desired choice.
Updating your script to use this function instead
I took the liberty of rewriting your script to use this approach, and restructured it a bit for better readability.
$results = New-object System.Collections.ArrayList
$computers = Get-Content -Path c:\Computers.txt
foreach ($computer in $computers){
#get VNC entries from remote computers registry
$VNCKeys = Invoke-Command -ComputerName $computer -ScriptBlock {
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Where-object DisplayName -like "VNC V*" |
Select-Object DisplayName, DisplayVersion, Publisher, UninstallString, #{Name=‘ComputerName‘;Expression={$computer}}
}#end of remote command
if ($VNCKeys -ne $null){
forEach($VNCKey in $VNCKeys){
[void]$results.Add($VNCKey)
}
}
}
$results | Sort-Object ComputerName | Export-CSV -path c:\VNCInstalled.csv -NoTypeInformation
I want to get the Display Name, Name, Start Mode, Start Name, and State of all Automatic services that are in a Stopped state on a Windows server. Normally, I would just do
get-wmiobject -class win32_service | ? {$_.StartMode -eq "Auto" -and $_.State -eq "Stopped"} | select DisplayName, Name, StartMode, StartName, State
However, the cmdlet above does not distinguish between a state of "Automatic" and "Automatic Delayed Start". The cmdlet I have below will not report any services that have a state of auto delayed start, but I don't know how to get it to also display the other properties I need.
(Get-WmiObject -Class Win32_Service -Filter "state = 'stopped' and startmode = 'auto'" | Select-Object -ExpandProperty name) | Where-Object {(Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services | Where-Object {$_.property -contains "DelayedAutoStart"} | Select-Object -ExpandProperty PSChildName) -notcontains $_} | Select-Object #{l='Service Name';e={$_}}
How can I modify the above cmdlet so that it will also display the other properties I want?
EDIT:
I know the method below will work, but its inefficient and non-powershell-like.
$auto_services = #((get-wmiobject -class win32_service -filter "state='stopped' and startmode='auto'" | select-object -expandproperty name) | ? {(get-childitem HKLM:\SYSTEM\CurrentControlSet\Services | ? {$_.property -contains "DelayedAutoStart"} | Select-Object -ExpandProperty PSChildName) -notcontains $_})
foreach ($service in $auto_services) { Get-WMIobject -class win32_service | ? {$_.Name -eq $service} | Select DisplayName, name, startmode, startname, state}
EDIT 2:
What would be even better is if you could list all services and the desired properties and somehow make it so that the "Automatic Delayed Start" services actually show "Auto Delayed Start" as the StartMode instead of showing just "Auto".
Using PowerShell 4.0 (with -PipelineVariable) you can do the following :
get-wmiobject -class win32_service -PipelineVariable s | ? {$_.StartMode -eq "Auto" -and $_.State -eq "Stopped"}| where {(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\services\$($_.name)" -Name 'DelayedAutoStart' -ErrorAction "silentlycontinue").DelayedAutoStart -eq 1} | % {select -InputObject $s -Property DisplayName, Name, StartMode, StartName, State}
Using previous versions you should assign the service object to a var during the pipeline.
get-wmiobject -class win32_service | % {$s=$_;$s} | ? {$_.StartMode -eq "Auto" -and $_.State -eq "Stopped"}| where {(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\services\$($_.name)" -Name 'DelayedAutoStart' -ErrorAction "silentlycontinue").DelayedAutoStart -eq 1} | % {select -InputObject $s -Property DisplayName, Name, StartMode, StartName, State}
Edited
Here is a version for PowerShell 2.0 of all stopped services that are in "Automatic" startmode and not not "Automatic Delayed Start" :
get-wmiobject -class win32_service | % {$s=$_;$s} | ? {$_.StartMode -eq "Auto" -and $_.State -eq "Stopped"}| % {$d = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\services\$($_.name)" -Name 'DelayedAutoStart' -ErrorAction "silentlycontinue").DelayedAutoStart;$_ } | where {$d -ne '1'} |% {select -InputObject $s -Property #{name="DelayedAutoStart";expression={$d}},DisplayName, Name, StartMode, StartName, State}