Public/Remove-PSCFNStack.ps1

function Remove-PSCFNStack
{
    <#
    .SYNOPSIS
        Delete one or more stacks.

    .DESCRIPTION
        Delete one or more stacks.
        If -Wait is specified, stack events are output to the console including events from any nested stacks.

        Deletion of multiple stacks can be either sequential or parallel.
        If deleting a gruop of stacks where there are dependencies between them
        use the -Sequential switch and list the stacks in dependency order.

    .PARAMETER StackName
        Either stack names or the object returned by Get-CFNStack, New-CFNStack, Update-CFNStack
        and other functions in this module when run with -Wait.

    .PARAMETER Wait
        If set and -Sequential is not set (so deleting in parallel), wait for all stacks to be deleted before returning.

    .PARAMETER Force
        If set, do not ask first.

    .PARAMETER ThrowOnAbort
        If set, and user answers no when asked if stack should be deleted, throw an exception instead of just warning.
        This switch is primarily for use by Reset-PSCFNStack to enable cancellation of the reset process.

    .PARAMETER Sequential
        If set, delete stacks in the order they are specified on the command line or received from the pipeline,
        waiting for each stack to delete successfully before proceeding to the next one.

    .PARAMETER BackupTemplate
        If set, back up the current version of the template stored by CloudFormation, along with the current parameter set if any to files in the current directory. This will assist with undoing any unwanted change.
        Note that if you have dropped or replaced a database or anything else associcated with stored data, then the data is lost forever!

    .INPUTS
        System.String[]
            You can pipe the names or ARNs of the stacks to delete to this function

    .OUTPUTS
        System.String[]
            ARN(s) of deleted stack(s) else nothing if the stack did not exist or no stacks were deleted.

    .EXAMPLE

        Remove-PSCFNStack -StackName MyStack

        Deletes a single stack.

    .EXAMPLE

        Remove-PSCFNStack -StackName MyStack -BackupTemplate

        As per the first example, but with the previous version of the template and its current parameter set saved to files in the current directory.

    .EXAMPLE

        'DependentStack', 'BaseStack' | Remove-PSCFNStack -Sequential

        Deletes 'DependentStack', waits for completion, then deletes 'BaseStack'.

    .EXAMPLE

        'Stack1', 'Stack2' | Remove-PSCFNStack -Wait

        Sets both stacks deleting in parallel, then waits for them both to complete.

    .EXAMPLE

        'Stack1', 'Stack2' | Remove-PSCFNStack

        Sets both stacks deleting in parallel, and returns immediately.
        See the CloudFormation console to monitor progress.

    .EXAMPLE

        Get-CFNStack | Remove-PSCFNStack

        You would NOT want to do this, just like you wouldn't do rm -rf / ! It is for illustration only.
        Sets ALL stacks in the region deleting simultaneously, which would probably trash some stacks
        and then others would fail due to dependent resources.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
        [string[]]$StackName,

        [switch]$Wait,

        [switch]$Sequential,

        [switch]$Force,

        [switch]$ThrowOnAbort,

        [switch]$BackupTemplate
    )

    DynamicParam
    {
        #Create the RuntimeDefinedParameterDictionary
        New-Object System.Management.Automation.RuntimeDefinedParameterDictionary |
            New-CredentialDynamicParameters
    }

    begin
    {
        $credentialArguments = Get-CommonCredentialParameters -CallerBoundParameters $PSBoundParameters
        $numberOfStacks = 0
        $exceptions = @()
    }

    process
    {
        # Issue #14 - Get-CFNStackEvents returns timestamp in local time
        $startTime = [DateTime]::Now

        $arns = $StackName |
            ForEach-Object {

            ++$numberOfStacks
            $cancelled = $false

            if (Test-StackExists -StackName $_ -CredentialArguments $credentialArguments)
            {
                $arn = (Get-CFNStack -StackName $_ @credentialArguments).StackId

                if ($BackupTemplate)
                {
                    Save-TemplateBackup -StackName $arn -CredentialArguments $credentialArguments -OutputPath (Get-Location).Path
                }

                if (-not $Force)
                {
                    $choice = $host.ui.PromptForChoice(
                        "Delete $StackName now?",
                        $null,
                        @(
                            New-Object System.Management.Automation.Host.ChoiceDescription ('&Yes', "Delete now." )
                            New-Object System.Management.Automation.Host.ChoiceDescription ('&No', 'Cancel operation.')
                        ),
                        1
                    )

                    if ($choice -ne 0)
                    {
                        if ($ThrowOnAbort)
                        {
                            throw "Delete $_ cancelled"
                        }

                        Write-Warning "Delete $_ cancelled."
                        $cancelled = $true
                    }
                }

                if (-not $cancelled)
                {
                    Remove-CFNStack -StackName $arn -Force @credentialArguments

                    if ($Sequential)
                    {
                        # Wait for this delete to complete before starting the next
                        Write-Host "Waiting for delete: $arn"

                        if (-not (Wait-PSCFNStack -StackArn $arn -CredentialArguments $credentialArguments -StartTime $startTime))
                        {
                            Write-Warning "Delete $_ unsuccessful"
                            $exceptions += (Get-ExceptionForFailedStack -Operation Delet -StackArn $arn -CredentialArguments $credentialArguments)
                        }
                    }
                    else
                    {
                        $arn
                    }
                }
            }
            else
            {
                Write-Warning "Stack does not exist: $StackName"
            }
        }
    }

    end
    {
        if ($Wait -and -not $Sequential -and ($arns | Measure-Object).Count -gt 0)
        {
            Write-Host "Waiting for delete:`n$($arns -join "`n - ")"

            if (-not (Wait-PSCFNStack -StackArn $arns -CredentialArguments $credentialArguments -StartTime $startTime))
            {
                $arns |
                ForEach-Object {
                    $ex = (Get-ExceptionForFailedStack -Operation Delete -StackArn $_ -CredentialArguments $credentialArguments)

                    if ($null -ne $ex)
                    {
                        $exceptions += $ex
                    }
                }
            }
        }
        else
        {
            $arns
        }

        $numExceptions = $exceptions | Measure-Object | Select-Object -ExpandProperty Count

        if ($numExceptions -eq 1)
        {
            throw ($exceptions | Select-Object -First 1)
        }
        elseif ($numExceptions -gt 1)
        {
            throw (New-Object System.AggregateException -ArgumentList "At least one stack failed to delete", $exceptions)
        }
    }
}