Created
January 18, 2016 13:50
-
-
Save tcr-ableton/63509ca3b27423b6f9fe to your computer and use it in GitHub Desktop.
ansible failing win_firewall_rule output
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!powershell | |
# | |
# (c) 2014, Timothy Vandenbrande <[email protected]> | |
# | |
# 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 <http://www.gnu.org/licenses/>. | |
# | |
# WANT_JSON | |
# This particular file snippet, and this file snippet only, is BSD licensed. | |
# Modules you write using this snippet, which is embedded dynamically by Ansible | |
# still belong to the author of the module, and may assign their own license | |
# to the complete work. | |
# | |
# Copyright (c), Michael DeHaan <[email protected]>, 2014, and others | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without modification, | |
# are permitted provided that the following conditions are met: | |
# | |
# * Redistributions of source code must retain the above copyright | |
# notice, this list of conditions and the following disclaimer. | |
# * Redistributions in binary form must reproduce the above copyright notice, | |
# this list of conditions and the following disclaimer in the documentation | |
# and/or other materials provided with the distribution. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | |
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
# | |
Set-StrictMode -Version 2.0 | |
# Ansible v2 will insert the module arguments below as a string containing | |
# JSON; assign them to an environment variable and redefine $args so existing | |
# modules will continue to work. | |
$complex_args = @' | |
{"direction": "In", "protocol": "TCP", "name": "jenkins-slave-135", "enabled": true, "state": "present", "action": "allow", "localport": 135} | |
'@ | |
Set-Content env:MODULE_COMPLEX_ARGS -Value $complex_args | |
$args = @('env:MODULE_COMPLEX_ARGS') | |
# Helper function to set an "attribute" on a psobject instance in powershell. | |
# This is a convenience to make adding Members to the object easier and | |
# slightly more pythonic | |
# Example: Set-Attr $result "changed" $true | |
Function Set-Attr($obj, $name, $value) | |
{ | |
# If the provided $obj is undefined, define one to be nice | |
If (-not $obj.GetType) | |
{ | |
$obj = New-Object psobject | |
} | |
Try | |
{ | |
$obj.$name = $value | |
} | |
Catch | |
{ | |
$obj | Add-Member -Force -MemberType NoteProperty -Name $name -Value $value | |
} | |
} | |
# Helper function to convert a powershell object to JSON to echo it, exiting | |
# the script | |
# Example: Exit-Json $result | |
Function Exit-Json($obj) | |
{ | |
# If the provided $obj is undefined, define one to be nice | |
If (-not $obj.GetType) | |
{ | |
$obj = New-Object psobject | |
} | |
echo $obj | ConvertTo-Json -Compress -Depth 99 | |
Exit | |
} | |
# Helper function to add the "msg" property and "failed" property, convert the | |
# powershell object to JSON and echo it, exiting the script | |
# Example: Fail-Json $result "This is the failure message" | |
Function Fail-Json($obj, $message = $null) | |
{ | |
# If we weren't given 2 args, and the only arg was a string, create a new | |
# psobject and use the arg as the failure message | |
If ($message -eq $null -and $obj.GetType().Name -eq "String") | |
{ | |
$message = $obj | |
$obj = New-Object psobject | |
} | |
# If the first args is undefined or not an object, make it an object | |
ElseIf (-not $obj -or -not $obj.GetType -or $obj.GetType().Name -ne "PSCustomObject") | |
{ | |
$obj = New-Object psobject | |
} | |
Set-Attr $obj "msg" $message | |
Set-Attr $obj "failed" $true | |
echo $obj | ConvertTo-Json -Compress -Depth 99 | |
Exit 1 | |
} | |
# Helper function to get an "attribute" from a psobject instance in powershell. | |
# This is a convenience to make getting Members from an object easier and | |
# slightly more pythonic | |
# Example: $attr = Get-AnsibleParam $response "code" -default "1" | |
#Get-AnsibleParam also supports Parameter validation to save you from coding that manually: | |
#Example: Get-AnsibleParam -obj $params -name "State" -default "Present" -ValidateSet "Present","Absent" -resultobj $resultobj -failifempty $true | |
#Note that if you use the failifempty option, you do need to specify resultobject as well. | |
Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj, $failifempty=$false, $emptyattributefailmessage, $ValidateSet, $ValidateSetErrorMessage) | |
{ | |
# Check if the provided Member $name exists in $obj and return it or the default. | |
Try | |
{ | |
If (-not $obj.$name.GetType) | |
{ | |
throw | |
} | |
if ($ValidateSet) | |
{ | |
if ($ValidateSet -contains ($obj.$name)) | |
{ | |
$obj.$name | |
} | |
Else | |
{ | |
if ($ValidateSetErrorMessage -eq $null) | |
{ | |
#Auto-generated error should be sufficient in most use cases | |
$ValidateSetErrorMessage = "Argument $name needs to be one of $($ValidateSet -join ",") but was $($obj.$name)." | |
} | |
Fail-Json -obj $resultobj -message $ValidateSetErrorMessage | |
} | |
} | |
Else | |
{ | |
$obj.$name | |
} | |
} | |
Catch | |
{ | |
If ($failifempty -eq $false) | |
{ | |
$default | |
} | |
Else | |
{ | |
If (!$emptyattributefailmessage) | |
{ | |
$emptyattributefailmessage = "Missing required argument: $name" | |
} | |
Fail-Json -obj $resultobj -message $emptyattributefailmessage | |
} | |
} | |
} | |
#Alias Get-attr-->Get-AnsibleParam for backwards compat. | |
New-Alias -Name Get-attr -Value Get-AnsibleParam | |
# Helper filter/pipeline function to convert a value to boolean following current | |
# Ansible practices | |
# Example: $is_true = "true" | ConvertTo-Bool | |
Function ConvertTo-Bool | |
{ | |
param( | |
[parameter(valuefrompipeline=$true)] | |
$obj | |
) | |
$boolean_strings = "yes", "on", "1", "true", 1 | |
$obj_string = [string]$obj | |
if (($obj.GetType().Name -eq "Boolean" -and $obj) -or $boolean_strings -contains $obj_string.ToLower()) | |
{ | |
$true | |
} | |
Else | |
{ | |
$false | |
} | |
return | |
} | |
# Helper function to parse Ansible JSON arguments from a "file" passed as | |
# the single argument to the module. | |
# Example: $params = Parse-Args $args | |
Function Parse-Args($arguments, $supports_check_mode = $false) | |
{ | |
$parameters = New-Object psobject | |
If ($arguments.Length -gt 0) | |
{ | |
$parameters = Get-Content $arguments[0] | ConvertFrom-Json | |
} | |
$check_mode = Get-Attr $parameters "_ansible_check_mode" $false | ConvertTo-Bool | |
If ($check_mode -and -not $supports_check_mode) | |
{ | |
$obj = New-Object psobject | |
Set-Attr $obj "skipped" $true | |
Set-Attr $obj "changed" $false | |
Set-Attr $obj "msg" "remote module does not support check mode" | |
Exit-Json $obj | |
} | |
$parameters | |
} | |
# Helper function to calculate a hash of a file in a way which powershell 3 | |
# and above can handle: | |
Function Get-FileChecksum($path) | |
{ | |
$hash = "" | |
If (Test-Path -PathType Leaf $path) | |
{ | |
$sp = new-object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider; | |
$fp = [System.IO.File]::Open($path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read); | |
$hash = [System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower(); | |
$fp.Dispose(); | |
} | |
ElseIf (Test-Path -PathType Container $path) | |
{ | |
$hash= "3"; | |
} | |
Else | |
{ | |
$hash = "1"; | |
} | |
return $hash | |
} | |
function getFirewallRule ($fwsettings) { | |
try { | |
#$output = Get-NetFirewallRule -name $($fwsettings.name); | |
$rawoutput=@(netsh advfirewall firewall show rule name="$($fwsettings.Name)") | |
if (!($rawoutput -eq 'No rules match the specified criteria.')){ | |
$rawoutput | 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; | |
} -End { | |
#$output=New-Object -TypeName PSCustomObject -Property $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.name + "' exists."); | |
if ($($output|measure).count -gt 1) { | |
$multi=$true | |
$msg += @("The rule '" + $fwsettings.name + "' has multiple entries."); | |
ForEach($rule in $output.GetEnumerator()) { | |
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 (($fwsetting.Key -eq 'RemoteIP') -and ($output.$($fwsetting.Key) -eq ($fwsettings.$($fwsetting.Key)+'-'+$fwsettings.$($fwsetting.Key)))) { | |
$donothing=$false | |
} elseif ((($fwsetting.Key -eq 'Name') -or ($fwsetting.Key -eq 'DisplayName')) -and ($output."Rule Name" -eq $fwsettings.$($fwsetting.Key))) { | |
$donothing=$false | |
} elseif (($fwsetting.Key -eq 'Profile') -and ($output."Profiles" -eq $fwsettings.$($fwsetting.Key))) { | |
$donothing=$false | |
} elseif (($fwsetting.Key -eq 'Enable') -and ($output."Enabled" -eq $fwsettings.$($fwsetting.Key))) { | |
$donothing=$false | |
} 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"); | |
} | |
} else { | |
$msg += @("No rule could be found"); | |
}; | |
$result = @{ | |
exists = $exists | |
identical = $correct | |
multiple = $multi | |
difference = $difference | |
msg = $msg | |
} | |
} catch [Exception]{ | |
$result = @{ | |
failed = $true | |
error = $_.Exception.Message | |
msg = $msg | |
} | |
}; | |
return $result | |
}; | |
function createFireWallRule ($fwsettings) { | |
$msg=@() | |
$execString="netsh advfirewall firewall add rule " | |
ForEach ($fwsetting in $fwsettings.GetEnumerator()) { | |
if ($fwsetting.key -eq 'Direction') { | |
$key='dir' | |
} else { | |
$key=$($fwsetting.key).ToLower() | |
}; | |
$execString+=" "; | |
$execString+=$key; | |
$execString+="="; | |
$execString+='"'; | |
$execString+=$fwsetting.value; | |
$execString+='"'; | |
}; | |
try { | |
#$msg+=@($execString); | |
$output=$(Invoke-Expression $execString| ? {$_}); | |
$msg+=@("Created firewall rule $name"); | |
$result=@{ | |
output=$output | |
changed=$true | |
msg=$msg | |
}; | |
} catch [Exception]{ | |
$msg=@("Failed to create the rule") | |
$result=@{ | |
output=$output | |
failed=$true | |
error=$_.Exception.Message | |
msg=$msg | |
}; | |
}; | |
return $result | |
}; | |
function removeFireWallRule ($fwsettings) { | |
$msg=@() | |
try { | |
$rawoutput=@(netsh advfirewall firewall delete rule name="$($fwsettings.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 | |
} | |
}; | |
return $result | |
} | |
# Mount Drives | |
$change=$false; | |
$fail=$false; | |
$msg=@(); | |
$fwsettings=@{} | |
# Variabelise the arguments | |
$params=Parse-Args $args; | |
$enable=Get-Attr $params "enable" $null; | |
$state=Get-Attr $params "state" "present"; | |
$name=Get-Attr $params "name" ""; | |
$direction=Get-Attr $params "direction" ""; | |
$force=Get-Attr $params "force" $false; | |
$action=Get-Attr $params "action" ""; | |
$misArg = '' | |
# Check the arguments | |
if ($enable -ne $null) { | |
if ($enable -eq $true) { | |
$fwsettings.Add("Enable", "yes"); | |
} elseif ($enable -eq $false) { | |
$fwsettings.Add("Enable", "no"); | |
} else { | |
$misArg+="enable"; | |
$msg+=@("for the enable parameter only yes and no is allowed"); | |
}; | |
}; | |
if (($state -ne "present") -And ($state -ne "absent")){ | |
$misArg+="state"; | |
$msg+=@("for the state parameter only present and absent is allowed"); | |
}; | |
if ($name -eq ""){ | |
$misArg+="Name"; | |
$msg+=@("name is a required argument"); | |
} else { | |
$fwsettings.Add("Name", $name) | |
#$fwsettings.Add("displayname", $name) | |
}; | |
if ((($direction.ToLower() -ne "In") -And ($direction.ToLower() -ne "Out")) -And ($state -eq "present")){ | |
$misArg+="Direction"; | |
$msg+=@("for the Direction parameter only the values 'In' and 'Out' are allowed"); | |
} else { | |
$fwsettings.Add("Direction", $direction) | |
}; | |
if ((($action.ToLower() -ne "allow") -And ($action.ToLower() -ne "block")) -And ($state -eq "present")){ | |
$misArg+="Action"; | |
$msg+=@("for the Action parameter only the values 'allow' and 'block' are allowed"); | |
} else { | |
$fwsettings.Add("Action", $action) | |
}; | |
$args=@( | |
"Description", | |
"LocalIP", | |
"RemoteIP", | |
"LocalPort", | |
"RemotePort", | |
"Program", | |
"Service", | |
"Protocol" | |
) | |
foreach ($arg in $args){ | |
New-Variable -Name $arg -Value $(Get-Attr $params $arg ""); | |
if ((Get-Variable -Name $arg -ValueOnly) -ne ""){ | |
$fwsettings.Add($arg, $(Get-Variable -Name $arg -ValueOnly)); | |
}; | |
}; | |
$winprofile=Get-Attr $params "profile" "current"; | |
$fwsettings.Add("profile", $winprofile) | |
if ($misArg){ | |
$result=New-Object psobject @{ | |
changed=$false | |
failed=$true | |
msg=$msg | |
}; | |
Exit-Json($result); | |
}; | |
$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; | |
} else { | |
$diff=$capture.difference | |
$msg+=$capture.msg; | |
$identical=$capture.identical; | |
$multiple=$capture.multiple; | |
} | |
switch ($state.ToLower()){ | |
"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; | |
} | |
} else { | |
$fail=$true | |
$msg+=@("There was already a rule $name with different values, use force=True to overwrite it"); | |
} | |
} 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; | |
} | |
} else { | |
$msg+=@("Firewall rule $name did not exist"); | |
}; | |
} | |
}; | |
$result=New-Object psobject @{ | |
failed=$fail | |
changed=$change | |
msg=$msg | |
difference=$diff | |
fwsettings=$fwsettings | |
}; | |
Exit-Json $result; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment