Public/Discovery/Get-ServicePrincipalCredential.ps1
function Get-ServicePrincipalCredential { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidatePattern('^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$', ErrorMessage = "It does not match expected GUID pattern")] [Alias('Id', 'object-id', 'application-id')] [string]$ObjectId, [Parameter(Mandatory = $false)] [ValidateSet('Password', 'Certificate', 'All')] [string]$CredentialType = 'All', [Parameter(Mandatory = $false)] [switch]$UseApplicationEndpoint, [Parameter(Mandatory = $false)] [switch]$IncludeExpired ) begin { Write-Verbose "Starting function $($MyInvocation.MyCommand.Name)" $MyInvocation.MyCommand.Name | Invoke-BlackCat -ResourceTypeName 'MSGraph' } process { try { # Determine if we're working with an Application or Service Principal $entityType = if ($UseApplicationEndpoint) { "applications" } else { "servicePrincipals" } Write-Verbose "Retrieving credentials for $entityType with ObjectId: $ObjectId" $entity = Invoke-MsGraph -relativeUrl "$entityType/$ObjectId" -NoBatch if (-not $entity) { Write-Message -FunctionName $($MyInvocation.MyCommand.Name) -Message "$entityType with ObjectId '$ObjectId' not found." -Severity 'Error' return } Write-Verbose "Found ${entityType}: $($entity.displayName)" $credentials = @() $currentDateTime = Get-Date # Process password credentials if ($CredentialType -eq 'All' -or $CredentialType -eq 'Password') { if ($entity.passwordCredentials) { foreach ($passwordCred in $entity.passwordCredentials) { $endDateTime = [DateTime]::Parse($passwordCred.endDateTime) $isExpired = $endDateTime -lt $currentDateTime if ($IncludeExpired -or -not $isExpired) { $credentialObject = [PSCustomObject]@{ EntityType = $entityType EntityDisplayName = $entity.displayName EntityId = $entity.id CredentialType = 'Password' KeyId = $passwordCred.keyId DisplayName = $passwordCred.displayName StartDateTime = if ($passwordCred.startDateTime) { [DateTime]::Parse($passwordCred.startDateTime) } else { $null } EndDateTime = $endDateTime IsExpired = $isExpired DaysUntilExpiry = if (-not $isExpired) { ($endDateTime - $currentDateTime).Days } else { $null } Hint = $passwordCred.hint SecretText = if ($passwordCred.secretText) { "[REDACTED - Available in response]" } else { $null } } $credentials += $credentialObject } } } } # Process key/certificate credentials if ($CredentialType -eq 'All' -or $CredentialType -eq 'Certificate') { if ($entity.keyCredentials) { foreach ($keyCred in $entity.keyCredentials) { $endDateTime = [DateTime]::Parse($keyCred.endDateTime) $isExpired = $endDateTime -lt $currentDateTime if ($IncludeExpired -or -not $isExpired) { $credentialObject = [PSCustomObject]@{ EntityType = $entityType EntityDisplayName = $entity.displayName EntityId = $entity.id CredentialType = 'Certificate' KeyId = $keyCred.keyId DisplayName = $keyCred.displayName StartDateTime = if ($keyCred.startDateTime) { [DateTime]::Parse($keyCred.startDateTime) } else { $null } EndDateTime = $endDateTime IsExpired = $isExpired DaysUntilExpiry = if (-not $isExpired) { ($endDateTime - $currentDateTime).Days } else { $null } Type = $keyCred.type Usage = $keyCred.usage CustomKeyIdentifier = $keyCred.customKeyIdentifier Thumbprint = if ($keyCred.customKeyIdentifier) { [System.BitConverter]::ToString([System.Convert]::FromBase64String($keyCred.customKeyIdentifier)).Replace('-', '').ToLower() } else { $null } } $credentials += $credentialObject } } } } if ($credentials.Count -eq 0) { $filterText = if ($IncludeExpired) { "" } else { "non-expired " } Write-Message -FunctionName $($MyInvocation.MyCommand.Name) -Message "No $filterText$($CredentialType.ToLower()) credentials found for $entityType '$($entity.displayName)'" -Severity 'Information' } else { Write-Verbose "Found $($credentials.Count) credentials for $entityType '$($entity.displayName)'" } return $credentials | Sort-Object CredentialType, EndDateTime } catch { Write-Message -FunctionName $($MyInvocation.MyCommand.Name) -Message $($_.Exception.Message) -Severity 'Error' } } end { Write-Verbose "Completed function $($MyInvocation.MyCommand.Name)" } <# .SYNOPSIS Retrieves credentials (passwords and certificates) for Microsoft Entra applications and service principals. .DESCRIPTION The Get-ServicePrincipalCredential function retrieves and displays credential information for Microsoft Entra applications and service principals. It can filter by credential type and include or exclude expired credentials. The function provides detailed information about each credential including expiration status and days until expiry. .PARAMETER ObjectId The Object ID (GUID) of the Microsoft Entra application or service principal. This parameter is mandatory and must match the pattern of a valid GUID. .PARAMETER CredentialType Specifies the type of credentials to retrieve. Valid values are: - All: Retrieves both password and certificate credentials (default) - Password: Retrieves only password credentials - Certificate: Retrieves only certificate credentials .PARAMETER UseApplicationEndpoint When specified, uses the applications endpoint instead of servicePrincipals endpoint. Use this when working with application registrations directly. .PARAMETER IncludeExpired When specified, includes expired credentials in the results. By default, only active (non-expired) credentials are returned. .OUTPUTS Returns an array of PSCustomObjects containing credential information with the following properties: - EntityType: The type of entity (applications or servicePrincipals) - EntityDisplayName: The display name of the entity - EntityId: The ID of the entity - CredentialType: Type of credential (Password or Certificate) - KeyId: Unique identifier for the credential - DisplayName: Display name of the credential - StartDateTime: When the credential becomes valid - EndDateTime: When the credential expires - IsExpired: Boolean indicating if the credential is expired - DaysUntilExpiry: Number of days until expiration (null if expired) - Additional properties specific to credential type .EXAMPLE Get-ServicePrincipalCredential -ObjectId "12345678-1234-1234-1234-123456789012" Retrieves all active credentials for the specified service principal. .EXAMPLE Get-ServicePrincipalCredential -ObjectId "12345678-1234-1234-1234-123456789012" -CredentialType Password Retrieves only password credentials for the specified service principal. .EXAMPLE Get-ServicePrincipalCredential -ObjectId "12345678-1234-1234-1234-123456789012" -IncludeExpired Retrieves all credentials including expired ones for the specified service principal. .EXAMPLE Get-ServicePrincipalCredential -ObjectId "12345678-1234-1234-1234-123456789012" -UseApplicationEndpoint Retrieves credentials using the applications endpoint instead of servicePrincipals endpoint. .EXAMPLE Get-ServicePrincipalCredential -ObjectId "12345678-1234-1234-1234-123456789012" | Where-Object { $_.DaysUntilExpiry -lt 30 } Retrieves credentials that will expire within the next 30 days. .NOTES - This function requires authentication to the Microsoft Graph API with appropriate permissions - Required permissions: Application.Read.All, Application.ReadWrite.All, or Directory.Read.All - The function works with both applications and servicePrincipals endpoints - Secret text values are redacted in the output for security - Certificate thumbprints are calculated from the customKeyIdentifier when available .LINK https://learn.microsoft.com/en-us/graph/api/application-get https://learn.microsoft.com/en-us/graph/api/serviceprincipal-get #> } |