Public/Set-HDTBootImageClientCertificate.ps1

function Set-HDTBootImageClientCertificate {
    <#
        .SYNOPSIS
            Names the machine certificate the boot image authenticates with.

        .DESCRIPTION
            THE CERTIFICATE THAT OPENS THE PORT. A switch running 802.1X
            authenticates a machine BEFORE it gives it an address, and a machine
            in WinPE has no domain identity to authenticate with - so the image
            boots, gets no lease, and looks exactly like a broken deployment
            share. MDT and ConfigMgr both grew a way to put a certificate in the
            boot media for this; so does HDT.

            IT IS A .pfx AND NOTHING ELSE. This is the certificate the machine
            authenticates WITH, which means it needs its private key: a .cer has
            none, and a machine holding one can prove nothing. It is imported
            into the LOCAL MACHINE MY store, not Root - a certificate authority
            is Add-HDTBootImageCertificate's.

            THE PASSWORD IS NOT IN THIS DOCUMENT. workspace.yaml is what an
            administrator hand-edits and commits, and a private key's password in
            git is worse than no certificate at all. Run
            Set-HDTBootImageCertificatePassword, which writes it to
            Control\certificate-password.json the way the share credential's is
            written - and obfuscated, not encrypted, with the file saying so.

            WHAT THE IMAGE COSTS YOU IS THE KEY ITSELF. A .pfx in a boot image
            served over PXE is a private key handed to anything that can boot;
            the certificate should be one issued for this purpose, scoped to it,
            and revocable on its own. That is true of MDT's and ConfigMgr's
            equivalents too, and none of them can make it untrue.

            IT IS SHAPED LIKE Set-HDTBootImageUnattend - one value, -Clear to
            take it away, and the key removed rather than written empty.

        .PARAMETER Line
            The workspace.yaml lines to edit.

        .PARAMETER Path
            The .pfx. Certs\winpe.pfx is read from the share; C:\pki\winpe.pfx is
            read from there.

        .PARAMETER Clear
            Build the image with no machine certificate.

        .INPUTS
            None. This command does not accept pipeline input.

        .OUTPUTS
            System.String[] - the workspace.yaml lines, spliced.

        .EXAMPLE
            $line = Set-HDTBootImageClientCertificate -Line $line -Path 'Certs\winpe.pfx'
            Set-HDTBootImageCertificatePassword -WorkspaceRoot 'C:\HDTLab\Share' -Password $secure

            Both halves: the document names the file, and the password is stored.

        .LINK
            Set-HDTBootImageCertificatePassword

        .LINK
            Add-HDTBootImageCertificate
    #>

    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'File')]
    [OutputType([string[]])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        [string[]] $Line,

        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'File')]
        [ValidateNotNullOrEmpty()]
        [string] $Path,

        [Parameter(Mandatory = $true, ParameterSetName = 'Clear')]
        [switch] $Clear
    )

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    # The document has to be readable before it is worth editing.
    [void] (ConvertFrom-HDTWorkspaceLine -Line $Line)

    # Read from the switch rather than from the parameter set name, as
    # Set-HDTBootImageUnattend and Set-HDTBootImageDriver do.
    if ($Clear) {
        if (-not $PSCmdlet.ShouldProcess('bootImage: clientCertificate', 'Build the boot image with no machine certificate')) {
            return [string[]] @($Line)
        }

        $result = [string[]] @(Set-HDTWorkspaceKey -Line $Line -Path @('bootImage', 'clientCertificate') `
                -Text ([string[]] @()))
    } else {
        if ($Path -notmatch '\.pfx$') {
            $PSCmdlet.ThrowTerminatingError((New-HDTErrorRecord -TargetObject $Path `
                        -Message ("'{0}' is not a .pfx. This is the certificate the machine authenticates WITH, so it needs its private key - a .cer has none, and an 802.1X port stays shut for a machine holding one. A certificate authority to TRUST is Add-HDTBootImageCertificate's." -f $Path)))
        }

        if (-not $PSCmdlet.ShouldProcess($Path, 'Authenticate with this certificate from the boot image')) {
            return [string[]] @($Line)
        }

        $result = [string[]] @(Set-HDTWorkspaceKey -Line $Line -Path @('bootImage', 'clientCertificate') `
                -Text ([string[]] @('clientCertificate: {0}' -f (ConvertTo-HDTRuleScalarText -Value $Path))))
    }

    try {
        [void] (ConvertFrom-HDTWorkspaceLine -Line $result)
    } catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }

    return [string[]] $result
}