Public/Remove-HDTTaskSequence.ps1

function Remove-HDTTaskSequence {
    <#
        .SYNOPSIS
            Removes a task sequence and the folder it lives in.
 
        .DESCRIPTION
            THE ONLY AUTHORING COMMAND THAT DELETES. Everything else in this
            toolkit splices lines and hands them back for Save-HDTSequenceDocument
            to write; this one takes a directory away, so every refusal below is
            about what it might delete INSTEAD of what it was asked to.
 
            THE ID IS THE FOLDER NAME - TaskSequences\<id>\sequence.yaml - which
            makes a separator, a colon or a '..' in it a PATH rather than a name.
            A delete built from one of those leaves the share it was pointed at,
            so the id is refused before anything is resolved, and the resolved
            path is checked to still sit under the workspace afterwards. Belt and
            braces, because the cost of being wrong here is somebody else's data.
 
            A FOLDER HOLDING NO sequence.yaml IS NOT A TASK SEQUENCE, and it is
            not removed. A folder under TaskSequences\ that somebody put notes in
            would otherwise be deleted on the strength of where it sits.
 
            IT REMOVES THE WHOLE FOLDER, not just the document: a sequence keeps
            its unattend.xml, and any script it names, beside it. Leaving those
            behind would leave a folder that looks like a sequence to anything
            that lists the directory.
 
            ConfirmImpact IS High, so it prompts unless the caller says
            -Confirm:$false. The console's Remove asks in its own dialog first
            and then passes that, which is the same decision made once.
 
        .PARAMETER Workspace
            The deployment share's root.
 
        .PARAMETER Id
            The sequence to remove, which is also its folder name.
 
        .PARAMETER FileSystem
            An IFileSystem. Defaults to the real one.
 
        .INPUTS
            None. This command does not accept pipeline input.
 
        .OUTPUTS
            System.Management.Automation.PSCustomObject with Id and Path.
 
        .EXAMPLE
            Remove-HDTTaskSequence -Workspace C:\HDTLab\Share -Id DEMO-05
 
        .EXAMPLE
            Remove-HDTTaskSequence -Workspace C:\HDTLab\Share -Id DEMO-05 -WhatIf
 
            What it would take, without taking it.
 
        .LINK
            New-HDTTaskSequence
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string] $Workspace,

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

        [Parameter()]
        [AllowNull()]
        [object] $FileSystem
    )

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

    if ($null -eq $FileSystem) { $FileSystem = New-HDTFileSystem }

    # -- the id, before it becomes a path ------------------------------------

    if ($Id -match '[\\/:*?"<>|]' -or $Id -match '\s' -or $Id -eq '.' -or $Id -eq '..') {
        $PSCmdlet.ThrowTerminatingError((New-HDTErrorRecord -TargetObject $Id `
                    -Message ("'{0}' cannot be a task sequence id. An id is a folder name: no spaces, and none of \\ / : * ? "" < > |. This command deletes the folder it names, so an id that is a path is refused rather than resolved." -f $Id)))
    }

    $folder = Get-HDTWorkspacePath -Root $Workspace -Kind TaskSequences -ChildPath $Id
    $document = Join-Path -Path $folder -ChildPath 'sequence.yaml'

    # -- and the path, after ---------------------------------------------------
    #
    # THE SECOND CHECK IS NOT THE FIRST ONE AGAIN. That one judged what was
    # typed; this judges what it resolved to, which is the thing about to be
    # removed. A delete target must be provably inside the share.
    $sequenceRoot = Get-HDTWorkspacePath -Root $Workspace -Kind TaskSequences

    if (-not ([System.IO.Path]::GetFullPath($folder)).StartsWith(
            ([System.IO.Path]::GetFullPath($sequenceRoot)).TrimEnd('\') + '\',
            [System.StringComparison]::OrdinalIgnoreCase)) {

        $PSCmdlet.ThrowTerminatingError((New-HDTErrorRecord -TargetObject $folder `
                    -Message ("'{0}' resolves to '{1}', which is not inside this workspace's TaskSequences folder. Nothing was removed." -f $Id, $folder)))
    }

    # -- is it a task sequence at all? ----------------------------------------

    if (-not $FileSystem.TestPath($folder)) {
        $PSCmdlet.ThrowTerminatingError((New-HDTErrorRecord -TargetObject $folder -Category ObjectNotFound `
                    -Message ("this workspace has no task sequence called '{0}'." -f $Id)))
    }

    if (-not $FileSystem.TestPath($document)) {
        $PSCmdlet.ThrowTerminatingError((New-HDTErrorRecord -TargetObject $folder `
                    -Message ("'{0}' holds no sequence.yaml, so it is not a task sequence - it is a folder that happens to sit under TaskSequences. Remove it yourself if that is what you meant." -f $folder)))
    }

    if (-not $PSCmdlet.ShouldProcess($folder, ("Remove task sequence '{0}' and everything in its folder" -f $Id))) {
        return [pscustomobject] @{
            Id   = $Id
            Path = $folder
        }
    }

    $FileSystem.RemoveItem($folder, $true)

    return [pscustomobject] @{
        Id   = $Id
        Path = $folder
    }
}