internal/functions/Remove-AzResourceRaw.ps1

function Remove-AzResourceRaw {

    <#
        .SYNOPSIS
            Performs resource deletion in Azure at any scope.
        .DESCRIPTION
            Performs resource deletion in Azure with FullyQualifiedResourceId and ScopeObject.
        .PARAMETER TemplateFilePath
            Path where the ARM templates can be found.
        .PARAMETER TemplateParameterFilePath
            Path where the parameters of the ARM templates can be found.
        .PARAMETER ScopeObject
            Resource to delete.
        .PARAMETER InputObject
            Object containing items for processing, used in combination with parameter Recursive.
        .PARAMETER Recursive
            If specified, performs recursive resource deletion and requires use of parameter InputObject.
        .EXAMPLE
            > Remove-AzResourceRaw -ScopeObject $ScopeObject -TemplateFilePath $TemplateFilePath -TemplateParameterFilePath $TemplateParameterFilePath
            Name Value
            ---- -----
            TemplateFilePath /root/managementgroup/subscription/resourcegroup/template.json
            TemplateParameterFilePath /root/managementgroup/subscription/resourcegroup/template.parameters.json
            ScopeObject ScopeObject
            Status success

            > Remove-AzResourceRaw -InputObject $retry -Recursive
            Name Value
            ---- -----
            TemplateFilePath /root/managementgroup/subscription/resourcegroup/template.json
            TemplateParameterFilePath /root/managementgroup/subscription/resourcegroup/template.parameters.json
            ScopeObject ScopeObject
            Status success
    #>


    [CmdletBinding()]
    param (
        [string]
        $TemplateFilePath,
        [string]
        $TemplateParameterFilePath,
        [AzOpsScope]
        $ScopeObject,
        [array]
        $InputObject,
        [switch]
        $Recursive
    )

    process {
        function Remove-AzResourceRawRecursive {

            <#
                .SYNOPSIS
                    Performs recursive resource deletion in Azure at any scope.
                .DESCRIPTION
                    Takes $InputObject and performs recursive resource deletion in Azure and exhaust any permutation.
                .PARAMETER InputObject
                    Parameter containing items for processing.
                .PARAMETER CurrentOrder
                    Internal parameter to track recursive progress.
                .PARAMETER OutputObject
                    Track item processing and return result.
                .EXAMPLE
                    > $successFullItems, $failedItems = Remove-AzResourceRawRecursive -InputObject $retry
                    Example of a $retry array with 6 items, the number of permutations will be 6×5×4×3×2×1=720
            #>


            [CmdletBinding()]
            param (
                [array]
                $InputObject,
                [array]
                $CurrentOrder = @(),
                [array]
                $OutputObject = @()
            )

            process {
                if ($InputObject.Count -eq 0) {
                    # Base case: All items have been used, perform action on the current order
                    foreach ($item in $CurrentOrder) {
                        if ($item.Status -eq 'failed' -or $null -eq $item.Status) {
                            Write-AzOpsMessage -LogLevel InternalComment -LogString 'Remove-AzResourceRawRecursive.Processing' -LogStringValues $item.ScopeObject.Resource, $item.ScopeObject.Scope
                            # Attempt to remove the resource
                            $result = Remove-AzResourceRaw -ScopeObject $item.ScopeObject -TemplateFilePath $item.TemplateFilePath -TemplateParameterFilePath $item.TemplateParameterFilePath
                            if ($result.Status -eq 'failed' -and $result.ScopeObject.Scope -notin $OutputObject.ScopeObject.Scope){
                                # Add failed result to the output object
                                $OutputObject += $result
                            }
                        }
                    }
                    # Return the final result
                    return $OutputObject
                }
                else {
                    if ($InputObject -and $OutputObject) {
                        # Filter out items already processed successfully
                        $filteredOutputObject = @()
                        foreach ($item in $InputObject) {
                            if ($item.ScopeObject.Scope -in $OutputObject.ScopeObject.Scope) {
                                foreach ($output in $OutputObject) {
                                    if ($output.ScopeObject.Scope -eq $item.ScopeObject.Scope -and $output.Status -eq 'failed') {
                                        # Add previously failed item to the filtered output
                                        $filteredOutputObject += $output
                                        continue
                                    }
                                }
                            }
                        }
                        if ($filteredOutputObject) {
                            $InputObject = $filteredOutputObject
                        }
                    }
                    # Recursive case: Try each item in the current position and recurse with the remaining items
                    foreach ($item in $InputObject) {
                        $remainingItems = $InputObject -ne $item
                        $newOrder = $CurrentOrder + $item
                        # Recursively call Remove-AzResourceRawRecursive
                        $OutputObject = Remove-AzResourceRawRecursive -InputObject $remainingItems -CurrentOrder $newOrder -OutputObject $OutputObject
                    }
                    # Return the output after all permutations
                    return $OutputObject
                }
            }
        }
        if ($null -ne $InputObject -and $Recursive) {
            # Perform recursive resource deletion
            $result = Remove-AzResourceRawRecursive -InputObject $InputObject
            if ($result) {
                return $result
            }
            else {
                return
            }
        }
        elseif ($null -eq $InputObject -and $Recursive) {
            # Recursive resource deletion missing input
            Write-AzOpsMessage -LogLevel Error -LogString 'Remove-AzResourceRaw.Resource.Recursive.Missing'
            return
        }
        else {
            if (-not $ScopeObject) {
                # Resource deletion missing input
                Write-AzOpsMessage -LogLevel Error -LogString 'Remove-AzResourceRaw.Resource.Missing'
                return
            }
            # Construct result object
            $result = [PSCustomObject]@{
                TemplateFilePath = $TemplateFilePath
                TemplateParameterFilePath = $TemplateParameterFilePath
                ScopeObject = $ScopeObject
                Status = 'success'
            }
            # Check if the resource exists
            $resource = Get-AzOpsResource -ScopeObject $ScopeObject -ErrorAction SilentlyContinue
            # Remove the resource if it exists
            if ($resource) {
                try {
                    # Set Azure context for removal operation
                    Set-AzOpsContext -ScopeObject $ScopeObject
                    $null = Remove-AzResource -ResourceId $ScopeObject.Scope -Force -ErrorAction Stop
                    $maxAttempts = 4
                    $attempt = 1
                    $gone = $false
                    while ($gone -eq $false -and $attempt -le $maxAttempts) {
                        Write-AzOpsMessage -LogLevel InternalComment -LogString 'Remove-AzResourceRaw.Resource.CheckExistence' -LogStringValues $ScopeObject.Scope
                        Start-Sleep -Seconds 10
                        $tryResource = Get-AzOpsResource -ScopeObject $ScopeObject -ErrorAction SilentlyContinue
                        if (-not $tryResource) {
                            $gone = $true
                        }
                        $attempt++
                    }
                }
                catch {
                    # Log failure message
                    Write-AzOpsMessage -LogLevel InternalComment -LogString 'Remove-AzResourceRaw.Resource.Failed' -LogStringValues $ScopeObject.Resource, $ScopeObject.Scope
                    $result.Status = 'failed'
                }
            }
            else {
                # Log not found message
                $result.Status = 'notfound'
            }
            # Return result object
            return $result
        }
    }
}