Syncro inconsistently showing whether or not Bitdefender is installed

So when I go to asset view for a client, and I have the “Bitdefender installed” column showing it says “true” for an asset
Snip - Assets Abuzz Technologies - Tech Support - Google Chrome

But when I click on the asset it shows:
MANAGED ANTIVIRUS

Bitdefender installed

AVG Antivirus installed (not managed)

Bitdefender install failed: Competitor product detected. Please remove conflicting product and reboot machine to continue. – 8 months ago

When I go into bd gravity zone it does not have the computer listed at all?

just from trial and error, I’m pretty sure the “Bitdefender Installed” column is just showing if it is enabled on the policy, and likely if Syncro is billing you for it. There is unfortunately no good way built into Syncro to see if BD actually successfully installed.

We are monitoring for the presence of 4 or 5 processes that BD runs on every machine it installs on to find the assets that do not have an active install. Unfortunately, most of these processes get killed when BD pushes an update, and either fail to restart until the next reboot, or Syncro never checks for new PIDs after the first boot. We haven’t gotten a chance to really dig into it, but it seems like it’s something along those lines where from time to time an asset will show missing those processes, and if we check the asset and Syncro shows BD as installed healthy we can generally clear the alert and it doesn’t come back.

Syncro could do a better job of making it easy to find devices with BD installs, but also BD just does a “:man_shrugging:” and quits if it sees residue from a competitor’s product during install.

You can do a saved asset search for installed application = bitdefender. Much more reliable that the built in detection unfortunately. But installed apps list only updates with 6 hour sync, so there is lag. I found and update a bitdefender monitor script you guys might find helpful:

<# Version 1.0.6
.DESCRIPTION
Test the health of the Bitdefender Endpoint Security Tools.
 
This function will check Bitdefender
* is installed
* is running
* is service healthy
* is up to date
* aggregate health of above checks
* signature version
* signature number
* signature published date
* signature update time
 
.EXAMPLE
IsBitdefenderInstalled : True
IsBitdefenderProcessRunning : True
IsBitdefenderServiceHealthy : True
IsBitdefenderUptodate : True
IsBitdefenderHealthy : True
SignatureVersion : 7.72508
SignatureNumber : 9697199
SignatureDate : 7/26/2017 8:03:50 AM
SignatureUpdateTime : 7/26/2017 10:54:59 AM
 
.NOTES
Created by: Jason Wasser @wasserja https://www.powershellgallery.com/packages/BitdefenderHealth/
Last updated by creator: 7/26/2017 v1.0.5
Modified by Isaac Good: 5/3/2021 v1.0.6
    - removed remote computer connection code/references and bundle all functions into one script for use via RMM
    - removed non-working component monitoring (Product.ActionCenter.conf no longer exists)
    - fix errors caused by multiple files returned using wildcards
    - expanded service status to check all services not just one
    - added delay if recent kernel power event
    - added alerting for Syncro
#>

Import-Module $env:SyncroModule -DisableNameChecking

# Check for a recent kernel power event and if found, give BD time to update
$lastkp = Get-EventLog -LogName system -Source Microsoft-Windows-Kernel-Power -Newest 1 | Select -ExpandProperty TimeGenerated
$recent = (New-Timespan -Start $lastkp -End (Get-Date)).TotalMinutes -le 15
if ($recent) {
    Start-Sleep -Seconds 300
}

function Convert-UnixTimeToDateTime {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [int]$UnixTime
    ) 
    $UnixConvertedDateTime = (New-Object DateTime(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc)).AddSeconds($UnixTime)
    Write-Verbose "Unix converted date time: $UnixConvertedDateTime"
    $UnixConvertedDateTime
}

function Convert-TimeZone {
    [CmdletBinding()]
    param (
        [datetime]$inputDateTime = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'),
        [string]$fromTimeZone = "E. Europe Standard Time",
        [string]$toTimeZone = [System.TimeZoneInfo]::Local.Id
    )
    
    begin {
        function Test-TimeZone {
            param (
                $TimeZone
            )
            $ValidTimeZones = [System.TimeZoneInfo]::GetSystemTimeZones()
            Write-Verbose -Message "Validating time zone $TimeZone"
            # $IsValidTimeZone = $ValidTimeZones.id -contains $TimeZone
            $IsValidTimeZone = ($ValidTimeZones | Select-Object -ExpandProperty Id) -contains $TimeZone
            Write-Verbose -Message "Timezone $TimeZone is $IsValidTimeZone"
            $IsValidTimeZone
        }
    }
    
    process {
        # Validate time zones
        if ((Test-TimeZone -TimeZone $fromTimeZone) -and (Test-TimeZone -TimeZone $toTimeZone)) {
            Write-Verbose -Message "Timezones have been validated."
            Write-Verbose -Message "Converting $inputDateTime from $fromTimeZone to $toTimeZone"
            $outputDateTime = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId(
                $inputDateTime, $fromTimeZone, $toTimeZone)

            Write-Output $outputDateTime
        }
        else {
            Write-Error "Invalid time zone entered."
        }
    }
}

function Test-BitdefenderProcess {
    param (
        [string]$BitdefenderProcessName
    )

    try {
        $BitdefenderProcess = Get-Process -Name $BitdefenderProcessName -ErrorAction Stop
        if ($BitdefenderProcess) {
            $IsBitdefenderProcessRunning = $true
            $IsBitdefenderProcessRunning
        }        
    }
    catch [Microsoft.PowerShell.Commands.ProcessCommandException] {
        Write-Warning "Unable to find process $BitdefenderProcessName"
        $IsBitdefenderProcessRunning = $false
        $IsBitdefenderProcessRunning
    }
    catch {
        Write-Warning $_.Exception.Message
        $IsBitdefenderProcessRunning = $false
        $IsBitdefenderProcessRunning
    }
}

function Test-BitdefenderInstallationPath {
    param (
        [string]$BitdefenderInstallationPath
    )

    $IsBitdefenderInstalled = Test-Path -Path $BitdefenderInstallationPath
    if (!($IsBitdefenderInstalled)) {
        Write-Warning "Unable to find $BitdefenderInstallationPath"
    }
    $IsBitdefenderInstalled
}

function Test-BitdefenderService {
    [CmdletBinding()]
    param (
        [string]$ServiceName
    )

    try {
        $Service = Get-Service -Name $ServiceName -ErrorAction Stop
        if ($Service) {
            $ServiceDetails = Get-WmiObject -Class win32_service -Filter "Name = '$ServiceName'"
            $ServiceHealthProperties = @{
                Name          = $Service.Name
                ServiceExists = $true
                IsRunning     = [bool]($Service.Status -eq 'Running')
                IsAutomatic   = [bool]($ServiceDetails.StartMode -eq 'Auto')
            }
            $ServiceHealth = New-Object -TypeName PSCustomObject -Property $ServiceHealthProperties
            $ServiceHealth
        }
    }
    catch {
        $ServiceHealthProperties = @{
            Name          = $ServiceName
            ServiceExists = $false
            IsRunning     = $false
            IsAutomatic   = $false
        }
        $ServiceHealth = New-Object -TypeName PSCustomObject -Property $ServiceHealthProperties
        $ServiceHealth
    }
}

function Get-BitdefenderSignatureDate {
    [CmdletBinding()]
    param (
        $TimeZone = [System.TimeZoneInfo]::Local.Id
    )

    process {
        $BitdefenderThreatScannerPath = 'C:\Program Files\Bitdefender\Endpoint Security\ThreatScanner'
        try {
            $BitdefenderUpdatePath = Get-Item -Path "$BitdefenderThreatScannerPath\Antivirus*\versions.id*" | Sort-Object LastWriteTime | Select-Object -last 1
            [xml]$BitdefenderVersionsFile = Get-Content -Path $BitdefenderUpdatePath
        
            # Bitdefender headquarters is in Eastern European Time Zone which also has daylight saving time.
            $BitdefenderSignatureDate = Convert-TimeZone -inputDateTime ([datetime]$BitdefenderVersionsFile.info.time.'#text') -fromTimeZone 'E. Europe Standard Time' -toTimeZone $TimeZone
            $BitdefenderSignatureDate
        }
        catch {
            Write-Warning -Message "Unable to determine the signature date."
            $BitdefenderSignatureDate = $null
            $BitdefenderSignatureDate
        }
    }
}

function Get-BitdefenderUpdateFileData {
    [CmdletBinding()]
    param (
        [ValidateScript({Test-Path -Path $_})]
        [string]$BitdefenderThreatScannerPath = 'C:\Program Files\Bitdefender\Endpoint Security\ThreatScanner'
    )
    
    process {
        $BitdefenderUpdatePath = Get-Item -Path "$BitdefenderThreatScannerPath\Antivirus*\Plugins\Update.txt"  | Sort-Object LastWriteTime | Select-Object -last 1
        $UpdateFile = Get-Content -Path $BitdefenderUpdatePath
        $BitdefenderUpdateFileDataProperties = ''
        $BitdefenderUpdateFileDataProperties = @{}
        foreach ($Line in $UpdateFile) {
            $BitdefenderUpdateFileDataProperties.Add(($Line.Split(':')[0]).Trim(), ($Line.Substring($Line.IndexOf(':') + 1)).Trim()) 
        }
        
        # Changing Update time to [datetime]
        #$BitdefenderUpdateFileDataProperties['Update time'] = ([datetime]::ParseExact($BitdefenderUpdateFileDataProperties['Update time'],'ddd MMM dd HH:mm:ss yyyy',$null)).ToLocalTime()
        $BitdefenderUpdateFileDataProperties['Update time'] = (Convert-UnixTimeToDateTime -UnixTime $BitdefenderUpdateFileDataProperties['Update time GMT'])
        $BitdefenderUpdateFileData = New-Object -TypeName PSCustomObject -Property $BitdefenderUpdateFileDataProperties
        $BitdefenderUpdateFileData
    }
}

function Test-Bitdefender {
    [CmdletBinding()]
    param (
        [parameter(
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true)]
        [string]$BitdefenderProcessName = 'epsecurityservice',
        [string[]]$BitdefenderServiceNames = @((Get-Service | Where-Object {$_.DisplayName -like "*bitdef*"}).Name),
        [string]$BitdefenderInstallationPath = 'C:\Program Files\Bitdefender\Endpoint Security\epsecurityservice.exe'
    )

    process {        
            try {
                # Is Bitdefender Installed
                Write-Verbose -Message "Check if Bitdefender is installed at $BitdefenderInstallationPath."
                $IsBitdefenderInstalled = Test-BitdefenderInstallationPath $BitdefenderInstallationPath

                if ($IsBitdefenderInstalled) {
                    # Is Bitdefender Running
                    Write-Verbose -Message "Check if $BitdefenderProcessName is running"
                    $IsBitdefenderProcessRunning = Test-BitdefenderProcess $BitdefenderProcessName

                    # Are Bitdefender Services Running and Automaticly Started
                    Write-Verbose -Message "Check Bitdefender services status"
                    foreach ($BitdefenderServiceName in $BitdefenderServiceNames) {
                        $BitdefenderService = Test-BitdefenderService $BitdefenderServiceName
                        $IsBitdefenderServiceHealthy = $BitdefenderService.ServiceExists -and $BitdefenderService.IsRunning -and $BitdefenderService.IsAutomatic
                        if ($IsBitdefenderServiceHealthy -eq $false) {
                            $IsBitdefenderServiceHealthyOverAll = $false
                        }
                        else { $IsBitdefenderServiceHealthyOverAll = $true }
                    }

                    # Is Bitdefender up to date
                    Write-Verbose -Message "Check if Bitdefender is up to date"
                    $BitdefenderSignatureDate = Get-BitdefenderSignatureDate
                    Write-Verbose "Bitdefender signature date: $BitdefenderSignatureDate"
                    $BitdefenderUpdateFileData = Get-BitdefenderUpdateFileData

                    if ($BitdefenderSignatureDate) {
                        $IsBitdefenderUptodate = (New-Timespan -Start $BitdefenderSignatureDate -End (Get-Date)).TotalDays -le 1
                    }
                    else {
                        $IsBitdefenderUptodate = (New-Timespan -Start $BitdefenderUpdateFileData.'Update time' -End (Get-Date)).TotalDays -le 1
                    }
                }
                else {
                    $IsBitdefenderProcessRunning = $false
                    $IsBitdefenderServiceHealthyOverall = $false
                    $IsBitdefenderUptodate = $false
                }

                # Creating Object
                $BitdefenderStatusProperties = [ordered]@{
                    IsBitdefenderInstalled          = $IsBitdefenderInstalled
                    IsBitdefenderProcessRunning     = $IsBitdefenderProcessRunning
                    IsBitdefenderServiceHealthy     = $IsBitdefenderServiceHealthyOverall
                    IsBitdefenderUptodate           = $IsBitdefenderUptodate
                    IsBitdefenderHealthy            = $IsBitdefenderInstalled -and $IsBitdefenderProcessRunning -and $IsBitdefenderServiceHealthy -and $IsBitdefenderUptodate
                    SignatureVersion                = $BitdefenderUpdateFileData.Version
                    SignatureNumber                 = $BitdefenderUpdateFileData.'Signature Number'
                    SignatureDate                   = $BitdefenderSignatureDate
                    SignatureUpdateTime             = $BitdefenderUpdateFileData.'Update time'
                }
                $BitdefenderStatus = New-Object -TypeName PSCustomObject -Property $BitdefenderStatusProperties | Out-String
                $BitdefenderStatus

                # Clear Variables
                $BitdefenderUpdateFileData = $null
                $BitdefenderSignatureDate = $null

                # Alert on False conditions
                $Status = $BitdefenderStatus -Join '|'
                if ($Status -match 'false') {
                    RMM-Alert -Category 'Monitor - Bitdefender' -Body "$BitdefenderStatus"
                    exit 1
                }
            }
            catch {
                Write-Error -Message $_.Exception
            }
    }
}

Test-Bitdefender

Here’s another script for cleaning up AV leftovers that may cause BD install to fail. Only run this if you have already used the built in AV uninstall program or run their removal tools, this is just for cleaning up leftovers. It also cleans out BD leftovers so not to be run on machines with BD installed. And it reboots the machine after running.

Import-Module $env:SyncroModule -DisableNameChecking

taskkill /IM explorer.exe /F

# AVG
reg delete "HKLM\SOFTWARE\AVG" /f

# Norton/Symantec
reg delete "HKLM\SOFTWARE\Symantec" /f
Remove-Item -Recurse -Force "C:\ProgramData\Norton"
Remove-Item -Recurse -Force "C:\ProgramData\NortonInstaller"
Remove-Item -Recurse -Force "C:\Program Files\NortonInstaller"

# ESET
reg delete "HKLM\SOFTWARE\ESET" /f
Remove-Item -Recurse -Force "C:\Program Files\ESET"

# TeamViewer's ITbrain Anti-Malware
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ITbrain Anti-Malware" /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ITbrain Monitoring" /f
Remove-Item -Recurse -Force "C:\Program Files\ITbrain"

# Malwarebytes
reg delete "HKLM\SOFTWARE\Malwarebytes " /f

# McAfee
reg delete "HKLM\SOFTWARE\Mcafee" /f
reg delete "HKLM\SOFTWARE\McAfee.com" /f
reg delete "HKLM\SOFTWARE\mcafeeupdater" /f
reg delete "HKLM\SOFTWARE\mcafeeupdater.com" /f
Remove-Item -Recurse -Force "C:\Program Files\Common Files\McAfee" /s /q

# SolarWinds AV Defender stuff
reg delete "HKLM\SOFTWARE\AVDefender" /f
reg delete "HKLM\SOFTWARE\N-Able Technologies\AVDefender" /f
Remove-Item -Recurse -Force "C:\Program Files(x86)\N-able Technologies\Windows Agent\AVDefender"
Remove-Item -Recurse -Force "C:\Program Files\N-able Technologies\AVDefender"
Remove-Item -Recurse -Force "C:\Program Data\N-Able Technologies"
Remove-Item -Force "C:\Program Files(x86)\N-Able Technologies\Windows Agent\config\AVDefender\Config.xml"
Remove-Item -Force "C:\Program Files(x86)\N-Able Technologies\Windows Agent\config\AVDefender\ErrorManager.xml"

# SolarWinds/LogicNow/Managed AV stuff
reg delete "HKCU\Software\Microsoft\Installer\Features\1CCD6B22A40736744A571AE34B990DE8" /f
reg delete "HKCU\Software\Microsoft\Installer\Products\1CCD6B22A40736744A571AE34B990DE8" /f
reg delete "HKEY_CLASSES_ROOT\Installer\Products\1CCD6B22A40736744A571AE34B990DE8" /f
reg delete "HKEY_CLASSES_ROOT\Installer\Products\4CD5C3BD0A7A09843BC123024BB352CE" /f
reg delete "HKLM\SOFTWARE\LogicNow" /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\1CCD6B22A40736744A571AE34B990DE8" /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\4CD5C3BD0A7A09843BC123024BB352CE" /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-21-2465414843-2580454656-430099928-1115\Products\1CCD6B22A40736744A571AE34B990DE8" /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-21-2535107696-3196376973-1651308919-1001\Products\1CCD6B22A40736744A571AE34B990DE8" /f
reg delete "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{22B6DCC1-704A-4763-A475-A13EB499D08E}" /f
reg delete "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{DB3C5DC4-A7A0-4890-B31C-3220B43B25EC}" /f
reg delete "HKLM\SYSTEM\ControlSet001\Services\WebProtectionDriver1.2" /f
reg delete "HKLM\SYSTEM\ControlSet002\Services\WebProtectionDriver1.2" /f
reg delete "HKLM\SYSTEM\Setup\FirstBoot\Services\Advanced Monitoring Agent" /f
reg delete "HKLM\SYSTEM\Setup\FirstBoot\Services\ManagedAntivirus" /f
reg delete "HKLM\SYSTEM\Setup\FirstBoot\Services\NetworkManagement" /f
reg delete "HKLM\SYSTEM\Setup\FirstBoot\Services\UpdateService" /f
Remove-Item -Recurse -Force "C:\Program Files\Managed Antivirus"
Remove-Item -Recurse -Force "C:\ProgramData\ManagedAntivirus"

# Bitdefender
reg delete "HKLM\SOFTWARE\Bitdefender" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\epag" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EPIntegrationService" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EPProtectedService" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EPRedline" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EPRedlineFiles" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EPSecurityService" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EPUpdateService" /f
reg delete "HKLM\SYSTEM\CurrentControlSet\Services\Ignis" /f
Remove-Item -Force "C:\ProgramData\*.bdinstall.*"
Remove-Item -Force "C:\ProgramData\Syncro\bin\setupdownloader_[*"
Remove-Item -Recurse -Force "C:\Program Files\Bitdefender"
Remove-Item -Recurse -Force "C:\Program Files (x86)\Bitdefender"
Remove-Item -Recurse -Force "C:\ProgramData\bdkitinstaller"
Remove-Item -Recurse -Force "C:\ProgramData\bdlogging"
Remove-Item -Recurse -Force "C:\ProgramData\bduninstalltool"
Remove-Item -Recurse -Force "C:\ProgramData\Bitdefender"

shutdown /r

Jordan is correct, that column lists only if BitDefender is installed on the policy. If anything prevents the installation when it runs then this is one way you can end up with it saying true even if it’s not installed. This could definitely be clarified.

Some good suggestions here, to add one more I usually say to do a Saved Asset Search since this will look at the installed apps list specifically.