Using Get-Content inside Get-Service - server

I have a list of servers (server.txt), and I want to get the service status of "Nimsoft Robot Watcher" of all the servers.
I tried the code:
$text1 = Get-Content server.txt
foreach ($text1 in Get-Content server.txt)
{
$text=$text1
}
Get-Service -ComputerName $text -DisplayName "Nimsoft Robot Watcher"
But only the status for the first server gets displayed.

The code you currently have will replace the value of $text with each iteration, leaving you with just the last name after the loop completes.
Either put Get-Service inside the loop
foreach ($text in Get-Content server.txt) {
Get-Service -ComputerName $text -DisplayName "Nimsoft Robot Watcher"
}
or collect the hosts in an array and pass that to Get-Service
$text = Get-Content server.txt
Get-Service -ComputerName $text -DisplayName "Nimsoft Robot Watcher"

Related

It returns all services even though there is one in list

I want my script to take the server and service as a list from a .txt file. After that I want my script to check that if this service exists on the servers on the txt file.
But when I run this script, it returns all of the services as it exists on the server and not the ones I specified in service list. Also it doesn't drop to catch even tho the service doesn't exist.
Can you tell me why it returns all of the services?
$ErrorActionPreference='stop'
$ServerList = Get-Content 'C:\Users\username\Desktop\service test\servers.txt'
$ServiceList = Get-Content 'C:\Users\username\Desktop\service test\services.txt'
try{
foreach($Server in $ServerList){
foreach($Service in $ServiceList){
$Result = Invoke-Command -ComputerName $Server -ScriptBlock {
Get-Service -Name $Service
}
foreach($List in $Result){
Write-Host "$List exists on $Server"
}
}
}
}
catch [System.Management.Automation.ActionPreferenceStopException]
{
Write-Host "Error"
}
Continuing from my comment. . . when using a local variable against a remote computer in a scriptblock, you have to pass it as an argument, or have it referenced using the remote variable of $using.
This is due to a new session being ran on the remote machine with no clue what $service is on that machine as it's never been declared on that side.
You can pass it using the -Arguments, parameter. Creating a param block to pass it onto or, using the remote variable of $using.
Also, there's really no need for you to invoke the command over to the remote machine as Get-Service has a -ComputerName parameter for remote machines:
$ServerList = Get-Content 'C:\Users\username\Desktop\service test\servers.txt'
$ServiceList = Get-Content 'C:\Users\username\Desktop\service test\services.txt'
foreach ($Server in $ServerList)
{
foreach ($Service in $ServiceList.trim())
{
[PSCustomObject]#{
ComputerName = $Server
Service = $Service
Exists = if (Get-Service -Name $service -ComputerName $Server -ErrorAction SilentlyContinue) { $true } else { $false }
}
}
}
as for what you tried:
$Result = Invoke-Command -ComputerName $Server -ScriptBlock {
Get-Service -Name $Service
}
$service (as mentioned above), is empty and not defined on the remote sessions scope.
When you switched it to $using:service it worked but, it returned the type of object and not the name itself, because you're returning the entirety of an object and not the name property. Instead, just reference the current $service that is being used in the loop and declare if it's there, or not.
the script is not structured correctly, an no need to us the invoke command, when you can use 'get-service -computername' Also the try, catch statement would only catch the last error not each.
I changed your original script to reflect this and moved the try, catch statement to catch each error (if the service does not exist).
$ErrorActionPreference='stop'
$ServerList = Get-Content 'C:\temp\servicetest\servers.txt'
$ServiceList = Get-Content 'C:\temp\servicetest\services.txt'
ForEach($Server in $ServerList){
#Get-Service -ComputerName $Server -Name 'XblAuthManager'
ForEach($Service in $ServiceList){
try {
$a = Get-Service -ComputerName $Server -Name $Service
IF ($a) {
Write-Host "Service - $Service exists on Server - $Server"
}
} catch {
Write-Host "Service - $Service does not exist on Server - $Server"
}
}
}

I'm trying to run a powershell command to uninstall a WinPcap, but after trying trying different methods I'm still not succesfull?

Below is the script that I'm running that always gives either a Program error or a The term 'x86' is not recognized as the name of a cmdlet error. The service stops but the Invoke-Command seems to be the problem.
$Servers = Get-Content "C:\DTServerScript\Servers.txt"
ForEach ($Server in $Servers) {
$Status = ""
do
{
#Stopping Service
$ServiceAgent = Get-Service -ComputerName $Server | Where {$_.Name -like "*oneagent*"}
Write-Host "Pending Stop on $Server"
Stop-Service $ServiceAgent
sleep 1
$Status = $ServiceAgent.Status
} until ($Status -eq "Stopped")
Write-Host "Service state is $Status on $Server"
# Execute config change
# Invoking Commands on Server
Invoke-Command -ComputerName $Server {cmd.exe/ c:\Program Files (x86)\WinPcap\uninstall.exe /S }
write-host "Service is starting on $Server"
# Starting Service
Start-Service $ServiceAgent
$ServiceAgent = Get-Service -ComputerName $Server | Where {$_.Name -like "*oneagent*"}
$Status = $ServiceAgent.Status
}
Try quoting the path and using braces around the name containing ()..
Invoke-Command -ComputerName $Server { & $Env:ComSpec /C "${Env:ProgramFiles(x86)}\WinPcap\uninstall.exe" /S }

How can I speed up this Powershell Loop

I am very new at Powershell, but I recently decided to try and recreate an old tool we sometimes use but as a Powershell script. The script takes in user input to get an IP Range then pings, gets hostname, checks if service exists and is running, and then exports it to a CSV file. I haven't done too much cleaning up of the code yet. It does what I want but when I was testing it on larger IP ranges it was taking several minutes ie. 1-254 took around 10 minutes. Any tips would be appreciated.
Add-Content -Path ".\$fullrange.csv" -Value '"Ping","IP","Hostname","Service State","Startup","Version"'
ForEach($IP in $range) {
$status = test-connection -computername ($IP) -count 1 -Quiet -ErrorAction SilentlyContinue
if ($status) {
$hostname = [System.Net.Dns]::GetHostbyAddress("$IP")."HostName"
$service = Get-WmiObject -Class Win32_Service -ComputerName $IP -ErrorAction SilentlyContinue | Where-Object Name -like "SysaidAgent"
if ($service) {
$xml = [xml](Get-Content "\\$IP\C$\Program Files\SysAid\Configuration\AgentConfigurationFile.xml")
$ver = $xml.SelectSingleNode("//AgentVersion")."#text"
}
else{
$service = "Not Found"
$ver = "N/A"
}
Add-Content -Path .\$fullrange.csv -Value "$status,$IP,$hostname,$service.state,$service.startmode,$ver"
}
else{
Add-Content -Path .\$fullrange.csv -Value "$status,$IP"
}
}

Export output from invoke command to a csv

I have a simple script below to give me the local admin group membership for all my servers which I have in a text file.
I get the desired result with no issues in the console but I am looking for a way to have this exported to a CSV file somehow.
Any ideas?
$server = Get-Content 'C:\powershell\Scripts\GetLocalAdmin\APAC+Sri
Lanka\servers.txt'
foreach($item in $servers){
write-host "`n"
$servername = Write-Host "Checking $($item.toupper())" -ForegroundColor
Green
write-host "`n"
$invoke = invoke-command {net localgroup Administrators} -comp $item
}
You can use the Export-CSV cmdlet. Note that you either have to create an array to store every return value from your Invoke-Command using $invokes += ... or just use this:
Get-Content 'C:\powershell\Scripts\GetLocalAdmin\APAC+SriLanka\servers.txt' |
ForEach-Object {
invoke-command {net localgroup Administrators} -comp $_ |
Export-Csv 'your_csv_path.csv'
}

start-job Run command in parallel and output result as they arrive

I am trying to get specific KBXXXXXX existence on a list of servers , but once my script one server it takes time and return result and come back and then move to next one . this script works perfectly fine for me .
I want my script to kick off and get-hotfix as job and other process just to collect the results and display them.
$servers = gc .\list.txt
foreach ($server in $servers)
{
$isPatched = (Get-HotFix -ComputerName $server | where HotFixID -eq 'KBxxxxxxx') -ne $null
If ($isPatched)
{
write-host $server + "Exist">> .\patchlist.txt}
Else
{
Write-host $server +"Missing"
$server >> C:\output.txt
}
}
The objective it to make the list execute faster rather than running serially.
With Powershell V2 you can use jobs as in #Andy answer or also in further detail in this link Can Powershell Run Commands in Parallel?
With PowerShell V2 you may also want to check out this script http://gallery.technet.microsoft.com/scriptcenter/Foreach-Parallel-Parallel-a8f3d22b using runspaces
With PowerShell V3 you have the foreach -parallel option.
for example (NB Measure-Command is just there for timing so you could make a comparison)
Workflow Test-My-WF {
param([string[]]$servers)
foreach -parallel ($server in $servers) {
$isPatched = (Get-HotFix -ComputerName $server | where {$_.HotFixID -eq 'KB9s82018'}) -ne $null
If ($isPatched)
{
$server | Out-File -FilePath "c:\temp\_patchlist.txt" -Append
}
Else
{
$server | Out-File -FilePath "c:\temp\_output.txt" -Append
}
}
}
Measure-Command -Expression { Test-My-WF $servers }
For this use PowerShell jobs.
cmdlets:
Get-Job
Receive-Job
Remove-Job
Start-Job
Stop-Job
Wait-Job
Here's an untested example:
$check_hotfix = {
param ($server)
$is_patched = (Get-HotFix -ID 'KBxxxxxxx' -ComputerName $server) -ne $null
if ($is_patched) {
Write-Output ($server + " Exist")
} else {
Write-Output ($server + " Missing")
}
}
foreach ($server in $servers) {
Start-Job -ScriptBlock $check_hotfix -ArgumentList $server | Out-Null
}
Get-Job | Wait-Job | Receive-Job | Set-Content patchlist.txt
Rather than use jobs, use the ability to query multiple computer that's built into the cmdlet. Many of Microsoft's cmdlets, especially those used for system management, take an array of strings as the input for a -Computername parameter. Pass in your list of servers, and the cmdlet will query all of them. Most of the cmdlets that have this ability will query the servers in series, but Invoke-Command will do it in parallel.
I haven't tested this as I don't have Windows booted at the moment, but this should get you started (in sequence).
$servers = gc .\list.txt
$patchedServers = Get-HotFix -ComputerName $servers | where HotFixID -eq 'KBxxxxxxx'|select machinename
$unpatchedServers = compare-object -referenceobject $patchedServers -differenceobject $servers -PassThru
$unpatchedServers |out-file c:\missing.txt;
$patchedServers|out-file c:\patched.txt;
In parallel:
$servers = gc .\list.txt
$patchedServers = invoke-command -computername $servers -scriptblock {Get-HotFix | where HotFixID -eq 'KBxxxxxxx'}|select -expandproperty pscomputername |sort -unique
As before, I don't have the right version of Windows available at the moment to test the above & check the output but it's a starting point.