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*" } } } } |