public/Remove-Lably.ps1

Function Remove-Lably {

    <#
    
    .SYNOPSIS
 
    Remove an existing lab in the current directory or defined path.
 
    .DESCRIPTION
 
    This function is used to remove a Lably from the current directory or defined path. It will also remove the VMs from Hyper-V and cleanup everything associated with the lab.
 
    .PARAMETER Path
     
    Optional parameter to define where the lably is stored. If this parameter is not defined, it will default to the path from which the function was called.
 
    .PARAMETER Name
 
    Optional name of the Lably. Used as a description when using and describing the lab, as well as the default prefix for the display name of VMs created in Hyper-V. If this parameter is not defined, it will default to the name of the folder it's being created it.
 
    .PARAMETER Confirm
 
    Optional Switch to bypass confirming that you want to delete the Lab and associated data.
 
    .INPUTS
 
    None. You cannot pipe objects to New-Lably.
 
    .OUTPUTS
 
    None. The function will either complete successfully or throw an error.
     
    .EXAMPLE
 
    Remove-Lably
 
    .EXAMPLE
 
    Remove-Lably -Path C:\Labs\Windows10-Lab
 
    #>


    [CmdLetBinding()]
    Param(
        [Parameter(Mandatory=$False)]    
        [String]$Path = $PWD,

        [Parameter(Mandatory=$False)]
        [Switch]$Confirm
    )

    ValidateModuleRun -RequiresAdministrator

    $LablyScaffold = Join-Path $Path -ChildPath "scaffold.lably.json"
    Write-Verbose "Reading Lably Scaffolding File at $LablyScaffold"

    If(-Not(Test-Path $LablyScaffold -ErrorAction SilentlyContinue)){
        Throw "There is no Lably at $Path."
    }

    Try {
        $Scaffold = Get-Content $LablyScaffold | ConvertFrom-Json
    } Catch {
        Throw "Unable to import Lably scaffold. $($_.Exception.Message)"
    }

    $VHDPath = $Scaffold.Meta.VirtualDiskPath
    $KeyFile = $Scaffold.Meta.KeyFile
    $Assets = $Scaffold.Assets
    $SwitchId = $Scaffold.Meta.SwitchId
    $SwitchCreated = $Scaffold.Meta.SwitchCreated
    $TemplatePath = Join-Path $Path -ChildPath "Template Cache"
    $NetNAT = $Scaffold.Meta.NATName

    $SwitchName = Get-VMSwitch -Id $SwitchId | Select-Object -ExpandProperty Name

    If(-Not($Confirm)) {
        Write-Host "WARNING! You are about to delete your Lably." -ForegroundColor Red
        Write-Host ""
        Write-Host "This will also stop and delete the following VMs and their associated disks:"
        ForEach($Asset in $Assets) { Write-Host " - $($Asset.DisplayName) ($($Asset.VmId))" }
        Write-Host ""
        If($NetNAT) { Write-Host "Your NAT '$NetNAT' will be deleted."; Write-Host "" }
        If($SwitchCreated) { Write-Host "VM Switch $SwitchName will be removed"}
        Write-Host "Your Template Cache ($TemplatePath) will be removed."
        Write-Host "Your current scaffold ($LablyScaffold) will be removed."
        Write-Host ""
        Write-Host "This operation cannot be undone." -ForegroundColor Red
        Write-Host ""
        If($(Read-Host "If you're certain you'd like to continue, type DESTROY and press enter") -ne "DESTROY") {
            Write-Host "Code 'DESTROY' was not entered. Aborting." -ForegroundColor Yellow
            Return
        }
    }

    ForEach($Asset in $Assets) {
        Remove-LablyVM -Path $Path -VMId $Asset.VMId -Confirm | Out-Null
    }

    If($SwitchCreated) {

        If(-Not(Get-VMNetworkAdapter -All | Where-Object { $_.SwitchName -eq $SwitchName -and $_.VMName })) {
            Write-Host "Removing Switch $SwitchName"
            Get-VMSwitch -Id $SwitchId | Remove-VMSwitch -Force
        } Else {
            Write-Host "Will not remove virtual switch as it's being used by other VMs."
        }

    }

    If($NetNAT) {
        Write-Host "Deleting NAT '$NetNAT'"
        Try {
            Get-NetNat -Name $NetNAT | Remove-NetNAT -Confirm:$False -ErrorAction Stop
        } Catch {
            Write-Warning "Could not removed NAT. $($_.Exception.Message)"
        }
    }

    Write-Host "Clearing Cached Templates from $TemplatePath"
    
    ForEach($CachedTemplate in (Get-ChildItem -Path $TemplatePath -Filter *.json -ErrorAction SilentlyContinue)) {
        Try {
            Remove-Item $CachedTemplate.FullName -ErrorAction Stop
        } Catch {
            Write-Warning "Could not delete $CachedTemplate. $($_.Exception.Message)"
        }
    }

    If(-Not (Get-ChildItem $TemplatePath -ErrorAction SilentlyContinue | Select-Object -First 1)) { 
        Write-Host "Removing $TemplatePath"
        Try {
            Remove-Item $TemplatePath -ErrorAction SilentlyContinue
        } Catch {
            Write-Warning "Could not remove $TemplatePath. You may need to manually delete this folder."
        }       
    } Else {
        Write-Host "There are files/folders left over in $TemplatePath. Will not remove."
    }

    Write-Host "Deleting Scaffold ($LablyScaffold)"

    Try {
        Remove-Item $LablyScaffold -Force
    } Catch {
        Write-Warning "Could not Scaffold. $($_.Exception.Message)"
    }

    Write-Host "Removing Empty Folders from $VHDPath"
    Get-ChildItem $VHDPath -Recurse -Force -Directory | Sort-Object -Property FullName -Descending | Where-Object { $($_ | Get-ChildItem -Force | Select-Object -First 1).Count -eq 0 } | Remove-Item -ErrorAction SilentlyContinue
    If(-Not (Get-ChildItem $VHDPath | Select-Object -First 1)) { Remove-Item $VHDPath -ErrorAction SilentlyContinue }
    
    If(-Not (Get-ChildItem $Path | Select-Object -First 1)) { 
        Write-Host "Removing $Path"
        Remove-Item $Path -ErrorAction SilentlyContinue
    } Else {
        Write-Host "There are files/folders left over in $Path. Will not remove."
    }

    Remove-Item $TemplatePath -ErrorAction SilentlyContinue -Recurse -Force

    If($KeyFile) {
        Write-Host "Your KeyFile has not been deleted. If you no longer require it, you may delete it manually."
        Write-Host $KeyFile
    }

    Write-Host "Done."

}