Windows Defender exclusions with Ansible

Baby robot types commands into a computer. There is an antivirus shield icon on the computer screen.

There isn’t yet an official Ansible module for managing Windows Defender exclusions. If you’re committed to idempotency, and not sure how to add a path to Windows Defender’s exclusions list, read on!

I’ll get straight into the task:

- name: Exclude directories from Windows Defender
  ansible.windows.win_powershell:
    script: |
      $Ansible.Changed = $false
      
      # Check if the folder is already excluded
      $excluded = Get-MpPreference | Select-Object -ExpandProperty ExclusionPath | Where-Object { $_ -eq "{{ item }}" }

      if ($null -eq $excluded) {
        # Add the folder to the exclusion list
        try {
          Add-MpPreference -ExclusionPath "{{ item }}"
          $Ansible.Changed = $true
        } catch {
          $Ansible.Failed = $true
        }
      }
  loop:
    - C:\foo\bar.exe
    - H:\baz\qux

To break it down:

  • loop: Pass the file/directory paths that you wish to exclude, as a list. Add-MpPreference does not accept wildcards, so you may need to be verbose, or add exclusions at the directory level rather than file level. These are the “target exclusions” referred to below.
  • ansible.windows.win_powershell: This module gives us the ability with custom PowerShell scripts to tell Ansible explicitly whether the task failed or resulted in a change.
  • $Ansible.Changed = $false: Ansible uses this variable to detect whether or not the task resulted in a change. We initialise it to $false, since no change has occurred yet.
  • Get-MpPreference: This PowerShell cmdlet retrieves lots of Windows Defender configuration imformation.
  • Select-Object -ExpandProperty ExclusionPath: Get the existing exclusions from the previous command, as a list.
  • Where-Object { $_ -eq "{{ item }}" }: Look for our target exclusion in the list.
  • if ($null -eq $excluded) {: If we didn’t find the target exclusion, run the next bit of code. If we did find the exclusion, we don’t execute any more code. the script will end with $Ansible.Changed still set to $false, which is what we want, to preserve idempotency.
  • try { ... } catch { ... }: If we fail to add the exclusion, PowerShell throws an error, so we wrap this in try..catch so we can handle that failure.
  • Add-MpPreference -ExclusionPath "{{ item }}": Add the target exclusion to Windows Defender’s exclusions list. Note that if this fails, PowerShell will immediately jump to the code in the catch block.
  • $Ansible.Changed = $true: We’ll only reach this line if (a) the target exclusion wasn’t already in the list and (b) adding the exclusion succeeded. Hence we set $Ansible.Changed to $true.
  • $Ansible.Failed = $true: We reach this code if Add-MpPreference failed, hence we set $Ansible.Failed to $true.

I hope this is helpful! If you know of a better or more elegant way of doing this, let me know in the comments. 🙂