1
votes

Basically each day I get a report of servers with a list of services not running. the report will read like ServerName, Services. I want to pull this report in from a csv file and then run the script to see the status of each service listed for the server.

So, If I get a report like Server1, ADobe not running Server1, SQL not running Server2, SQL not running

I am most of the way there but, the loops are running get-service on servers that don't have the service listed. I am trying to find a way to remove the service from the array after each iteration of the loop but, I am finding it hard to do with powershell. There also might just be a better way of doing it all together.

What I have so far is

$servers = Import-Csv -Path 'C:\Users\zada\Documents\New Text Document.csv' | % {$_.Servername}
$services = Import-Csv -Path 'C:\Users\zada\Documents\New Text Document.csv' | % {$_.Servicename}

foreach ($server in $servers) {
    Write-Host ***************** getting services list for $server *******************
    foreach ($service in $services)   {
        Get-Service -ComputerName $server -Name $services | Format-Table -Property Status, Name, DisplayName -AutoSize

    }

}

The problem is it runs through each server as many times as there are services listed and not all the servers have the same services. I toyed around with running running $service.remove($services) but, that didn't get me anywhere.

Below is an example of how the csv file looks.

Servername  Servicename
server1 SQL Server Agent (SQLEXPRESS)
server1 WSUS
server2 Sophos System Protection Service
server3 WindowsRM
2

2 Answers

0
votes

At first glance you are over complicating this.

The problem is it runs through each server as many times as there are services listed and not all the servers have the same services.

This is because of how you built your loop structure of course. Let Import-CSV do its job and then iterate over each row individually.

Import-Csv -Path 'C:\Users\zada\Documents\New Text Document.csv' | Foreach-Object{
    Get-Service -ComputerName $_.ServerName -Name $_.ServiceName 
} | select MachineName, Status, Name, DisplayName

However that would be considered inefficient since you are querying the same server several times needlessly. What we should do is group all the servers together and then query them once. ...

Import-Csv -Path 'C:\Users\zada\Documents\New Text Document.csv' | Group-Object ServerName | ForEach-Object{
    # The name of the group we are in is the ServerName
    $serverName = $_.Name
    # Create an array of all services we are checking on this host
    $services = $_.Group.ServiceName

    Get-Service -ComputerName $serverName -Name $services
} | select MachineName, Status, Name, DisplayName

So we group all of the rows with the same server name and query for all services in the CSV associated to that server in one cmdlet call.

-1
votes

Here's an example where you don't need to know the services beforehand:

$Services = Get-Service -ComputerName $Servers |
              Where-Object { $_.Name -eq 'stopped' } |
              ForEach-Object { <# ... #> }

But this will put all the stopped services into an array, not particular to which server they were stopped on. I'm not entirely certain of what you want to accomplish without an example dataset.


Update based on feedback:

$CSV = Import-Csv -Path 'C:\Users\zada\Documents\New Text Document.csv'

[HashTable]$Services = @{}

For ($i = 0; $i -lt $CSV.Count; $i++)
{
    $Services[$CSV[$i].Servers] += ,$CSV[$i].ServiceName
}

This will generate your hashtable so you can do what you want:

ForEach ($Server in $Services.Keys) {
    Write-Host "Getting services for $Server"
    Get-Service -ComputerName $Server -Include $Services.$Server |
      Format-Table -Property Status,Name,DisplayName -Autosize
}