Public/Steps/Invoke-HDTGatherStep.ps1

function Invoke-HDTGatherStep {
    <#
        .SYNOPSIS
            Re-reads the machine's facts into the sequence's variables.
 
        .DESCRIPTION
            MDT'S "Gather local only", which is the whole of its Initialization
            group - and which it runs again in Preinstall and again in State
            Restore, because the machine a sequence finishes on is not the
            machine it started on.
 
            WHY IT IS A STEP AND NOT ONLY ENGINE START-UP. HDT gathers once
            before the sequence begins, and DESIGN 3.2.1 already says the facts
            are "refreshed after OS apply". Making the refresh a step is what
            lets a sequence SAY WHERE it happens, in a file an administrator
            reads, instead of it being a rule inside the engine that nothing on
            screen mentions.
 
            IT IS NOT A RESET. Only the facts it gathers are replaced;
            HDTComputerName from rules.yaml, a variable a SetVariable step
            wrote, a drive letter a partition step published - none of them are
            touched. A gather that cleared what it did not produce would throw
            away the deployment's own decisions half way through.
 
            IT REFUSES WITHOUT A CIM PROVIDER rather than gathering nothing and
            reporting success. A green step that quietly gathered nothing leaves
            every later condition false and every later check unmade, which is
            the most expensive way for this to fail.
 
            IT TOUCHES NO HARDWARE ITSELF. Get-HDTMachineFact takes the injected
            ICimProvider, so this runs under Pester against a fake like every
            other step here.
 
        .PARAMETER Step
            A flattened step from Import-HDTSequenceDocument.
 
        .PARAMETER Context
            The execution context, carrying the service catalog and the live
            variables.
 
        .INPUTS
            None. This command does not accept pipeline input.
 
        .OUTPUTS
            System.Management.Automation.PSCustomObject - a step result.
 
        .EXAMPLE
            Invoke-HDTGatherStep -Step $step -Context $context
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Step',
        Justification = 'The step contract requires -Step on every step command. A Gather step declares no properties - what to gather is not a choice - so the parameter is bound and unread, which is the contract being honoured rather than an oversight.')]
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [object] $Step,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [object] $Context
    )

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

    # THREE PORTS, BECAUSE THE FACTS COME FROM THREE PLACES. CIM for the
    # hardware, the registry for SecureBoot, and the environment for the
    # firmware type - all injected, none of them touched directly here.
    $cim = $null
    $registry = $null
    $environment = $null

    try {
        $cim = $Context.Service.GetRequired('Cim', 'Gather')
        $registry = $Context.Service.GetRequired('Registry', 'Gather')
        $environment = $Context.Service.GetRequired('Environment', 'Gather')
    } catch {
        $message = 'this step gathers the machine facts and a service it needs was not supplied, so there is nothing to gather them with: {0}' -f $_.Exception.Message

        Write-HDTLog -Context $Context.Log -Message $message -Severity Error -Event step.fail -Component 'Gather'

        return (New-HDTStepResult -Status Failed -Message $message `
                -Data ([ordered] @{ errorId = 'HDTConfigurationError' }))
    }

    try {
        $fact = Get-HDTMachineFact -CimProvider $cim -RegistryService $registry -EnvironmentProvider $environment
    } catch {
        $message = [string] $_.Exception.Message

        Write-HDTLog -Context $Context.Log -Message $message -Severity Error -Event step.fail -Component 'Gather'

        return (New-HDTStepResult -Status Failed -Message $message `
                -Data ([ordered] @{ errorId = 'HDTGatherFailed' }))
    }

    $written = 0
    $changed = New-Object -TypeName System.Collections.ArrayList

    foreach ($name in @($fact.Keys)) {
        $before = $null
        if ($Context.Variable.Contains($name)) { $before = $Context.Variable[$name] }

        $Context.Variable[$name] = $fact[$name]
        $written++

        # WHAT MOVED IS WORTH A LINE IN THE LOG. On the second gather of a
        # deployment that is the whole reason the step ran, and on the first it
        # is empty - which is also the right answer.
        if ([string] $before -ne [string] $fact[$name]) {
            [void] $changed.Add(('{0}: {1} -> {2}' -f $name, $before, $fact[$name]))
        }
    }

    foreach ($line in $changed) {
        Write-HDTLog -Context $Context.Log -Message $line -Severity Debug -Event var.resolve -Component 'Gather'
    }

    $message = '{0} machine facts gathered, {1} changed.' -f $written, @($changed).Count

    Write-HDTLog -Context $Context.Log -Message $message -Severity Info -Event step.complete -Component 'Gather'

    return (New-HDTStepResult -Status Completed -Message $message `
            -Data ([ordered] @{ gathered = $written; changed = @($changed).Count }))
}