Only shows one computer name instead of many systems in servers.txt - powershell

I am trying to run this simple script to get the services and the accounts that are running them. My problem is that the data looks correct in the report but the server name does not change in the report. Here is the code.
$servers = get-content C:\temp\servers.txt
foreach ($server in $servers)
{
Get-WmiObject win32_service | Select-Object -Property SystemName, DisplayName, startname | format-list -GroupBy startname | Out-File c:\temp\results.txt
}

This will work.
$servers = get-content C:\temp\servers.txt
foreach ($server in $servers)
{
Get-WmiObject win32_service -Computername $server | Select-Object -Property SystemName, DisplayName, startname | format-list -GroupBy startname | Out-File -append c:\temp\results.txt
}
You can also use the following but note SystemName is not a property of Win32_Service. To troubleshoot this, try using Get-Service | Select -first 1 | Get-Member will display the available attributes (sorry if this is the wrong term) with the important one being MachineName thus replace SystemName for MachineName and you'll get the desired results. Look up Hey Scripting Guy articles for further information. For example, this page
gc .\Servers.txt | foreach {get-service -ComputerName $_ | `
Select-Object -Property MachineName, DisplayName, startname | `
format-list -GroupBy startname | Out-File -append c:\temp\results.txt}
Note, I'm using the backtick ` to make the command readable, paste into the Powershell ISE to save/run the script or just paste the lines into the console and press enter twice to run the multi-line script.

Related

issues getting output from powershell

$ErrorActionPreference = 'SilentlyContinue'
$ComputerName =Get-ADComputer -Filter {(Name -like "*")} -SearchBase "OU=AsiaPacific,OU=Sales,OU=UserAccounts,DC=FABRIKAM,DC=COM" | Select-Object -ExpandProperty Name
$results = #{}
ForEach ($computer in $ComputerName) {
$Results += Get-NetAdapter -CimSession $ComputerName | Select-Object PsComputerName, InterfaceAlias, Status, MacAddress
}
$results | Export-csv -path C\users\bret.hooker\desktop\macaddress.csv -Append
Please note the base and filter are just examples and not the actual code due to work place confidentiality. Code currently will pull from AD all computer name, then will run the ForEach command to get the NetAdapter Information. I am unable to get it to output to the CSV file however. Any advice would be great.
My recommendations are 1) don't continuously append objects to an array, 2) avoid the -Append parameter of Export-Csv, and 3) take advantage of the pipeline. Example:
$computerNames = Get-ADComputer -Filter * -SearchBase "OU=AsiaPacific,OU=Sales,OU=UserAccounts,DC=FABRIKAM,DC=COM" | Select-Object -ExpandProperty Name
$computerNames | ForEach-Object {
Get-NetAdapter -CimSession $_ | Select-Object PSComputerName,InterfaceAlias,Status,MACAddress
} | Export-Csv "C\users\bret.hooker\desktop\macaddress.csv" -NoTypeInformation

Modify Powershell script to read from a txt

Good morning,
I have the following script and it works just fine, but instead of hard coding 100 servers I want it to read the servers from a .txt file. Here is my code any help would be great:
[string[]]$servers = 'Server1,Server2,Server3' -split ','
$credential = Get-Credential
Invoke-Command -ComputerName $servers -Credential $credential -ScriptBlock {
Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate
} | Format-Table PSComputerName, DisplayName, DisplayVersion, Publisher, InstallDate | Format-Table –AutoSize | Out-File C:\Temp\SoftwareListByServer.txt
$servers = get-content servers.txt
Where servers.txt contains (only) a list of servers one per line.

Remove column name from PowerShell select-object results

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

Query Help - wmiobject win32_product filter items from text file

I've been working on a simple script to read the win32_product off a remote PC, which is working fine. However, I would like the query to ignore some common applications on my domain. I've been building a list of apps and their IdentifyingNumber and putting the IdentifyingNumber into a txt file. I load the text file into a variable with the script and I'm trying to figure out how to get the query to filter each item in the variable...so I have this::
$PC = Read-Host "What is target workstation..."
$logfile = "d:\$PC.txt"
$ignore = [IO.File]::ReadAllText("D:\INCOMING\AppListing\ignore.txt")
get-wmiobject -class win32_product -computer $PC | where {$_.IdentifyingNumber -notlike $ignore} | Select Name, IdentifyingNumber | sort-object Name | export-csv $logfile -encoding "unicode"
However, this is not filtering at all, not even the first or last item from the txt file. I used write-host $ignore to verify it is loading the items...but I am at a lost as to how to make this work. Perhaps a foreach loop? I can't find anything about putting a foreach loop into a where filter though...
Thanks for the assistance...
If the file is like this:
aRandomId
anotherRandonId
...
with one id on each line and nothing else, then try this using -notlike with wildcards on the ends. Ex:
$PC = Read-Host "What is target workstation..."
$logfile = "d:\$PC.txt"
$ignore = [IO.File]::ReadAllText("D:\INCOMING\AppListing\ignore.txt")
get-wmiobject -class win32_product -computer $PC | where { $ignore -notlike "*$($_.identifyingnumber)*" } |
Select Name, IdentifyingNumber | sort-object Name | export-csv $logfile -encoding "unicode"
You could also read your file as an array using ReadAllLines like you would have had to do if you wanted to use a foreach-loop or -notcontains. Ex:
$PC = Read-Host "What is target workstation..."
$logfile = "d:\$PC.txt"
$ignore = [IO.File]::ReadAllLines("D:\INCOMING\AppListing\ignore.txt")
get-wmiobject -class win32_product -computer $PC | where { $ignore -notcontains $_.identifyingnumber } |
Select Name, IdentifyingNumber | sort-object Name | export-csv $logfile -encoding "unicode"
$prods = Compare-Object -ReferenceObject (Get-Content $file) -DifferenceObject ((Get-WmiObject -Class Win32_Product -Computer $computer).IdentifyingNumber) -PassThru
Compare-Object is a great Cmdlet.

Get-Service on multiple remote machines

I can only get the command to return the services on the first computer in the text file.
Is there a better way than for-each for this task?
Get-Service *vault* -ComputerName (Get-Content c:\users\sean\desktop\js.txt) | select name,status,machinename | sort machinename | format-table -autosize
Try it without the get-content. Try this:
Get-Service *vault* -ComputerName c:\users\sean\desktop\js.txt | select name,status,machinename | sort machinename | format-table -autosize
If that doesn't work, then try:
$Computers = Get-Content c:\users\sean\desktop\js.txt
Get-Service *vault* -computername $Computers | Select name,status,machinename |sort machinename |format-table -autosize
If you are eager for a one-liner then try this:
Get-Content c:\users\sean\desktop\js.txt | Get-Service *vault* | Select name,status,machinename |sort machinename |format-table -autosize
I would try the top one first. I would test, but I don't have access to anything I can do a proper test right now.
$Computers = get-content .\desktop\test.txt
$Service = "Vault"
foreach ($computer in $computers) {
$computer
$Servicestatus = get-service -name $Service -ComputerName $computer
}
$Servicestatus | select-object Name,Status,MachineName | format-table -Autosize
This works for me, it gives me each of the computers in the text file, and it looks for the service.
This is what I use. I get the list of computers from an OU in AD.
Import-Module ActiveDirectory
$ou = "OU=Servers,DC=Domain,DC=com"
$servers = Get-ADComputer -Filter * -SearchBase $ou | select-object -expandproperty name
Foreach ($server in $servers){
$Data = Get-Service -ServiceName *IIS*,*TomCat*,*httpd* -ComputerName $server | select machinename,name | sort machinename | format-table -AutoSize
Write($Data) | Out-File .\WebServices.txt -Append
}
$servers = Get-Content .\servers.txt
Foreach ($server in $servers) {
"$server"
Get-Service -ComputerName $Server -name -like "*vault*"
"-------------------"
}
Following a memory limitation limit with older versions of PowerShell, I was required to refresh my code:
Old code:
gwmi win32_service -computer $allcomputers | Select-Object __SERVER,Name,state,startmode,StartName
New code:
`$servers = Get-Content "computers.txt"
Foreach ($server in $servers) {
Get-WmiObject -Class WIN32_service -ComputerName $server |
Select-Object __SERVER,Name,state,startmode,StartName |
Export-Csv -path "Report.CSV" -NoTypeInformation -Append
}`
This is how you can get list of all services in your AD domain:
Get-ADComputer -Filter {OperatingSystem -Like “Windows 10*”} | ForEach-Object {Get-WmiObject -Class Win32_Service -Computer $_.Name}
More useful examples on this (get list of services for all computer listed in a text file, etc.):
https://www.action1.com/kb/list_of_services_on_remote_computer.html
Get-Service -ComputerName ... has a bug in PowerShell 2.0 that only returns the first computer. This is fixed in newer versions so if you upgrade to PowerShell 3.0 or newer, your original code will work fine.
As a workaround, use a foreach-loop to run Get-Service once for each computer:
Get-Content c:\users\sean\desktop\js.txt |
ForEach-Object { Get-Service -Name *vault* -ComputerName $_ } |
Select-Object -Property Name, Status, MachineName |
Sort-Object -Property MachineName |
Format-Table -AutoSize
Nick's solution totally doesn't work for me. I ended up writing a quick and dirty one that works:
$servers = Get-Content .\servers.txt
Foreach ($server in $servers) {
"$server"
Get-Service *vault*
"-------------------"
}