Tests/Public/Test-AadkerbReadiness.Tests.ps1

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '', Justification = 'Tests intentionally swallow errors to verify side-effects')]
param()

BeforeAll {
    $modulePath = "$PSScriptRoot/../.."
    Get-ChildItem -Path "$modulePath/Private/*.ps1" | ForEach-Object { . $_.FullName }
    Get-ChildItem -Path "$modulePath/Public/*.ps1"  | ForEach-Object { . $_.FullName }
}

Describe 'Test-AadkerbReadiness' {
    BeforeEach {
        Mock Write-Host {}
        Mock Write-Warning {}
        Mock Assert-AzCliLogin { [PSCustomObject]@{ user = @{ name = 'test@contoso.com' } } }
    }

    Context 'Pre-flight' {
        It 'Calls Assert-AzCliLogin before any checks' {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 1
                'Error'
            }
            try { Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' } catch {}
            Should -Invoke Assert-AzCliLogin -Times 1
        }
    }

    Context 'When storage account retrieval fails' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 1
                throw 'Not found'
            }
        }

        It 'Reports FAIL and returns gracefully' {
            Test-AadkerbReadiness -StorageAccountName 'badaccount' -ResourceGroupName 'rg-test'
            Should -Invoke Write-Host -ParameterFilter { $Object -like '*FAIL*Unable to retrieve*' }
        }
    }

    Context 'When storage account has AADKERB configured' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                # Return different responses based on the command
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0;SMB3.1.1","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM;AES-256-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') {
                    '{"value":[]}'
                }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') {
                    '[]'
                }
                else {
                    '{}'
                }
            }
        }

        It 'Reports PASS for AADKERB directory type' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*AADKERB*" }
        }

        It 'Reports PASS for active directory properties' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*activeDirectoryProperties*contoso*" }
        }

        It 'Reports PASS for default share permission' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*Default share-level permission*StorageFileDataSmbShareContributor*" }
        }

        It 'Reports PASS for Kerberos authentication type' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*Kerberos is listed*" }
        }

        It 'Reports FAIL for missing service principal' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*Service principal*not found*" }
        }

        It 'Prints summary with pass/warn/fail counts' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*SUMMARY*" }
        }
    }

    Context 'When directory type is None' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"None","defaultSharePermission":"None","activeDirectoryProperties":null},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"NTLMv2","kerberosTicketEncryption":null,"channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') { '{"value":[]}' }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Reports FAIL for directory type None' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*Directory service type is 'None'*" }
        }

        It 'Reports FAIL for default share permission None' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*Default share-level permission is*None*" }
        }

        It 'Reports WARN for no Kerberos in auth types' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*Kerberos is not explicitly listed*" }
        }
    }

    Context 'When directory type is AD (not AADKERB)' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AD","defaultSharePermission":"StorageFileDataSmbShareReader","activeDirectoryProperties":{"domainName":"corp.local","domainGuid":"guid-ad"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') { '{"value":[]}' }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Reports WARN for non-AADKERB directory type' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*Directory service type is 'AD'*" }
        }
    }

    Context 'Service principal found with admin consent' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') {
                    $urlArg = $allArgs | Where-Object { $_ -like 'https://graph.microsoft.com/*' }
                    if ($urlArg -like '*oauth2PermissionGrants*') {
                        '{"value":[{"scope":"openid profile User.Read","consentType":"AllPrincipals","resourceId":"res-1"}]}'
                    }
                    elseif ($urlArg -like '*servicePrincipals*') {
                        '{"value":[{"id":"sp-obj-1","appId":"app-123","displayName":"[Storage Account] sttest.file.core.windows.net"}]}'
                    }
                    elseif ($urlArg -like '*applications*') {
                        '{"value":[{"id":"app-obj-1","tags":["kdc_enable_cloud_group_sids"]}]}'
                    }
                    elseif ($urlArg -like '*signIns*') {
                        '{"value":[]}'
                    }
                    elseif ($urlArg -like '*conditionalAccess/policies*') {
                        '{"value":[]}'
                    }
                    else { '{"value":[]}' }
                }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') {
                    '[{"roleDefinitionName":"Storage File Data SMB Share Contributor","principalName":"user@contoso.com","principalType":"User"}]'
                }
                else { '{}' }
            }
        }

        It 'Reports PASS for service principal found' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*Service principal found*" }
        }

        It 'Reports PASS for required OAuth scopes' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*Required scope 'openid'*" }
        }

        It 'Reports PASS for kdc_enable_cloud_group_sids tag' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*kdc_enable_cloud_group_sids*" }
        }

        It 'Reports no blocking CA policies' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*No CA policies found*" }
        }

        It 'Reports PASS for RBAC role assignments' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*SMB Share role assignment*" }
        }

        It 'Reports no sign-in failures' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*INFO*No recent sign-in failures*" }
        }
    }

    Context 'Conditional Access blocking policies' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') {
                    $urlArg = $allArgs | Where-Object { $_ -like 'https://graph.microsoft.com/*' }
                    if ($urlArg -like '*oauth2PermissionGrants*') {
                        '{"value":[{"scope":"openid profile User.Read","consentType":"AllPrincipals","resourceId":"res-1"}]}'
                    }
                    elseif ($urlArg -like '*servicePrincipals*') {
                        '{"value":[{"id":"sp-obj-1","appId":"app-123","displayName":"[Storage Account] sttest.file.core.windows.net"}]}'
                    }
                    elseif ($urlArg -like '*applications*') {
                        '{"value":[{"id":"app-obj-1","tags":["kdc_enable_cloud_group_sids"]}]}'
                    }
                    elseif ($urlArg -like '*signIns*') {
                        '{"value":[{"createdDateTime":"2026-04-10T09:00:00Z","userPrincipalName":"user@contoso.com","appDisplayName":"Storage","status":{"errorCode":50076,"failureReason":"MFA required"},"conditionalAccessStatus":"failure","clientAppUsed":"kerberos/1.0"}]}'
                    }
                    elseif ($urlArg -like '*conditionalAccess/policies*') {
                        '{"value":[{"id":"ca-1","displayName":"Require MFA","state":"enabled","conditions":{"applications":{"includeApplications":["All"],"excludeApplications":[]}},"grantControls":{"builtInControls":["mfa"],"authenticationStrength":null,"termsOfUse":[]}}]}'
                    }
                    else { '{"value":[]}' }
                }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Reports FAIL for blocking CA policies that require MFA' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*CA policy*will block*" }
        }

        It 'Reports sign-in failures' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*recent sign-in failure*" }
        }
    }

    Context 'Missing admin consent scopes' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') {
                    $urlArg = $allArgs | Where-Object { $_ -like 'https://graph.microsoft.com/*' }
                    if ($urlArg -like '*oauth2PermissionGrants*') {
                        # Missing User.Read scope
                        '{"value":[{"scope":"openid","consentType":"AllPrincipals","resourceId":"res-1"}]}'
                    }
                    elseif ($urlArg -like '*servicePrincipals*') {
                        '{"value":[{"id":"sp-obj-1","appId":"app-123","displayName":"[Storage Account] sttest.file.core.windows.net"}]}'
                    }
                    elseif ($urlArg -like '*applications*') {
                        # Missing kdc tag
                        '{"value":[{"id":"app-obj-1","tags":[]}]}'
                    }
                    elseif ($urlArg -like '*signIns*') { '{"value":[]}' }
                    elseif ($urlArg -like '*conditionalAccess/policies*') { '{"value":[]}' }
                    else { '{"value":[]}' }
                }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Reports FAIL for missing User.Read scope' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*Required scope 'User.Read'*NOT granted*" }
        }

        It 'Reports FAIL for missing kdc_enable_cloud_group_sids tag' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*kdc_enable_cloud_group_sids*MISSING*" }
        }

        It 'Reports no admin consent when grants are empty' {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') {
                    $urlArg = $allArgs | Where-Object { $_ -like 'https://graph.microsoft.com/*' }
                    if ($urlArg -like '*oauth2PermissionGrants*') {
                        '{"value":[]}'
                    }
                    elseif ($urlArg -like '*servicePrincipals*') {
                        '{"value":[{"id":"sp-obj-1","appId":"app-123","displayName":"[Storage Account] sttest.file.core.windows.net"}]}'
                    }
                    else { '{"value":[]}' }
                }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*No admin consent grants found*" }
        }

        It 'Reports WARN for no RBAC role assignments' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*No*Storage File Data SMB Share*role assignments found*" }
        }
    }

    Context 'AADKERB with missing activeDirectoryProperties' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":null},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') { '{"value":[]}' }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Reports FAIL for missing activeDirectoryProperties' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*activeDirectoryProperties is not set*" }
        }
    }

    Context 'SubscriptionId parameter' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-custom/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"test.onmicrosoft.com","domainGuid":"guid-1"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') { '{"value":[]}' }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Accepts optional SubscriptionId' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SubscriptionId 'sub-custom' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*AADKERB*" }
        }
    }

    Context 'SkipClientChecks' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"None","activeDirectoryProperties":{"domainName":"test.onmicrosoft.com","domainGuid":"guid-1"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                } else {
                    '{"value":[]}'
                }
            }
        }

        It 'Skips client checks section when -SkipClientChecks is specified' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like '*Client Device*SKIPPED*' }
        }
    }

    Context 'SkipConditionalAccessChecks' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"None","activeDirectoryProperties":{"domainName":"test.onmicrosoft.com","domainGuid":"guid-1"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                } else {
                    '{"value":[]}'
                }
            }
        }

        It 'Skips CA section when -SkipConditionalAccessChecks is specified' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like '*Conditional Access*SKIPPED*' }
        }
    }

    Context 'Parameter validation' {
        It 'Requires StorageAccountName' {
            $cmd = Get-Command -Name Test-AadkerbReadiness
            $param = $cmd.Parameters['StorageAccountName']
            $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] } | ForEach-Object {
                $_.Mandatory | Should -BeTrue
            }
        }

        It 'Requires ResourceGroupName' {
            $cmd = Get-Command -Name Test-AadkerbReadiness
            $param = $cmd.Parameters['ResourceGroupName']
            $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] } | ForEach-Object {
                $_.Mandatory | Should -BeTrue
            }
        }
    }

    Context 'Client-side checks (not skipped)' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') { '{"value":[]}' }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
            Mock Get-CimInstance { [PSCustomObject]@{ Caption = 'Windows 11 Enterprise'; BuildNumber = '22621' } }
            Mock -CommandName 'dsregcmd' -MockWith { 'AzureAdJoined : YES' }
            Mock Get-ItemProperty { [PSCustomObject]@{ CloudKerberosTicketRetrievalEnabled = 1 } }
            Mock Get-Service { [PSCustomObject]@{ Status = 'Running' } }
            Mock -CommandName 'klist' -MockWith { @('krbtgt/KERBEROS.MICROSOFTONLINE.COM', 'cifs/sttest.file.core.windows.net') }
            Mock Test-NetConnection { [PSCustomObject]@{ TcpTestSucceeded = $true } }
        }

        It 'Reports PASS for supported OS build' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*OS build*supported*" }
        }

        It 'Reports WARN for old OS build' {
            Mock Get-CimInstance { [PSCustomObject]@{ Caption = 'Windows 10 Enterprise'; BuildNumber = '19045' } }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*OS build*may not support*" }
        }

        It 'Reports PASS for Entra joined device' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*Entra joined*" }
        }

        It 'Reports FAIL for non-Entra-joined device' {
            Mock -CommandName 'dsregcmd' -MockWith { 'AzureAdJoined : NO' }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*not appear to be Entra joined*" }
        }

        It 'Reports PASS for CloudKerberosTicketRetrievalEnabled registry' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*CloudKerberosTicketRetrievalEnabled*enabled*" }
        }

        It 'Reports FAIL for missing registry key' {
            Mock Get-ItemProperty { $null }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*CloudKerberosTicketRetrievalEnabled*not found*" }
        }

        It 'Reports PASS for running services' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*Service*running*" }
        }

        It 'Reports WARN for stopped service' {
            Mock Get-Service { [PSCustomObject]@{ Status = 'Stopped' } }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*not running*" }
        }

        It 'Reports PASS for Entra Kerberos TGT' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*Entra Kerberos TGT*" }
        }

        It 'Reports WARN when no Kerberos TGT found' {
            Mock -CommandName 'klist' -MockWith { 'no tickets' }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*No Entra Kerberos TGT*" }
        }

        It 'Reports PASS for TCP 445 connectivity' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*TCP 445*reachable*" }
        }

        It 'Reports FAIL for TCP 445 unreachable' {
            Mock Test-NetConnection { [PSCustomObject]@{ TcpTestSucceeded = $false } }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*TCP 445*NOT reachable*" }
        }

        It 'Reports CIFS service ticket when found' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*CIFS service ticket found*" }
        }

        It 'Reports INFO when no CIFS service ticket cached' {
            Mock -CommandName 'klist' -MockWith { @('krbtgt/KERBEROS.MICROSOFTONLINE.COM') }
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*INFO*No CIFS service ticket*" }
        }
    }

    Context 'RBAC role assignment failure' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') { '{"value":[]}' }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') {
                    throw 'AuthorizationFailed'
                }
                else { '{}' }
            }
        }

        It 'Reports WARN when role assignment listing throws' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks -SkipConditionalAccessChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*WARN*Unable to list RBAC*" }
        }
    }

    Context 'CA policy with authenticationStrength' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') {
                    $urlArg = $allArgs | Where-Object { $_ -like 'https://graph.microsoft.com/*' }
                    if ($urlArg -like '*servicePrincipals*' -and $urlArg -notlike '*oauth2*') {
                        '{"value":[{"id":"sp-obj-1","appId":"app-123","displayName":"[Storage Account] sttest.file.core.windows.net"}]}'
                    }
                    elseif ($urlArg -like '*oauth2PermissionGrants*') {
                        '{"value":[{"scope":"openid profile User.Read","consentType":"AllPrincipals","resourceId":"res-1"}]}'
                    }
                    elseif ($urlArg -like '*applications*') {
                        '{"value":[{"id":"app-obj-1","tags":["kdc_enable_cloud_group_sids"]}]}'
                    }
                    elseif ($urlArg -like '*signIns*') { '{"value":[]}' }
                    elseif ($urlArg -like '*conditionalAccess/policies*') {
                        '{"value":[{"id":"ca-2","displayName":"Require phishing-resistant","state":"enabled","conditions":{"applications":{"includeApplications":["All"],"excludeApplications":[]}},"grantControls":{"builtInControls":[],"authenticationStrength":{"id":"str-1","displayName":"Phishing-resistant MFA"},"termsOfUse":[]}}]}'
                    }
                    else { '{"value":[]}' }
                }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Detects blocking CA policy with authenticationStrength' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*FAIL*CA policy*will block*" }
        }
    }

    Context 'CA policy excludes storage app' {
        BeforeEach {
            Mock -CommandName 'az' -MockWith {
                $global:LASTEXITCODE = 0
                $allArgs = @($args)
                if ($allArgs -contains 'storage' -or $allArgs[0] -eq 'storage') {
                    '{"id":"/subscriptions/sub-123/resourceGroups/rg-test/providers/Microsoft.Storage/storageAccounts/sttest","azureFilesIdentityBasedAuthentication":{"directoryServiceOptions":"AADKERB","defaultSharePermission":"StorageFileDataSmbShareContributor","activeDirectoryProperties":{"domainName":"contoso.onmicrosoft.com","domainGuid":"guid-123"}},"fileServiceProperties":{"protocolSettings":{"smb":{"versions":"SMB3.0","authenticationMethods":"Kerberos","kerberosTicketEncryption":"AES-256","channelEncryption":"AES-128-GCM"}}}}'
                }
                elseif ($allArgs -contains 'rest' -or $allArgs[0] -eq 'rest') {
                    $urlArg = $allArgs | Where-Object { $_ -like 'https://graph.microsoft.com/*' }
                    if ($urlArg -like '*servicePrincipals*' -and $urlArg -notlike '*oauth2*') {
                        '{"value":[{"id":"sp-obj-1","appId":"app-123","displayName":"[Storage Account] sttest.file.core.windows.net"}]}'
                    }
                    elseif ($urlArg -like '*oauth2PermissionGrants*') {
                        '{"value":[{"scope":"openid profile User.Read","consentType":"AllPrincipals","resourceId":"res-1"}]}'
                    }
                    elseif ($urlArg -like '*applications*') {
                        '{"value":[{"id":"app-obj-1","tags":["kdc_enable_cloud_group_sids"]}]}'
                    }
                    elseif ($urlArg -like '*signIns*') { '{"value":[]}' }
                    elseif ($urlArg -like '*conditionalAccess/policies*') {
                        # Policy includes All apps but EXCLUDES the storage app
                        '{"value":[{"id":"ca-3","displayName":"MFA for all except storage","state":"enabled","conditions":{"applications":{"includeApplications":["All"],"excludeApplications":["app-123"]}},"grantControls":{"builtInControls":["mfa"],"authenticationStrength":null,"termsOfUse":[]}}]}'
                    }
                    else { '{"value":[]}' }
                }
                elseif ($allArgs -contains 'role' -or $allArgs[0] -eq 'role') { '[]' }
                else { '{}' }
            }
        }

        It 'Reports PASS when CA policy excludes the storage app' {
            Test-AadkerbReadiness -StorageAccountName 'sttest' -ResourceGroupName 'rg-test' -SkipClientChecks
            Should -Invoke Write-Host -ParameterFilter { $Object -like "*PASS*No CA policies found*" }
        }
    }
}