tests/Get-MsecEntraNamedLocation.Tests.ps1
|
BeforeAll { Import-Module (Join-Path $PSScriptRoot '..' 'Msec.psd1') -Force # Defined here, not in the Describe body: a discovery-time variable is gone by the time an # It runs, which reads as the command returning nothing rather than as a test bug. $script:Locations = @( @{ id = 'loc-trusted'; displayName = 'HQ'; '@odata.type' = '#microsoft.graph.ipNamedLocation' isTrusted = $true; ipRanges = @(@{ cidrAddress = '10.0.0.0/24' }, @{ cidrAddress = '10.0.1.0/24' }) } @{ id = 'loc-guest'; displayName = 'Guest WiFi'; '@odata.type' = '#microsoft.graph.ipNamedLocation' isTrusted = $false; ipRanges = @(@{ cidrAddress = '192.0.2.0/24' }) } @{ id = 'loc-unused'; displayName = 'Old Office'; '@odata.type' = '#microsoft.graph.ipNamedLocation' isTrusted = $true; ipRanges = @(@{ cidrAddress = '198.51.100.0/24' }) } # A country location has NO isTrusted property at all - it cannot be trusted. @{ id = 'loc-country'; displayName = 'Blocked Countries' '@odata.type' = '#microsoft.graph.countryNamedLocation' countriesAndRegions = @('KP', 'IR'); includeUnknownCountriesAndRegions = $true } ) } Describe 'Get-MsecEntraNamedLocation' { BeforeEach { InModuleScope msec { Mock Assert-MsecSession {} } } It 'expands AllTrusted to the trusted locations and not to the untrusted one' { # AllTrusted is a TOKEN, not an id. Matching policies on id alone finds nothing and # reports every trusted location as unreferenced - the opposite of the truth. InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @(@{ displayName = 'MFA policy'; state = 'enabled' conditions = @{ locations = @{ excludeLocations = @('AllTrusted') } } }) } $rows = Get-MsecEntraNamedLocation ($rows | Where-Object Id -eq 'loc-trusted').IsReferenced | Should -BeTrue ($rows | Where-Object Id -eq 'loc-guest').IsReferenced | Should -BeFalse ($rows | Where-Object Id -eq 'loc-trusted').ReferencedAs | Should -Match 'Exclude:AllTrusted' } } It 'expands All to every location, trusted or not' { InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @(@{ displayName = 'Everywhere'; state = 'enabled' conditions = @{ locations = @{ includeLocations = @('All') } } }) } $rows = Get-MsecEntraNamedLocation @($rows | Where-Object IsReferenced).Count | Should -Be 4 ($rows | Where-Object Id -eq 'loc-guest').ReferencedAs | Should -Match 'Include:All' } } It 'flags a location no policy names as unreferenced' { # A trusted location nobody uses does NOTHING. Reporting it as merely 'trusted' invites # a finding about a setting with no effect. InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @(@{ displayName = 'Named one'; state = 'enabled' conditions = @{ locations = @{ includeLocations = @('loc-guest') } } }) } $rows = Get-MsecEntraNamedLocation ($rows | Where-Object Id -eq 'loc-unused').IsReferenced | Should -BeFalse ($rows | Where-Object Id -eq 'loc-unused').UsedByPolicy | Should -BeNullOrEmpty (Get-MsecEntraNamedLocation -UnreferencedOnly).Id | Should -Not -Contain 'loc-guest' } } It 'records both the direction and the policy state' { # An Exclude is not an exemption - CA grants are additive - and a disabled policy # references nothing in practice. Both have to be visible on the row. InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @(@{ displayName = 'Off policy'; state = 'disabled' conditions = @{ locations = @{ includeLocations = @('loc-trusted') } } }) } $row = Get-MsecEntraNamedLocation | Where-Object Id -eq 'loc-trusted' $row.ReferencedAs | Should -Be @('Include (disabled)') } } It 'reports a country location as untrusted rather than inventing a trust flag' { # isTrusted does not exist on a country location; it cannot be trusted at all. False # here is the fact, not a default standing in for a missing property. InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @() } $c = Get-MsecEntraNamedLocation | Where-Object Id -eq 'loc-country' $c.LocationType | Should -Be 'Country' $c.IsTrusted | Should -BeFalse $c.Ranges | Should -Be @('KP', 'IR') $c.IncludeUnknownCountries | Should -BeTrue } } It 'lists IP ranges as CIDR strings' { InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @() } $hq = Get-MsecEntraNamedLocation | Where-Object Id -eq 'loc-trusted' $hq.Ranges | Should -Be @('10.0.0.0/24', '10.0.1.0/24') $hq.RangeCount | Should -Be 2 } } It '-TrustedOnly drops the untrusted locations, country ones included' { InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @() } (Get-MsecEntraNamedLocation -TrustedOnly).Id | Should -Be @('loc-trusted', 'loc-unused') } } It 'handles a tenant whose policies have no location conditions at all' { # The common case: every location unreferenced, and nothing should throw on the # absent includeLocations/excludeLocations properties. InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @(@{ displayName = 'No locations'; state = 'enabled'; conditions = @{} }) } $rows = Get-MsecEntraNamedLocation @($rows).Count | Should -Be 4 @($rows | Where-Object IsReferenced).Count | Should -Be 0 } } It 'names the missing permission when Graph refuses' { InModuleScope msec { Mock Invoke-MsecGraphRequest { throw 'Response status code does not indicate success: 403 (Forbidden).' } { Get-MsecEntraNamedLocation } | Should -Throw '*Policy.Read.All*' } } It 'emits the documented type so the table view applies' { InModuleScope msec -Parameters @{ L = $script:Locations } { param($L) Mock Invoke-MsecGraphRequest { if ($Path -like '*namedLocations*') { return $L } return @() } (Get-MsecEntraNamedLocation)[0].PSObject.TypeNames | Should -Contain 'MsecEntraNamedLocation' } } } |