Getting service status on multiple servers

Hi All,

I’m new to PowerShell…

I want to check periodically for service status on several servers for specific services.
additionally, all these services not exists on all servers. for example, one server can have one or more services from desired list of services.

Not so sure what i’m doing wrong in below function but i’m getting unexpected results as
server2 does not having IISAdmin service on it but result show so…

can someone please advice what am I missing here? Thanks.


Function Check-Service ()
{
$arrServerStatus = @()
$arrAllSiteServers = ‘Server1’, ‘Server2’
$Servicestat= Get-Service -name ‘IISAdmin’, ‘BITS’
Foreach ($siteServer in $arrAllSiteServers)
{
foreach ($service in $Servicestat)
{
$srv = New-Object psobject
Add-Member -InputObject $srv -MemberType NoteProperty -Name SiteSystem -Value $siteServer
Add-Member -InputObject $srv -MemberType NoteProperty -Name ServiceName -Value $service.Name
Add-Member -InputObject $srv -MemberType NoteProperty -Name ServiceDisplayName -Value $service.DisplayName
Add-Member -InputObject $srv -MemberType NoteProperty -Name ServiceStatus -Value $service.Status
$arrServerStatus += $srv
}
}

     Return, $arrServerStatus
}

Output

SiteSystem ServiceName ServiceDisplayName ServiceStatus


Server1 BITS Background Intel… Running
Server1 IISAdmin IIS Admin Service Running
Server2 BITS Background Intel… Running
Server2 IISAdmin IIS Admin Service Running

There are a lot of ways to do things, especially in scripting. For instance, when you search for a service that doesn’t exist, Get-Service produces a exception:

PS C:\Windows\System32\WindowsPowerShell\v1.0> Get-Service -Name ServiceDoesntExist
Get-Service : Cannot find any service with service name 'ServiceDoesntExist'.
At line:1 char:1
+ Get-Service -Name ServiceDoesntExist
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (ServiceDoesntExist:String) [Get-Service], ServiceCommandException
    + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand

In order to trap an exception, you would do something like this:

try {
    Get-Service -Name WSearch, IISAdmin -ErrorAction Stop
}
catch {
    $_.Exception.Message
}

This would give you the error message that was displayed, which you could parse to get the name of the service and create a custom message.

Cannot find any service with service name 'IISAdmin'.

The pro to this is that you are only searching for certain services, but the parsing can be a pain. As you are a beginner, take a look at this approach:

$ComputerName = "Computer1", "Computer2"
foreach ($computer in $ComputerName) {
    #Array of services to check
    $servicesToCheck = "WSearch", "IISAdmin"
    #Get all services on the computer
    $services = Get-Service -ComputerName $computer | Select Name, Status
    #Below we are selecting individual objects, so we want to return the results
    #to a variable with all of the results
    $results = foreach ($service in $servicesToCheck) {
        #Query all services to get the service status
        $checkService = $services | Where{ $_.Name -eq $service }
        #if the computer is not null
        if ($checkService) {
            #Select creates a PSObject, so this should be choice 1 when creating objects
            #The last property is a calculated property, since Get-Service does not contain PSComputerName, we
            #create a custom property. An expression is code, so you can even call other commands like 
            #Get-ADComputer -ComputerName $computer | Select -ExpandProperty Description
            #to put the computer's AD description here
            $checkService | Select Name, Status, @{Name="ComputerName";Expression={$computer}}
        }
        else {
            #Since the checkservice is null because the service doesn't exist, you need to generate a custom
            #object. You can do the below, use [pscustomobject] or Add-Member.  You've used the Add-Member, I
            #think you'll see how much cleaner and easier to read the other methods are for create objects
            $props = @{ComputerName=$computer;
                       Name=$service;
                       Status="Not Installed"}
            New-Object -TypeName PSObject -Property $props
        }
    }
}

$results

This can easily converted into a function, but it’s important to understand the logic and methods used. The con of this approach is that we are getting all of the services and then searching against those results, but most systems will have a couple hundred services and there shouldn’t be a huge performance loss unless you are running this against thousands of servers. You’ll see references to filter as far left as you can, which means you want to only return what you want in the initial query. Now, let’s look at this method using WMI:

Get-WMIObject -ComputerName "Computer1", "Computer2" -Class Win32_Service -Filter "Name='ZeroConfigService' or Name='IISAdmin'" | Select PSComputerName, Name, State

WMI will only return the service if it’s installed, we are only getting the services we want (Not all services and then trying to see the service exists) and Select is creating the PSObject with all of the information you need. As I said, multiple ways to do things, but hopefully you will understand some of the logic and ways to get service information.

There are multiple ways to solve this problem, I took what you had and made it into a function that could accept parameters. You can pass the function the parameters instead of hard coding the computer names and services. If you only have one purpose for it then hard coding the computer names and services would be ok, I just try to make all the functions that I create reusable. I also just return the raw data from services and let the formatting system display the service name, status, and the PSComputername. Invoke-Command adds the PSComputername property which is nice for something like this. You could easily change this to output a PSObject as well if you wanted to.

I used a simple approach to stop the displays of errors that would show if the computer didn’t have that service name by changing the ErrorActionPreference of the cmdlet to SilentlyContinue, this suppresses the output and only s
hows the status of services that exists. The downside to this approach is it does pollute your $Error variable which is probably not a problem if you are just using powershelgl interactively.

Im am fairly new to PowerShell as well so my way might not be the best way but its what I came up with. Hope it helps

function Check-Service
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true)]
        [String[]]
        $Service,
        [Parameter(Mandatory=$true)]
        [String[]]
        $ComputerName
    )

    Invoke-Command -ComputerName $ComputerName -ScriptBlock {Get-Service -Name $args -ErrorAction SilentlyContinue} -ArgumentList $Service
}

call the function like this. This requires that PowerShell remoting is turned on.

Check-Service -Service IISAdmin, BITS -ComputerName Server1, Server2

Thank you very much Rob for detailed and informative explanation…!

will check your suggestion and let you know results

Thanks.