Tests/LocalPolicy-ToGPO.Tests.ps1
|
#Requires -Module Pester <# .SYNOPSIS Pester tests for the LocalPolicy-ToGPO module. .DESCRIPTION Tests module loading, parameter validation, mock-based function logic, and manifest correctness. #> BeforeAll { $ModulePath = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) $ModuleName = 'LocalPolicy-ToGPO' $ManifestPath = Join-Path $ModulePath "$ModuleName.psd1" # Remove module if loaded, then import fresh if (Get-Module -Name $ModuleName) { Remove-Module -Name $ModuleName -Force } Import-Module $ManifestPath -Force } AfterAll { if (Get-Module -Name 'LocalPolicy-ToGPO') { Remove-Module -Name 'LocalPolicy-ToGPO' -Force } } # ============================================================================== # Module Loading Tests # ============================================================================== Describe 'Module Loading' { It 'Should import the module without errors' { $module = Get-Module -Name 'LocalPolicy-ToGPO' $module | Should -Not -BeNullOrEmpty } It 'Should export exactly 5 public functions' { $module = Get-Module -Name 'LocalPolicy-ToGPO' $module.ExportedFunctions.Count | Should -Be 5 } It 'Should export Get-LocalFirewallPolicy' { Get-Command -Module 'LocalPolicy-ToGPO' -Name 'Get-LocalFirewallPolicy' | Should -Not -BeNullOrEmpty } It 'Should export Get-LocalSecurityPolicy' { Get-Command -Module 'LocalPolicy-ToGPO' -Name 'Get-LocalSecurityPolicy' | Should -Not -BeNullOrEmpty } It 'Should export Copy-FirewallToGPO' { Get-Command -Module 'LocalPolicy-ToGPO' -Name 'Copy-FirewallToGPO' | Should -Not -BeNullOrEmpty } It 'Should export Copy-SecurityPolicyToGPO' { Get-Command -Module 'LocalPolicy-ToGPO' -Name 'Copy-SecurityPolicyToGPO' | Should -Not -BeNullOrEmpty } It 'Should export Compare-PolicyCompliance' { Get-Command -Module 'LocalPolicy-ToGPO' -Name 'Compare-PolicyCompliance' | Should -Not -BeNullOrEmpty } It 'Should NOT export New-HtmlDashboard (private function)' { $exported = Get-Command -Module 'LocalPolicy-ToGPO' | Select-Object -ExpandProperty Name $exported | Should -Not -Contain 'New-HtmlDashboard' } } # ============================================================================== # Manifest Validation # ============================================================================== Describe 'Manifest Validation' { It 'Should have a valid module manifest' { { Test-ModuleManifest -Path $ManifestPath -ErrorAction Stop } | Should -Not -Throw } It 'Should have the correct GUID' { $manifest = Test-ModuleManifest -Path $ManifestPath $manifest.GUID.ToString() | Should -Be 'd4e5f6a7-1b02-4d8e-cf90-5b6c7d8e9f01' } It 'Should require PowerShell 5.1' { $manifest = Test-ModuleManifest -Path $ManifestPath $manifest.PowerShellVersion.ToString() | Should -Be '5.1' } It 'Should have the correct author' { $manifest = Test-ModuleManifest -Path $ManifestPath $manifest.Author | Should -BeLike '*Larry Roberts*' } It 'Should list required tags' { $manifest = Test-ModuleManifest -Path $ManifestPath $tags = $manifest.PrivateData.PSData.Tags $tags | Should -Contain 'GroupPolicy' $tags | Should -Contain 'GPO' $tags | Should -Contain 'Firewall' $tags | Should -Contain 'LocalPolicy' $tags | Should -Contain 'Migration' } } # ============================================================================== # Parameter Validation Tests # ============================================================================== Describe 'Parameter Validation' { Context 'Get-LocalFirewallPolicy' { It 'Should have a ProfileFilter parameter with ValidateSet' { $cmd = Get-Command -Name 'Get-LocalFirewallPolicy' $param = $cmd.Parameters['ProfileFilter'] $param | Should -Not -BeNullOrEmpty $validateSet = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $validateSet | Should -Not -BeNullOrEmpty $validateSet.ValidValues | Should -Contain 'Domain' $validateSet.ValidValues | Should -Contain 'Private' $validateSet.ValidValues | Should -Contain 'Public' $validateSet.ValidValues | Should -Contain 'Any' } It 'Should accept pipeline input for ComputerName' { $cmd = Get-Command -Name 'Get-LocalFirewallPolicy' $param = $cmd.Parameters['ComputerName'] $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.ValueFromPipeline } | Should -Not -BeNullOrEmpty } } Context 'Copy-FirewallToGPO' { It 'Should require -SourceComputer as mandatory' { $cmd = Get-Command -Name 'Copy-FirewallToGPO' $param = $cmd.Parameters['SourceComputer'] $mandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } $mandatory | Should -Not -BeNullOrEmpty } It 'Should require -GPOName as mandatory' { $cmd = Get-Command -Name 'Copy-FirewallToGPO' $param = $cmd.Parameters['GPOName'] $mandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } $mandatory | Should -Not -BeNullOrEmpty } It 'Should support -WhatIf' { $cmd = Get-Command -Name 'Copy-FirewallToGPO' $cmd.Parameters.ContainsKey('WhatIf') | Should -Be $true } It 'Should support -Confirm' { $cmd = Get-Command -Name 'Copy-FirewallToGPO' $cmd.Parameters.ContainsKey('Confirm') | Should -Be $true } } Context 'Copy-SecurityPolicyToGPO' { It 'Should require -SourceComputer as mandatory' { $cmd = Get-Command -Name 'Copy-SecurityPolicyToGPO' $param = $cmd.Parameters['SourceComputer'] $mandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } $mandatory | Should -Not -BeNullOrEmpty } It 'Should require -GPOName as mandatory' { $cmd = Get-Command -Name 'Copy-SecurityPolicyToGPO' $param = $cmd.Parameters['GPOName'] $mandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } $mandatory | Should -Not -BeNullOrEmpty } It 'Should have a Categories parameter with ValidateSet' { $cmd = Get-Command -Name 'Copy-SecurityPolicyToGPO' $param = $cmd.Parameters['Categories'] $param | Should -Not -BeNullOrEmpty $validateSet = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $validateSet | Should -Not -BeNullOrEmpty $validateSet.ValidValues | Should -Contain 'SystemAccess' $validateSet.ValidValues | Should -Contain 'AuditPolicy' $validateSet.ValidValues | Should -Contain 'UserRights' $validateSet.ValidValues | Should -Contain 'SecurityOptions' $validateSet.ValidValues | Should -Contain 'All' } It 'Should support -WhatIf' { $cmd = Get-Command -Name 'Copy-SecurityPolicyToGPO' $cmd.Parameters.ContainsKey('WhatIf') | Should -Be $true } } Context 'Compare-PolicyCompliance' { It 'Should require -ComputerName as mandatory' { $cmd = Get-Command -Name 'Compare-PolicyCompliance' $param = $cmd.Parameters['ComputerName'] $mandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } $mandatory | Should -Not -BeNullOrEmpty } It 'Should require -GPOName as mandatory' { $cmd = Get-Command -Name 'Compare-PolicyCompliance' $param = $cmd.Parameters['GPOName'] $mandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } $mandatory | Should -Not -BeNullOrEmpty } It 'Should have a CompareType parameter with ValidateSet' { $cmd = Get-Command -Name 'Compare-PolicyCompliance' $param = $cmd.Parameters['CompareType'] $param | Should -Not -BeNullOrEmpty $validateSet = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $validateSet | Should -Not -BeNullOrEmpty $validateSet.ValidValues | Should -Contain 'Firewall' $validateSet.ValidValues | Should -Contain 'SecurityPolicy' $validateSet.ValidValues | Should -Contain 'Both' } } } # ============================================================================== # Mock-Based Function Tests # ============================================================================== Describe 'Get-LocalFirewallPolicy (Mocked)' { BeforeAll { # Mock firewall rules -- mix of local and GPO-delivered $mockRules = @( [PSCustomObject]@{ DisplayName = 'Allow HTTP Inbound' Direction = 'Inbound' Action = 'Allow' Enabled = 'True' Profile = 'Any' Description = 'HTTP web traffic' PolicyStoreSourceType = 'Local' }, [PSCustomObject]@{ DisplayName = 'Allow HTTPS Inbound' Direction = 'Inbound' Action = 'Allow' Enabled = 'True' Profile = 'Domain' Description = 'HTTPS web traffic' PolicyStoreSourceType = 'Local' }, [PSCustomObject]@{ DisplayName = 'GPO Rule - Should Be Excluded' Direction = 'Inbound' Action = 'Allow' Enabled = 'True' Profile = 'Domain' Description = 'This comes from GPO' PolicyStoreSourceType = 'GroupPolicy' }, [PSCustomObject]@{ DisplayName = 'Disabled Local Rule' Direction = 'Outbound' Action = 'Block' Enabled = 'False' Profile = 'Private' Description = 'A disabled rule' PolicyStoreSourceType = 'Local' } ) $mockAddressFilter = [PSCustomObject]@{ LocalAddress = 'Any' RemoteAddress = '10.0.0.0/8' } $mockPortFilter = [PSCustomObject]@{ Protocol = 'TCP' LocalPort = '80' RemotePort = 'Any' } $mockAppFilter = [PSCustomObject]@{ Program = 'C:\inetpub\w3wp.exe' } } It 'Should filter out GPO-delivered rules' { Mock -ModuleName 'LocalPolicy-ToGPO' Invoke-Command { # Simulate the remote script block behavior $localRules = $mockRules | Where-Object { $_.PolicyStoreSourceType -ne 'GroupPolicy' } $localRules = $localRules | Where-Object { $_.Enabled -eq 'True' } foreach ($rule in $localRules) { [PSCustomObject]@{ DisplayName = $rule.DisplayName Direction = $rule.Direction Action = $rule.Action Protocol = 'TCP' LocalPort = '80' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = '10.0.0.0/8' Program = 'C:\inetpub\w3wp.exe' Profile = $rule.Profile Enabled = $true Description = $rule.Description PolicySource = 'Local' } } } $results = Get-LocalFirewallPolicy -ComputerName 'SVR-TEST-01' $results | Should -Not -BeNullOrEmpty $results.DisplayName | Should -Not -Contain 'GPO Rule - Should Be Excluded' } It 'Should return objects with expected properties' { Mock -ModuleName 'LocalPolicy-ToGPO' Invoke-Command { [PSCustomObject]@{ DisplayName = 'Allow HTTP Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '80' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = '10.0.0.0/8' Program = 'C:\inetpub\w3wp.exe' Profile = 'Any' Enabled = $true Description = 'HTTP web traffic' PolicySource = 'Local' } } $results = Get-LocalFirewallPolicy -ComputerName 'SVR-TEST-01' $result = $results | Select-Object -First 1 $result.PSObject.Properties.Name | Should -Contain 'ComputerName' $result.PSObject.Properties.Name | Should -Contain 'DisplayName' $result.PSObject.Properties.Name | Should -Contain 'Direction' $result.PSObject.Properties.Name | Should -Contain 'Action' $result.PSObject.Properties.Name | Should -Contain 'Protocol' $result.PSObject.Properties.Name | Should -Contain 'LocalPort' $result.PSObject.Properties.Name | Should -Contain 'RemotePort' $result.PSObject.Properties.Name | Should -Contain 'LocalAddress' $result.PSObject.Properties.Name | Should -Contain 'RemoteAddress' $result.PSObject.Properties.Name | Should -Contain 'Program' $result.PSObject.Properties.Name | Should -Contain 'Profile' $result.PSObject.Properties.Name | Should -Contain 'Enabled' $result.PSObject.Properties.Name | Should -Contain 'Description' $result.PSObject.Properties.Name | Should -Contain 'PolicySource' } It 'Should set PolicySource to Local for all returned rules' { Mock -ModuleName 'LocalPolicy-ToGPO' Invoke-Command { [PSCustomObject]@{ DisplayName = 'Allow HTTP Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '80' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Any' Enabled = $true Description = 'HTTP' PolicySource = 'Local' } } $results = Get-LocalFirewallPolicy -ComputerName 'SVR-TEST-01' $results | ForEach-Object { $_.PolicySource | Should -Be 'Local' } } It 'Should only return enabled rules by default' { Mock -ModuleName 'LocalPolicy-ToGPO' Invoke-Command { # Simulates the script block returning only enabled, local rules [PSCustomObject]@{ DisplayName = 'Allow HTTP Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '80' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Any' Enabled = $true Description = 'HTTP' PolicySource = 'Local' } } $results = Get-LocalFirewallPolicy -ComputerName 'SVR-TEST-01' $results | ForEach-Object { $_.Enabled | Should -Be $true } } } Describe 'Get-LocalSecurityPolicy (Mocked)' { It 'Should parse secedit .inf output into structured objects' { $mockInfContent = @" [Unicode] Unicode=yes [System Access] MinimumPasswordAge = 1 MaximumPasswordAge = 42 MinimumPasswordLength = 14 PasswordComplexity = 1 PasswordHistorySize = 24 LockoutBadCount = 5 [Event Audit] AuditSystemEvents = 3 AuditLogonEvents = 3 AuditObjectAccess = 1 AuditPolicyChange = 3 [Privilege Rights] SeNetworkLogonRight = *S-1-5-32-544,*S-1-5-32-545 SeDenyNetworkLogonRight = *S-1-5-32-546 [Registry Values] MACHINE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\DefaultLevel=4,0 "@ Mock -ModuleName 'LocalPolicy-ToGPO' Invoke-Command { return $mockInfContent } $results = Get-LocalSecurityPolicy -ComputerName 'SVR-TEST-01' $results | Should -Not -BeNullOrEmpty # Verify we got settings from multiple sections $categories = $results | Select-Object -ExpandProperty Category -Unique $categories | Should -Contain 'SystemAccess' $categories | Should -Contain 'AuditPolicy' $categories | Should -Contain 'UserRights' $categories | Should -Contain 'SecurityOptions' } It 'Should return correct property values for System Access settings' { $mockInfContent = @" [System Access] MinimumPasswordLength = 14 PasswordComplexity = 1 "@ Mock -ModuleName 'LocalPolicy-ToGPO' Invoke-Command { return $mockInfContent } $results = Get-LocalSecurityPolicy -ComputerName 'SVR-TEST-01' $pwdLength = $results | Where-Object { $_.SettingName -eq 'MinimumPasswordLength' } $pwdLength | Should -Not -BeNullOrEmpty $pwdLength.SettingValue | Should -Be '14' $pwdLength.Category | Should -Be 'SystemAccess' } It 'Should return ComputerName on each result' { $mockInfContent = @" [System Access] MinimumPasswordLength = 14 "@ Mock -ModuleName 'LocalPolicy-ToGPO' Invoke-Command { return $mockInfContent } $results = Get-LocalSecurityPolicy -ComputerName 'SVR-TEST-01' $results | ForEach-Object { $_.ComputerName | Should -Be 'SVR-TEST-01' } } } Describe 'Copy-FirewallToGPO (Mocked)' { BeforeAll { $testRules = @( [PSCustomObject]@{ ComputerName = 'SVR-TEST-01' DisplayName = 'Allow HTTP Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '80' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Any' Enabled = $true Description = 'HTTP web traffic' PolicySource = 'Local' }, [PSCustomObject]@{ ComputerName = 'SVR-TEST-01' DisplayName = 'Allow HTTPS Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '443' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Any' Enabled = $true Description = 'HTTPS web traffic' PolicySource = 'Local' }, [PSCustomObject]@{ ComputerName = 'SVR-TEST-01' DisplayName = 'Allow RDP Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '3389' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = '10.0.0.0/8' Program = $null Profile = 'Domain' Enabled = $true Description = 'Remote Desktop' PolicySource = 'Local' } ) } It 'Should call New-GPO when -CreateGPO is specified' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return $testRules } Mock -ModuleName 'LocalPolicy-ToGPO' Get-GPO { return $null } Mock -ModuleName 'LocalPolicy-ToGPO' New-GPO { return [PSCustomObject]@{ DisplayName = 'Test-GPO'; Id = [guid]::NewGuid() } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' New-NetFirewallRule { } $result = Copy-FirewallToGPO -SourceComputer 'SVR-TEST-01' -GPOName 'Test-GPO' -CreateGPO -Confirm:$false Should -Invoke -CommandName 'New-GPO' -ModuleName 'LocalPolicy-ToGPO' -Times 1 -Exactly } It 'Should call New-NetFirewallRule for each rule' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return $testRules } Mock -ModuleName 'LocalPolicy-ToGPO' Get-GPO { return [PSCustomObject]@{ DisplayName = 'Test-GPO'; Id = [guid]::NewGuid() } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' New-NetFirewallRule { } $result = Copy-FirewallToGPO -SourceComputer 'SVR-TEST-01' -GPOName 'Test-GPO' -Confirm:$false Should -Invoke -CommandName 'New-NetFirewallRule' -ModuleName 'LocalPolicy-ToGPO' -Times 3 -Exactly } It 'Should use correct PolicyStore format for New-NetFirewallRule' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return @($testRules[0]) } Mock -ModuleName 'LocalPolicy-ToGPO' Get-GPO { return [PSCustomObject]@{ DisplayName = 'Test-GPO'; Id = [guid]::NewGuid() } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' New-NetFirewallRule -ParameterFilter { $PolicyStore -eq 'contoso.com\Test-GPO' } -MockWith { } $result = Copy-FirewallToGPO -SourceComputer 'SVR-TEST-01' -GPOName 'Test-GPO' -Confirm:$false Should -Invoke -CommandName 'New-NetFirewallRule' -ModuleName 'LocalPolicy-ToGPO' -Times 1 -Exactly -ParameterFilter { $PolicyStore -eq 'contoso.com\Test-GPO' } } It 'Should return a summary with correct migration count' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return $testRules } Mock -ModuleName 'LocalPolicy-ToGPO' Get-GPO { return [PSCustomObject]@{ DisplayName = 'Test-GPO'; Id = [guid]::NewGuid() } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' New-NetFirewallRule { } $result = Copy-FirewallToGPO -SourceComputer 'SVR-TEST-01' -GPOName 'Test-GPO' -Confirm:$false $result.RulesRead | Should -Be 3 $result.RulesMigrated | Should -Be 3 $result.RulesFailed | Should -Be 0 $result.GPOName | Should -Be 'Test-GPO' $result.SourceComputer | Should -Be 'SVR-TEST-01' } It 'Should NOT call New-GPO when -CreateGPO is NOT specified and GPO exists' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return $testRules } Mock -ModuleName 'LocalPolicy-ToGPO' Get-GPO { return [PSCustomObject]@{ DisplayName = 'Test-GPO'; Id = [guid]::NewGuid() } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' New-GPO { } Mock -ModuleName 'LocalPolicy-ToGPO' New-NetFirewallRule { } $result = Copy-FirewallToGPO -SourceComputer 'SVR-TEST-01' -GPOName 'Test-GPO' -Confirm:$false Should -Invoke -CommandName 'New-GPO' -ModuleName 'LocalPolicy-ToGPO' -Times 0 -Exactly } It 'Should handle rule creation failures gracefully' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return $testRules } Mock -ModuleName 'LocalPolicy-ToGPO' Get-GPO { return [PSCustomObject]@{ DisplayName = 'Test-GPO'; Id = [guid]::NewGuid() } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' New-NetFirewallRule { throw 'Rule creation failed' } $result = Copy-FirewallToGPO -SourceComputer 'SVR-TEST-01' -GPOName 'Test-GPO' -Confirm:$false -WarningAction SilentlyContinue $result.RulesRead | Should -Be 3 $result.RulesMigrated | Should -Be 0 $result.RulesFailed | Should -Be 3 } } Describe 'Compare-PolicyCompliance (Mocked)' { BeforeAll { $localRules = @( [PSCustomObject]@{ ComputerName = 'SVR-TEST-01' DisplayName = 'Allow HTTP Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '80' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Any' Enabled = $true Description = 'HTTP web traffic' PolicySource = 'Local' }, [PSCustomObject]@{ ComputerName = 'SVR-TEST-01' DisplayName = 'Allow HTTPS Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '443' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Any' Enabled = $true Description = 'HTTPS web traffic' PolicySource = 'Local' }, [PSCustomObject]@{ ComputerName = 'SVR-TEST-01' DisplayName = 'Local Only Rule' Direction = 'Outbound' Action = 'Block' Protocol = 'TCP' LocalPort = '445' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Private' Enabled = $true Description = 'Block SMB outbound' PolicySource = 'Local' } ) # GPO rules: match HTTP, mismatch HTTPS (different port), missing Local Only, extra GPO-only rule $mockGpoNetRules = @( # Matches HTTP exactly [PSCustomObject]@{ DisplayName = 'Allow HTTP Inbound' Direction = 1 # Inbound Action = 2 # Allow Enabled = 'True' Profile = 'Any' }, # Mismatches HTTPS -- different Action [PSCustomObject]@{ DisplayName = 'Allow HTTPS Inbound' Direction = 1 # Inbound Action = 4 # Block (mismatch) Enabled = 'True' Profile = 'Any' }, # Extra rule only in GPO [PSCustomObject]@{ DisplayName = 'GPO Extra Rule' Direction = 1 Action = 2 Enabled = 'True' Profile = 'Domain' } ) } It 'Should detect MATCH findings for identical rules' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return $localRules } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalSecurityPolicy { return @() } Mock -ModuleName 'LocalPolicy-ToGPO' Get-GPO { return [PSCustomObject]@{ DisplayName = 'Test-GPO'; Id = [guid]::NewGuid() } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallRule { return @( [PSCustomObject]@{ DisplayName = 'Allow HTTP Inbound' Direction = [PSCustomObject]@{ value__ = 1 } Action = [PSCustomObject]@{ value__ = 2 } Enabled = 'True' Profile = [PSCustomObject]@{ value__ = 0 } } ) } -ModuleName 'LocalPolicy-ToGPO' Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallAddressFilter { return [PSCustomObject]@{ LocalAddress = 'Any'; RemoteAddress = 'Any' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallPortFilter { return [PSCustomObject]@{ Protocol = 'TCP'; LocalPort = '80'; RemotePort = 'Any' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallApplicationFilter { return [PSCustomObject]@{ Program = 'Any' } } Mock -ModuleName 'LocalPolicy-ToGPO' Test-Path { return $false } -ParameterFilter { $Path -like '*GptTmpl*' } $results = Compare-PolicyCompliance -ComputerName 'SVR-TEST-01' -GPOName 'Test-GPO' -CompareType Firewall $matchResults = $results | Where-Object { $_.Finding -eq 'MATCH' } $matchResults | Should -Not -BeNullOrEmpty } It 'Should detect MISSING FROM GPO findings' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return $localRules } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallRule { return @() } -ModuleName 'LocalPolicy-ToGPO' $results = Compare-PolicyCompliance -ComputerName 'SVR-TEST-01' -GPOName 'Test-GPO' -CompareType Firewall $missingResults = $results | Where-Object { $_.Finding -eq 'MISSING FROM GPO' } $missingResults.Count | Should -Be 3 } It 'Should detect MISMATCH findings for differing rules' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return @([PSCustomObject]@{ ComputerName = 'SVR-TEST-01' DisplayName = 'Allow HTTPS Inbound' Direction = 'Inbound' Action = 'Allow' Protocol = 'TCP' LocalPort = '443' RemotePort = 'Any' LocalAddress = 'Any' RemoteAddress = 'Any' Program = $null Profile = 'Any' Enabled = $true Description = 'HTTPS' PolicySource = 'Local' }) } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallRule { return @( [PSCustomObject]@{ DisplayName = 'Allow HTTPS Inbound' Direction = [PSCustomObject]@{ value__ = 1 } Action = [PSCustomObject]@{ value__ = 4 } Enabled = 'True' Profile = [PSCustomObject]@{ value__ = 0 } } ) } -ModuleName 'LocalPolicy-ToGPO' Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallAddressFilter { return [PSCustomObject]@{ LocalAddress = 'Any'; RemoteAddress = 'Any' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallPortFilter { return [PSCustomObject]@{ Protocol = 'TCP'; LocalPort = '443'; RemotePort = 'Any' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallApplicationFilter { return [PSCustomObject]@{ Program = 'Any' } } $results = Compare-PolicyCompliance -ComputerName 'SVR-TEST-01' -GPOName 'Test-GPO' -CompareType Firewall $mismatchResults = $results | Where-Object { $_.Finding -like 'MISMATCH*' } $mismatchResults | Should -Not -BeNullOrEmpty } It 'Should return objects with expected properties' { Mock -ModuleName 'LocalPolicy-ToGPO' Get-LocalFirewallPolicy { return @($localRules[0]) } Mock -ModuleName 'LocalPolicy-ToGPO' Get-ADDomain { return [PSCustomObject]@{ DNSRoot = 'contoso.com' } } Mock -ModuleName 'LocalPolicy-ToGPO' Get-NetFirewallRule { return @() } -ModuleName 'LocalPolicy-ToGPO' $results = Compare-PolicyCompliance -ComputerName 'SVR-TEST-01' -GPOName 'Test-GPO' -CompareType Firewall $result = $results | Select-Object -First 1 $result.PSObject.Properties.Name | Should -Contain 'SettingName' $result.PSObject.Properties.Name | Should -Contain 'LocalValue' $result.PSObject.Properties.Name | Should -Contain 'GPOValue' $result.PSObject.Properties.Name | Should -Contain 'Match' $result.PSObject.Properties.Name | Should -Contain 'CompareType' $result.PSObject.Properties.Name | Should -Contain 'Finding' } } |