Az.CloneVirtualMachine.psm1

Function New-AzVMClone {
    <#
    .SYNOPSIS
        From an existing Virtual Machine, clone its configuration and change it's Availability Set
        affiliation (migrate, remove, leave 'as-is')

    .DESCRIPTION
        This function is designed to MIGRATE or REMOVE an Azure Virtual Machine to or from an
        Availability Set. This function requires that the incoming object, through pipeline
        or parameter, is a Virtual Machine object. Most commonly, this object is created
        using the Get-AzVM cmdlet and storing the VM(s) into a variable.

        This function assumes that IF you are migrating a Virutal Machine to NEW Availability
        Set, the target Availability Set has already been created.

        WARNING: THIS FUNCTION DOES NOT CURRENTLY ADDRESS ANY EXTENSION FOR THE VIRTUAL MACHINE
        AND THEY WILL NEED TO BE RE-INSTALLED MANUALLY.
    .EXAMPLE
        Get-AzVM -ResourceGroupName MyResourceGroup -Name TestVM001 | New-AzVMClone -MigrateAvailabilitySet -AvailabilitySetName MyNewAS01
        
        - OR -

        PS C:\>$myVM = Get-AzVM -ResourceGroupName MyResourceGroup -Name TestVM001
        PS C:\>$myVM | New-AzVMClone -MigrateAvailabilitySet -AvailabilitySetName MyNewAS01

        - OR -

        PS C:\>$myVM = Get-AzVM -ResourceGroupName MyResourceGroup -Name TestVM001
        PS C:\>New-AzVMClone -VMObject $myVM -MigrateAvailabilitySet -AvailabilitySetName MyNewAS01

        Description: Migrate a single Virtual Machine to a new Availability Set
    .EXAMPLE
        Get-AzVM -ResourceGroupName MyResourceGroup -Name TestVM001 | New-AzVMClone -RemoveAvailabilitySet
        
        - OR -

        PS C:\>$myVM = Get-AzVM -ResourceGroupName MyResourceGroup -Name TestVM001
        PS C:\>$myVM | New-AzVMClone -RemoveAvailabilitySet

        - OR -

        PS C:\>$myVM = Get-AzVM -ResourceGroupName MyResourceGroup -Name TestVM001
        PS C:\>New-AzVMClone -VMObject $myVM -RemoveAvailabilitySet

        Description: Remove a single Virtual Machine from an Availability Set
    .EXAMPLE
        Get-AzVM -ResourceGroupName MyResourceGroup | New-AzVMClone -MigrateAvailabilitySet -AvailabilitySetName MyNewAS01

        - OR -

        PS C:\>$myVMs = Get-AzVM -ResourceGroupName MyResourceGroup
        PS C:\>$myVMs | New-AzVMClone -MigrateAvailabilitySet -AvailabilitySetName MyNewAS01

        - OR -

        PS C:\>$myVMs = Get-AzVM -ResourceGroupName MyResourceGroup
        PS C:\>New-AzVMClone -VMObject $myVMs -MigrateAvailabilitySet -AvailabilitySetName MyNewAS01

        Description: Migrate a GROUP of Virtual Machines in a Resource Group to a new Availability Set
    .EXAMPLE
        Get-AzVM -ResourceGroupName MyResourceGroup | New-AzVMClone -RemoveAvailabilitySet

        - OR -

        PS C:\>$myVMs = Get-AzVM -ResourceGroupName MyResourceGroup
        PS C:\>$myVMs | New-AzVMClone -RemoveAvailabilitySet

        - OR -

        PS C:\>$myVMs = Get-AzVM -ResourceGroupName MyResourceGroup
        PS C:\>New-AzVMClone -VMObject $myVMs -RemoveAvailabilitySet

        Description: Remove a GROUP of Virtual Machines in a Resource Group from an Availability Set
    .INPUTS
        Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine

        Microsoft.Azure.Commands.Compute.Models.PSVirtualMachineList
    .OUTPUTS
        None. This function does not provide an output, only on screen information
    #>

    <#
        CHANGE LOG
        ---------------------------------------------------------------------------------
        v1.2
          - Primary production release
        v1.3
          - Fixed some string values that called a split method, but if the value was NULL, the method would error. Changed to an operator.
        v1.4
          - Fixed issue where grabbing the string from the StorageUri would incorrectly split the string
          - Moved the Show-Menu helper function inside the main function
        v1.5
          - Added support for VM Resize during clone operation (new function Select-AzVMSize)
          - Added support for VM(s) that are using a Marketplace plan
          - Added feature where the VMObject is converted to JSON (in the same working directory) before it is deleted from Azure
    #>

    [CmdletBinding(DefaultParameterSetName="Default",SupportsShouldProcess,ConfirmImpact="High")]
    Param(
        # The VM Object(s) to be cloned
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
        [Object]$VMObject,

        # The name of the new Availability Set to associated with the cloned VM(s)
        [Parameter(Mandatory=$true,ParameterSetName="Migrate",Position=1)]
        [String]$AvailabilitySetName,

        # Switch to enable Availability Set migration
        [Parameter(Mandatory=$true,ParameterSetName="Migrate",Position=0)]
        [Switch]$MigrateAvailabilitySet,

        # Switch to remove a VM from its current availability set after cloning
        [Parameter(Mandatory=$true,ParameterSetName="Remove",Position=0)]
        [Switch]$RemoveAvailabilitySet,

        # Switch to resize the VM to a new size
        [Parameter(Mandatory=$false,ParameterSetName="Migrate",Position=2)]
        [Parameter(Mandatory=$false,ParameterSetName="Remove",Position=1)]
        [Switch]$Resize,

        #Switch parameter to bypass confirmation
        [Switch]$Force
    )

    BEGIN {
        try {
            # Checks for legacy Azure RM PowerShell Module
            If ((Get-InstalledModule -Name AzureRm -ErrorAction SilentlyContinue -Debug:$false)) {
                Write-Verbose ("AzureRM Module installed, this cmdlet requires Az Module v1.5.0 from PSGallery")
                $PSCmdlet.ThrowTerminatingError(
                    [System.Management.Automation.ErrorRecord]::New(
                        [System.SystemException]::New("Invalid Azure Powershell module is installed (AzureRM)"),
                        "InvalidPowerShellModule",
                        [System.Management.Automation.ErrorCategory]::InvalidResult,
                        "AzureRm Module"
                    )
                )
            }
            # Checks for current Azure PowerShell Module
            ElseIf (-NOT (Get-InstalledModule -Name Az -MinimumVersion 1.5 -ErrorAction SilentlyContinue -Debug:$false)) {
                Write-Warning ("Missing valid Azure PowerShell Module - Please install v1.5.0 from PSGallery")
                $PSCmdlet.ThrowTerminatingError(
                    [System.Management.Automation.ErrorRecord]::New(
                        [System.SystemException]::New("Missing correct Azure Powershell module (Az)"),
                        "InvalidPowerShellModule",
                        [System.Management.Automation.ErrorCategory]::InvalidResult,
                        "Az Module"
                    )
                )
            }
            Else {Write-Verbose ("Azure Powershell Module verified")}

            # Helper function for display output
            Function Show-Menu {
                Param(
                    [string]$Menu,
                    [string]$Title = $(Throw [System.Management.Automation.PSArgumentNullException]::new("Title")),
                    [switch]$ClearScreen,
                    [Switch]$DisplayOnly,
                    [ValidateSet("Full","Mini","Info")]
                    $Style = "Full",
                    [ValidateSet("White","Cyan","Magenta","Yellow","Green","Red","Gray","DarkGray")]
                    $Color = "Gray"
                )
                if ($ClearScreen) {[System.Console]::Clear()}
            
                If ($Style -eq "Full") {
                    #build the menu prompt
                    $menuPrompt = "`n`r"
                    $menuPrompt = "/" * (95)
                    $menuPrompt += "`n`r////`n`r//// $Title`n`r////`n`r"
                    $menuPrompt += "/" * (95)
                    $menuPrompt += "`n`n"
                }
                ElseIf ($Style -eq "Mini") {
                    $menuPrompt = "`n`r"
                    $menuPrompt = "\" * (80)
                    $menuPrompt += "`n$Title`n"
                    $menuPrompt += "\" * (80)
                    $menuPrompt += "`n"
                }
                ElseIf ($Style -eq "Info") {
                    $menuPrompt = "`n`r"
                    $menuPrompt = "-" * (80)
                    $menuPrompt += "`n-- $Title`n"
                    $menuPrompt += "-" * (80)
                }
            
                #add the menu
                $menuPrompt+=$menu
            
                [System.Console]::ForegroundColor = $Color
                If ($DisplayOnly) {Write-Host $menuPrompt}
                Else {Read-Host -Prompt $menuprompt}
                [system.console]::ResetColor()
            }

            Function Select-AzVMSize {
                [CmdletBinding()]
                Param(
                    [String]$ResourceGroupName,
                    [Parameter(ParameterSetName="AVSet",Position=0)]
                    [String]$AvailabilitySetName,
                    [Parameter(ParameterSetName="Location",Position=0)]
                    [String]$Location
                )

                # Getting possible sizes from Availability Set
                If ($AvailabilitySetName) {$ResizeOptions = Get-AzVMSize -ResourceGroupName $ResourceGroupName -AvailabilitySetName $AvailabilitySetName}
                ElseIf ($Location) {$ResizeOptions = Get-AzVMSize -ResourceGroupName $ResourceGroupName -AvailabilitySetName $AvailabilitySetName}
                Else {Return}
                $ResizeFamilies = $ResizeOptions.Name.Substring(9,1) | Select-Object -Unique | Sort-Object

                Write-Verbose ("Found {0} Virtual Machine Series and {1} total size options" -f $ResizeFamilies.Count,$ResizeOptions.count)
                # Building prompts for VM Resizing
                $ResizeMsgBanner = ("`n`r Azure VM Size Information: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sizes `n`r")
                $Range = 0..($ResizeFamilies.Count -1)
                For ($i = 0; $i -lt $ResizeFamilies.Count; $i++) {$ResizeMsgBanner += " [$i] $($ResizeFamilies[$i]) Series`n"}
                $ResizeMsgBanner += "`n Please select a Virtual Machine Family - 99 to Cancel"

                # Prompt for VM Family
                Do {$FamilyChoice = Show-Menu -Title "Select a Virtual Machine Family" -Menu $ResizeMsgBanner -Style Info -Color Yellow}
                Until (($Range -contains $FamilyChoice) -OR ($FamilyChoice -eq "99"))

                If ($FamilyChoice -eq "99") {Return}

                # Prompt for VM Size
                $SelectionCount = 0
                Do {
                    $VMSize = ($ResizeOptions.Where{$_.Name -like "Standard_$($ResizeFamilies[$FamilyChoice])*"} | Sort-Object NumberOfCores,MemoryInMB | Out-GridView -PassThru).Name

                    If ([System.String]::IsNullOrEmpty($VMSize)) {
                        $SelectionCount++
                        Write-Warning "Invalid or No Virtual Machine Size selected!"
                        If ($SelectionCount -ge 2) {
                            Write-Warning ("No Virtual Machine Size selection was made!" -f $VMObject.GetType().Name)
                            $PSCmdlet.ThrowTerminatingError(
                                [System.Management.Automation.ErrorRecord]::New(
                                    [System.SystemException]::New("Invalid Virtual Machine Size Selection"),
                                    "InvalidSelection",
                                    [System.Management.Automation.ErrorCategory]::InvalidResult,
                                    "NoSelectionFound"
                                )
                            )
                            Return
                        }
                        Else {Start-Sleep -Milliseconds 2500}
                    }
                }
                Until ($ResizeOptions.Name -contains $VMSize)
                Return $VMSize
            }
        }
        catch {$PSCmdlet.ThrowTerminatingError($PSItem)}
    }
    PROCESS {
        try {
            # Checks to see if the incoming $VMObject is of the correct type
            If ($VMObject.GetType().Name -ne "PSVirtualMachine" -and $VMObject.GetType().Name -ne "PSVirtualMachineList") {
                Write-Warning ("The VMObject passed via pipeline or parameter is invalid {0}, Virtual Machine types ONLY!" -f $VMObject.GetType().Name)
                $PSCmdlet.ThrowTerminatingError(
                    [System.Management.Automation.ErrorRecord]::New(
                        [System.SystemException]::New("The VMObject type is not supported or is not an Azure Virtual Machine"),
                        "InvalidObjectType",
                        [System.Management.Automation.ErrorCategory]::InvalidResult,
                        $VMObject.GetType().Name
                    )
                )
            }
            Else {Write-Verbose ("Virtual Machine object type verified ({0})" -f $VMObject.GetType().Name)}
            
            If ([System.String]::IsNullOrEmpty($VMObject.AvailabilitySetReference.Id)) {
                $CurrentAvailabilitySetName = "Standalone VM"
            }
            Else {$CurrentAvailabilitySetName = ($VMObject.AvailabilitySetReference.Id -Split "/")[-1]}

            # Simple hashtable to show which components have been cloned
            $ConfigStatus = [Ordered]@{
                VMName = $VMObject.Name
                OS = $VMObject.StorageProfile.OsDisk.OsType
                Size = $VMObject.HardwareProfile.VmSize
                AvailabilitySet = $AvailabilitySetName
                Tags = $true
                OSDisk = $false
                DataDisks = $false
                Network = $false
                Plan = $false
                BootDiagnostics = $false
                Extensions = $false
            }

            # Checks that this is a migration and that the new AVSet is not the same as the current
            If ($MigrateAvailabilitySet -and $AvailabilitySetName -ne $CurrentAvailabilitySetName) {
                $AvailabilitySet = Get-AzAvailabilitySet -ResourceGroupName $VMObject.ResourceGroupName -Name $AvailabilitySetName -Debug:$false
                Show-Menu -Title ("{0} will be migrated to a new Availability Set ({1})" -f $VMObject.Name,$AvailabilitySetName) -DisplayOnly -Style Info -Color Green
                # Continuation prompt asking to clone the configuration for the VM
                If ($Force -or $PSCmdlet.ShouldProcess("$($VMObject.Name)","Virtual Machine migration to new Availability Set")) {
                    # Create Base VM Configuration object
                    If ($Resize) {
                        # Create new VM Object with new size
                        $VMSize = Select-AzVMSize -ResourceGroupName $VMObject.ResourceGroupName -AvailabilitySetName $AvailabilitySet.Name
                        Write-Host ("`n`r [{0}] Virtual Machine will be resized from {1} to {2} during the cloning operation! `n`r" -f $VMObject.Name,$VMObject.HardwareProfile.VmSize,$VMSize) -ForegroundColor Black -BackgroundColor Green
                        $newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMSize -AvailabilitySetId $AvailabilitySet.Id -Tags $VMObject.Tags -Debug:$false
                    }
                    Else {$newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMObject.HardwareProfile.VmSize -AvailabilitySetId $AvailabilitySet.Id -Tags $VMObject.Tags -Debug:$false}
                }
                Else {Return}
            }
            # Checks that this is an AVSet removal
            ElseIf ($RemoveAvailabilitySet) {
                If ([System.String]::IsNullOrEmpty($VMObject.AvailabilitySetReference.Id)){
                    # Does NOT process VM(s) not in an AVSet
                    Show-Menu -Title ("{0} is not part of an Availability Set and will not be processed." -f $VMObject.Name) -DisplayOnly -Style Info -Color Red
                    Return
                }
                Else {
                    Show-Menu -Title ("{0} is associated with {1} Availability Set and will be removed" -f $VMObject.Name,($VMObject.AvailabilitySetReference.Id -Split "/")[-1]) -DisplayOnly -Style Info -Color Cyan
                    If ($Force -or $PSCmdlet.ShouldProcess("$($VMObject.Name)","Virtual Machine removal from Availability Set [$CurrentAvailabilitySetName]")) {
                        If ($Resize) {
                            # Getting possible sizes from Location
                            $VMSize = Select-AzVMSize -ResourceGroupName $VMObject.ResourceGroupName -Location $VMObject.Location    
                            # Create new VM Object with new size
                            Write-Host ("`n`r [{0}] Virtual Machine will be resized from {1} to {2} during the cloning operation! `n`r" -f $VMObject.Name,$VMObject.HardwareProfile.VmSize,$VMSize) -ForegroundColor Black -BackgroundColor Green
                            $newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMSize -Tags $VMObject.Tags -Debug:$false
                        }
                        Else {$newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMObject.HardwareProfile.VmSize -Tags $VMObject.Tags -Debug:$false}
                    }
                    Else {Return}
                }
            }
            # If no other conditions exist, the VM(s) are cloned 'AS-IS'
            Else {
                If ([System.String]::IsNullOrEmpty($VMObject.AvailabilitySetReference.Id)){
                    Show-Menu -Title ("{0} is NOT part of an Availability Set" -f $VMObject.Name) -DisplayOnly -Style Info -Color Yellow
                    # Continuation prompt asking to remove the AVSet from the configuration for the VM
                    If ($Force -or $PSCmdlet.ShouldProcess("$($VMObject.Name)","Clone the Virtual Machine 'AS IS'")) {
                        If ($Resize) {
                            # Getting possible sizes from Location
                            $VMSize = Select-AzVMSize -ResourceGroupName $VMObject.ResourceGroupName -Location $VMObject.Location    
                            # Create new VM Object with new size
                            Write-Host ("`n`r [{0}] Virtual Machine will be resized from {1} to {2} during the cloning operation! `n`r" -f $VMObject.Name,$VMObject.HardwareProfile.VmSize,$VMSize) -ForegroundColor Black -BackgroundColor Green
                            $newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMSize -Tags $VMObject.Tags -Debug:$false
                        }
                        Else {$newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMObject.HardwareProfile.VmSize -Tags $VMObject.Tags -Debug:$false}
                    }
                    Else {Return}
                }
                Else {
                    Show-Menu -Title ("{0} IS associated with {1} Availability Set (NO CHANGE)" -f $VMObject.Name,($VMObject.AvailabilitySetReference.Id -Split "/")[-1]) -DisplayOnly -Style Info -Color Magenta
                    If ($Force -or $PSCmdlet.ShouldProcess("$($VMObject.Name)","Clone the Virtual Machine 'AS IS'")) {
                        If ($Resize) {
                            # Create new VM Object with new size
                            $VMSize = Select-AzVMSize -ResourceGroupName $VMObject.ResourceGroupName -AvailabilitySetName $AvailabilitySet.Name
                            Write-Host ("`n`r [{0}] Virtual Machine will be resized from {1} to {2} during the cloning operation! `n`r" -f $VMObject.Name,$VMObject.HardwareProfile.VmSize,$VMSize) -ForegroundColor Black -BackgroundColor Green
                            $newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMSize -AvailabilitySetId $AvailabilitySet.Id -Tags $VMObject.Tags -Debug:$false
                        }
                        Else {$newVMConfig = New-AzVMConfig -VMName $VMObject.Name -VMSize $VMObject.HardwareProfile.VmSize -AvailabilitySetId $VMObject.AvailabilitySetReference.Id -Tags $VMObject.Tags -Debug:$false}
                    }
                    Else {Return}
                }
            }

            # Check OS from OSDisk and set OSDisk object
            Switch ($VMObject.StorageProfile.OsDisk.OsType) {
                "Windows" {
                    Set-AzVMOSDisk -VM $newVMConfig -Name $VMObject.StorageProfile.OsDisk.Name -Caching $VMObject.StorageProfile.OsDisk.Caching -CreateOption Attach -Windows -ManagedDiskId $VMObject.StorageProfile.OsDisk.ManagedDisk.Id -Debug:$false | Out-Null
                    # Used for Hybrid Use Benefit
                    $newVMConfig.LicenseType = "Windows_Server"
                    $ConfigStatus.OsDisk = $true
                }
                "Linux" {
                    Set-AzVMOSDisk -VM $newVMConfig -Name $VMObject.StorageProfile.OsDisk.Name -Caching $VMObject.StorageProfile.OsDisk.Caching -CreateOption Attach -Linux -ManagedDiskId $VMObject.StorageProfile.OsDisk.ManagedDisk.Id -Debug:$false | Out-Null
                    $ConfigStatus.OsDisk = $true
                }
            }

            # Add data disks if they exist
            Foreach ($Datadisk in $VMObject.StorageProfile.DataDisks) {
                If ($Datadisk.ManagedDisk) {
                    Add-AzVMDataDisk -VM $newVMConfig -Name $Datadisk.Name -Caching $Datadisk.Caching -ManagedDiskId $Datadisk.ManagedDisk.Id -DiskSizeInGB $Datadisk.DiskSizeGB -Lun $Datadisk.Lun -CreateOption Attach -Debug:$false | Out-Null
                    $ConfigStatus.DataDisks = $true
                }
                Else {
                    Add-AzVMDataDisk -VM $newVMConfig -Name $Datadisk.Name -Caching $Datadisk.Caching -DiskSizeInGB $Datadisk.DiskSizeGB -Lun $Datadisk.Lun -CreateOption Attach -Debug:$false | Out-Null
                    $ConfigStatus.DataDisks = $true
                }
            }

            # Add network interfaces
            foreach ($vNic in $VMObject.NetworkProfile.NetworkInterfaces) {
                If ($vNic.Primary) {
                    Add-AzVMNetworkInterface -VM $newVMConfig -Id $vNic.Id -Primary -Debug:$false | Out-Null
                    $ConfigStatus.Network = $true
                }
                Else {
                    Add-AzVMNetworkInterface -VM $newVMConfig -Id $vNic.Id -Debug:$false | Out-Null
                    $ConfigStatus.Network = $true
                }
            }

            # Add vm plan information
            If ($VMObject.Plan) {
                Set-AzVMPlan -VM $newVMConfig -Name $VMObject.Plan.Name -Product $VMObject.Plan.Product -PromotionCode $VMObject.Plan.PromotionCode -Publisher $VMObject.Plan.Publisher
                $ConfigStatus.Plan = $true
            }

            # Add boot diagnostics
            If ($VMObject.DiagnosticsProfile.BootDiagnostics.Enabled -eq $true) {
                $StorageAccount = ($VMObject.DiagnosticsProfile.BootDiagnostics.StorageUri.Split("."))[0].SubString(8)
                Set-AzVMBootDiagnostic -VM $newVMConfig -Enable -ResourceGroupName $VMObject.ResourceGroupName -StorageAccountName $StorageAccount -Debug:$false | Out-Null
                $ConfigStatus.BootDiagnostics = $true
            }

            <# Place holder for possible addition for extension configuration cloning #>
            
            # Displays the cloning configuration status based on the components that were added to the configuration
            $Banner = @"

-------------------------------
Virtual Machine Clone Status
-------------------------------
Name: $($ConfigStatus.VMName)
OS: $($ConfigStatus.OS)
Size: $($ConfigStatus.Size)
Availability Set: $CurrentAvailabilitySetName --> $AvailabilitySetName
OSDisk: $($ConfigStatus.OSDisk)
DataDisks: $($ConfigStatus.DataDisks)
Network: $($ConfigStatus.Network)
Boot Diagnostics: $($ConfigStatus.BootDiagnostics)
Extensions: $($ConfigStatus.Extensions)

"@


            Show-Menu -Title $banner -DisplayOnly -Style Mini -Color Yellow
            Write-Host "`n`r"
            Show-Menu -Title "IF YOU CONTINUE, THE VM WILL BE DELETED AND RECREATED USING THE ABOVE CONFIGURATION!!" -DisplayOnly -Style Full -Color Red
            
            # Continuation prompt asking DELETE and RE-CREATE the VM(s) using the cloned configuration
            If ($PSCmdlet.ShouldProcess($VMObject.Name,"Clone Virtual Machine")) {
                Write-Verbose ("Converting VMObject to JSON for reference and backup (in the current directory)")
                $VMObject | ConvertTo-Json | Out-File ("{0}_AzVMClone_{1}.json" -f $VMObject.Name,(Get-Date -Format yyyyMMdd))
                Write-Host ("`n`r[{0}] Removing the Virtual Machine (long running operation)" -f $VMObject.Name) -NoNewline -ForegroundColor Magenta
                # VM removal in a background job - monitored for completion and received for status
                $jobRemove = Remove-AzVM -ResourceGroupName $VMObject.ResourceGroupName -Name $VMObject.Name -Force -AsJob
                While ($jobRemove.State -eq "Running") {
                    Write-Host (".") -NoNewline
                    Start-Sleep -Seconds 3
                }
                $jobDetails = $jobRemove | Receive-Job
                If ($jobRemove.State -eq "Completed" -and $jobDetails.Status -eq "Succeeded") {
                    Write-Host ("DONE!") -BackgroundColor Green -ForegroundColor Black
                    Write-Host ("[{0}] Removing Virtual Machine - JOB: {1} | TASK: {2} | TIME: {3:N2} minutes" -f $VMObject.Name,$jobRemove.State,$jobDetails.Status,($jobDetails.EndTime - $jobDetails.Starttime).TotalMinutes) -ForegroundColor Green
                    Write-Host ("`n`r[{0}] Cloning Virtual Machine" -f $VMObject.Name) -NoNewline -ForegroundColor Cyan
                    # VM creation in a background job - monitored for completion and received for status
                    $jobCreate = New-AzVM -ResourceGroupName $VMObject.ResourceGroupName -Location $VMObject.Location -VM $newVMConfig -DisableBginfoExtension -AsJob
                    While ($jobCreate.State -eq "Running") {
                        Write-Host (".") -NoNewline
                        Start-Sleep -Milliseconds 2750
                    }
                    $jobDetails = $jobCreate | Receive-Job
                    If ($jobCreate.State -eq "Completed" -and $jobDetails.StatusCode -eq "OK") {
                        Write-Host ("DONE!") -BackgroundColor Green -ForegroundColor Black
                        Write-Host ("[{0}] Cloning Virtual Machine - JOB: {1} | TASK: {2} | TIME: {3:N2} minutes" -f $VMObject.Name,$jobCreate.State,$jobDetails.StatusCode,($jobCreate.PSEndTime - $jobCreate.PSBeginTime).TotalMinutes) -ForegroundColor Green
                    }
                    Else {
                        Write-Host ("FAILED!") -BackgroundColor Red -ForegroundColor White
                        Write-Host ("[{0}] Cloning Virtual Machine - JOB: {1} | TASK: {2} | TIME: {3:N2} minutes" -f $VMObject.Name,$jobCreate.State,$jobDetails.StatusCode,($jobCreate.PSEndTime - $jobCreate.PSBeginTime).TotalMinutes) -ForegroundColor Red
                    }
                }
                Else {
                    Write-Host ("FAILED!") -BackgroundColor Red -ForegroundColor White
                    Write-Host ("[{0}] Removing Virtual Machine - JOB: {1} | TASK: {2} | TIME: {3:N2} minutes" -f $VMObject.Name,$jobRemove.State,$jobDetails.Status,($jobDetails.EndTime - $jobDetails.StartTime).TotalMinutes) -ForegroundColor Red
                }
            }
            Else {Return}
        }
        catch {$PSCmdlet.ThrowTerminatingError($PSItem)}
    }
}