Providers/FreeIPA/Public/Get-FreeIPAServiceApp.ps1

function Get-FreeIPAServiceApp {
    <#
    .SYNOPSIS
        Reports the stored service account credential and whether it still works

    .DESCRIPTION
        Reads the credential record for a realm and says what this machine would connect as:
        the account, where its password is kept and how it is protected, and whether the
        record pins a certificate authority. Connected, it also checks that the account still
        exists in the realm. With -TestCredential it proves the password by logging in with
        it, which is the check to run before rotating anything when a connect has failed.

        The password itself is never in the output.

    .PARAMETER CredentialPath
        The record to read, when not in the default location.

    .PARAMETER BaseUrl
        The realm whose default record to read, when not connected.

    .PARAMETER VaultPassword
        The SecretStore password, when the record's password is in a vault whose password is
        not a default.

    .PARAMETER TestCredential
        Prove the password authenticates.

    .OUTPUTS
        PSCustomObject with BaseUrl, Username, Protection, VaultName, SecretName, PinnedCa,
        CredentialPath, CreatedUtc, AccountExists and CredentialWorks.

    .EXAMPLE
        PS> Get-FreeIPAServiceApp -TestCredential

        DESCRIPTION: Reads the record and proves the password
        OUTPUT: The account's identifiers with CredentialWorks true or false
        USE CASE: Diagnosing a failed Connect-TestEnvironment -ServiceAccount

    .NOTES
        Author: Jeffrey Stuhr
        Blog: https://www.techbyjeff.net
        LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/
    #>


    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'CredentialPath',
        Justification = 'A file path to a credential record, not a credential.')]
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter()]
        [string]$CredentialPath,

        [Parameter()]
        [string]$BaseUrl,

        [Parameter()]
        [System.Security.SecureString]$VaultPassword,

        [Parameter()]
        [switch]$TestCredential
    )

    $connection = Get-FreeIPAConnection -AllowNone
    $resolvedUrl = if ($BaseUrl) { $BaseUrl } elseif ($connection) { $connection.BaseUrl } else { $null }
    if (-not $resolvedUrl -and -not $CredentialPath) {
        throw 'Not connected. Pass -BaseUrl, or -CredentialPath, or connect first.'
    }

    $existingPath = if ($CredentialPath) { $CredentialPath } elseif ($connection) { $connection.CredentialPath } else { $null }
    $recordPath = Get-FreeIPACredentialPath -BaseUrl $(if ($resolvedUrl) { $resolvedUrl } else { 'https://unknown' }) -Path $existingPath

    if (-not (Test-Path -LiteralPath $recordPath)) {
        Write-Warning "No credential record at $recordPath. Run New-TestServiceApp after connecting with a credential."
        return
    }

    $credential = Import-FreeIPACredential -Path $recordPath -VaultPassword $VaultPassword

    $accountExists = $null
    if ($connection) {
        try {
            $shown = Invoke-FreeIPARequest -Method 'user_show' -Arguments $credential.Username -IgnoreError 'NotFound' -Connection $connection
            $accountExists = [bool]$shown
        }
        catch {
            $accountExists = $false
            Write-Verbose "Could not read the account back: $($_.Exception.Message)"
        }
    }

    $credentialWorks = $null
    if ($TestCredential) {
        $probeHttp = $null
        try {
            $probeHttp = New-FreeIPAHttpClient -BaseUrl $credential.BaseUrl -CaCertificate $credential.CaCertificate
            $probe = @{
                BaseUrl = $credential.BaseUrl; Username = $credential.Username; Password = $credential.Password; AuthType = 'ServiceAccount'
                Client = $probeHttp.Client; Cookies = $probeHttp.Cookies
            }
            $login = Connect-FreeIPASession -Connection $probe
            $credentialWorks = [bool]$login.Success
            if (-not $login.Success) { Write-Verbose "Credential test failed: $($login.Reason)" }
        }
        catch {
            $credentialWorks = $false
            Write-Verbose "Credential test failed: $($_.Exception.Message)"
        }
        finally {
            if ($probeHttp) { $probeHttp.Client.Dispose() }
        }
    }

    return [PSCustomObject]@{
        PSTypeName      = 'FreeIPAServiceAppStatus'
        BaseUrl         = $credential.BaseUrl
        Username        = $credential.Username
        Protection      = $credential.Protection
        VaultName       = $credential.VaultName
        SecretName      = $credential.SecretName
        PinnedCa        = [bool]$credential.CaCertificate
        CredentialPath  = $recordPath
        CreatedUtc      = $credential.CreatedUtc
        AccountExists   = $accountExists
        CredentialWorks = $credentialWorks
    }
}