Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoPipelineFolder/Remove-AzDoPipelineFolder.ps1

<#
.SYNOPSIS
Removes an Azure DevOps pipeline folder.
 
.DESCRIPTION
Deletes the folder. Deleting a pipeline folder deletes every pipeline definition beneath it, so a
folder that still has contents is left alone unless AllowRecursiveDelete is set. Without that
guard, narrowing a configuration to remove one folder would take an arbitrary number of pipelines
with it.
 
.PARAMETER ProjectName
The name of the Azure DevOps project.
 
.PARAMETER Path
The full path of the pipeline folder.
 
.PARAMETER Description
The folder description.
 
.PARAMETER AllowRecursiveDelete
Permit deletion of a folder that still contains pipelines or sub-folders.
 
.PARAMETER LookupResult
The lookup result supplied by the DSC base class.
 
.PARAMETER Ensure
The desired state, supplied by the DSC base class.
 
.PARAMETER Force
Forces the operation, supplied by the DSC base class.
 
.EXAMPLE
Remove-AzDoPipelineFolder -ProjectName 'Contoso' -Path '\Platform' -AllowRecursiveDelete $true
#>

Function Remove-AzDoPipelineFolder
{
    [CmdletBinding()]
    [OutputType([System.Management.Automation.PSObject[]])]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('Name')]
        [System.String]$ProjectName,

        [Parameter(Mandatory = $true)]
        [Alias('FolderPath')]
        [System.String]$Path,

        [Parameter()]
        [AllowEmptyString()]
        [System.String]$Description,

        [Parameter()]
        [System.Boolean]$AllowRecursiveDelete,

        [Parameter()]
        [HashTable]$LookupResult,

        [Parameter()]
        [Ensure]$Ensure,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]$Force
    )

    Write-Verbose "[Remove-AzDoPipelineFolder] Started."

    $organization   = Get-AzDoOrganizationName
    $normalizedPath = Format-AzDoPipelineFolderPath -Path $Path

    if ($normalizedPath -eq '\')
    {
        Write-Error "[Remove-AzDoPipelineFolder] The project build root cannot be removed."
        return
    }

    $folders = List-DevOpsPipelineFolders -Organization $organization -ProjectName $ProjectName -Path $normalizedPath
    $folder  = $folders | Where-Object { (Format-AzDoPipelineFolderPath -Path $_.path) -eq $normalizedPath } | Select-Object -First 1

    if ($null -eq $folder)
    {
        Write-Verbose "[Remove-AzDoPipelineFolder] Pipeline folder '$normalizedPath' does not exist. Nothing to remove."
        return
    }

    if (-not $AllowRecursiveDelete)
    {
        # Anything listed beneath this path other than the folder itself means the delete would
        # take definitions or sub-folders with it.
        $children = @($folders | Where-Object { (Format-AzDoPipelineFolderPath -Path $_.path) -ne $normalizedPath })

        try
        {
            $definitions = Get-DevOpsPipelineDefinitionsInFolder -Organization $organization -ProjectName $ProjectName -Path $normalizedPath
        }
        catch
        {
            # Emptiness could not be established, so the guard cannot be satisfied. Refusing is
            # the safe outcome: the alternative is deleting pipelines on the strength of a failed
            # lookup.
            Write-Error "[Remove-AzDoPipelineFolder] Could not determine whether pipeline folder '$normalizedPath' is empty, so it has not been removed. Error: $_"
            return
        }

        if ($children.Count -gt 0 -or (@($definitions).Count -gt 0))
        {
            Write-Error "[Remove-AzDoPipelineFolder] Pipeline folder '$normalizedPath' in project '$ProjectName' is not empty. Deleting it would delete every pipeline and sub-folder beneath it. Set AllowRecursiveDelete = \$true to permit this."
            return
        }
    }
    else
    {
        Write-Warning "[Remove-AzDoPipelineFolder] Recursively deleting pipeline folder '$normalizedPath' and everything beneath it."
    }

    Write-Verbose "[Remove-AzDoPipelineFolder] Removing pipeline folder '$normalizedPath'."

    return (Remove-DevOpsPipelineFolder -Organization $organization -ProjectName $ProjectName -Path $normalizedPath)
}