Public/Set-PWSHPUKMGT-UserDevice.ps1

<#
.SYNOPSIS
    Generates a new PUK, links a crypto device serial number to a user, and persists the record.
.DESCRIPTION
    Resolves the given identity in Active Directory, generates a new PUK compliant with the
    configured weakness rules, and stores the user/serial/PUK association in the configured
    hosting backend (flat file or Active Directory). Fails if a record already exists for the
    identity unless -Force is specified. The generated PUK is returned exactly once, as a
    SecureString -- it is never written to disk, a log, or the verbose stream in clear text.
    This cmdlet requires confirmation by default (ConfirmImpact = High); pass -Confirm:$false
    to suppress the prompt in unattended/automation scenarios.
.PARAMETER Identity
    A SamAccountName, UserPrincipalName, or DistinguishedName identifying the user.
.PARAMETER SerialNumber
    The crypto device serial number to link to the user.
.PARAMETER PukLength
    The PUK length to generate. Defaults to the configured puk.defaultLength.
.PARAMETER Force
    Overwrite an existing record for this identity.
.PARAMETER Server
    The domain controller to run the underlying LDAP requests against. Defaults to the value
    configured in hosting.activeDirectory.server, or, if that is not set either, to the primary
    domain controller (PDC emulator) of the current domain.
.EXAMPLE
    Set-PWSHPUKMGT-UserDevice -Identity 'alice' -SerialNumber 'YK-00123456'
.OUTPUTS
    System.Security.SecureString
    Contains the newly generated PUK.
#>

function Set-PWSHPUKMGT-UserDevice {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'The freshly generated plaintext PUK is intentionally converted to a SecureString exactly once, at this return boundary, per the confirmed design decision that Set-PWSHPUKMGT-UserDevice never returns the PUK in clear text.')]
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([securestring])]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Identity,

        [Parameter(Mandatory, Position = 1)]
        [string]$SerialNumber,

        [int]$PukLength,

        [string]$Server,

        [switch]$Force
    )

    $config = Get-PukModuleConfig
    $certificate = Test-PukCertificate -Thumbprint $config.certificate.thumbprint -StoreLocation $config.certificate.storeLocation -RequiredEkuOids $config.certificate.requiredEkuOids
    $effectiveServer = Resolve-PukServer -Server $Server -Config $config

    $distinguishedName = Resolve-PukIdentity -Identity $Identity -Server $effectiveServer

    $pukParams = @{ Config = $config }
    if ($PSBoundParameters.ContainsKey('PukLength')) {
        $pukParams.Length = $PukLength
    }
    $puk = New-PWSHPUKMGT-Puk @pukParams

    $record = [PSCustomObject]@{
        DistinguishedName = $distinguishedName
        SerialNumber      = $SerialNumber
        Puk               = $puk
    }

    if (-not $PSCmdlet.ShouldProcess($distinguishedName, "Set PUK and link device serial '$SerialNumber'")) {
        return
    }

    Set-PukRecord -Config $config -Certificate $certificate -Record $record -Server $effectiveServer -Force:$Force -Confirm:$false

    Write-PukLog -Level Info -Config $config -Message "Set-PWSHPUKMGT-UserDevice: linked device serial '$SerialNumber' to '$distinguishedName'."

    return ConvertTo-SecureString -String $puk -AsPlainText -Force
}