We need to capture services installed on our Windows servers other than default services

Our Windows servers are provisioned with a set of default services. Application teams deploy applications on these servers. We need to capture these application services for the purpose of monitoring.

Basically, I need to use Powershell to compare the services running on our servers, compare this list with the list of our default services and output the difference in a .CSV file named after the host. For example, if the application server is named “xyz” then the output file should also be named "xyz.csv

I have not produced any code yet. I have tried but it has proven extremely difficult.

Thank you for helping out.

Kind Regards,
Victor.

Victor,

you’re not even asking any question? What help would expect?

I have tried” means you wrote code. :man_shrugging:t3:

For the vast majority of the tasks an administrator has to get done you’re not the very first one. There are very likely already code examples out there what either already fits your particular needs or can be adapted easily.

So why don’t you start searching the internet using your preferred internet search engine to find something you can start with. If you get stuck you’re welcome to come here, post your code, along with the explanation what’s not working as expected and along with the error messages you might get and we would be pleased to help you further.

BTW: Getting a list of running services from a given computer is one of the easiest tasks you can accomplish with PowerShell. Very often it is used as an example in beginner tutorials or books teaching basic PowerShell.

I know how to get services running on Windows machines using the get-service commandlet. My problem is comparing these with services running on another computer, detecting and outputting the difference.

I have searched for a solution, but what I found assumed that the number of rows (services) would be identical, only the data would be different. What I need is the opposite.

Hi Olaf,

I will keep looking… If I find something close, I’ll come back to the forum.

Kind Regards,
Victor.

And why don’t you explain that in your initial question?

In PowerShell we work with objects and their properties. Now, if you want to compare objects you can use the cmdlet

Most of us comming here not asking for help are eager to help others because it is fun for us and it is very satisfying. :wink: So we only expect a very tiny amount of effort from the questioneer to show that we won’t just be used. So just show your efforts and post the code snippets you have so far even if it’s not doing what you want yet and describe what you need. I’d bet it will not take long for someone to either point you to the right direction or even deliver what you need. :man_shrugging:t3: :man_shrugging:t3: :man_shrugging:t3:

1 Like

I don’t think you even need Compare-Object, just provide the names of your baseline list of services to the -Exclude parameter:

I have only one machine on at the moment, so this is a basic example.
You could populate $defaultServices dynamically from one of your standard servers, or from a text file.

I have used Invoke-Command as this will work with both Windows PowerShell and PowerShell Core.

# Get all services, excluding two well-known services for testing

$defaultServices = Get-Service -Exclude 'Spooler','wuauserv' | 
    Select-Object -ExpandProperty Name

# Check servers for non-default services

$serverList = 'localhost'

foreach ($Server in $ServerList) {
    Invoke-Command -ComputerName $Server { Get-Service -Exclude $Using:defaultServices } | 
        Export-Csv -Path "E:\Temp\Files\$Server.csv" -NoTypeInformation
}

Contents of CSV:

PS E:\Temp> Import-Csv E:\Temp\Files\localhost.csv | Select Name,DisplayName,Status

Name     DisplayName    Status
----     -----------    ------
Spooler  Print Spooler  Running
wuauserv Windows Update Running
1 Like

Very nice idea. :+1:t3: :love_you_gesture:t3: :star_struck:

Thank you Matt.

I had in the meantime came up with an approach after doing some web search for ideas:

On the reference server, I ran

Get-Service | export-csv -Path C:\xyz\xxx.csv

For testing, I copied xxx.csv to a target server that we need to check the difference in installed services - regardless of their status. I ran the following code on the target server:

Get-Service | export-csv -Path C:\xyz\TTT.csv
$reference = import -csv C:\xyz\xxx.csv
$Target = import -csv C:\xyz\xxx.csv
Compare-Object $reference $Target - Property Name | Where {$_.SideIndicator -eq “=>”} | Export-Csv C:\xyz\difference.csv NoTypeInformation

I recieved the output below in "difference.csv
Name SideIndicator
DHCP Server =>

The above result is correct.

We have a network share that contains the list of services installed on our servers. Each in its own .CSV file. The assistance I need now is to loop the “Compare-Object” against each of this .csv files. We have more than 2000 of them. Otherwise, I would have to do this manually.

Thank you.

I’d expect you know how to do a loop … so my question would be … what kind of assistance exactly do you need?

So you have more than 2000 servers to check or how do I understand this? How do distinguish the CSV files from each other or how to you match a CSV file with the according server?

AND PLEASE Format your code as code - DO NOT FORMAT IT AS QUOTE!

When you post code, sample data, console output or error messages please format it as code using the preformatted text button ( </> ). Simply place your cursor on an empty line, click the button and paste your code.

Thanks in advance

How to format code in PowerShell.org 1 <---- Click :point_up_2:t4: :wink:

( !! Sometimes the preformatted text button hides behind the settings gear symbol. :wink: )

Thank you Olaf!

I need to determine differences between the services running on a reference server and the rest of our windows servers.
I finally came up with the script below. I simply want it to output the differences into an .CSV file with a filename as the difference server names. The services running on our servers are already saved to a .CSV file bearing their hostnames. I simply want to reuse these host names for ease of identification. The Export-CSV file will be saved to a different directory, so there will be no conflict. This process works manually, just having issues creating to script that does the same thing!

$beevehOIIMS13 = import-csv "C:\Temp\KAIZEN\Non-default services detection\beevehOIIMS13.csv" # This is to load the services running on our reference server into beevehooims13 variable

#The $AllServerServices below contains all the installed services on each of our servers
$AllServerServices = Get-Childitem -Path C:\Temp\KAIZEN\Server_services | 

Foreach-Object
{$Server = Get-Content
Compare-Object $beevehOIIMS13 $Server -Property Name | Where{$_.SideIndicator -eq "=>"} Export-CSV C:\Temp\KAIZEN\Server_services\Differences\$_.Name.csv}

I am getting this after running the script:

Thank you.

Your code is incomplete and badly formatted. I’d recommend to use VSCode for developing PowerShell scripts. It will give you a lot of help and lets you avoid beginner failures. :wink:

Something like this may be a strating point:

$OutputPath = 'C:\Temp\Output'
$Timestamp = Get-Date

$ReferenceServiceList = 
Import-Csv -Path 'C:\Temp\KAIZEN\Non-default services detection\beevehOIIMS13.csv'

foreach ($ComputerName in $ServerList) {
    $RemoteServicesList =
    Invoke-Command -ComputerName $ComputerName -ScriptBlock { Get-Service }

    $ReportFile = Join-Path -Path $OutputPath -ChildPath ($ComputerName + '_' + $Timestamp.ToString('yyyyMMdd_HHmmss') + '.csv')
    
    Compare-Object -ReferenceObject $ReferenceServiceList -DifferenceObject $RemoteServicesList -Property Name |
    Where-Object -Property SideIndicator -EQ -Value '=>' |
    Export-Csv -Path $ReportFile -NoTypeInformation
}

Of course you need to fill in the variable $ServerList and $OutputPath

Thank you Olaf. I feel so uncomfortable coming back. You have given your time and expertise to a complete struggling stranger working in a hostile environment. I don’t have much choice at the moment.

Olaf, I already have the list of services running on each of our servers in .CSV format. These are saved on a network share. Each file is named “services_%servername%.csv”, so I don’t have to connect to servers to get their installed services. These are already available in a network share. This is what I have to work with.

I hope you now better understand my need. This is what I wanted to address in my script. I need to compare each entry with the reference list and save the result in %servername.csv".

Below is the fields of the services for each server.

Thank you.

Victor.

One of the problems you’re going to have with a comparison, the way you’re currently doing it, is that there will always be a difference because your CSV file contains the system name which will never be the same as the reference system. You need to make the comparison on just the service name.
Edit: just noticed you are specifying the name property :man_facepalming: . Anyway, here’s a solution not using Compare-Object.

Example with Sample Data

# Example server CSV - replace with Import-Csv
$server = @"
Domain,SystemName,Name,DisplayName,StartMode,StartName,State
example.com,System01,DHCP,DHCP Service,Disabled,DHCP,Stopped
example.com,System01,DNS,DNS Service,Disabled,DNS,Stopped
example.com,System01,Spooler,Print Spooler,Automatic,Spooler,Started
example.com,System01,CustomApp,My Custom App,Automatic,Custom,Started
"@

$serverData = $server | ConvertFrom-CSV

# Example reference CSV - replace with Import-Csv
$reference = @"
Domain,SystemName,Name,DisplayName,StartMode,StartName,State
example.com,RefSystem,DHCP,DHCP Service,Disabled,DHCP,Stopped
example.com,RefSystem,DNS,DNS Service,Disabled,DNS,Stopped
example.com,RefSystem,Spooler,Print Spooler,Automatic,Spooler,Started
"@

$referenceData = $reference | ConvertFrom-CSV

foreach ($row in $serverData) {
    if ($row.Name -notin $referenceData.Name) {
        $row | Export-CSV -Path "E:\Temp\$($row.SystemName).csv" -Append
    }
}

Output

PS E:\Temp> Import-Csv .\System01.csv | Format-Table

Domain      SystemName Name      DisplayName   StartMode StartName State
------      ---------- ----      -----------   --------- --------- -----
example.com System01   CustomApp My Custom App Automatic Custom    Started

Your actual code would look something like this:

$referenceData = Import-CSV -Path 'C:\Temp\KAIZEN\Non-default services detection\beevehOIIMS13.csv'

$fileList = Get-ChildItem -Path 'C:\Temp\KAIZEN\Server_service'

foreach ($file in $fileList) {

    $csv = Import-Csv -Path $file.FullName

    foreach ($row in $csv) {
        if ($row.Name -notin $referenceData.Name) {
            $row | Export-CSV -Path "C:\Temp\$($row.SystemName).csv" -Append
        }
    }
    
}
1 Like

Dear Matt,

From my heart, I truly thank you. I don’t pray for a back to the wall situation but should it happen, I pray that you will find help. A truly satisfying help.

Your solution works!

@Olaf, please accept my appreciation for your support. I understand the encouragement your gave to me to dig deeper. I understand that… I am not sure I dug deep enough but I tried.

Thank you both.

Blessings

1 Like

Good day,

I ran the script yesterday, there were no errors and the .CSV files had the right names. I simply assumed everything was OK.

I took a look at the files yesterday and noticed that they contain more information than the differences to services installed on the reference server. All I need are the differences between $referenceData & $file in the “C:\Temp$($row.SystemName).csv”. in your code. Everything is perfect!

Thank you.

Victor.

Hi. Can you explain the problem more clearly? I’m not sure what (if anything) is wrong, as you finish by saying ‘Everything is perfect!’

As it currently stands, the entire row in $file will be sent to the output file if the value in the Name column for that row does not appear in the Name column in $referenceData.

If you only want some elements of that row, then you should pipe it to Select-Object before the export.

1 Like

Hi Matt,

The second paragraph describes what’s supposed to happen but that’s not the case. For example, the screenshot below shows the difference between Reference server & Host ATVIEMSICOGO1:
image

The next screenshot is that from your script for the same server:

In the first, there only 15 rows, but in the second there are 80 rows. I could only capture 26 in the screenshot.

It is observed that the first 15 rows are identical between the 2 screenshots. These are the real differences. Your script repeated the sequence from row 29, not visible in the second screenshot. It does this for all the output files - It generates the differences first then added some more rows and after which it repeats the sequence.

By “perfect” I meant everything else the script does is OK.

Thank you,
Victor.

Did you use my exact code, or did you modify it at all?

If it’s modified, please can you provide the modified script as I cannot replicate the duplicate lines when using my code. It sounds like something is misplaced in one of the loops.

This is the exact code below: The only change I made was in the $FileList declaration. I added a ‘s’ to the path to read “services”. This is to reflect the correct path. You think that’s the reason?

I am sorry Matt for making you work more on my behalf.

Thank you,
Victor.

$referenceData = Import-CSV -Path 'C:\Temp\KAIZEN\Non-default services detection\beevehOIIMS13.csv'

$fileList = Get-ChildItem -Path 'C:\Temp\KAIZEN\Server_services'

foreach ($file in $fileList) {

    $csv = Import-Csv -Path $file.FullName

    foreach ($row in $csv) {
        if ($row.Name -notin $referenceData.Name) {
            $row | Export-CSV -Path "C:\Temp\KAIZEN\Matt\$($row.SystemName).csv" -Append
        }
    }
    
}

When I removed the ‘s’ from $FileList file path, I received: