From bf43eb92f50ea8edc9e7b704ef91b3121ec972f0 Mon Sep 17 00:00:00 2001
From: Dag Wieers <dag@wieers.com>
Date: Tue, 13 Jun 2017 18:32:22 +0200
Subject: [PATCH] win_firewall: check-mode support, integration tests (#25127)

* win_firewall: check-mode support, integration tests

This PR includes:
- Check-mode implementation
- Documentation improvements
- Ensure module output is consistent (no matter what profiles are provided)
- Fixed indentation
- Cosmetic changes
- Integration tests

* win_firewall: check-mode support, integration tests

This PR includes:
- Check-mode implementation
- Documentation improvements
- Ensure module output is consistent (no matter what profiles are provided)
- Fixed indentation
- Cosmetic changes
- Integration tests
---
 lib/ansible/modules/windows/win_firewall.ps1  |  70 ++++---
 lib/ansible/modules/windows/win_firewall.py   |  51 ++---
 test/integration/targets/win_firewall/aliases |   1 +
 .../targets/win_firewall/tasks/main.yml       |  52 +++++
 .../targets/win_firewall/tasks/tests.yml      | 185 ++++++++++++++++++
 5 files changed, 306 insertions(+), 53 deletions(-)
 create mode 100644 test/integration/targets/win_firewall/aliases
 create mode 100644 test/integration/targets/win_firewall/tasks/main.yml
 create mode 100644 test/integration/targets/win_firewall/tasks/tests.yml

diff --git a/lib/ansible/modules/windows/win_firewall.ps1 b/lib/ansible/modules/windows/win_firewall.ps1
index c33789c02cb..dbfca5b5f5c 100644
--- a/lib/ansible/modules/windows/win_firewall.ps1
+++ b/lib/ansible/modules/windows/win_firewall.ps1
@@ -19,50 +19,60 @@
 # WANT_JSON
 # POWERSHELL_COMMON
 
+$ErrorActionPreference = "Stop"
+$firewall_profiles = @('Domain', 'Private', 'Public')
 
-# get params
-$params = Parse-Args $args -supports_check_mode $false
+$params = Parse-Args $args -supports_check_mode $true
+$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
 
-$profiles = Get-AnsibleParam -obj $params -name "profiles" -type "list" -default [ "Public", "Domain", "Private" ]
-$wantedstate = Get-AnsibleParam -obj $params -name "state" -type "str" -failifempty $true -validateset 'enabled', 'disabled'
+$profiles = Get-AnsibleParam -obj $params -name "profiles" -type "list" -default @("Domain", "Private", "Public")
+$state = Get-AnsibleParam -obj $params -name "state" -type "str" -failifempty $true -validateset 'disabled','enabled'
 
 $result = @{
     changed = $false
+    profiles = $profiles
+    state = $state
+}
 
+if ($PSVersionTable.PSVersion -lt [Version]"5.0") {
+    Fail-Json $result "win_firewall requires Windows Management Framework 5 or higher."
 }
 
 Try {
 
-  ForEach($profile in $profiles)
+    ForEach ($profile in $firewall_profiles) {
 
-  {
-
-      $currentstate = (Get-NetFirewallProfile -Name $profile).Enabled
-
-      if ($wantedstate -eq 'enabled')
-      {
-        if ($currentstate -eq $false)
-        {
-            Set-NetFirewallProfile -name $profile -Enabled true
-            $result.enabled = $true
-            $result.changed = $true
-        }
-      }
-      else
-      {
-        if ($currentstate -eq $true)
-        {
-           Set-NetFirewallProfile -name $profile -Enabled false
-           $result.enabled = $false
-           $result.changed = $true
+        $currentstate = (Get-NetFirewallProfile -Name $profile).Enabled
+        $result.$profile = @{
+            enabled = ($currentstate -eq 1)
+            considered = ($profiles -contains $profile)
+            currentstate = $currentstate
         }
 
-      }
+        if ($profiles -notcontains $profile) {
+            continue
+        }
 
-  }
-}
-Catch {
+        if ($state -eq 'enabled') {
+
+            if ($currentstate -eq $false) {
+                Set-NetFirewallProfile -name $profile -Enabled true -WhatIf:$check_mode
+                $result.changed = $true
+                $result.$profile.enabled = $true
+            }
+
+        } else {
+
+            if ($currentstate -eq $true) {
+                Set-NetFirewallProfile -name $profile -Enabled false -WhatIf:$check_mode
+                $result.changed = $true
+                $result.$profile.enabled = $false
+            }
+
+        }
+    }
+} Catch {
     Fail-Json $result "an error occurred when attempting to change firewall status for profile $profile $($_.Exception.Message)"
 }
 
-Exit-Json $result
\ No newline at end of file
+Exit-Json $result
diff --git a/lib/ansible/modules/windows/win_firewall.py b/lib/ansible/modules/windows/win_firewall.py
index 5c09d69af16..fe13cc50da9 100644
--- a/lib/ansible/modules/windows/win_firewall.py
+++ b/lib/ansible/modules/windows/win_firewall.py
@@ -28,55 +28,60 @@ ANSIBLE_METADATA = {'metadata_version': '1.0',
 DOCUMENTATION = r'''
 ---
 module: win_firewall
-version_added: "2.4"
-short_description: Manages Windows Firewall
+version_added: '2.4'
+short_description: Enable or disable the Windows Firewall
 description:
-    - Manages Windows Firewall
+- Enable or Disable Windows Firewall profiles.
 options:
-  profile:
+  profiles:
     description:
-    - specify the profile to change
+    - Specify one or more profiles to change.
     choices:
-    - Public
     - Domain
     - Private
+    - Public
+    default: [Domain, Private, Public]
   state:
     description:
-    - set state of firewall for given profile
+    - Set state of firewall for given profile.
     choices:
     - enabled
     - disabled
-
-author: "Michael Eaton (@MichaelEaton83)"
+author: Michael Eaton (@MichaelEaton83)
 '''
 
 EXAMPLES = r'''
-- name: Enable all firewalls
+- name: Enable firewall for Domain, Public and Private profiles
   win_firewall:
-      state: enabled
-      profiles:
-      - Domain
-      - Public
-      - Private
+    state: enabled
+    profiles:
+    - Domain
+    - Private
+    - Public
   tags: enable_firewall
 
 - name: Disable Domain firewall
   win_firewall:
-     state: disabled
-     profiles:
-     - Domain
+    state: disabled
+    profiles:
+    - Domain
   tags: disable_firewall
 '''
 
 RETURN = r'''
-profile:
-    description: chosen profile
-    returned: always
-    type: string
-    sample: Domain
 enabled:
     description: current firewall status for chosen profile (after any potential change)
     returned: always
     type: bool
     sample: true
+profiles:
+    description: chosen profile
+    returned: always
+    type: string
+    sample: Domain
+state:
+    description: desired state of the given firewall profile(s)
+    returned: always
+    type: list
+    sample: enabled
 '''
diff --git a/test/integration/targets/win_firewall/aliases b/test/integration/targets/win_firewall/aliases
new file mode 100644
index 00000000000..ee0ed5974e9
--- /dev/null
+++ b/test/integration/targets/win_firewall/aliases
@@ -0,0 +1 @@
+windows/ci/group2
diff --git a/test/integration/targets/win_firewall/tasks/main.yml b/test/integration/targets/win_firewall/tasks/main.yml
new file mode 100644
index 00000000000..1c42dccf4a1
--- /dev/null
+++ b/test/integration/targets/win_firewall/tasks/main.yml
@@ -0,0 +1,52 @@
+# NOTE: The win_firewall module only works on WMF 5+
+
+- setup:
+
+- name: Test Windows capabilities
+  raw: Get-Command Get-NetFirewallProfile -ErrorAction SilentlyContinue; return $?
+  failed_when: no
+  register: get_netfirewallprofile
+
+- name: Only run tests when Windows is capable
+  when: get_netfirewallprofile.rc == 0 and ansible_powershell_version >= 5
+  block:
+  - name: Turn off Windows Firewall (begin)
+    win_firewall:
+      profiles: [ Domain, Private, Public ]
+      state: disabled
+    register: firewall_off
+
+  - name: Test firewall_off
+    assert:
+      that:
+      - not firewall_off.Domain.enabled
+      - not firewall_off.Private.enabled
+      - not firewall_off.Public.enabled
+
+
+  - name: Test in normal mode
+    include: tests.yml
+    vars:
+      in_check_mode: no
+
+
+  - name: Test in check-mode
+    include: tests.yml
+    vars:
+      in_check_mode: yes
+    check_mode: yes
+
+
+  - name: Turn on Windows Firewall (end)
+    win_firewall:
+      profiles: [ Domain, Private, Public ]
+      state: enabled
+    register: firewall_on
+
+  - name: Test firewall_on
+    assert:
+      that:
+      - firewall_on|changed
+      - firewall_on.Domain.enabled
+      - firewall_on.Private.enabled
+      - firewall_on.Public.enabled
diff --git a/test/integration/targets/win_firewall/tasks/tests.yml b/test/integration/targets/win_firewall/tasks/tests.yml
new file mode 100644
index 00000000000..9d38dcb537c
--- /dev/null
+++ b/test/integration/targets/win_firewall/tasks/tests.yml
@@ -0,0 +1,185 @@
+# We start with firewall turned off
+
+- name: Turn off Windows Firewall again
+  win_firewall:
+    profiles: [ Domain, Private, Public ]
+    state: disabled
+  register: firewall_off_again
+
+- name: Test firewall_off_again
+  assert:
+    that:
+    - not firewall_off_again|changed
+    - not firewall_off_again.Domain.enabled
+    - not firewall_off_again.Private.enabled
+    - not firewall_off_again.Public.enabled
+
+- name: Turn on Windows Firewall on Public
+  win_firewall:
+    profiles: [ Public ]
+    state: enabled
+  register: firewall_public_on
+
+- name: Test firewall_public_on
+  assert:
+    that:
+    - firewall_public_on|changed
+    - not firewall_public_on.Domain.enabled
+    - not firewall_public_on.Private.enabled
+    - firewall_public_on.Public.enabled
+
+
+- name: Turn on Windows Firewall on Public again
+  win_firewall:
+    profiles: [ Public ]
+    state: enabled
+  register: firewall_public_on_again
+
+- name: Test firewall_public_on_again (normal mode)
+  assert:
+    that:
+    - not firewall_public_on_again|changed
+    - not firewall_public_on_again.Domain.enabled
+    - not firewall_public_on_again.Private.enabled
+    - firewall_public_on_again.Public.enabled
+  when: not in_check_mode
+
+- name: Test firewall_public_on_again (check-mode)
+  assert:
+    that:
+    - firewall_public_on_again|changed
+    - not firewall_public_on_again.Domain.enabled
+    - not firewall_public_on_again.Private.enabled
+    - firewall_public_on_again.Public.enabled
+  when: in_check_mode
+
+
+# On purpose not a list
+- name: Turn on Windows Firewall on Domain
+  win_firewall:
+    profiles: Domain
+    state: enabled
+  register: firewall_domain_on
+
+- name: Test firewall_domain_on (normal mode)
+  assert:
+    that:
+    - firewall_domain_on|changed
+    - firewall_domain_on.Domain.enabled
+    - not firewall_domain_on.Private.enabled
+    - firewall_domain_on.Public.enabled
+  when: not in_check_mode
+
+- name: Test firewall_domain_on (check-mode)
+  assert:
+    that:
+    - firewall_domain_on|changed
+    - firewall_domain_on.Domain.enabled
+    - not firewall_domain_on.Private.enabled
+    - not firewall_domain_on.Public.enabled
+  when: in_check_mode
+
+
+- name: Turn on Windows Firewall on Domain again
+  win_firewall:
+    profiles: [ Domain ]
+    state: enabled
+  register: firewall_domain_on_again
+
+- name: Test firewall_domain_on_again (normal mode)
+  assert:
+    that:
+    - not firewall_domain_on_again|changed
+    - firewall_domain_on.Domain.enabled
+    - not firewall_domain_on.Private.enabled
+    - firewall_domain_on.Public.enabled
+  when: not in_check_mode
+
+- name: Test firewall_domain_on_again (check-mode)
+  assert:
+    that:
+    - firewall_domain_on_again|changed
+    - firewall_domain_on.Domain.enabled
+    - not firewall_domain_on.Private.enabled
+    - not firewall_domain_on.Public.enabled
+  when: in_check_mode
+
+
+- name: Turn on Windows Firewall
+  win_firewall:
+    profiles: [ Domain, Private, Public ]
+    state: enabled
+  register: firewall_on
+
+- name: Test firewall_on
+  assert:
+    that:
+    - firewall_on|changed
+    - firewall_on.Domain.enabled
+    - firewall_on.Private.enabled
+    - firewall_on.Public.enabled
+
+
+# On purpose no profiles added
+- name: Turn on Windows Firewall again
+  win_firewall:
+    state: enabled
+  register: firewall_on_again
+
+- name: Test firewall_on_again (normal mode)
+  assert:
+    that:
+    - not firewall_on_again|changed
+    - firewall_on_again.Domain.enabled
+    - firewall_on_again.Private.enabled
+    - firewall_on_again.Public.enabled
+  when: not in_check_mode
+
+- name: Test firewall_on_again (check-mode)
+  assert:
+    that:
+    - firewall_on_again|changed
+    - firewall_on_again.Domain.enabled
+    - firewall_on_again.Private.enabled
+    - firewall_on_again.Public.enabled
+  when: in_check_mode
+
+
+# On purpose no profiles added
+- name: Turn off Windows Firewall
+  win_firewall:
+    state: disabled
+  register: firewall_off2
+
+- name: Test firewall_off2 (normal mode)
+  assert:
+    that:
+    - firewall_off2|changed
+    - not firewall_off2.Domain.enabled
+    - not firewall_off2.Private.enabled
+    - not firewall_off2.Public.enabled
+  when: not in_check_mode
+
+- name: Test firewall_off2 (check-mode)
+  assert:
+    that:
+    - not firewall_off2|changed
+    - not firewall_off2.Domain.enabled
+    - not firewall_off2.Private.enabled
+    - not firewall_off2.Public.enabled
+  when: in_check_mode
+
+
+- name: Turn off Windows Firewall again
+  win_firewall:
+    profiles: [ Domain, Private, Public ]
+    state: disabled
+  register: firewall_off2_again
+
+- name: Test firewall_off2_again (normal mode)
+  assert:
+    that:
+    - not firewall_off2_again|changed
+    - not firewall_off2_again.Domain.enabled
+    - not firewall_off2_again.Private.enabled
+    - not firewall_off2_again.Public.enabled