tests/Test-Assessment.21896.ps1
|
<# .SYNOPSIS Checks Service principals have certificates or credentials associated with them #> function Test-Assessment-21896 { [ZtTest( Category = 'Access control', ImplementationCost = 'Medium', Pillar = 'Identity', RiskLevel = 'Medium', SfiPillar = 'Protect identities and secrets', TenantType = ('Workforce', 'External'), TestId = 21896, Title = "Service principals don't have certificates or credentials associated with them", UserImpact = 'Low' )] [CmdletBinding()] param() Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose $activity = 'Checking Service principals have certificates or credentials associated with them' Write-ZtProgress -Activity $activity -Status 'Getting Service Principals' # Start test as passed $passed = $true # Q2: Get service principals with any credentials using single SQL query $sqlPassCreds = @" SELECT distinct ON (id) id, appId, displayName, appOwnerOrganizationId, try_cast(unnest(passwordCredentials).endDateTime as date) as keyEndDateTime FROM ServicePrincipal WHERE passwordCredentials != '[]' and appOwnerOrganizationId != 'f8cdef31-a31e-4b4a-93e4-5f571e91255a' ORDER BY displayName, keyEndDateTime DESC "@ # SQL query to find service principals with key credentials $sqlKeyCreds = @" SELECT distinct ON (id) id, appId, displayName, appOwnerOrganizationId, try_cast(unnest(keyCredentials).endDateTime as date) as keyEndDateTime FROM ServicePrincipal WHERE keyCredentials != '[]' and appOwnerOrganizationId != 'f8cdef31-a31e-4b4a-93e4-5f571e91255a' ORDER BY displayName, keyEndDateTime DESC "@ $resultsPassCreds = Invoke-DatabaseQuery -Database $Database -Sql $sqlPassCreds $resultsKeyCreds = Invoke-DatabaseQuery -Database $Database -Sql $sqlKeyCreds if ($resultsPassCreds.Count -eq 0 -and $resultsKeyCreds.Count -eq 0) { $passed = $true $testResultMarkdown = "Service principals don't have credentials associated with them." } else { $passed = $false $testResultMarkdown = "Found Service Principals with credentials configured in the tenant, which represents a security risk.`n`n%TestResult%" } # Build the detailed sections of the markdown # Define variables to insert into the format string $reportTitle = 'Service Principals with credentials configured in the tenant' $tableRows = "" if ($resultsPassCreds.Count -gt 0 -or $resultsKeyCreds.Count -gt 0) { # Create a here-string with format placeholders {0}, {1}, etc. $formatTemplate = @' ## {0} | Service Principal Name | Credentials Type | Credentials Expiration Date | Expiry Status | | :--------------------- | :--------------- | :-------------------------- | :------------ | {1} '@ foreach ($sp in $resultsPassCreds) { $portalLink = 'https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ManagedAppMenuBlade/~/Overview/objectId/{0}/appId/{1}' -f $sp.id, $sp.appId $expiryDate = $sp.keyEndDateTime.ToDateTime([TimeOnly]::MinValue) $expiryStatus = if ( (Get-Date) -gt $expiryDate) { '❗ Expired' } else { '✅ Current' } $tableRows += @" | [$(Get-SafeMarkdown($sp.displayName))]($portalLink) | Password Credentials | $(Get-FormattedDate($sp.keyEndDateTime)) | $expiryStatus |`n "@ } foreach ($sp in $resultsKeyCreds) { $portalLink = 'https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ManagedAppMenuBlade/~/Overview/objectId/{0}/appId/{1}' -f $sp.id, $sp.appId $expiryDate = $sp.keyEndDateTime.ToDateTime([TimeOnly]::MinValue) $expiryStatus = if ( (Get-Date) -gt $expiryDate) { '❗ Expired' } else { '✅ Current' } $tableRows += @" | [$(Get-SafeMarkdown($sp.displayName))]($portalLink) | Key Credentials | $(Get-FormattedDate($sp.keyEndDateTime)) | $expiryStatus |`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 $params = @{ TestId = '21896' Title = "Service principals don't have certificates or credentials associated with them" UserImpact = 'Low' Risk = 'Medium' ImplementationCost = 'Medium' AppliesTo = 'Identity' Tag = 'Identity' Status = $passed Result = $testResultMarkdown } if (!$passed) { $params.CustomStatus = 'Investigate' } Add-ZtTestResultDetail @params } |