Public/Remove-SshId.ps1

function Remove-SshId {
    <#
    .SYNOPSIS
        Removes SSH public keys previously imported by Import-SshId for a
        given identity.
 
    .DESCRIPTION
        Deletes every key line in the target authorized_keys file whose
        trailing '# ssh-import-id <id>' comment matches the given identity
        exactly. Keys not imported by Import-SshId (no matching comment), or
        imported under a different identity, are left untouched.
 
        The identity does not need to resolve against a known source prefix
        (built-in or custom) - this lets you remove keys even after
        unregistering the custom source they were imported from.
 
    .PARAMETER UserId
        One or more identities in '<prefix>:<username>' form whose tagged
        keys should be removed, e.g. 'gh:octocat'.
 
    .PARAMETER AuthorizedKeysPath
        Path to the authorized_keys file to update. Defaults to
        '$HOME\.ssh\authorized_keys', or to the Windows OpenSSH
        administrators_authorized_keys path when -ForAdministrators is set.
 
    .PARAMETER ForAdministrators
        Target Windows OpenSSH's shared
        '%ProgramData%\ssh\administrators_authorized_keys' file instead of
        the per-user authorized_keys file (see Import-SshId's
        -ForAdministrators for background). Removing from it requires an
        elevated (Run as Administrator) session.
 
    .PARAMETER PassThru
        Emit a result object per identity describing how many keys were
        removed.
 
    .EXAMPLE
        Remove-SshId gh:octocat
 
        Removes every key previously imported via 'gh:octocat'.
 
    .EXAMPLE
        Remove-SshId gh:octocat -WhatIf
 
        Previews which keys would be removed without changing the file.
 
    .EXAMPLE
        Remove-SshId gh:octocat -ForAdministrators
 
        Removes keys tagged 'gh:octocat' from
        '%ProgramData%\ssh\administrators_authorized_keys'. Must be run
        elevated.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('Id')]
        [string[]] $UserId,

        [string] $AuthorizedKeysPath,

        [switch] $ForAdministrators,

        [switch] $PassThru
    )

    begin {
        if (-not $PSBoundParameters.ContainsKey('AuthorizedKeysPath')) {
            $AuthorizedKeysPath = Get-SshImportIdDefaultAuthorizedKeysPath -ForAdministrators:$ForAdministrators
        }
    }

    process {
        foreach ($uid in $UserId) {
            if ($uid -notmatch '^[a-zA-Z0-9_-]+:.+$') {
                Write-Error "Invalid user id '$uid'. Expected format '<prefix>:<username>', e.g. 'gh:octocat'."
                continue
            }

            if (-not (Test-Path -Path $AuthorizedKeysPath)) {
                Write-Warning "'$AuthorizedKeysPath' does not exist; nothing to remove for '$uid'."
                continue
            }

            if ((Test-SshImportIdAdministratorsAuthorizedKeysPath -Path $AuthorizedKeysPath) -and -not (Test-SshImportIdElevated)) {
                Write-Error "Writing to '$AuthorizedKeysPath' requires an elevated (Run as Administrator) PowerShell session."
                continue
            }

            if (-not $PSCmdlet.ShouldProcess($AuthorizedKeysPath, "Remove SSH keys tagged '$uid'")) {
                continue
            }

            $result = Remove-SshImportIdAuthorizedKey -Path $AuthorizedKeysPath -Tag $uid

            if ($result.Removed.Count -eq 0) {
                Write-Warning "No keys tagged '$uid' were found in '$AuthorizedKeysPath'."
            }

            foreach ($key in $result.Removed) {
                $preview = if ($key.Length -gt 60) { $key.Substring(0, 60) + '...' } else { $key }
                Write-Host "[-] $uid : $preview" -ForegroundColor Yellow
            }

            $obj = [pscustomobject]@{
                UserId  = $uid
                Removed = $result.Removed.Count
                Path    = $AuthorizedKeysPath
            }

            if ($PassThru) {
                $obj
            }
        }
    }
}