diff --git a/lib/ansible/modules/windows/win_firewall_rule.ps1 b/lib/ansible/modules/windows/win_firewall_rule.ps1 index a61e1a1547a..b3077f7c956 100644 --- a/lib/ansible/modules/windows/win_firewall_rule.ps1 +++ b/lib/ansible/modules/windows/win_firewall_rule.ps1 @@ -1,273 +1,164 @@ #!powershell -# -# (c) 2014, Timothy Vandenbrande -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -# +# Copyright (c) 2017 Artem Zinenko +# Copyright (c) 2014 Timothy Vandenbrande +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + # WANT_JSON # POWERSHELL_COMMON -# TODO: Reimplement this using Powershell cmdlets +function Parse-ProtocolType { + param($protocol) + + $protocolNumber = $protocol -as [int] + if ($protocolNumber -is [int]) { + return $protocolNumber + } + + switch -wildcard ($protocol) { + "tcp" { return [System.Net.Sockets.ProtocolType]::Tcp -as [int] } + "udp" { return [System.Net.Sockets.ProtocolType]::Udp -as [int] } + "icmpv4*" { return [System.Net.Sockets.ProtocolType]::Icmp -as [int] } + "icmpv6*" { return [System.Net.Sockets.ProtocolType]::IcmpV6 -as [int] } + default { throw "Unknown protocol '$protocol'." } + } +} + +# See 'Direction' constants here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364724(v=vs.85).aspx +function Parse-Direction { + param($directionStr) + + switch ($directionStr) { + "in" { return 1 } + "out" { return 2 } + default { throw "Unknown direction '$directionStr'." } + } +} + +# See 'Action' constants here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364724(v=vs.85).aspx +function Parse-Action { + param($actionStr) + + switch ($actionStr) { + "block" { return 0 } + "allow" { return 1 } + default { throw "Unknown action '$actionStr'." } + } +} + +# Profile enum values: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366303(v=vs.85).aspx +function Parse-Profiles +{ + param($profilesStr) + + $profiles = ($profilesStr.Split(',') | Select -uniq | ForEach { + switch ($_) { + "domain" { return 1 } + "private" { return 2 } + "public" { return 4 } + default { throw "Unknown profile '$_'." } + } + } | Measure-Object -Sum).Sum + + if ($profiles -eq 7) { return 0x7fffffff } + return $profiles +} + +function Parse-InterfaceTypes +{ + param($interfaceTypesStr) + + return ($interfaceTypesStr.Split(',') | Select -uniq | ForEach { + switch ($_) { + "wireless" { return "Wireless" } + "lan" { return "Lan" } + "ras" { return "RemoteAccess" } + default { throw "Unknown interface type '$_'." } + } + }) -Join "," +} + +function Parse-EdgeTraversalOptions +{ + param($edgeTraversalOptionsStr) + + switch ($edgeTraversalOptionsStr) { + "yes" { return 1 } + "deferapp" { return 2 } + "deferuser" { return 3 } + default { throw "Unknown edge traversal options '$edgeTraversalOptionsStr'." } + } +} + +function Parse-SecureFlags +{ + param($secureFlagsStr) + + switch ($secureFlagsStr) { + "authnoencap" { return 1 } + "authenticate" { return 2 } + "authdynenc" { return 3 } + "authenc" { return 4 } + default { throw "Unknown secure flags '$secureFlagsStr'." } + } +} + +function New-FWRule +{ + param ( + [string]$name, + [string]$description, + [string]$applicationName, + [string]$serviceName, + [string]$protocol, + [string]$localPorts, + [string]$remotePorts, + [string]$localAddresses, + [string]$remoteAddresses, + [string]$direction, + [string]$action, + [bool]$enabled, + [string]$profiles, + [string]$interfaceTypes, + [string]$edgeTraversalOptions, + [string]$secureFlags + ) + + # INetFwRule interface description: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365344(v=vs.85).aspx + $rule = New-Object -ComObject HNetCfg.FWRule + $rule.Name = $name + $rule.Enabled = $enabled + if ($description) { $rule.Description = $description } + if ($applicationName) { $rule.ApplicationName = $applicationName } + if ($serviceName) { $rule.ServiceName = $serviceName } + if ($protocol -and $protocol -ne "any") { $rule.Protocol = Parse-ProtocolType -protocol $protocol } + if ($localPorts -and $localPorts -ne "any") { $rule.LocalPorts = $localPorts } + if ($remotePorts -and $remotePorts -ne "any") { $rule.RemotePorts = $remotePorts } + if ($localAddresses -and $localAddresses -ne "any") { $rule.LocalAddresses = $localAddresses } + if ($remoteAddresses -and $remoteAddresses -ne "any") { $rule.RemoteAddresses = $remoteAddresses } + if ($direction) { $rule.Direction = Parse-Direction -directionStr $direction } + if ($action) { $rule.Action = Parse-Action -actionStr $action } + if ($profiles) { $rule.Profiles = Parse-Profiles -profilesStr $profiles } + if ($interfaceTypes -and $interfaceTypes -ne "any") { $rule.InterfaceTypes = Parse-InterfaceTypes -interfaceTypesStr $interfaceTypes } + if ($edgeTraversalOptions -and $edgeTraversalOptions -ne "no") { + # EdgeTraversalOptions property exists only from Windows 7/Windows Server 2008 R2: https://msdn.microsoft.com/en-us/library/windows/desktop/dd607256(v=vs.85).aspx + if ($rule | Get-Member -Name 'EdgeTraversalOptions') { + $rule.EdgeTraversalOptions = Parse-EdgeTraversalOptions -edgeTraversalOptionsStr $edgeTraversalOptions + } + } + if ($secureFlags -and $secureFlags -ne "notrequired") { + # SecureFlags property exists only from Windows 8/Windows Server 2012: https://msdn.microsoft.com/en-us/library/windows/desktop/hh447465(v=vs.85).aspx + if ($rule | Get-Member -Name 'SecureFlags') { + $rule.SecureFlags = Parse-SecureFlags -secureFlagsStr $secureFlags + } + } + + return $rule +} $ErrorActionPreference = "Stop" -function convertToNetmask($maskLength) { - [IPAddress] $ip = 0 - $ip.Address = ([UInt32]::MaxValue) -shl (32 - $maskLength) -shr (32 - $maskLength) - return $ip.IPAddressToString -} - -function ConvertTo-TitleCase($string) { - return (Get-Culture).TextInfo.ToTitleCase($string.ToLower()) -} - -function ConvertTo-SortedKV($object, $unsupported = @()) { - $output = "" - foreach($item in $object.GetEnumerator() | Sort -Property Name) { - if (($item.Name -notin $unsupported) -and ($item.Value -ne $null)) { - $output += "$($item.Name): $($item.Value)`n" - } - } - return $output -} - -function preprocessAndCompare($key, $outputValue, $fwsettingValue) { - if ($key -eq 'RemoteIP') { - if ($outputValue -eq $fwsettingValue) { - return $true - } - - if ($outputValue -eq $fwsettingValue+'-'+$fwsettingValue) { - return $true - } - - if (($outputValue -eq $fwsettingValue+'/32') -or ($outputValue -eq $fwsettingValue+'/255.255.255.255')) { - return $true - } - - if ($outputValue -match '^([\d\.]+)\/(\d+)$') { - $netmask = convertToNetmask($Matches[2]) - if ($fwsettingValue -eq $Matches[1]+"/"+$netmask) { - return $true - } - } - - if ($fwsettingValue -match '^([\d\.]+)\/(\d+)$') { - $netmask = convertToNetmask($Matches[2]) - if ($outputValue -eq $Matches[1]+"/"+$netmask) { - return $true - } - } - } - - return $false -} - -function getFirewallRule ($fwsettings) { - $diff = $false - $result = @{ - changed = $false - identical = $false - exists = $false - failed = $false - msg = @() - multiple = $false - } - - try { - $command = "netsh advfirewall firewall show rule name=`"$($fwsettings.'Rule Name')`" verbose" - #$output = Get-NetFirewallRule -name $($fwsettings.'Rule Name') - $result.output = Invoke-Expression $command | Where { $_ } - $rc = $LASTEXITCODE - if ($rc -eq 1) { - $result.msg += @("No rule '$name' could be found") - } elseif ($rc -eq 0) { - # Process command output - $result.output | Where {$_ -match '^([^:]+):\s*(\S.*)$'} | ForEach -Begin { - $FirstRun = $true - $HashProps = @{} - } -Process { - if (($Matches[1] -eq 'Rule Name') -and (-not $FirstRun)) { - $output = $HashProps - $HashProps = @{} - } - $HashProps.$($Matches[1]) = $Matches[2] - $FirstRun = $false - } -End { - $output = $HashProps - } - if ($($output|measure).count -gt 0) { - $diff = $false - $result.exists = $true - #$result.msg += @("The rule '$($fwsettings.'Rule Name')' exists.") - if ($($output|measure).count -gt 1) { - $result.multiple = $true - $result.msg += @("The rule '$($fwsettings.'Rule Name')' has multiple entries.") - $result.diff = @{} - $result.diff.after = ConvertTo-SortedKV $fwsettings - $result.diff.before = ConvertTo-SortedKV $rule $unsupported - if ($result.diff.after -ne $result.diff.before ) { - $diff = $true - } - } else { - if ($diff_support) { - $result.diff = @{} - $result.diff.after = ConvertTo-SortedKV $fwsettings - $result.diff.before = ConvertTo-SortedKV $output $unsupported - } - ForEach($fwsetting in $fwsettings.GetEnumerator()) { - if ($output.$($fwsetting.Key) -ne $fwsettings.$($fwsetting.Key)) { - if ((preprocessAndCompare -key $fwsetting.Key -outputValue $output.$($fwsetting.Key) -fwsettingValue $fwsettings.$($fwsetting.Key))) { - Continue - } elseif (($fwsetting.Key -eq 'DisplayName') -and ($output."Rule Name" -eq $fwsettings.$($fwsetting.Key))) { - Continue - } elseif (($fwsetting.Key -eq 'Program') -and ($output.$($fwsetting.Key) -eq (Expand-Environment($fwsettings.$($fwsetting.Key))))) { - # Ignore difference caused by expanded environment variables - Continue - } else { - $diff = $true - Break - } - } - } - } - if (-not $diff) { - $result.identical = $true - } - if ($result.identical) { - $result.msg += @("The rule '$name' exists and is identical") - } else { - $result.msg += @("The rule '$name' exists but has different values") - } - } - } else { - $result.failed = $true - } - } catch [Exception] { - $result.failed = $true - $result.error = $_.Exception.Message - } - return $result -} - -function createFireWallRule ($fwsettings) { - $result = @{ - changed = $false - failed = $false - msg = @() - } - - $command = "netsh advfirewall firewall add rule" - ForEach ($fwsetting in $fwsettings.GetEnumerator()) { - if ($fwsetting.value -ne $null) { - switch($fwsetting.key) { - "Direction" { $option = "dir" } - "Rule Name" { $option = "name" } - "Enabled" { $option = "enable" } - "Profiles" { $option = "profile" } - "InterfaceTypes" { $option = "interfacetype" } - "Security" { $option = "security" } - "Edge traversal" { $option = "edge" } - default { $option = $($fwsetting.key).ToLower() } - } - $command += " $option='$($fwsetting.value)'" - } - } - - try { - $rc = 0 - if (-not $check_mode) { - $result.output = Invoke-Expression $command | Where { $_ } - $rc = $LASTEXITCODE - } - if ($rc -eq 0) { - if ($diff_support) { - $result.diff = @{} - $result.diff.after = ConvertTo-SortedKV $fwsettings - $result.diff.before= "" - } - $result.changed = $true - $result.msg += @("Created firewall rule '$name'") - } else { - $result.failed = $true - $result.msg += @("Create command '$command' failed with rc=$rc") - } - } catch [Exception]{ - $result.error = $_.Exception.Message - $result.failed = $true - $result.msg = @("Failed to create the rule '$name'") - } - return $result -} - -function removeFireWallRule ($fwsettings) { - $result = @{ - changed = $false - failed = $false - msg = @() - } - - $command = "netsh advfirewall firewall delete rule name='$($fwsettings.'Rule Name')'" - try { - $rc = 0 - if (-not $check_mode) { - $result.output = Invoke-Expression $command | Where { $_ } - $rc = $LASTEXITCODE - $result.output | Where {$_ -match '^([^:]+):\s*(\S.*)$'} | Foreach -Begin { - $FirstRun = $true - $HashProps = @{} - } -Process { - if (($Matches[1] -eq 'Rule Name') -and (-not $FirstRun)) { - $result.output = $HashProps - $HashProps = @{} - } - $HashProps.$($Matches[1]) = $Matches[2] - $FirstRun = $false - } -End { - $result.output = $HashProps - } - } - if ($rc -eq 0 -or $rc -eq 1) { - if ($diff_support) { - $result.diff = @{} - $result.diff.after = "" - $result.diff.before = ConvertTo-SortedKV $fwsettings - } - $result.changed = $true - $result.msg += @("Removed the rule '$name'") - } else { - $result.failed = $true - $result.msg += @("Remove command '$command' failed with rc=$rc") - } - } catch [Exception]{ - $result.error = $_.Exception.Message - $result.failed = $true - $result.msg += @("Failed to remove the rule '$name'") - } - return $result -} - -# FIXME: Unsupported keys -#$unsupported = @("Grouping", "Rule source") -$unsupported = @("Rule source") - $result = @{ changed = $false - fwsettings = @{} - msg = @() } $params = Parse-Args $args -supports_check_mode $true @@ -287,167 +178,99 @@ $remoteip = Get-AnsibleParam -obj $params -name "remoteip" -type "str" -default $localport = Get-AnsibleParam -obj $params -name "localport" -type "str" $remoteport = Get-AnsibleParam -obj $params -name "remoteport" -type "str" $protocol = Get-AnsibleParam -obj $params -name "protocol" -type "str" -default "any" -$edge = Get-AnsibleParam -obj $params -name "edge" -type "str" -default "no" -validateset "no","yes","deferapp","deferuser" $interfacetypes = Get-AnsibleParam -obj $params -name "interfacetypes" -type "str" -default "any" -$security = Get-AnsibleParam -obj $params -name "security" -type "str" -default "notrequired" +$edge = Get-AnsibleParam -obj $params -name "edge" -type "str" -default "no" -validateset "no","yes","deferapp","deferuser" +$security = Get-AnsibleParam -obj $params -name "security" -type "str" -default "notrequired" -validateset "notrequired","authnoencap","authenticate","authdynenc","authenc" $state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent" $force = Get-AnsibleParam -obj $params -name "force" -type "bool" -default $false -# Check the arguments -if ($enabled) { - $result.fwsettings.Add("Enabled", "Yes") -} else { - $result.fwsettings.Add("Enabled", "No") +if ($diff_support) { + $result.diff = @{} + $result.diff.prepared = "" } -$result.fwsettings.Add("Rule Name", $name) -#$result.fwsettings.Add("displayname", $name) +try { + $fw = New-Object -ComObject HNetCfg.FwPolicy2 -if ($state -eq "present") { - $result.fwsettings.Add("Direction", $(ConvertTo-TitleCase($direction))) - $result.fwsettings.Add("Action", $(ConvertTo-TitleCase $action)) -} + $existingRule = $fw.Rules | Where { $_.Name -eq $name } -if ($description -ne $null) { - $result.fwsettings.Add("Description", $description) -} + if ($existingRule -is [System.Array]) { + Fail-Json $result "Multiple firewall rules with name '$name' found." + } -if ($program -ne $null) { - $result.fwsettings.Add("Program", $program) -} + $rule = New-FWRule -name $name ` + -description $description ` + -direction $direction ` + -action $action ` + -applicationName $program ` + -serviceName $service ` + -enabled $enabled ` + -profiles $profiles ` + -localAddresses $localip ` + -remoteAddresses $remoteip ` + -localPorts $localport ` + -remotePorts $remoteport ` + -protocol $protocol ` + -interfaceTypes $interfacetypes ` + -edgeTraversalOptions $edge ` + -secureFlags $security -$result.fwsettings.Add("LocalIP", $localip) -$result.fwsettings.Add("RemoteIP", $remoteip) - -if ($localport -ne $null) { - $result.fwsettings.Add("LocalPort", $localport) -} - -if ($remoteport -ne $null) { - $result.fwsettings.Add("RemotePort", $remoteport) -} - -if ($service -ne $null) { - $result.fwsettings.Add("Service", $(ConvertTo-TitleCase($service))) -} - -if ($protocol -eq "Any") { - $result.fwsettings.Add("Protocol", $protocol) -} else { - $result.fwsettings.Add("Protocol", $protocol.toupper()) -} - -if ($profiles -eq "Any") { - $result.fwsettings.Add("Profiles", "Domain,Private,Public") -} else { - $result.fwsettings.Add("Profiles", $(ConvertTo-TitleCase($profiles))) -} - -$result.fwsettings.Add("Edge traversal", $(ConvertTo-TitleCase($edge))) - -if ($interfacetypes -ne $null) { - $result.fwsettings.Add("InterfaceTypes", $(ConvertTo-TitleCase($interfacetypes))) -} - -switch($security) { - "Authenticate" { $security = "Authenticate" } - "AuthDynEnc" { $security = "AuthDynEnc" } - "AuthEnc" { $security = "AuthEnc" } - "AuthNoEncap" { $security = "AuthNoEncap" } - "NotRequired" { $security = "NotRequired" } -} -$result.fwsettings.Add("Security", $security) - -# FIXME: Define unsupported options -#$result.fwsettings.Add("Grouping", "") -#$result.fwsettings.Add("Rule source", "Local Setting") - -$get = getFirewallRule($result.fwsettings) -$result.msg += $get.msg - -if ($get.failed) { - $result.error = $get.error - $result.output = $get.output - Fail-Json $result $result.msg -} - -$result.diff = $get.diff - -if ($state -eq "present") { - if (-not $get.exists) { - - $create = createFireWallRule($result.fwsettings) - $result.msg += $create.msg - $result.diff = $create.diff - - if ($create.failed) { - $result.error = $create.error - $result.output = $create.output - Fail-Json $result $result.msg - } - - $result.changed = $true - - } elseif (-not $get.identical) { - # FIXME: This ought to use netsh advfirewall firewall set instead ! - if ($force) { - - $remove = removeFirewallRule($result.fwsettings) - # NOTE: We retain the diff output from $get.diff here - $result.msg += $remove.msg - - if ($remove.failed) { - $result.error = $remove.error - $result.output = $remove.output - Fail-Json $result $result.msg - } - - $create = createFireWallRule($result.fwsettings) - # NOTE: We retain the diff output from $get.diff here - $result.msg += $create.msg - - if ($create.failed) { - $result.error = $create.error - $result.output = $create.output - Fail-Json $result $result.msg - } - - $result.changed = $true + $fwPropertiesToCompare = @('Name','Description','Direction','Action','ApplicationName','ServiceName','Enabled','Profiles','LocalAddresses','RemoteAddresses','LocalPorts','RemotePorts','Protocol','InterfaceTypes', 'EdgeTraversalOptions', 'SecureFlags') + if ($state -eq "absent") { + if ($existingRule -eq $null) { + $result.msg = "Firewall rule '$name' does not exist." } else { + if ($diff_support) { + foreach ($prop in $fwPropertiesToCompare) { + $result.diff.prepared += "-[$($prop)='$($existingRule.$prop)']`n" + } + } - $result.msg += @("There was already a rule '$name' with different values, use the 'force' parameter to overwrite it") - Fail-Json $result $result.msg - + if (-not $check_mode) { + $fw.Rules.Remove($existingRule.Name) + } + $result.changed = $true + $result.msg = "Firewall rule '$name' removed." } - } else { + } elseif ($state -eq "present") { + if ($existingRule -eq $null) { + if ($diff_support) { + foreach ($prop in $fwPropertiesToCompare) { + $result.diff.prepared += "+[$($prop)='$($existingRule.$prop)']`n" + } + } - $result.msg += @("Firewall rule '$name' was already created") + if (-not $check_mode) { + $fw.Rules.Add($rule) + } + $result.changed = $true + $result.msg = "Firewall rule '$name' created." + } else { + foreach ($prop in $fwPropertiesToCompare) { + if ($existingRule.$prop -ne $rule.$prop) { + if ($diff_support) { + $result.diff.prepared += "-[$($prop)='$($existingRule.$prop)']`n" + $result.diff.prepared += "+[$($prop)='$($rule.$prop)']`n" + } - } + if (-not $check_mode) { + $existingRule.$prop = $rule.$prop + } + $result.changed = $true + } + } -} elseif ($state -eq "absent") { - - if ($get.exists) { - - $remove = removeFirewallRule($result.fwsettings) - $result.diff = $remove.diff - $result.msg += $remove.msg - - if ($remove.failed) { - $result.error = $remove.error - $result.output = $remove.output - Fail-Json $result $result.msg + if ($result.changed) { + $result.msg = "Firewall rule '$name' changed." + } else { + $result.msg = "Firewall rule '$name' already exists." + } } - - $result.changed = $true - - } else { - - $result.msg += @("Firewall rule '$name' did not exist") - } +} catch [Exception] { + Fail-Json $result $_.Exception.Message } Exit-Json $result diff --git a/lib/ansible/modules/windows/win_firewall_rule.py b/lib/ansible/modules/windows/win_firewall_rule.py index 60fd296f61b..2819a0fff3e 100644 --- a/lib/ansible/modules/windows/win_firewall_rule.py +++ b/lib/ansible/modules/windows/win_firewall_rule.py @@ -1,21 +1,7 @@ #!/usr/bin/env python - -# (c) 2014, Timothy Vandenbrande -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . +# Copyright (c) 2017 Artem Zinenko +# Copyright (c) 2014 Timothy Vandenbrande +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], @@ -26,80 +12,70 @@ DOCUMENTATION = r''' --- module: win_firewall_rule version_added: "2.0" -author: Timothy Vandenbrande +author: + - Artem Zinenko (@ar7z1) + - Timothy Vandenbrande (@TimothyVandenbrande) short_description: Windows firewall automation description: - - Allows you to create/remove/update firewall rules + - Allows you to create/remove/update firewall rules. options: - enabled: - description: - - Is this firewall rule enabled or disabled - default: 'yes' - choices: [ 'no', 'yes' ] - aliases: [ 'enable' ] - state: - description: - - Should this rule be added or removed - default: "present" - choices: ['present', 'absent'] - name: - description: - - The rules name - required: true - direction: - description: - - Is this rule for inbound or outbound traffic - required: true - choices: ['in', 'out'] - action: - description: - - What to do with the items this rule is for - required: true - choices: ['allow', 'block', 'bypass'] + enabled: description: - description: - - Description for the firewall rule - localip: - description: - - The local ip address this rule applies to - default: 'any' - remoteip: - description: - - The remote ip address/range this rule applies to - default: 'any' - localport: - description: - - The local port this rule applies to - remoteport: - description: - - The remote port this rule applies to - program: - description: - - The program this rule applies to - service: - description: - - The service this rule applies to - protocol: - description: - - The protocol this rule applies to - default: 'any' - profiles: - description: - - The profile this rule applies to - default: 'domain,private,public' - aliases: [ 'profile' ] - force: - description: - - Replace any existing rule by removing it first. - default: 'no' - choices: [ 'no', 'yes' ] -notes: -- The implementation uses C(netsh advfirewall) underneath, a pure-Powershell - reimplementation would be more powerful. -- Modifying existing firewall rules is not possible, the module does allow - replacing complete rules based on name, but that works by removing the - existing rule completely, and recreating it with provided information - (when using C(force)). + - Is this firewall rule enabled or disabled. + type: bool + default: 'yes' + aliases: [ 'enable' ] + state: + description: + - Should this rule be added or removed. + default: "present" + choices: ['present', 'absent'] + name: + description: + - The rules name + required: true + direction: + description: + - Is this rule for inbound or outbound traffic. + required: true + choices: ['in', 'out'] + action: + description: + - What to do with the items this rule is for. + required: true + choices: ['allow', 'block', 'bypass'] + description: + description: + - Description for the firewall rule. + localip: + description: + - The local ip address this rule applies to. + default: 'any' + remoteip: + description: + - The remote ip address/range this rule applies to. + default: 'any' + localport: + description: + - The local port this rule applies to. + remoteport: + description: + - The remote port this rule applies to. + program: + description: + - The program this rule applies to. + service: + description: + - The service this rule applies to. + protocol: + description: + - The protocol this rule applies to. + default: 'any' + profiles: + description: + - The profile this rule applies to. + default: 'domain,private,public' + aliases: [ 'profile' ] ''' EXAMPLES = r''' diff --git a/test/integration/targets/win_firewall_rule/tasks/main.yml b/test/integration/targets/win_firewall_rule/tasks/main.yml index b8717507a92..b847367ee5e 100644 --- a/test/integration/targets/win_firewall_rule/tasks/main.yml +++ b/test/integration/targets/win_firewall_rule/tasks/main.yml @@ -2,11 +2,8 @@ win_firewall_rule: name: http state: absent - action: "{{ item }}" + action: allow direction: in - with_items: - - allow - - block - name: Add firewall rule win_firewall_rule: @@ -82,7 +79,7 @@ direction: in protocol: tcp -- name: Add different firewall rule +- name: Change firewall rule win_firewall_rule: name: http enabled: yes @@ -91,31 +88,12 @@ action: block direction: in protocol: tcp - ignore_errors: yes - register: add_different_firewall_rule_without_force + register: change_firewall_rule -- name: Check that creating different firewall rule without enabling force setting fails +- name: Check that changing firewall rule succeeds assert: that: - - add_different_firewall_rule_without_force.failed == true - - add_different_firewall_rule_without_force.changed == false - -- name: Add different firewall rule with force setting - win_firewall_rule: - name: http - enabled: yes - state: present - localport: 80 - action: block - direction: in - protocol: tcp - force: yes - register: add_different_firewall_rule_with_force - -- name: Check that creating different firewall rule with enabling force setting succeeds - assert: - that: - - add_different_firewall_rule_with_force.changed == true + - change_firewall_rule.changed == true - name: Add firewall rule when remoteip is range win_firewall_rule: @@ -127,7 +105,6 @@ action: allow direction: in protocol: tcp - force: yes - name: Add same firewall rule when remoteip is range (again) win_firewall_rule: @@ -156,7 +133,6 @@ action: allow direction: in protocol: tcp - force: yes - name: Add same firewall rule when remoteip in CIDR notation (again) win_firewall_rule: @@ -181,11 +157,10 @@ enabled: yes state: present localport: 80 - remoteip: 192.168.0.0/255.255.255.0 + remoteip: 192.168.1.0/255.255.255.0 action: allow direction: in protocol: tcp - force: yes - name: Add same firewall rule when remoteip contains a netmask (again) win_firewall_rule: @@ -193,7 +168,7 @@ enabled: yes state: present localport: 80 - remoteip: 192.168.0.0/255.255.255.0 + remoteip: 192.168.1.0/255.255.255.0 action: allow direction: in protocol: tcp @@ -214,7 +189,6 @@ action: allow direction: in protocol: tcp - force: yes - name: Add same firewall rule when remoteip is IPv4 (again) win_firewall_rule: @@ -232,3 +206,122 @@ assert: that: - add_firewall_rule_with_ipv4_remoteip_again.changed == false + +- name: Add firewall rule when remoteip contains a netmask + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.2.0/255.255.255.0 + action: allow + direction: in + protocol: tcp + +- name: Add same firewall rule when remoteip in CIDR notation + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.2.0/24 + action: allow + direction: in + protocol: tcp + register: add_same_firewall_rule_with_cidr_remoteip + +- name: Check that creating same firewall rule succeeds without a change when remoteip contains a netmask or CIDR + assert: + that: + - add_same_firewall_rule_with_cidr_remoteip.changed == false + +- name: Add firewall rule with multiple ports + win_firewall_rule: + name: http + enabled: yes + state: present + localport: '80,81' + action: allow + direction: in + protocol: tcp + register: add_firewall_rule_with_multiple_ports + +- name: Check that creating firewall rule with multiple ports succeeds with a change + assert: + that: + - add_firewall_rule_with_multiple_ports.changed == true + +- name: Add firewall rule with interface types + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + interfacetypes: 'ras,lan,wireless' + register: add_firewall_rule_with_interface_types + +- name: Check that creating firewall rule with interface types succeeds with a change + assert: + that: + - add_firewall_rule_with_interface_types.changed == true + +- name: Add firewall rule with interface type 'any' + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + interfacetypes: any + register: add_firewall_rule_with_interface_type_any + +- name: Check that creating firewall rule with interface type 'any' succeeds with a change + assert: + that: + - add_firewall_rule_with_interface_type_any.changed == true + +- name: Add firewall rule with edge traversal option 'deferapp' + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + edge: deferapp + register: add_firewall_rule_with_edge_traversal + +# Setup action creates ansible_distribution_version variable +- action: setup + +- name: Check that creating firewall rule with enge traversal option 'deferapp' succeeds with a change + assert: + that: + - add_firewall_rule_with_edge_traversal.changed == true + # Works on windows >= Windows 7/Windows Server 2008 R2 + when: ansible_distribution_version | version_compare('6.1', '>=') + +- name: Add firewall rule with 'authenticate' secure flag + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + security: authenticate + register: add_firewall_rule_with_secure_flags + +- name: Check that creating firewall rule with secure flag 'authenticate' succeeds with a change + assert: + that: + - add_firewall_rule_with_secure_flags.changed == true + # Works on windows >= Windows 8/Windows Server 2012 + when: ansible_distribution_version | version_compare('6.2', '>=')