Public/Reset-GkAppCredential.ps1

function Reset-GkAppCredential {
    <#
    .SYNOPSIS
        Add or remove an app registration's client secret.

    .DESCRIPTION
        Manages application client secrets:
          * Add (default) POST /applications/{id}/addPassword — returns the new secretText ONCE
            (it cannot be retrieved again, so it is surfaced on the result object). Use -DisplayName
            and -ValidMonths to control the credential.
          * Remove POST /applications/{id}/removePassword -RemoveKeyId <keyId>.

        Certificate rotation is intentionally not supported: addKey/removeKey require a
        proof-of-possession JWT signed by an existing private key, which is outside this module's
        scope. Manage certificates with a dedicated tool that holds the key material.

        State-changing: supports -WhatIf / -Confirm and prompts by default. Accepts applications from
        the pipeline (by the Id property, i.e. the application OBJECT id from
        Get-GkAppRegistrationReport) and yields a PSGraphKit.AppCredentialResult per application.
        Requires Application.ReadWrite.All plus a supporting Entra role (e.g. Application Administrator).

    .PARAMETER ApplicationId
        One or more application OBJECT IDs (the Id from Get-GkAppRegistrationReport, not AppId).

    .PARAMETER DisplayName
        Friendly name for the new secret (Add set). Default 'PSGraphKit secret'.

    .PARAMETER ValidMonths
        Lifetime of the new secret in months (Add set). Default 12.

    .PARAMETER RemoveKeyId
        keyId of the secret to remove (Remove set).

    .EXAMPLE
        Reset-GkAppCredential -ApplicationId $appObjectId -DisplayName 'rotated 2026' -Confirm:$false |
            Select-Object ApplicationId, KeyId, SecretText

        Add a new client secret and capture the generated value (shown once).

    .EXAMPLE
        Reset-GkAppCredential -ApplicationId $appObjectId -RemoveKeyId $oldKeyId -WhatIf

        Preview removing a specific secret.

    .EXAMPLE
        Get-GkAppRegistrationReport -ExpiringOnly | Reset-GkAppCredential -WhatIf

        Preview adding a fresh secret to every app with an expiring credential.

    .OUTPUTS
        PSGraphKit.AppCredentialResult
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'AddSecret')]
    [OutputType('PSGraphKit.AppCredentialResult')]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('Id')]
        [string[]] $ApplicationId,

        [Parameter(ParameterSetName = 'AddSecret')]
        [string] $DisplayName = 'PSGraphKit secret',

        [Parameter(ParameterSetName = 'AddSecret')]
        [ValidateRange(1, 24)]
        [int] $ValidMonths = 12,

        [Parameter(Mandatory, ParameterSetName = 'RemoveSecret')]
        [string] $RemoveKeyId
    )

    begin {
        Test-GkConnection -FunctionName 'Reset-GkAppCredential' | Out-Null
        $isRemove = $PSCmdlet.ParameterSetName -eq 'RemoveSecret'
    }

    process {
        foreach ($appId in $ApplicationId) {
            if ([string]::IsNullOrWhiteSpace($appId)) { continue }
            $enc = [uri]::EscapeDataString($appId)

            $action = if ($isRemove) { "Remove client secret $RemoveKeyId" } else { "Add client secret ($DisplayName, ${ValidMonths}mo)" }
            if (-not $PSCmdlet.ShouldProcess($appId, $action)) { continue }

            $outcome = if ($isRemove) { 'SecretRemoved' } else { 'SecretAdded' }
            $keyId = if ($isRemove) { $RemoveKeyId } else { $null }
            $secretText = $null
            $endDate = $null
            $errMsg = $null

            try {
                if ($isRemove) {
                    Invoke-GkGraphRequest -Method POST -Uri "/applications/$enc/removePassword" `
                        -Body @{ keyId = $RemoveKeyId } -CallerFunction 'Reset-GkAppCredential' | Out-Null
                }
                else {
                    $end = [datetime]::UtcNow.AddMonths($ValidMonths).ToString('o')
                    $resp = Invoke-GkGraphRequest -Raw -Method POST -Uri "/applications/$enc/addPassword" `
                        -Body @{ passwordCredential = @{ displayName = $DisplayName; endDateTime = $end } } `
                        -CallerFunction 'Reset-GkAppCredential'
                    $secretText = [string](Get-GkDictValue $resp 'secretText')
                    $keyId = [string](Get-GkDictValue $resp 'keyId')
                    $endDate = ConvertTo-GkDateTime (Get-GkDictValue $resp 'endDateTime')
                }
            }
            catch {
                $outcome = 'Failed'
                $errMsg = $_.Exception.Message
                Write-Warning "Failed to $(if ($isRemove) { 'remove secret from' } else { 'add secret to' }) app '$appId': $errMsg"
            }

            [pscustomobject]@{
                PSTypeName    = 'PSGraphKit.AppCredentialResult'
                ApplicationId = $appId
                Action        = if ($isRemove) { 'RemoveSecret' } else { 'AddSecret' }
                KeyId         = $keyId
                SecretText    = $secretText
                EndDateTime   = $endDate
                Outcome       = $outcome
                Error         = $errMsg
            }
        }
    }
}