tests/Test-Assessment.24568.ps1


<#
.SYNOPSIS

#>




function Test-Assessment-24568 {
    [ZtTest(
        Category = 'Device management',
        ImplementationCost = 'High',
        Pillar = 'Devices',
        RiskLevel = 'High',
        SfiPillar = 'Protect engineering systems',
        TenantType = ('Workforce'),
        TestId = 24568,
        Title = 'macOS - Platform SSO is configured and assigned',
        UserImpact = 'Low'
    )]
    [CmdletBinding()]
    param()

    #region Helper Functions
    function Test-PolicyAssignment {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $false)]
            [array]$Policies
        )

        # Return false if $Policies is null or empty
        if (-not $Policies) {
            return $false
        }

        # Check if at least one policy has assignments
        $assignedPolicies = $Policies | Where-Object {
            $_.PSObject.Properties.Match("assignments") -and $_.assignments -and $_.assignments.Count -gt 0
        }

        return $assignedPolicies.Count -gt 0
    }

    <#
.SYNOPSIS
Filters policies based on specific setting configurations and their expected values.

.DESCRIPTION
This function filters an array of policies by checking if they contain specific settings with expected values.
Each setting can have its own path configuration for navigating the policy's JSON structure, making this
function flexible enough to work with different policy types and structures.

.PARAMETER Policies
An array of policy objects to filter. Each policy should contain settings that can be navigated using
the paths specified in RequiredSettings.

.PARAMETER RequiredSettings
A hashtable where each key is a setting definition ID and the value is a configuration object containing:
- ExpectedValues: Array of values that the setting should have
- ContainerPath: Dot-separated path to the container holding the settings (e.g., 'settings.settinginstance.groupSettingCollectionValue.children')
- SettingIdPath: Property name or path to access the setting ID within each container item
- ValuePath: Property name or path to access the setting value within the target setting
- Description: (Optional) Human-readable description of what the setting controls

.OUTPUTS
System.Array
Returns an array of policy objects that contain at least one of the required settings with matching values.

.EXAMPLE
$requiredSettings = @{
    'com.apple.extensiblesso_extensionidentifier' = @{
        ExpectedValues = @('com.microsoft.CompanyPortalMac.ssoextension')
        ContainerPath = 'settings.settinginstance.groupSettingCollectionValue.children'
        SettingIdPath = 'settingDefinitionId'
        ValuePath = 'simplesettingvalue.value'
        Description = 'Microsoft SSO Extension Identifier'
    }
}

$filteredPolicies = Get-FilteredPoliciesBySetting -Policies $allPolicies -RequiredSettings $requiredSettings

Filters policies to include only those that have the Microsoft SSO extension configured.

.EXAMPLE
$multipleSettings = @{
    'setting.id.one' = @{
        ExpectedValues = @('value1', 'value2')
        ContainerPath = 'settings.configurations'
        SettingIdPath = 'id'
        ValuePath = 'value'
    }
    'setting.id.two' = @{
        ExpectedValues = @('requiredvalue')
        ContainerPath = 'customSettings.payloadContent'
        SettingIdPath = 'payloadType'
        ValuePath = 'payloadContent.value'
    }
}

$filteredPolicies = Get-FilteredPoliciesBySetting -Policies $policies -RequiredSettings $multipleSettings

Filters policies that contain either of two different settings, each located in different JSON paths.

.NOTES
- The function uses a nested helper function Get-NestedProperty to safely navigate object properties
- If any path evaluation fails, the function logs a verbose message and continues with the next setting
- The function returns policies that match ANY of the required settings (OR logic)
- Empty or null policy arrays are handled gracefully by returning an empty array
    #>

    function Get-FilteredPoliciesBySetting {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory)]
            [array]$Policies,

            [Parameter(Mandatory)]
            [hashtable]$RequiredSettings
        )

        <#
        .SYNOPSIS
        Retrieves a nested property value from an object using a dot-separated path.

        .DESCRIPTION
        Traverses an object's properties according to the provided dot-separated path and returns the value found at that path, or $null if any segment is missing.

        .PARAMETER InputObject
        The object from which to retrieve the nested property.

        .PARAMETER Path
        The dot-separated path string indicating the property to retrieve (e.g., 'settings.settinginstance.groupSettingCollectionValue').

        .OUTPUTS
        Returns the value at the specified path, or $null if not found.
        #>

        function Get-NestedProperty {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory)][object]$InputObject,
                [Parameter(Mandatory)][string]$Path
            )
            $current = $InputObject
            foreach ($segment in $Path -split '\.') {
                if ($null -eq $current) {
                    return $null
                }
                $current = $current."$segment"
            }
            return $current
        }

        $filteredPolicies = @()

        foreach ($policy in $Policies) {
            $hasValidConfiguration = $false

            # Check each required setting configuration
            foreach ($settingId in $RequiredSettings.Keys) {
                $settingConfig = $RequiredSettings[$settingId]
                $expectedValues = $settingConfig.ExpectedValues
                $containerPath = $settingConfig.ContainerPath
                $settingIdPath = $settingConfig.SettingIdPath
                $valuePath = $settingConfig.ValuePath

                try {
                    # Get the settings container using the configured path
                    $settingsContainer = Get-NestedProperty -InputObject $policy -Path $containerPath

                    if ($settingsContainer) {
                        # Find the specific setting by ID
                        $targetSetting = $settingsContainer | Where-Object {
                            (Get-NestedProperty -InputObject $_ -Path $settingIdPath) -eq $settingId
                        }

                        if ($targetSetting) {
                            # Get actual values using the configured value path
                            $actualValues = @(Get-NestedProperty -InputObject $targetSetting -Path $valuePath)

                            # Check if any actual value matches any expected value
                            foreach ($actualValue in $actualValues) {
                                if ($expectedValues -contains $actualValue) {
                                    $hasValidConfiguration = $true
                                    break
                                }
                            }
                        }
                    }
                }
                catch {
                    Write-Verbose "Failed to evaluate paths for setting $settingId : $_"
                    continue
                }

                if ($hasValidConfiguration) {
                    break
                }
            }

            if ($hasValidConfiguration) {
                $filteredPolicies += $policy
            }
        }

        return $filteredPolicies
    }
    #endregion Helper Functions

    #region Data Collection
    Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose

    $activity = "Checking macOS Platform SSO is configured and assigned"
    Write-ZtProgress -Activity $activity -Status "Getting policy"

    # Retrieve all configuration policies for macOS that use MDM and Apple Remote Management
    $macOSPolicies_Uri = "deviceManagement/configurationPolicies?`$filter=(platforms has 'macOS') and (technologies has 'mdm' and technologies has 'appleRemoteManagement')&`$expand=settings,assignments"
    $macOSPolicies = Invoke-ZtGraphRequest -RelativeUri $macOSPolicies_Uri -ApiVersion beta

    # Define setting configurations with their specific paths and expected values
    $requiredSettings = @{
        'com.apple.extensiblesso_extensionidentifier' = @{
            ExpectedValues = @('com.microsoft.CompanyPortalMac.ssoextension')
            ContainerPath  = 'settings.settinginstance.groupSettingCollectionValue.children'
            SettingIdPath  = 'settingDefinitionId'
            ValuePath      = 'simplesettingvalue.value'
            Description    = 'Microsoft SSO Extension Identifier'
        }

        # Example: Different setting with different path structure
        # 'com.apple.anothersetting_id' = @{
        # ExpectedValues = @('expectedvalue1', 'expectedvalue2')
        # ContainerPath = 'settings.deviceSettings.configurations'
        # SettingIdPath = 'id'
        # ValuePath = 'configurationValue.stringValue'
        # Description = 'Another SSO Setting'
        # }
    }

    # Filter policies to include only those with required SSO settings
    $macOSSSOPolicies = Get-FilteredPoliciesBySetting -Policies $macOSPolicies -RequiredSettings $requiredSettings
    <#
    $macOSSSOPolicies = @()
    foreach ($macOSPolicy in $macOSPolicies) {
        # Get all setting definition IDs from the policy
        $policySettingIds = @($macOSPolicy.settings.settinginstance.groupSettingCollectionValue.children.settingDefinitionId)

        # Check if policy has any of our required settings with correct values
        $hasValidConfiguration = $false

        foreach ($settingId in $policySettingIds) {
            if ($requiredSettings.ContainsKey($settingId)) {
                $expectedValues = $requiredSettings[$settingId]
                $validSetting = $macOSPolicy.settings?.settinginstance?.groupSettingCollectionValue?.children.where{ $_.settingDefinitionId -eq $settingId }

                # Get actual values from the policy
                $actualValues = @($validSetting.simplesettingvalue?.value)

                # Check if any actual value matches any expected value
                foreach ($actualValue in $actualValues) {
                    if ($expectedValues -contains $actualValue) {
                        $hasValidConfiguration = $true
                        break
                    }
                }

                if ($hasValidConfiguration) {
                    break
                }
            }
        }

        if ($hasValidConfiguration) {
            $macOSSSOPolicies += $macOSPolicy
        }
    }
    #>

    #endregion Data Collection

    #region Assessment Logic
    $passed = $false
    $testResultMarkdown = ""

    # Test macOS SSO policy assignments
    $passed = Test-PolicyAssignment -Policies $macOSSSOPolicies

    if ($passed) {
        $testResultMarkdown = "macOS SSO policies are configured and assigned in Intune.`n`n%TestResult%"
    }
    else {
        $testResultMarkdown = "No assigned macOS SSO policy was found in Intune.`n`n%TestResult%"
    }
    #endregion Assessment Logic

    #region Report Generation
    # Build the detailed sections of the markdown

    # Define variables to insert into the format string
    $reportTitle = "macOS SSO Policies"
    $tableRows = ""

    if ($macOSSSOPolicies.Count -gt 0) {
        # Create a here-string with format placeholders {0}, {1}, etc.
        $formatTemplate = @'

## {0}

| Policy Name | Status | Assignment Target |
| :---------- | :----- | :---------------- |
{1}

'@


        foreach ($policy in $macOSSSOPolicies) {

            $policyName = $policy.Name
            $portalLink = 'https://intune.microsoft.com/#view/Microsoft_Intune_DeviceSettings/DevicesMenu/~/configuration'

            if ($policy.assignments -and $policy.assignments.Count -gt 0) {
                $status = "✅ Assigned"
            }
            else {
                $status = "❌ Not assigned"
            }

            # Get assignment details for this specific policy
            $assignmentTarget = "None"

            if ($policy.assignments -and $policy.assignments.Count -gt 0) {
                $assignmentTarget = Get-PolicyAssignmentTarget -Assignments $policy.assignments
            }

            $tableRows += @"
| [$(Get-SafeMarkdown($policyName))]($portalLink) | $status | $assignmentTarget |`n
"@

        }

        # Format the template by replacing placeholders with values
        $mdInfo = $formatTemplate -f $reportTitle, $tableRows
    }

    # Replace the placeholder with the detailed information
    $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo
    #endregion Report Generation

    $params = @{
        TestId = '24568'
        Title  = 'macOS - Platform SSO is configured and assigned'
        Status = $passed
        Result = $testResultMarkdown
    }

    Add-ZtTestResultDetail @params
}