Simultaneous Uninstalls:
# removeWindowsDefender.ps1
# Version 0.02
$computerNames=@(
'server1',
'server2'
)
function removeWindowsDefender($computerNames){
function uninstallWindefend($computername){
$session=try{
New-PSSession $computername -EA Stop
}catch{
New-PSSession $computername -SessionOption (New-PSSessionOption -IncludePortInSPN)
}
if($session.state -eq 'opened'){
$result=invoke-command -Session $session -scriptblock{
$serverOs=(Get-WmiObject Win32_OperatingSystem).Name -match "^Microsoft Windows Server"
$null=Set-MpPreference -DisableRealtimeMonitoring $True -EA SilentlyContinue
if($serverOs){
$null=Remove-WindowsFeature Windows-Defender-GUI -EA SilentlyContinue
$null=Remove-WindowsFeature Windows-Defender -EA SilentlyContinue
return [hashtable]@{$env:computername=(Get-WindowsFeature windows-defender).InstallState -ne 'Installed'}
}else{
return [hashtable]@{$env:computername=(Get-MpComputerStatus).AntivirusEnabled -ne $true}
}
}
Remove-PSSession $session
}else{
write-warning "cannot connect to $computername via WinRM"
$result=[hashtable]@{$env:computername='Unknown'}
}
return $result
}
$timer=[System.Diagnostics.Stopwatch]::StartNew()
$jobResults=@()
$lineBreak=60
$dotCount=0
$minute=0
$processorsCount=(Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
$cpuLoad=(Get-WmiObject win32_processor|Measure-Object -property LoadPercentage -Average).Average
$maxSimultaneousJobs=if($cpuLoad -gt 90){$processorsCount}else{($processorsCount*2)-1} # dynamically limiting concurrent jobs basing on available CPU cores
write-host "CPU load detected as: $cpuLoad`%`r`nSetting concurrent jobs max count to be $maxSimultaneousJobs"
foreach($computerName in $computerNames){
$thisIterationCompleted=$false
do {
$jobsCount=(Get-Job -State 'Running').Count
if ($jobsCount -lt $maxSimultaneousJobs){
if($verbose){write-host "Initiating job for $computerName"}
$null=Start-Job -name $computerName -ScriptBlock {
param($uninstallWindefend,$computerName)
[scriptblock]::create($uninstallWindefend).invoke($computerName)
} -Args ${function:uninstallWindefend},$computerName
$thisIterationCompleted=$true
}else{
if($verbose){
if($dotCount++ -lt $lineBreak){
write-host '.' -NoNewline
}else{
$minute++
write-host "`r`n$minute`t:" -ForegroundColor Yellow -NoNewline
$dotCount=0
}
}
sleep -seconds 1
}
}until ($thisIterationCompleted)
}
$totalJobsCount=(get-job).count
$processedCount=0
while($processedCount -lt $totalJobsCount){
$completedJobs=get-job|?{$_.State -eq 'Completed'}
if($completedJobs){
foreach ($job in $completedJobs){
$computer=$job.Name
if($verbose){
write-host "`r`n===================================================`r`n$computer job completed with these messages:`r`n===================================================`r`n"
}
$jobResult=receive-job -id $job.id
$jobResults+=,$jobResult
remove-job -id $job.id -force
$processedCount++
}
}
}
$minutesElapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
$timer.stop()
write-host "$($computerNames.count) computers were processed in $minutesElapsed minutes."
return $jobResults #|select -property * -excludeproperty RunspaceId
}
removeWindowsDefender $computerNames
Error:
Remove-WindowsFeature : The request to add or remove features on the specified server failed.
Removal of one or more roles, role services, or features failed.
The referenced assembly could not be found. Error: 0x80073701
+ CategoryInfo : InvalidOperation: (@{Vhd=; Credent...Name=localhost}:PSObject) [Uninstall-WindowsFeature
], Exception
+ FullyQualifiedErrorId : DISMAPI_Error__Failed_Disabling_Updates,Microsoft.Windows.ServerManager.Commands.RemoveW
indowsFeatureCommand
Success Restart Needed Exit Code Feature Result
------- -------------- --------- --------------
False No Failed {}
Resolution:
It’s most likely being caused by a pending reboot. Recommendation is to reboot the server, then install all required Windows patches. Repeat reboots if necessary to clear all flags before attempting to run the Remove-WindowsFeature command again.
Convenient Function (sequential execution):
# removeWindowsDefender.ps1
# Version 0.01
$computerNames=@(
'server1',
'server2'
)
function removeWindowsDefender($computerNames){
$results=@()
foreach($computername in $computernames){
write-host "Checking $computername..."
$session=try{
New-PSSession $computername -EA Stop
}catch{
New-PSSession $computername -SessionOption (New-PSSessionOption -IncludePortInSPN)
}
if($session.state -eq 'opened'){
$result=invoke-command -Session $session -scriptblock{
$null=Set-MpPreference -DisableRealtimeMonitoring $True -EA SilentlyContinue
$null=Remove-WindowsFeature Windows-Defender-GUI -EA SilentlyContinue
$null=Remove-WindowsFeature Windows-Defender -EA SilentlyContinue
return [hashtable]@{$env:computername=(Get-WindowsFeature windows-defender).InstallState -ne 'Installed'}
}
Remove-PSSession $session
}else{
write-warning "cannot connect to $computername via WinRM"
$result=[hashtable]@{$env:computername='Unknown'}
}
$results+=$result
}
}
removeWindowsDefender $computerNames
Why?
Even though Windows Defender can run along side Enterprise antivirus software such as McAfee or Norton, it would be necessary to disable it on systems that already have those security applications installed. Here are two automated methods to get this done.
1. PowerShell on Localhost
If this is a Windows Server 2016 or Later, run this command
# Uninstall WinDefend
Remove-WindowsFeature Windows-Defender, Windows-Defender-GUI
On Windows 2012 R2, these commands would have worked
# Disable Defender's Real Time scanning engine
Set-MpPreference -DisableRealtimeMonitoring $True
# Deactivate the scanning engine via registry
Set-ItemProperty -Path "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender" -Name "DisableAntiSpyware" -Value 1 -Force
However, the above lines are no longer valid for Windows 10 & 2016 (source: https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-antivirus-updates. On KB4052623, Microsoft has changed to registry location of Defender. Hence, Active Directory Group Policy and PowerShell commands to disable this feature are no longer effective.
For those who are interested in security topics, these are the related advisories leading to this change:
- https://msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2017-11937
- https://msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2017-11940
# My fancy way to check if WinDefend is installed
try{Get-MpComputerStatus;"Windows Defender IS enabled on this system.";}
catch{"Windows Defender is NOT enabled on this system.";}
# This is the better method provided by Michael Shoff
sc query windefend
# Disable it within the registry (failed attempt)
Set-ItemProperty -Path "Registry::HKLM\SOFTWARE\Microsoft\Windows Defender" -Name "DisableAntiSpyware" -Value 1 -Force
# Error
PS C:\Users\tester> Set-ItemProperty -Path "Registry::HKLM\SOFTWARE\Microsoft\Windows Defender" -Name "DisableAntiSpyware" -Value 1 -Force
Set-ItemProperty : Requested registry access is not allowed.
At line:1 char:1
+ Set-ItemProperty -Path "Registry::HKLM\SOFTWARE\Microsoft\Windows Def ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (HKLM\SOFTWARE\Microsoft\Windows Defender:String) [Set-ItemProperty],
SecurityException
+ FullyQualifiedErrorId : System.Security.SecurityException,Microsoft.PowerShell.Commands.SetItemPropertyCommand
# Attempt to set permissions (failed)
$acl = Get-Acl "Registry::HKLM\SOFTWARE\Microsoft\Windows Defender"
$fullControl = New-Object System.Security.AccessControl.RegistryAccessRule ("$env:COMPUTERNAME\Administrators","FullControl","Allow")
$acl.SetAccessRule($fullControl)
2. Group Policy
Run ADUC: Create new GPO as follows:
Computer configuration > Administrative templates > Windows components > Windows Defender > Set these values:
Turn off Windows Defender = Enabled
Real-time protection = Off (optional as Defender has already turned off per the preceding setting)
Please note that this GP instruction is to be applied toward Windows 2016 & 2019. This may not work for Windows 2008, 2012, and Linux.