Private/Get-HDTStartnetScript.ps1

function Get-HDTStartnetScript {
    <#
        .SYNOPSIS
            The exact text Update-HDTBootImage writes to
            <mount>\Windows\System32\startnet.cmd.
 
        .DESCRIPTION
            THE ONLY THING BETWEEN "A MACHINE BOOTED OUR IMAGE" AND "A MACHINE
            RAN OUR ENGINE". WinPE runs startnet.cmd when it finishes booting;
            everything HDT does on that machine follows from these five lines.
 
            It is a pure function, separate from the fifteen-minute build, so the
            bytes can be asserted in milliseconds - and
            tests/integration/BootImage.Integration.Tests.ps1 reads the same text
            back out of a MOUNTED IMAGE and compares it line by line. One
            function, two witnesses.
 
            The five lines, and why each is there:
 
              @echo off
                  WinPE echoes commands otherwise, and the first screen a
                  technician sees should be HDT's, not cmd's.
 
              rem Written by Update-HDTBootImage...
                  Somebody will open this file inside a mounted image at three
                  in the morning. It should say where it came from and that
                  editing it there is pointless - the next build overwrites it.
 
              set HDT_LAUNCHED_BY=startnet
                  The field Start-HDTDeployment.ps1 records into RESULT.json.
                  05-05 asserts it to prove NOBODY TYPED THE COMMAND: phase 04's
                  E2E had to type a line at the WinPE prompt because no
                  startnet.cmd existed, and a run that still needed that would
                  look identical in every other respect.
 
              wpeinit
                  BEFORE PowerShell, always. wpeinit is what brings networking
                  up; a share connect before it fails on a machine that was
                  going to work.
 
              powershell.exe -NoProfile -ExecutionPolicy Bypass -File
                  X:\HDT\Start-HDTDeployment.ps1
 
            X: IS WRITTEN LITERALLY, AND IT IS THE ONLY DRIVE LETTER ALLOWED
            HERE. The RAM disk is the one letter WinPE guarantees - a lab test
            recorded WinPE giving the content disk C: while the RAM disk was X:.
            Any other letter here would be a guess about a machine that has not
            booted yet. Where the CONTENT is, is bootstrap.json's business and
            Resolve-HDTDeployRoot's; this file only has to reach the engine.
 
            THERE IS NO DRIVE SCAN AND NO for LOOP. The phase-04 harness types
            'for %d in (C D E F G H) ...' at the prompt because a human was
            typing it with no bootstrap document to read. Inheriting that line
            here would carry a workaround into the thing that made it
            unnecessary.
 
            CRLF, and ASCII with no BOM when it is written. cmd.exe reading a
            byte order mark as a command is a class of failure that produces no
            useful message at all, so Update-HDTBootImage writes this through
            IFileSystem.WriteAllText, which is BOM-free on both engines
            (tests/helpers/README.md F11).
 
            THE START COMMANDS GO BETWEEN wpeinit AND THE ENTRY COMMAND, and
            both halves of that are load-bearing. AFTER wpeinit, because a tool
            started before it has no network - which is the whole reason a VNC
            server or a BGInfo background is in the image at all. BEFORE the
            entry command, because the entry command is the deployment and it
            does not return; anything queued after it never runs.
 
            Each one is written verbatim on its own line, in the order it was
            given. cmd.exe runs them synchronously, so a tool that has to stay up
            is launched with `start` by the administrator who declared it -
            deciding that here would be deciding it for every image.
 
        .PARAMETER Command
            The last line, for a caller that wants a different entry point -
            standalone media, a diagnostic image. The default is the engine.
            wpeinit and the environment variable are kept whatever this says: a
            different entry point is still a WinPE boot.
 
        .PARAMETER StartCommand
            Commands to run after wpeinit and before the entry command, in
            order. Empty produces the same five lines as before.
 
            A BATCH FILE IS EMITTED WITH `call`, AND WITHOUT IT THE DEPLOYMENT
            NEVER STARTS. cmd.exe does not return from one batch file to
            another: a bare `X:\Tools\run.cmd` on a line here TRANSFERS control,
            and the entry command below it is never reached. The machine sits
            wherever run.cmd left it - booted, initialised, tools running - and
            looks exactly like a deployment that hung.
 
            It is done here rather than where the command is authored so that a
            hand-edited workspace.yaml gets it too. What the administrator typed
            is what the document keeps; `call` is a fact about cmd.exe and
            belongs with the code that writes cmd.
 
            ALREADY-CALLED AND ALREADY-STARTED LINES ARE LEFT ALONE. `start`
            returns immediately by design - it is what an administrator writes
            for a tool that has to stay up - and doubling `call` would be
            rewriting an instruction that was already right.
 
        .PARAMETER UnattendPath
            A WinPE answer file inside the image, passed to wpeinit as
            -unattend:. Empty means the plain wpeinit line.
 
            IT IS AN ARGUMENT ON THE LINE THAT ALREADY EXISTS, not a line of its
            own, because wpeinit is what processes it: Display, EnableFirewall,
            EnableNetwork, LogPath, PageFile, Restart, RunSynchronous and
            RunAsynchronous are the settings it accepts. The firewall in
            particular has no other supported switch - `wpeutil disablefirewall`
            is a manual command at a prompt, not something an image is built
            with.
 
        .PARAMETER CertificateScript
            A PowerShell script inside the image that imports the boot image's
            certificates, run BEFORE wpeinit. Empty writes no line at all.
 
        .PARAMETER TimeZone
            A Windows time zone id. Empty writes no line, which leaves WinPE on
            the hardware clock - what every image did before this existed.
 
            BEFORE, NOT AFTER, unlike everything else here. A client certificate
            exists so that a network which authenticates the machine will give
            it an address, and wpeinit is what asks for the address - so an
            import that ran afterwards would be an import that ran too late.
 
        .INPUTS
            None. This command does not accept pipeline input.
 
        .OUTPUTS
            System.String - five CRLF-terminated lines, plus one per start
            command.
 
        .EXAMPLE
            Get-HDTStartnetScript
 
        .EXAMPLE
            Get-HDTStartnetScript -Command 'powershell.exe -NoProfile -File X:\HDT\Start-HDTDiagnostic.ps1'
 
        .EXAMPLE
            Get-HDTStartnetScript -StartCommand @('X:\HDT\Tools\BGInfo\bginfo.exe X:\HDT\Tools\BGInfo\hdt.bgi /timer:0 /nolicprompt')
 
            A boot image that shows the machine's own details on the desktop
            before the deployment starts.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Command = 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File X:\HDT\Start-HDTDeployment.ps1',

        [Parameter()]
        [AllowNull()]
        [AllowEmptyCollection()]
        [string[]] $StartCommand = @(),

        [Parameter()]
        [AllowEmptyString()]
        [string] $UnattendPath = '',

        [Parameter()]
        [AllowEmptyString()]
        [string] $CertificateScript = '',

        [Parameter()]
        [AllowEmptyString()]
        [string] $TimeZone = ''
    )

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

    $line = New-Object -TypeName System.Collections.ArrayList

    [void] $line.Add('@echo off')
    [void] $line.Add('rem Written by Update-HDTBootImage. Do not edit inside the image; edit HDT.')
    [void] $line.Add('set HDT_LAUNCHED_BY=startnet')

    # BEFORE wpeinit, AND THAT IS THE WHOLE POINT OF THE LINE. wpeinit is what
    # brings the network up; a machine certificate imported after it has missed
    # the authentication it was carried for, and a root CA imported after it has
    # missed whatever wpeinit's own answer file reached for. Everything else in
    # this file goes after wpeinit for the opposite reason.
    #
    # PowerShell RUNS FINE HERE. It is an application, not a service - what
    # wpeinit initialises is devices and networking, neither of which an
    # X509Store needs.
    if (-not [string]::IsNullOrWhiteSpace($CertificateScript)) {
        [void] $line.Add('powershell.exe -NoProfile -ExecutionPolicy Bypass -File {0}' -f $CertificateScript)
    }

    # QUOTED ONLY WHEN IT HAS TO BE. cmd.exe splits on the space, so a path with
    # one in it reaches wpeinit as two arguments and the answer file is silently
    # not processed - which looks exactly like an answer file that did nothing.
    # Quoting unconditionally would be safe too, but the unquoted form is what
    # every Microsoft example shows and what an administrator compares against.
    $wpeinit = 'wpeinit'
    if (-not [string]::IsNullOrWhiteSpace($UnattendPath)) {
        $unattendText = [string] $UnattendPath
        if ($unattendText.Contains(' ')) { $unattendText = '"{0}"' -f $unattendText }

        $wpeinit = 'wpeinit -unattend:{0}' -f $unattendText
    }

    [void] $line.Add($wpeinit)

    # THE TIME ZONE, WHICH THE ANSWER FILE CANNOT SET. WinPE's windowsPE pass
    # carries locale and nothing else - Microsoft's TimeZone setting belongs to
    # Shell-Setup and is valid only in the DEPLOYED OS's passes - so a booted
    # WinPE runs on whatever the hardware clock says. tzutil is the supported
    # way to move it and this is the file that runs on every boot.
    #
    # QUOTED ALWAYS. Every id worth naming has a space in it, and cmd.exe splits
    # on the space: an unquoted 'Israel Standard Time' reaches tzutil as three
    # arguments and leaves the clock where it was, silently.
    if (-not [string]::IsNullOrWhiteSpace($TimeZone)) {
        [void] $line.Add('tzutil /s "{0}"' -f $TimeZone)
    }

    # After wpeinit and before the entry command. A blank entry is skipped rather
    # than written: a blank line in a .cmd is harmless, and this text is compared
    # byte for byte against a mounted image.
    foreach ($current in @($StartCommand)) {
        if ([string]::IsNullOrWhiteSpace([string] $current)) { continue }

        # THE `call` RULE LIVES IN ONE PLACE. The WinPE window shows the same
        # answer, so a second copy here would be a second copy to get wrong.
        $effective = ConvertTo-HDTStartnetCommandLine -Command ([string] $current)

        # ANNOUNCED BEFORE IT IS RUN, AND A REAL BOOT IS WHY. An image carrying
        # a firewall command, a VNC server and a BGInfo showed a console with
        # ONE line on it - "The command completed successfully" - which came
        # from the firewall command. The VNC server two lines later never
        # returned and held the whole deployment, and the screen was still
        # naming the last thing that PRINTED rather than the thing that was
        # stuck. With this, the last line on screen is the command that is
        # actually running.
        #
        # IT ANNOUNCES THE LINE THAT WILL RUN, call AND ALL, so the console can
        # be read top to bottom against the file.
        #
        # ESCAPED, BECAUSE AN ECHO IS STILL A COMMAND. `echo run.exe > log.txt`
        # writes a file instead of printing, and %PATH% in an echo prints the
        # variable rather than the text - so a line meant to inform would act.
        # The caret goes first: escaping it after the others would escape the
        # carets this very line just added.
        $announce = $effective -replace '\^', '^^'
        $announce = $announce -replace '&', '^&'
        $announce = $announce -replace '<', '^<'
        $announce = $announce -replace '>', '^>'
        $announce = $announce -replace '\|', '^|'
        $announce = $announce -replace '%', '%%'

        [void] $line.Add(('echo about to run the command: {0}' -f $announce))
        [void] $line.Add($effective)
    }

    [void] $line.Add($Command)

    # Joined with CRLF and terminated with one, so every line ends the way
    # cmd.exe expects. -join alone would leave the last line unterminated.
    return ((@($line) -join "`r`n") + "`r`n")
}