Public/Set-HDTStepFlag.ps1

function Set-HDTStepFlag {
    <#
        .SYNOPSIS
            Switches a step off, or lets it fail without stopping the
            deployment, without reformatting the document.
 
        .DESCRIPTION
            THE OPTIONS TAB'S TWO CHECKBOXES, AS A COMMAND. Deployment
            Workbench puts "Disable this step" and "Continue on error" on a tab
            beside Properties; the console may not do anything
            the cmdlets can't, so both are this cmdlet before they are controls.
 
            SWITCHING A STEP OFF IS NOT DELETING IT, and that is the whole point
            of the box. The reason to disable one is almost always to run the
            sequence again and watch what changes - which needs the step, its
            properties and the comment above it to still be in the file when the
            answer turns out to be no. The engine skips a disabled step
            (Invoke-HDTTaskSequence branch 2a) and the tree draws it struck
            through; this writes the fact.
 
            IT SPLICES. See Add-HDTStep for why nothing here parses YAML:
            a round trip through ConvertFrom-HDTYaml returns a dictionary, a
            dictionary has no comments in it, and this toolkit forbids a UI that
            reformats the file. Exactly one line is inserted or rewritten and
            every other byte comes back as it went in.
 
            SETTING A FLAG TO WHAT IT ALREADY MEANS WRITES NOTHING. An absent
            `disabled:` already means false, so setting false on a step that has
            no such key would add a line that changes no behaviour and shows up
            in somebody's review as a question to answer. The default is left
            unwritten, which is how every sample sequence in this repository is
            written.
 
            A GROUP TAKES THE FLAG TOO, AND ITS STEPS ARE LEFT ALONE.
            Get-HDTStepKey stops at the group's `steps:`, so switching
            off 'Install' does not rewrite the first line inside it that happens
            to say `disabled:`.
 
        .PARAMETER Line
            The document, already split into lines.
 
        .PARAMETER Name
            The step or group to change. Ambiguous names are refused rather
            than guessed - see Resolve-HDTStepBlock.
 
        .PARAMETER Flag
            Disabled or ContinueOnError.
 
        .PARAMETER Value
            What it becomes.
 
        .INPUTS
            None. This command does not accept pipeline input.
 
        .OUTPUTS
            System.String[] - the document, with one line changed.
 
        .EXAMPLE
            Set-HDTStepFlag -Line $line -Name 'Apply OS' -Flag Disabled -Value $true
 
            Switches a step off, keeping it and its comment in the file.
 
        .EXAMPLE
            $line = Set-HDTStepFlag -Line $line -Name 'Install Applications' -Flag ContinueOnError -Value $true
            Save-HDTSequenceDocument -Path $path -Line $line -FileSystem (New-HDTFileSystem)
    #>

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

        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string] $Name,

        [Parameter(Mandatory = $true, Position = 2)]
        [ValidateSet('Disabled', 'ContinueOnError')]
        [string] $Flag,

        [Parameter(Mandatory = $true, Position = 3)]
        [bool] $Value
    )

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

    $target = Resolve-HDTStepBlock -Line $Line -Name $Name

    # The parameter is named the way the console reads; the file is written the
    # way the engine reads. Import-HDTSequenceDocument's common-key list is the
    # authority for both spellings.
    $key = 'disabled'
    if ($Flag -eq 'ContinueOnError') { $key = 'continueOnError' }

    $found = Get-HDTStepKey -Line $Line -Block $target -Key $key

    $text = 'false'
    if ($Value) { $text = 'true' }

    # An absent key already means false. Writing it would add a line that
    # changes nothing and asks a reviewer a question with no answer.
    if ($found.Index -lt 0 -and -not $Value) {
        return [string[]] @($Line)
    }

    $action = 'Set {0} to {1}' -f $key, $text

    if (-not $PSCmdlet.ShouldProcess($Name, $action)) {
        return [string[]] @($Line)
    }

    $written = '{0}{1}: {2}' -f (' ' * $found.Indent), $key, $text

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

    for ($i = 0; $i -lt $Line.Count; $i++) {
        if ($found.Index -ge 0 -and $i -eq $found.Index) {
            # In place, so the keys around it keep the order they were written
            # in and the diff is one line.
            [void] $result.Add($written)
            continue
        }

        [void] $result.Add($Line[$i])

        if ($found.Index -lt 0 -and $i -eq $found.Insert) {
            [void] $result.Add($written)
        }
    }

    return [string[]] @($result)
}