If you’ve ever administered clients running Symantec Endpoint Protection 11 (SEP), you know how woefully inadequate the SEP management interface is for determining the health of your clients. If a computer get’s a corrupt policy, chances are the Symantec Management Client (SMC) service will stop and refuse to restart. When this happens, the component of SEP responsible for updating definitions and reporting out of date definitions stops working. The real-time scanning component – the Symantec Endpoint Protection service – will continue to run, but it will never update. Since the service that tells the SEP Manager that a client is out of date doesn’t work, chances are you will never know about it unless one (or more) of your end users happens to report it.
So, in order to find the broken SEP clients in my company – about 14,000 computers in various locations around the country – I turned to Powershell and the .NET Framework to do the heavy lifting. The results were both very surprising and disconcerting. Hundreds of our computers were out of date and not reporting this to the SEP Manager. This information allowed us to update our computers using a combination of some automation and the good ol’ fashioned Sneaker Net.
This script assumes a few things:
You must have admin privileges on the computers you are auditing. File and Print Sharing is enabled on the computers. You have a pretty solid list of computer names to run the Powershell script against (I used an SCCM list of all discovered Active Directory computer objects).
################################################################# # # # - Antivirus Health Check Script # - Audit computer objects from a text file specified at # runtime. Checks for AV version, stopped/running # services, and definition dates. # # - Written by Jim Melton # ** Feel free to use and modify this script as needed. # Just please give credit if you do. # ################################################################# ## Setup command line parameter to allow user to specify content ## file at runtime. Parameter is -FileName param ([string]$FileName = $(throw "Text Input is Required")) Write-Host $FileName ## Load assembly used to interrogate services on the remote machine. [System.Reflection.Assembly]::LoadWithPartialName('system.serviceprocess') $erroractionpreference = "Continue" ## Set variables $SMCsvc = "Symantec Management Client" $SMCstatus = "" $SEPsvc = "Symantec Endpoint Protection" $SAVsvc = "Symantec Antivirus" $SAV8svc = "SymantecAntivirus Client" $Path = "c$\Program Files\Common Files\Symantec Shared\VirusDefs" $SavePath = "\\SERVER\SHARE\" ## Instantiate Excel ComObject and write headings on spreadsheet. $excel = New-Object -comobject Excel.Application $excel.visible = $true $workbook = $excel.Workbooks.Add() $worksheet = $workbook.Worksheets.Item(1) $worksheet.Cells.Item(1,1) = "Machine Name" $worksheet.Cells.Item(1,2) = "IP Address" $worksheet.Cells.Item(1,3) = "AV Version" $worksheet.Cells.Item(1,4) = "Virus Definition" $worksheet.Cells.Item(1,5) = "Status" $range = $worksheet.UsedRange $range.Interior.ColorIndex = 19 $range.Font.ColorIndex = 11 $range.Font.Bold = $True $intRow = 2 ## Create Ping object $ping = New-Object System.Net.NetworkInformation.Ping ## Get data from user specified file $colComputers = get-content $FileName foreach ($computer in $colComputers){ ## Zero variables at the top of the loop and ping the next machin ## in the list. $IPaddress = "" $AVservice ="" [string]$service = "" $VirDate = "" $reply = $ping.send($computer) $IPaddress = [string]$reply.Address ## Check ping reply. If it's a success, test the Symantec Shared files path to see if ## it exists. If the path is valid, get the content of definfo.dat and parse the ## definition date and rev. If ($reply.status -eq 'Success'){ $ValidPath = Test-Path "\\$computer\$path" If ($ValidPath -eq "True"){ $DefInfo = Get-Content "\\$computer\$Path\definfo.dat" $DefLine = $DefInfo[1] $DefYear = $DefLine.substring(8,4) $DefMonth = $DefLine.substring(12,2) $DefDay = $DefLine.substring(14,2) $Revision = $DefLine.substring(17,3) [int]$DefDate = $DefLine.Substring(8,8) [string]$DefDate = "$DefMonth" "-" "$DefDay" "-" "$DefYear" $VirDate = [datetime]$DefDate } ## If the path or definfo.dat doesn't exist, bail out of the if statement. Else{ $VirDate = $null Continue } ## Query the remotemachine for the SEP, SAV, or SAV 8x RTVScan services. Also, ## query the SMC service for SEP. These are separate in order to make sure ## that both the SEP and SMC services are running on a machine. $services = [System.ServiceProcess.ServiceController]::GetServices($computer) | ` where { (($_.displayname -eq $SEPsvc) -or ($_.displayname -eq $SAVsvc) ` -or ($_.displayname -eq $SAV8svc))} $SMCservice = [System.ServiceProcess.ServiceController]::GetServices($computer) | ` where { ($_.displayname -eq $SMCsvc)} ## Check to see what services are present. If there are no services on the machine, write that ## into the spreadsheet and continue on to the next computer. foreach ($service in $services){ If ($service.displayname -eq $null){ $worksheet.Cells.Item($intRow,1) = $computer $worksheet.Cells.Item($intRow,2) = $IPaddress $worksheet.Cells.Item($intRow,5) = "Services are not present on $computer." $intRow = $intRow 1 } ## Otherwise, if it's SAV or SAV 8, insert SAV into $AVService. Else{ If (($service.DisplayName -eq $SAVsvc) -or ($service.DisplayName -eq $SAV8svc)){ $AVservice = "Symantec Antivirus" } ## If not Null or SAV, then it must be SEP. Insert SEP into $AVService. Also check to see if ## the SMC service is stopped. If so, set $SMCstatus to Stopped. Else { If ($service.DisplayName -eq $SEPsvc){ $AVservice = "Symantec Endpoint Protection" If ($SMCservice.DisplayName -eq $SMCsvc){ If ([string]$SMCservice.status -eq 'Stopped'){ $SMCstatus = "Stopped" } } } } ## If computer is online and has AV installed, insert its information into the spreadsheet. ## If the SMC service is stopped, append that info to the Status block showing the SEP ## RTVScan status. Otherwise,just insert the status of RTVScan. $worksheet.Cells.Item($intRow,1) =$computer $worksheet.Cells.Item($intRow,2) = $IPaddress $worksheet.Cells.Item($intRow,3) = $AVservice $worksheet.Cells.Item($intRow,4) = "$DefDate" " rev " "$Revision" If ($SMCstatus -eq "Stopped"){ $worksheet.Cells.Item($intRow,5) = [string]$service.Status ", ` however, the SMC Service is stopped." } Else{ $worksheet.Cells.Item($intRow,5) = [string]$service.Status } } ## Increment the spreadsheet row and reset $SMCstatus to blank. $intRow = $intRow 1 $SMCstatus = "" } } ## If the machine doesn't respond to a ping, insert that in the spreadsheet. Else{ If ($reply.status -eq "TimedOut"){ $worksheet.Cells.Item($intRow,1) = $computer $worksheet.Cells.Item($intRow,3) = $IPaddress $worksheet.Cells.Item($intRow,5) = "Did not respond to a ping." $intRow = $intRow 1 } ## If the machine doesn't respond to a ping and the IP is not valid, the computer name ## must not bevalid any longer. Else{ $IPaddress = "Computer name isnot valid." $worksheet.Cells.Item($intRow,1) = $computer $worksheet.Cells.Item($intRow,3) = $IPaddress $worksheet.Cells.Item($intRow,5) = "Did not respond to a ping." $intRow = $intRow 1 } } ## Once the data is completely written to the spreadsheet, auto-fit all columns to the data width. $range.EntireColumn.AutoFit() | Out-Null } ## Switch statement using a regular expression switch to determine location being audited based on the ## input file filename. If you need an Adhoc report, use computers.txt as the input file. Switch -regex ($FileName){ "loc01comps.txt" {$Location = "Location1"} "loc02comps.txt" {$Location = "Location2"} "loc03comps.txt" {$Location = "Location3"} "loc04comps.txt" {$Location = "Location4"} "loc05comps.txt" {$Location = "Location5"} "loc06comps.txt" {$Location = "Location6"} "loc07comps.txt" {$Location = "Location7"} "loc08comps.txt" {$Location = "Location8"} "loc09comps.txt" {$Location = "Location9"} "loc10comps.txt"{$Location = "Location10"} "loc11comps.txt" {$Location = "Location11"} "computers.txt" {$Location = "Adhoc Report"} default {$Location = "Other"} } ## Save the spreadsheet to the path in the variable using the name determined from the above Switch. $SpreadSheetName = $SavePath+$Location+".xls" $workbook.Worksheets.item(1).Name = $Location ## Check to see if the spreadsheet exists. If it does, delete it and recreate it, and save it. ## Attempt to close Excel and end the ComObject. This doesn't seem to work well with Excel 2K7, ## so we are killing processes called Excel. This will close all Excel processes. If(Test-Path $SpreadSheetName){ Remove-Item $SpreadSheetName Write-host $SpreadSheetName $excel.ActiveWorkbook.SaveAs($SpreadSheetName) $excel.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) Remove-Variable excel Get-Process -Name Excel |Kill } Else{ $excel.ActiveWorkbook.SaveAs($SpreadSheetName) $excel.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) Remove-Variable excel Get-Process -Name Excel | Kill }