win_firewall_rule: Implement idempotency, check-mode and diff support (#23162)

* win_firewall_rule: Small idempotency fix

This PR includes the following changes:
- an idempotency fix when `profile: any`
- better difference output to debug idempotency issues
- documentation fixes (remove `required: false`)
- Parameter handling fixes
- RDP example that matches default RDP rule
- Renamed parameter 'enable' to 'enabled' (kept alias)
- Renamed parameter 'profile' to 'profiles' (kept alias)

* Rewrite module completely

The logic is still intact, but various changes with a single goal:

- Make the module idempotent
- Implement check-mode
- Implement diff-mode
- Adapted integration tests

This fixes #18807 and #23455.

* Change casing to lowercase

* Improve the logic wrt. diff
This commit is contained in:
Dag Wieers 2017-05-31 01:10:34 +02:00 committed by Matt Davis
parent 0e160d5c7e
commit d958440bcb
3 changed files with 448 additions and 412 deletions

View file

@ -20,12 +20,30 @@
# WANT_JSON
# POWERSHELL_COMMON
# TODO: Reimplement this using Powershell cmdlets
$ErrorActionPreference = "Stop"
function convertToNetmask($maskLength) {
[IPAddress] $ip = 0;
[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) {
@ -54,351 +72,382 @@ function preprocessAndCompare($key, $outputValue, $fwsettingValue) {
}
}
}
elseif ($key -eq 'Profiles') {
if (($fwsettingValue -eq "any") -and ($outputValue -eq "Domain,Private,Public")) {
return $true
}
}
return $false
}
function getFirewallRule ($fwsettings) {
try {
$diff = $false
$result = @{
changed = $false
identical = $false
exists = $false
failed = $false
msg = @()
multiple = $false
}
#$output = Get-NetFirewallRule -name $($fwsettings.'Rule Name');
$rawoutput=@(netsh advfirewall firewall show rule name="$($fwsettings.'Rule Name')" verbose)
if (!($rawoutput -eq 'No rules match the specified criteria.')){
$rawoutput | Where {$_ -match '^([^:]+):\s*(\S.*)$'} | Foreach -Begin {
$FirstRun = $true;
$HashProps = @{};
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 (!($FirstRun))) {
#$output=New-Object -TypeName PSCustomObject -Property $HashProps;
$output=$HashProps;
$HashProps = @{};
};
$HashProps.$($Matches[1]) = $Matches[2];
$FirstRun = $false;
if (($Matches[1] -eq 'Rule Name') -and (-not $FirstRun)) {
$output = $HashProps
$HashProps = @{}
}
$HashProps.$($Matches[1]) = $Matches[2]
$FirstRun = $false
} -End {
#$output=New-Object -TypeName PSCustomObject -Property $HashProps;
$output=$HashProps;
$output = $HashProps
}
}
$exists=$false;
$correct=$true;
$diff=$false;
$multi=$false;
$correct=$false;
$difference=@();
$msg=@();
if ($($output|measure).count -gt 0) {
$exists=$true;
$msg += @("The rule '" + $fwsettings.'Rule Name' + "' exists.");
if ($($output|measure).count -gt 1) {
$multi=$true
$msg += @("The rule '" + $fwsettings.'Rule Name' + "' has multiple entries.");
ForEach($rule in $output.GetEnumerator()) {
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 ( $rule.$fwsetting -ne $fwsettings.$fwsetting) {
$diff=$true;
#$difference+=@($fwsettings.$($fwsetting.Key));
$difference+=@("output:$rule.$fwsetting,fwsetting:$fwsettings.$fwsetting");
};
};
if ($diff -eq $false) {
$correct=$true
};
};
} else {
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
} else {
$diff=$true;
$difference+=@($fwsettings.$($fwsetting.Key));
};
};
};
if ($diff -eq $false) {
$correct=$true
};
};
if ($correct) {
$msg += @("An identical rule exists");
} else {
$msg += @("The rule exists but has different values");
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 {
$msg += @("No rule could be found");
};
$result = @{
failed = $false
exists = $exists
identical = $correct
multiple = $multi
difference = $difference
msg = $msg
$result.failed = $true
}
} catch [Exception]{
$result = @{
failed = $true
error = $_.Exception.Message
msg = $msg
}
};
} catch [Exception] {
$result.failed = $true
$result.error = $_.Exception.Message
}
return $result
};
}
function createFireWallRule ($fwsettings) {
$msg=@()
$execString="netsh advfirewall firewall add rule"
$result = @{
changed = $false
failed = $false
msg = @()
}
$command = "netsh advfirewall firewall add rule"
ForEach ($fwsetting in $fwsettings.GetEnumerator()) {
if ($fwsetting.key -eq 'Direction') {
$key='dir'
} elseif ($fwsetting.key -eq 'Rule Name') {
$key='name'
} elseif ($fwsetting.key -eq 'Enabled') {
$key='enable'
} elseif ($fwsetting.key -eq 'Profiles') {
$key='profile'
} else {
$key=$($fwsetting.key).ToLower()
};
$execString+=" ";
$execString+=$key;
$execString+="=";
$execString+='"';
$execString+=$fwsetting.value;
$execString+='"';
};
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 {
#$msg+=@($execString);
$output=$(Invoke-Expression $execString| ? {$_});
$msg+=@("Created firewall rule $name");
$result=@{
failed = $false
output=$output
changed=$true
msg=$msg
};
$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]{
$msg=@("Failed to create the rule")
$result=@{
output=$output
failed=$true
error=$_.Exception.Message
msg=$msg
};
};
$result.error = $_.Exception.Message
$result.failed = $true
$result.msg = @("Failed to create the rule '$name'")
}
return $result
};
}
function removeFireWallRule ($fwsettings) {
$msg=@()
$result = @{
changed = $false
failed = $false
msg = @()
}
$command = "netsh advfirewall firewall delete rule name='$($fwsettings.'Rule Name')'"
try {
$rawoutput=@(netsh advfirewall firewall delete rule name="$($fwsettings.'Rule Name')")
$rawoutput | Where {$_ -match '^([^:]+):\s*(\S.*)$'} | Foreach -Begin {
$FirstRun = $true;
$HashProps = @{};
} -Process {
if (($Matches[1] -eq 'Rule Name') -and (!($FirstRun))) {
$output=$HashProps;
$HashProps = @{};
};
$HashProps.$($Matches[1]) = $Matches[2];
$FirstRun = $false;
} -End {
$output=$HashProps;
};
$msg+=@("Removed the rule")
$result=@{
failed=$false
changed=$true
msg=$msg
output=$output
};
} catch [Exception]{
$msg+=@("Failed to remove the rule")
$result=@{
failed=$true
error=$_.Exception.Message
msg=$msg
$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
}
# Mount Drives
$change=$false;
$fail=$false;
$msg=@();
$fwsettings=@{}
# FIXME: Unsupported keys
#$unsupported = @("Grouping", "Rule source")
$unsupported = @("Rule source")
# Variabelise the arguments
$params=Parse-Args $args;
$result = @{
changed = $false
fwsettings = @{}
msg = @()
}
$params = Parse-Args $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
$diff_support = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false
$name = Get-AnsibleParam -obj $params -name "name" -failifempty $true
$direction = Get-AnsibleParam -obj $params -name "direction" -failifempty $true -validateSet "in","out"
$action = Get-AnsibleParam -obj $params -name "action" -failifempty $true -validateSet "allow","block","bypass"
$program = Get-AnsibleParam -obj $params -name "program"
$service = Get-AnsibleParam -obj $params -name "service" -default "any"
$description = Get-AnsibleParam -obj $params -name "description"
$enable = ConvertTo-Bool (Get-AnsibleParam -obj $params -name "enable" -default "true")
$winprofile = Get-AnsibleParam -obj $params -name "profile" -default "any"
$localip = Get-AnsibleParam -obj $params -name "localip" -default "any"
$remoteip = Get-AnsibleParam -obj $params -name "remoteip" -default "any"
$localport = Get-AnsibleParam -obj $params -name "localport" -default "any"
$remoteport = Get-AnsibleParam -obj $params -name "remoteport" -default "any"
$protocol = Get-AnsibleParam -obj $params -name "protocol" -default "any"
$description = Get-AnsibleParam -obj $params -name "description" -type "str"
$direction = Get-AnsibleParam -obj $params -name "direction" -type "str" -failifempty $true -validateset "in","out"
$action = Get-AnsibleParam -obj $params -name "action" -type "str" -failifempty $true -validateset "allow","block","bypass"
$program = Get-AnsibleParam -obj $params -name "program" -type "str"
$service = Get-AnsibleParam -obj $params -name "service" -type "str"
$enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool" -default $true -aliases "enable"
$profiles = Get-AnsibleParam -obj $params -name "profiles" -type "str" -default "domain,private,public" -aliases "profile"
$localip = Get-AnsibleParam -obj $params -name "localip" -type "str" -default "any"
$remoteip = Get-AnsibleParam -obj $params -name "remoteip" -type "str" -default "any"
$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"
$state = Get-AnsibleParam -obj $params -name "state" -failifempty $true -validateSet "present","absent"
$force = ConvertTo-Bool (Get-AnsibleParam -obj $params -name "force" -default "false")
$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 ($enable -eq $true) {
$fwsettings.Add("Enabled", "yes");
} Else {
$fwsettings.Add("Enabled", "no");
};
$fwsettings.Add("Rule Name", $name)
#$fwsettings.Add("displayname", $name)
$state = $state.ToString().ToLower()
If ($state -eq "present"){
$fwsettings.Add("Direction", $direction)
$fwsettings.Add("Action", $action)
};
If ($description) {
$fwsettings.Add("Description", $description);
}
If ($program) {
$fwsettings.Add("Program", $program);
}
$fwsettings.Add("LocalIP", $localip);
$fwsettings.Add("RemoteIP", $remoteip);
$fwsettings.Add("LocalPort", $localport);
$fwsettings.Add("RemotePort", $remoteport);
$fwsettings.Add("Service", $service);
$fwsettings.Add("Protocol", $protocol);
$fwsettings.Add("Profiles", $winprofile)
$output=@()
$capture=getFirewallRule ($fwsettings);
if ($capture.failed -eq $true) {
$msg+=$capture.msg;
$result=New-Object psobject @{
changed=$false
failed=$true
error=$capture.error
msg=$msg
};
Exit-Json $result;
if ($enabled) {
$result.fwsettings.Add("Enabled", "Yes")
} else {
$diff=$capture.difference
$msg+=$capture.msg;
$identical=$capture.identical;
$multiple=$capture.multiple;
$result.fwsettings.Add("Enabled", "No")
}
$result.fwsettings.Add("Rule Name", $name)
#$result.fwsettings.Add("displayname", $name)
switch ($state){
"present" {
if ($capture.exists -eq $false) {
$capture=createFireWallRule($fwsettings);
$msg+=$capture.msg;
$change=$true;
if ($capture.failed -eq $true){
$result=New-Object psobject @{
failed=$capture.failed
error=$capture.error
output=$capture.output
changed=$change
msg=$msg
difference=$diff
fwsettings=$fwsettings
};
Exit-Json $result;
}
} elseif ($capture.identical -eq $false) {
if ($force -eq $true) {
$capture=removeFirewallRule($fwsettings);
$msg+=$capture.msg;
$change=$true;
if ($capture.failed -eq $true){
$result=New-Object psobject @{
failed=$capture.failed
error=$capture.error
changed=$change
msg=$msg
output=$capture.output
fwsettings=$fwsettings
};
Exit-Json $result;
}
$capture=createFireWallRule($fwsettings);
$msg+=$capture.msg;
$change=$true;
if ($capture.failed -eq $true){
$result=New-Object psobject @{
failed=$capture.failed
error=$capture.error
changed=$change
msg=$msg
difference=$diff
fwsettings=$fwsettings
};
Exit-Json $result;
}
if ($state -eq "present") {
$result.fwsettings.Add("Direction", $(ConvertTo-TitleCase($direction)))
$result.fwsettings.Add("Action", $(ConvertTo-TitleCase $action))
}
} else {
$fail=$true
$msg+=@("There was already a rule $name with different values, use force=True to overwrite it");
if ($description -ne $null) {
$result.fwsettings.Add("Description", $description)
}
if ($program -ne $null) {
$result.fwsettings.Add("Program", $program)
}
$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
}
} elseif ($capture.identical -eq $true) {
$msg+=@("Firewall rule $name was already created");
};
}
"absent" {
if ($capture.exists -eq $true) {
$capture=removeFirewallRule($fwsettings);
$msg+=$capture.msg;
$change=$true;
if ($capture.failed -eq $true){
$result=New-Object psobject @{
failed=$capture.failed
error=$capture.error
changed=$change
msg=$msg
output=$capture.output
fwsettings=$fwsettings
};
Exit-Json $result;
$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
} else {
$msg+=@("Firewall rule $name did not exist");
};
$result.msg += @("There was already a rule '$name' with different values, use the 'force' parameter to overwrite it")
Fail-Json $result $result.msg
}
} else {
$result.msg += @("Firewall rule '$name' was already created")
}
};
} elseif ($state -eq "absent") {
$result=New-Object psobject @{
failed=$fail
changed=$change
msg=$msg
difference=$diff
fwsettings=$fwsettings
};
if ($get.exists) {
$remove = removeFirewallRule($result.fwsettings)
$result.diff = $remove.diff
$result.msg += $remove.msg
Exit-Json $result;
if ($remove.failed) {
$result.error = $remove.error
$result.output = $remove.output
Fail-Json $result $result.msg
}
$result.changed = $true
} else {
$result.msg += @("Firewall rule '$name' did not exist")
}
}
Exit-Json $result

View file

@ -29,100 +29,98 @@ version_added: "2.0"
author: Timothy Vandenbrande
short_description: Windows firewall automation
description:
- allows you to create/remove/update firewall rules
- Allows you to create/remove/update firewall rules
options:
enable:
enabled:
description:
- is this firewall rule enabled or disabled
default: true
required: false
- Is this firewall rule enabled or disabled
default: 'yes'
choices: [ 'no', 'yes' ]
aliases: [ 'enable' ]
state:
description:
- should this rule be added or removed
- Should this rule be added or removed
default: "present"
required: true
choices: ['present', 'absent']
name:
description:
- the rules name
default: null
- The rules name
required: true
direction:
description:
- is this rule for inbound or outbound traffic
default: null
- 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
default: null
- What to do with the items this rule is for
required: true
choices: ['allow', 'block', 'bypass']
description:
description:
- description for the firewall rule
default: null
required: false
- Description for the firewall rule
localip:
description:
- the local ip address this rule applies to
- The local ip address this rule applies to
default: 'any'
required: false
remoteip:
description:
- the remote ip address/range this rule applies to
- The remote ip address/range this rule applies to
default: 'any'
required: false
localport:
description:
- the local port this rule applies to
default: 'any'
required: false
- The local port this rule applies to
remoteport:
description:
- the remote port this rule applies to
default: 'any'
required: false
- The remote port this rule applies to
program:
description:
- the program this rule applies to
default: null
required: false
- The program this rule applies to
service:
description:
- the service this rule applies to
default: 'any'
required: false
- The service this rule applies to
protocol:
description:
- the protocol this rule applies to
- The protocol this rule applies to
default: 'any'
required: false
profile:
profiles:
description:
- the profile this rule applies to, e.g. Domain,Private,Public
default: 'any'
required: false
- The profile this rule applies to
default: 'domain,private,public'
aliases: [ 'profile' ]
force:
description:
- Enforces the change if a rule with different values exists
default: false
required: false
- 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)).
'''
EXAMPLES = r'''
- name: Firewall rule to allow smtp on TCP port 25
action: win_firewall_rule
args:
name: smtp
enable: yes
state: present
localport: 25
action: allow
direction: In
protocol: TCP
- name: Firewall rule to allow SMTP on TCP port 25
win_firewall_rule:
name: SMTP
localport: 25
action: allow
direction: in
protocol: tcp
state: present
enabled: yes
- name: Firewall rule to allow RDP on TCP port 3389
win_firewall_rule:
name: Remote Desktop
localport: 3389
action: allow
direction: in
protocol: tcp
profiles: private
state: present
enabled: yes
'''

View file

@ -3,7 +3,7 @@
name: http
state: absent
action: "{{ item }}"
direction: In
direction: in
with_items:
- allow
- block
@ -11,90 +11,86 @@
- name: Add firewall rule
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: add_firewall_rule
- name: Check that creating new firewall rule succeeds with a change
assert:
that:
- add_firewall_rule.failed == false
- add_firewall_rule.changed == true
- name: Add same firewall rule (again)
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: add_firewall_rule_again
- name: Check that creating same firewall rule succeeds without a change
assert:
that:
- add_firewall_rule_again.failed == false
- add_firewall_rule_again.changed == false
- name: Remove firewall rule
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: absent
localport: 80
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: remove_firewall_rule
- name: Check that removing existing firewall rule succeeds with a change
assert:
that:
- remove_firewall_rule.failed == false
- remove_firewall_rule.changed == true
- name: Remove absent firewall rule
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: absent
localport: 80
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: remove_absent_firewall_rule
- name: Check that removing non existing firewall rule succeeds without a change
assert:
that:
- remove_absent_firewall_rule.failed == false
- remove_absent_firewall_rule.changed == false
- name: Add firewall rule
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
- name: Add different firewall rule
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
action: block
direction: In
protocol: TCP
direction: in
protocol: tcp
ignore_errors: yes
register: add_different_firewall_rule_without_force
@ -103,143 +99,136 @@
that:
- add_different_firewall_rule_without_force.failed == true
- add_different_firewall_rule_without_force.changed == false
- add_different_firewall_rule_without_force.difference == ["block"]
- name: Add different firewall rule with force setting
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
action: block
direction: In
protocol: TCP
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.failed == false
- add_different_firewall_rule_with_force.changed == true
- add_different_firewall_rule_with_force.difference == ["block"]
- name: Add firewall rule when remoteip is range
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.1-192.168.0.5
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
force: yes
- name: Add same firewall rule when remoteip is range (again)
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.1-192.168.0.5
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: add_firewall_rule_with_range_remoteip_again
- name: Check that creating same firewall rule when remoteip is range succeeds without a change
assert:
that:
- add_firewall_rule_with_range_remoteip_again.failed == false
- add_firewall_rule_with_range_remoteip_again.changed == false
- name: Add firewall rule when remoteip in CIDR notation
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.0/24
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
force: yes
- name: Add same firewall rule when remoteip in CIDR notation (again)
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.0/24
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: add_firewall_rule_with_cidr_remoteip_again
- name: Check that creating same firewall rule succeeds without a change when remoteip in CIDR notation
assert:
that:
- add_firewall_rule_with_cidr_remoteip_again.failed == false
- add_firewall_rule_with_cidr_remoteip_again.changed == false
- name: Add firewall rule when remoteip contains a netmask
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.0/255.255.255.0
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
force: yes
- name: Add same firewall rule when remoteip contains a netmask (again)
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.0/255.255.255.0
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: add_firewall_rule_remoteip_contains_netmask_again
- name: Check that creating same firewall rule succeeds without a change when remoteip contains a netmask
assert:
that:
- add_firewall_rule_remoteip_contains_netmask_again.failed == false
- add_firewall_rule_remoteip_contains_netmask_again.changed == false
- name: Add firewall rule when remoteip is IPv4
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.1
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
force: yes
- name: Add same firewall rule when remoteip is IPv4 (again)
win_firewall_rule:
name: http
enable: yes
enabled: yes
state: present
localport: 80
remoteip: 192.168.0.1
action: allow
direction: In
protocol: TCP
direction: in
protocol: tcp
register: add_firewall_rule_with_ipv4_remoteip_again
- name: Check that creating same firewall rule when remoteip is IPv4 succeeds without a change
assert:
that:
- add_firewall_rule_with_ipv4_remoteip_again.failed == false
- add_firewall_rule_with_ipv4_remoteip_again.changed == false