DSCResources/MSFT_xFirewall/MSFT_xFirewall.psm1
# Default Display Group for the Firewall cmdlets $DefaultDisplayGroup = "DSC_FirewallRule" # DSC uses the Get-TargetResource cmdlet to fetch the status of the resource instance specified in the parameters for the target machine function Get-TargetResource { [OutputType([System.Collections.Hashtable])] param ( # Name of the Firewall Rule [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String]$Name, # Permit or Block the supplied configuration [Parameter(Mandatory)] [ValidateSet("NotConfigured", "Allow", "Block")] [String]$Access ) # Hash table for Get $getTargetResourceResult = @{} # Populate the properties for get target resource $getTargetResourceResult.Name = $Name $getTargetResourceResult.Ensure = "Present" Write-Verbose "GET: Get Rules for the specified Name[$Name]" $firewallRule = Get-NetFirewallRule -Name $Name -ErrorAction SilentlyContinue if (-not $firewallRule) { Write-Verbose "GET: Firewall Rule does not exist, there is nothing interesting to do" $getTargetResourceResult.Ensure = "Absent" return $getTargetResourceResult } $getTargetResourceResult.DisplayName = $firewallRule.DisplayName $getTargetResourceResult.DisplayGroup = $firewallRule.DisplayGroup $getTargetResourceResult.Access = $Access $getTargetResourceResult.State = $firewallRule.Enabled $getTargetResourceResult.Profile = $firewallRule.Profile.ToString() -replace(" ", "") -split(",") $getTargetResourceResult.Direction = $firewallRule.Direction $properties = Get-FirewallRuleProperty -FirewallRule $firewallRule -Property All $getTargetResourceResult.RemotePort = $properties.PortFilters.RemotePort $getTargetResourceResult.LocalPort = $properties.PortFilters.LocalPort $getTargetResourceResult.Protocol = $properties.PortFilters.Protocol $getTargetResourceResult.Description = $firewallRule.Description $getTargetResourceResult.ApplicationPath = $properties.ApplicationFilters.Program $getTargetResourceResult.Service = $properties.ServiceFilters.Service return $getTargetResourceResult; } # DSC uses Set-TargetResource cmdlet to create, delete or configure the resource instance on the target machine 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, # Name of the Firewall Group where we want to put the Firewall Rules [ValidateNotNullOrEmpty()] [String]$DisplayGroup = $DefaultDisplayGroup, # Ensure the presence/absence of the resource [ValidateSet("Present", "Absent")] [String]$Ensure = "Present", # Permit or Block the supplied configuration [Parameter(Mandatory)] [ValidateSet("NotConfigured", "Allow", "Block")] [String]$Access = "Allow", # Enable or disable the supplied configuration [ValidateSet("Enabled", "Disabled")] [String]$State = "Enabled", # Specifies one or more profiles to which the rule is assigned [String[]]$Profile = ("Any"), # 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 "SET: Find firewall rules with specified parameters for Name = $Name, DisplayGroup = $DisplayGroup" $firewallRules = Get-FirewallRules -Name $Name -DisplayGroup $DisplayGroup $exists = ($firewallRules -ne $null) if ($Ensure -eq "Present") { Write-Verbose "SET: We want the firewall rule to exist since Ensure is set to $Ensure" if ($exists) { Write-Verbose "SET: We want the firewall rule to exist and it does exist. Check for valid properties" foreach ($firewallRule in $firewallRules) { Write-Verbose "SET: Check each defined parameter against the existing firewall rule - $($firewallRule.Name)" if (Test-RuleHasProperties -FirewallRule $firewallRule ` -Name $Name ` -DisplayGroup $DisplayGroup ` -State $State ` -Profile $Profile ` -Direction $Direction ` -Access $Access ` -RemotePort $RemotePort ` -LocalPort $LocalPort ` -Protocol $Protocol ` -Description $Description ` -ApplicationPath $ApplicationPath ` -Service $Service ) { } else { Write-Verbose "SET: Removing existing firewall rule [$Name] to recreate one based on desired configuration" Remove-NetFirewallRule -Name $Name # Set the Firewall rule based on specified parameters Set-FirewallRule -Name $Name ` -DisplayName $DisplayName ` -DisplayGroup $DisplayGroup ` -State $State ` -Profile $Profile ` -Direction $Direction ` -Access $Access ` -RemotePort $RemotePort ` -LocalPort $LocalPort ` -Protocol $Protocol ` -Description $Description ` -ApplicationPath $ApplicationPath ` -Service $Service -Verbose } } } else { Write-Verbose "SET: We want the firewall rule [$Name] to exist, but it does not" # Set the Firewall rule based on specified parameters Set-FirewallRule -Name $Name ` -DisplayName $DisplayName ` -DisplayGroup $DisplayGroup ` -State $State ` -Profile $Profile ` -Direction $Direction ` -Access $Access ` -RemotePort $RemotePort ` -LocalPort $LocalPort ` -Protocol $Protocol ` -Description $Description ` -ApplicationPath $ApplicationPath ` -Service $Service -Verbose } } elseif ($Ensure -eq "Absent") { Write-Verbose "SET: We do not want the firewall rule to exist" if ($exists) { Write-Verbose "SET: We do not want the firewall rule to exist, but it does. Removing the Rule(s)" foreach ($firewallRule in $firewallRules) { Remove-NetFirewallRule -Name $firewallRule.Name } } else { Write-Verbose "SET: We do not want the firewall rule to exist, and it does not" # Do Nothing } } } # 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, # 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", # Permit or Block the supplied configuration [Parameter(Mandatory)] [ValidateSet("NotConfigured", "Allow", "Block")] [String]$Access, # Enable or disable the supplied configuration [ValidateSet("Enabled", "Disabled")] [String]$State, # 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 "TEST: Find rules with specified parameters" $firewallRules = Get-FirewallRules -Name $Name -DisplayGroup $DisplayGroup if (!$firewallRules) { Write-Verbose "TEST: Get-FirewallRules returned NULL" # Returns whether complies with $Ensure $returnValue = ($false -eq ($Ensure -eq "Present")) Write-Verbose "TEST: Returning $returnValue" return $returnValue } $exists = $true $valid = $true foreach ($firewallRule in $firewallRules) { Write-Verbose "TEST: Check each defined parameter against the existing Firewall Rule - $($firewallRule.Name)" if (Test-RuleHasProperties -FirewallRule $firewallRule ` -Name $Name ` -DisplayGroup $DisplayGroup ` -State $State ` -Profile $Profile ` -Direction $Direction ` -Access $Access ` -RemotePort $RemotePort ` -LocalPort $LocalPort ` -Protocol $Protocol ` -Description $Description ` -ApplicationPath $ApplicationPath ` -Service $Service ) { } else { $valid = $false } } # Returns whether or not $exists complies with $Ensure $returnValue = ($valid -and $exists -eq ($Ensure -eq "Present")) Write-Verbose "TEST: Returning $returnValue" return $returnValue } #region HelperFunctions ###################### ## Helper Functions ## ###################### # Function to Set a Firewall Rule based on specified parameters function Set-FirewallRule { param ( [Parameter(Mandatory)] [String]$Name, [String]$DisplayName, [String]$DisplayGroup, [String]$State, [String[]]$Profile, [String]$Direction, [String]$Access, [String[]]$RemotePort, [String[]]$LocalPort, [String]$Protocol, [String]$Description, [String]$ApplicationPath, [String]$Service ) $parameters = @{} $commandName = "New-NetFirewallRule" $parameters["Name"] = $Name if($DisplayName) { $parameters["DisplayName"] = $DisplayName } if($DisplayGroup) { $parameters["Group"] = $DisplayGroup } else { $parameters["Group"] = $DefaultGroup } if($State) { if($State -eq "Enabled") { $parameters["Enabled"] = "True" } else { $parameters["Enabled"] = "False" } } if($Profile) { $parameters["Profile"] = $Profile } if($Direction) { $parameters["Direction"] = $Direction } if($Access) { $parameters["Action"] = $Access } if($RemotePort) { $parameters["RemotePort"] = $RemotePort } if($LocalPort) { $parameters["LocalPort"] = $LocalPort } if($Protocol) { $parameters["Protocol"] = $Protocol } if($Description) { $parameters["Description"] = $Description } if($ApplicationPath) { $parameters["Program"] = $ApplicationPath } if($Service) { $parameters["Service"] = $Service } Write-Verbose "SET: Invoke Set-NetFirewallRule [$Name] with splatting its parameters" & $commandName @parameters } # Function to validate if the supplied Rule adheres to all parameters set function Test-RuleHasProperties { param ( [Parameter(Mandatory)] $FirewallRule, [String]$Name, [String]$DisplayGroup, [String]$State, [String[]]$Profile, [String]$Direction, [String]$Access, [String[]]$RemotePort, [String[]]$LocalPort, [String]$Protocol, [String]$Description, [String]$ApplicationPath, [String]$Service ) $properties = Get-FirewallRuleProperty -FirewallRule $FirewallRule -Property All $desiredConfigurationMatch = $true if ($Name -and ($FirewallRule.Name -ne $Name)) { Write-Verbose "Test-RuleHasProperties: Name property value - $FirewallRule.Name does not match desired state - $Name" $desiredConfigurationMatch = $false } if ($Access -and ($FirewallRule.Action -ne $Access)) { Write-Verbose "Test-RuleHasProperties: Access property value - $($FirewallRule.Action) does not match desired state - $Access" $desiredConfigurationMatch = $false } if ($State -and ($FirewallRule.Enabled.ToString() -eq ("Enabled" -ne $State))) { Write-Verbose "Test-RuleHasProperties: State property value - $FirewallRule.Enabled.ToString() does not match desired state - $State" $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 "Test-RuleHasProperties: Profile property value - '$networkProfileinRule' does not match desired state - '$Profile'" $desiredConfigurationMatch = $false } } } else { Write-Verbose "Test-RuleHasProperties: Profile property value - '$networkProfileinRule' does not match desired state - '$Profile'" $desiredConfigurationMatch = $false } } if ($Direction -and ($FirewallRule.Direction -ne $Direction)) { Write-Verbose "Test-RuleHasProperties: Direction property value - $FirewallRule.Direction does not match desired state - $Direction" $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 "Test-RuleHasProperties: RemotePort property value - '$remotePortInRule' does not match desired state - '$RemotePort'" $desiredConfigurationMatch = $false } } } else { Write-Verbose "Test-RuleHasProperties: RemotePort property value - '$remotePortInRule' does not match desired state - '$RemotePort'" $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 "Test-RuleHasProperties: LocalPort property value - '$localPortInRule' does not match desired state - '$LocalPort'" $desiredConfigurationMatch = $false } } } else { Write-Verbose "Test-RuleHasProperties: LocalPort property value - '$localPortInRule' does not match desired state - '$LocalPort'" $desiredConfigurationMatch = $false } } if ($Protocol -and ($properties.PortFilters.Protocol -ne $Protocol)) { Write-Verbose "Test-RuleHasProperties: Protocol property value - $properties.PortFilters.Protocol does not match desired state - $Protocol" $desiredConfigurationMatch = $false } if ($Description -and ($FirewallRule.Description -ne $Description)) { Write-Verbose "Test-RuleHasProperties: Description property value - $FirewallRule.Description does not match desired state - $Description" $desiredConfigurationMatch = $false } if ($ApplicationPath -and ($properties.ApplicationFilters.Program -ne $ApplicationPath)) { Write-Verbose "Test-RuleHasProperties: ApplicationPath property value - $properties.ApplicationFilters.Program does not match desired state - $ApplicationPath" $desiredConfigurationMatch = $false } if ($Service -and ($properties.ServiceFilters.Service -ne $Service)) { Write-Verbose "Test-RuleHasProperties: Service property value - $properties.ServiceFilters.Service does not match desired state - $Service" $desiredConfigurationMatch = $false } Write-Verbose "Test-RuleHasProperties returning $desiredConfigurationMatch" return $desiredConfigurationMatch } # Returns a list of FirewallRules that comply to the specified parameters. function Get-FirewallRules { param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String]$Name, [String]$DisplayGroup ) $firewallRules = @(Get-NetFirewallRule -Name $Name -ErrorAction SilentlyContinue) if (-not $firewallRules) { Write-Verbose "Get-FirewallRules: No Firewall Rules found for [$Name]" return $null } else { if ($DisplayGroup) { foreach ($firewallRule in $firewallRules) { if ($firewallRule.DisplayGroup -eq $DisplayGroup) { Write-Verbose "Get-FirewallRules: Found a Firewall Rule for Name: [$Name] and DisplayGroup [$DisplayGroup]" return $firewallRule } } } } return $firewallRules } # Returns the filters associated with the given firewall rule function Get-FirewallRuleProperty { param ( [Parameter(Mandatory)] $FirewallRule, [Parameter(Mandatory)] [ValidateSet("All", "AddressFilter", "ApplicationFilter", "InterfaceFilter", "InterfaceTypeFilter", "PortFilter", "Profile", "SecurityFilter", "ServiceFilter")] $Property ) if ($Property -eq "All") { Write-Verbose "Get-FirewallRuleProperty: Get all the properties" $properties = @{} Write-Verbose "Get-FirewallRuleProperty: Add filter info to rule map" $properties.AddressFilters = @(Get-NetFirewallAddressFilter -AssociatedNetFirewallRule $FirewallRule) $properties.ApplicationFilters = @(Get-NetFirewallApplicationFilter -AssociatedNetFirewallRule $FirewallRule) $properties.InterfaceFilters = @(Get-NetFirewallInterfaceFilter -AssociatedNetFirewallRule $FirewallRule) $properties.InterfaceTypeFilters = @(Get-NetFirewallInterfaceTypeFilter -AssociatedNetFirewallRule $FirewallRule) $properties.PortFilters = @(Get-NetFirewallPortFilter -AssociatedNetFirewallRule $FirewallRule) $properties.Profile = @(Get-NetFirewallProfile -AssociatedNetFirewallRule $FirewallRule) $properties.SecurityFilters = @(Get-NetFirewallSecurityFilter -AssociatedNetFirewallRule $FirewallRule) $properties.ServiceFilters = @(Get-NetFirewallServiceFilter -AssociatedNetFirewallRule $FirewallRule) return $properties } if ($Property -eq "AddressFilter" -or $Property -eq "ApplicationFilter" -or $Property -eq "InterfaceFilter" ` -or $Property -eq "InterfaceTypeFilter" -or $Property -eq "PortFilter" -or $Property -eq "Profile" ` -or $Property -eq "SecurityFilter" -or $Property -eq "ServiceFilter") { Write-Verbose "Get-FirewallRuleProperty: Get only [$Property] property" return &(Get-Command "Get-NetFirewall$Property") -AssociatedNetFireWallRule $FireWallRule } } #endregion Export-ModuleMember -Function *-TargetResource |