DSCResources/MSFT_xFirewall/MSFT_xFirewall.psm1
####################################################################################### # xFirewall : DSC Resource that will set/test/get Firewall Rules ####################################################################################### data LocalizedData { # culture="en-US" ConvertFrom-StringData -StringData @' GettingFirewallRuleMessage=Getting firewall rule with Name '{0}'. FirewallRuleDoesNotExistMessage=Firewall rule with Name '{0}' does not exist. ApplyingFirewallRuleMessage=Applying settings for firewall rule with Name '{0}'. FindFirewallRuleMessage=Find firewall rule with Name '{0}'. FirewallRuleShouldExistMessage=We want the firewall rule with Name '{0}' to exist since Ensure is set to {1}. FirewallRuleShouldExistAndDoesMessage=We want the firewall rule with Name '{0}' to exist and it does. Check for valid properties. CheckFirewallRuleParametersMessage=Check each defined parameter against the existing firewall rule with Name '{0}'. UpdatingExistingFirewallMessage=Updating existing firewall rule with Name '{0}'. FirewallRuleShouldExistAndDoesNotMessage=We want the firewall rule with Name '{0}' to exist, but it does not. FirewallRuleShouldNotExistMessage=We do not want the firewall rule with Name '{0}' to exist since Ensure is set to {1}. FirewallRuleShouldNotExistButDoesMessage=We do not want the firewall rule with Name '{0}' to exist, but it does. Removing it. FirewallRuleShouldNotExistAndDoesNotMessage=We do not want the firewall rule with Name '{0}' to exist, and it does not. CheckingFirewallRuleMessage=Checking settings for firewall rule with Name '{0}'. CheckingFirewallReturningMessage=Check Firewall rule with Name '{0}' returning {1}. CheckingFirewallParametersMessage=Check each defined parameter against the existing Firewall Rule with Name '{0}'. PropertyNoMatchMessage={0} property value '{1}' does not match desired state '{2}'. TestFirewallRuleReturningMessage=Test Firewall rule with Name '{0}' returning {1}. FirewallRuleNotFoundMessage=No Firewall Rule found with Name '{0}'. GetAllPropertiesMessage=Get all the properties and add filter info to rule map. RuleNotUniqueError={0} Firewall Rules with the Name '{1}' were found. Only one expected. '@ } ###################################################################################### # The Get-TargetResource cmdlet. ###################################################################################### function Get-TargetResource { [OutputType([System.Collections.Hashtable])] param ( # Name of the Firewall Rule [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String] $Name ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.GettingFirewallRuleMessage) -f $Name ) -join '') Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FindFirewallRuleMessage) -f $Name ) -join '') $firewallRule = Get-FirewallRule -Name $Name if (-not $firewallRule) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleDoesNotExistMessage) -f $Name ) -join '') return @{ Name = $Name Ensure = 'Absent' } } $properties = Get-FirewallRuleProperty -FirewallRule $firewallRule # Populate the properties for get target resource return @{ Name = $Name Ensure = 'Present' DisplayName = $firewallRule.DisplayName Group = $firewallRule.Group DisplayGroup = $firewallRule.DisplayGroup Enabled = $firewallRule.Enabled Action = $firewallRule.Action Profile = $firewallRule.Profile.ToString() -replace(' ', '') -split(',') Direction = $firewallRule.Direction Description = $firewallRule.Description RemotePort = @($properties.PortFilters.RemotePort) LocalPort = @($properties.PortFilters.LocalPort) Protocol = $properties.PortFilters.Protocol ApplicationPath = $properties.ApplicationFilters.Program Service = $properties.ServiceFilters.Service } } ###################################################################################### # The Set-TargetResource cmdlet. ###################################################################################### function Set-TargetResource { param ( # Name of the Firewall Rule [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String] $Name, # Localized, user-facing name of the Firewall Rule being created [ValidateNotNullOrEmpty()] [String] $DisplayName, # Name of the Firewall Group where we want to put the Firewall Rules [ValidateNotNullOrEmpty()] [String] $DisplayGroup, # Ensure the presence/absence of the resource [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', # Enable or disable the supplied configuration [ValidateSet('True', 'False')] [String] $Enabled, [ValidateSet('NotConfigured', 'Allow', 'Block')] [String] $Action, # Specifies one or more profiles to which the rule is assigned [String[]] $Profile, # Direction of the connection [ValidateSet('Inbound', 'Outbound')] [String] $Direction, # Specific Port used for filter. Specified by port number, range, or keyword [ValidateNotNullOrEmpty()] [String[]] $RemotePort, # Local Port used for the filter [ValidateNotNullOrEmpty()] [String[]] $LocalPort, # Specific Protocol for filter. Specified by name, number, or range [ValidateNotNullOrEmpty()] [String] $Protocol, # Documentation for the Rule [String] $Description, # Path and file name of the program for which the rule is applied [ValidateNotNullOrEmpty()] [String] $ApplicationPath, # Specifies the short name of a Windows service to which the firewall rule applies [ValidateNotNullOrEmpty()] [String] $Service ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.ApplyingFirewallRuleMessage) -f $Name ) -join '') # Remove any parameters not used in Splats $null = $PSBoundParameters.Remove('Ensure') # Effectively renaming DisplayGroup to Group if ($DisplayGroup) { $null = $PSBoundParameters.Add('Group', $DisplayGroup) } $null = $PSBoundParameters.Remove('DisplayGroup') Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FindFirewallRuleMessage) -f $Name ) -join '') $firewallRule = Get-FirewallRule -Name $Name $exists = ($firewallRule -ne $null) if ($Ensure -eq 'Present') { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleShouldExistMessage) -f $Name,$Ensure ) -join '') if ($exists) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleShouldExistAndDoesMessage) -f $Name ) -join '') Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.CheckFirewallRuleParametersMessage) -f $Name ) -join '') if (-not (Test-RuleProperties -FirewallRule $firewallRule @PSBoundParameters)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.UpdatingExistingFirewallMessage) -f $Name ) -join '') # Set the existing Firewall rule based on specified parameters Set-NetFirewallRule @PSBoundParameters } } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleShouldExistAndDoesNotMessage) -f $Name ) -join '') # Set any default parameter values if (-not $DisplayName) { if (-not $PSBoundParameters.ContainsKey('DisplayName')) { $null = $PSBoundParameters.Add('DisplayName',$Name) } else { $PSBoundParameters.DisplayName = $Name } } # Add the new Firewall rule based on specified parameters New-NetFirewallRule @PSBoundParameters } } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleShouldNotExistMessage) -f $Name,$Ensure ) -join '') if ($exists) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleShouldNotExistButDoesMessage) -f $Name ) -join '') # Remove the existing Firewall rule Remove-NetFirewallRule -Name $Name } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleShouldNotExistAndDoesNotMessage) -f $Name ) -join '') # Do Nothing } } } ###################################################################################### # The Test-TargetResource cmdlet. # DSC uses Test-TargetResource cmdlet to check the status of the resource instance on # the target machine ###################################################################################### function Test-TargetResource { [OutputType([System.Boolean])] param ( # Name of the Firewall Rule [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String] $Name, # Localized, user-facing name of the Firewall Rule being created [ValidateNotNullOrEmpty()] [String] $DisplayName, # Name of the Firewall Group where we want to put the Firewall Rules [ValidateNotNullOrEmpty()] [String] $DisplayGroup, # Ensure the presence/absence of the resource [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', # Enable or disable the supplied configuration [ValidateSet('True', 'False')] [String] $Enabled, [ValidateSet('NotConfigured', 'Allow', 'Block')] [String] $Action, # Specifies one or more profiles to which the rule is assigned [String[]] $Profile, # Direction of the connection [ValidateSet('Inbound', 'Outbound')] [String] $Direction, # Specific Port used for filter. Specified by port number, range, or keyword [ValidateNotNullOrEmpty()] [String[]] $RemotePort, # Local Port used for the filter [ValidateNotNullOrEmpty()] [String[]] $LocalPort, # Specific Protocol for filter. Specified by name, number, or range [ValidateNotNullOrEmpty()] [String] $Protocol, # Documentation for the Rule [String] $Description, # Path and file name of the program for which the rule is applied [ValidateNotNullOrEmpty()] [String] $ApplicationPath, # Specifies the short name of a Windows service to which the firewall rule applies [ValidateNotNullOrEmpty()] [String] $Service ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.CheckingFirewallRuleMessage) -f $Name ) -join '') # Remove any parameters not used in Splats $null = $PSBoundParameters.Remove('Ensure') Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FindFirewallRuleMessage) -f $Name ) -join '') $firewallRule = Get-FirewallRule -Name $Name $exists = ($firewallRule -ne $null) if (-not $exists) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleDoesNotExistMessage) -f $Name ) -join '') # Returns whether complies with $Ensure $returnValue = ($false -eq ($Ensure -eq 'Present')) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.CheckingFirewallReturningMessage) -f $Name,$returnValue ) -join '') return $returnValue } Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.CheckingFirewallParametersMessage) -f $Name ) -join '') $desiredConfigurationMatch = Test-RuleProperties -FirewallRule $firewallRule @PSBoundParameters # Returns whether or not $exists complies with $Ensure $returnValue = ($desiredConfigurationMatch -and $exists -eq ($Ensure -eq 'Present')) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.CheckingFirewallReturningMessage) -f $Name,$returnValue ) -join '') return $returnValue } #region HelperFunctions ###################### ## Helper Functions ## ###################### ###################################################################################### # Function to validate if the supplied Rule adheres to all parameters set ###################################################################################### function Test-RuleProperties { param ( [Parameter(Mandatory)] $FirewallRule, [String] $Name, [String] $DisplayName = $Name, [String] $DisplayGroup, [String] $Group, [String] $Enabled = 'True', [string] $Action = 'Allow', [String[]] $Profile = 'Any', [String] $Direction = 'Inbound', [String[]] $RemotePort, [String[]] $LocalPort, [String] $Protocol, [String] $Description, [String] $ApplicationPath, [String] $Service ) $properties = Get-FirewallRuleProperty -FirewallRule $FirewallRule $desiredConfigurationMatch = $true if ($Name -and ($FirewallRule.Name -ne $Name)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Name',$FirewallRule.Name,$Name ) -join '') $desiredConfigurationMatch = $false } if ($Enabled -and ($FirewallRule.Enabled.ToString() -ne $Enabled)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Enabled',$FirewallRule.Enabled.ToString(),$Enabled ) -join '') $desiredConfigurationMatch = $false } if ($Action -and ($FirewallRule.Action -ne $Action)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Action',$FirewallRule.Action,$Action ) -join '') $desiredConfigurationMatch = $false } if ($Profile) { [String[]] $networkProfileinRule = $FirewallRule.Profile.ToString() -replace(' ', '') -split(',') if ($networkProfileinRule.Count -eq $Profile.Count) { foreach($networkProfile in $Profile) { if (-not ($networkProfileinRule -contains $networkProfile)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Profile',$networkProfileinRule,$Profile ) -join '') $desiredConfigurationMatch = $false break } } } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Profile',$networkProfileinRule,$Profile ) -join '') $desiredConfigurationMatch = $false } } if ($Direction -and ($FirewallRule.Direction -ne $Direction)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Direction',$FirewallRule.Direction,$Direction ) -join '') $desiredConfigurationMatch = $false } if ($RemotePort) { [String[]]$remotePortInRule = $properties.PortFilters.RemotePort if ($remotePortInRule.Count -eq $RemotePort.Count) { foreach($port in $RemotePort) { if (-not ($remotePortInRule -contains($port))) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'RemotePort',$remotePortInRule,$RemotePort ) -join '') $desiredConfigurationMatch = $false } } } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'RemotePort',$remotePortInRule,$RemotePort ) -join '') $desiredConfigurationMatch = $false } } if ($LocalPort) { [String[]]$localPortInRule = $properties.PortFilters.LocalPort if ($localPortInRule.Count -eq $LocalPort.Count) { foreach($port in $LocalPort) { if (-not ($localPortInRule -contains($port))) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'LocalPort',$localPortInRule,$LocalPort ) -join '') $desiredConfigurationMatch = $false } } } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'LocalPort',$localPortInRule,$LocalPort ) -join '') $desiredConfigurationMatch = $false } } if ($Protocol -and ($properties.PortFilters.Protocol -ne $Protocol)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Protocol',$properties.PortFilters.Protocol,$Protocol ) -join '') $desiredConfigurationMatch = $false } if ($Description -and ($FirewallRule.Description -ne $Description)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Description',$FirewallRule.Description,$Description ) -join '') $desiredConfigurationMatch = $false } if ($ApplicationPath -and ($properties.ApplicationFilters.Program -ne $ApplicationPath)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'ApplicationPath',$properties.ApplicationFilters.Program,$ApplicationPath ) -join '') $desiredConfigurationMatch = $false } if ($Service -and ($properties.ServiceFilters.Service -ne $Service)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.PropertyNoMatchMessage) -f 'Service',$properties.ServiceFilters.Service,$Service ) -join '') $desiredConfigurationMatch = $false } Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.TestFirewallRuleReturningMessage) -f $Name,$desiredConfigurationMatch ) -join '') return $desiredConfigurationMatch } ###################################################################################### # Returns a list of FirewallRules that comply to the specified parameters. ###################################################################################### function Get-FirewallRule { param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String] $Name ) $firewallRule = @(Get-NetFirewallRule -Name $Name -ErrorAction SilentlyContinue) if (-not $firewallRule) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.FirewallRuleNotFoundMessage) -f $Name ) -join '') return $null } # If more than one rule is returned for a name, then throw an exception # because this should not be possible. if ($firewallRule.Count -gt 1) { $errorId = 'RuleNotUnique' $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation $errorMessage = $($LocalizedData.RuleNotUniqueError) -f $firewallRule.Count,$Name $exception = New-Object -TypeName System.InvalidOperationException ` -ArgumentList $errorMessage $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord ` -ArgumentList $exception, $errorId, $errorCategory, $null $PSCmdlet.ThrowTerminatingError($errorRecord) } return $firewallRule } ###################################################################################### # Returns the filters associated with the given firewall rule ###################################################################################### function Get-FirewallRuleProperty { param ( [Parameter(Mandatory)] $FirewallRule ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($LocalizedData.GetAllPropertiesMessage) ) -join '') return @{ AddressFilters = @(Get-NetFirewallAddressFilter -AssociatedNetFirewallRule $FirewallRule) ApplicationFilters = @(Get-NetFirewallApplicationFilter -AssociatedNetFirewallRule $FirewallRule) InterfaceFilters = @(Get-NetFirewallInterfaceFilter -AssociatedNetFirewallRule $FirewallRule) InterfaceTypeFilters = @(Get-NetFirewallInterfaceTypeFilter -AssociatedNetFirewallRule $FirewallRule) PortFilters = @(Get-NetFirewallPortFilter -AssociatedNetFirewallRule $FirewallRule) Profile = @(Get-NetFirewallProfile -AssociatedNetFirewallRule $FirewallRule) SecurityFilters = @(Get-NetFirewallSecurityFilter -AssociatedNetFirewallRule $FirewallRule) ServiceFilters = @(Get-NetFirewallServiceFilter -AssociatedNetFirewallRule $FirewallRule) } } #endregion Export-ModuleMember -Function *-TargetResource |