Copy-AzureVMDiskToSA.ps1

<#PSScriptInfo
 
.VERSION 1.0.1
 
.GUID 44ce6bf4-ca04-4237-9088-f905aef1150e
 
.AUTHOR dareynol
 
.COMPANYNAME Microsoft Corporation
 
.COPYRIGHT (c) 2019 Microsoft. All rights reserved.
 
.TAGS Azure Image Images Copy VM Storage Account
 
.LICENSEURI https://github.com/dcrreynolds/Azure-CopyingImages/blob/master/LICENSE
 
.PROJECTURI https://github.com/dcrreynolds/Azure-CopyingImages
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
.PRIVATEDATA
 
#>


<#
    .SYNOPSIS
        Generalize an Azure VM and export its Disk as VHDs to another region or subscription.
 
    .Description
        Generalize an Azure VM and export its Disk as VHDs to another region or subscription.
 
    .PARAMETER StorageAccountSubscriptionId
        Subscription ID of the target storage account
 
    .PARAMETER StorageAccountName
        Name of the target storage account
 
    .PARAMETER StorageAccountResourceGroup
        Resource group iof the target storage account
 
    .PARAMETER StorageAccountContainer
        Container to place VHDs in
 
    .PARAMETER VMSubscriptionID
        Subscription ID of VM
 
    .PARAMETER VMName
        Name of the VM
 
    .PARAMETER VMResourceGroup
        Resource group of the VM
 
    .EXAMPLE
        .\Copy-AzureVMDiskToSA.ps1 `
            -StorageAccountSubscriptionId "Sub 1 ID" `
            -StorageAccountName "SA-Name" `
            -StorageAccountResourceGroup "SA-RG-Name" `
            -StorageAccountContainer "SA-RG-Container-Name" `
            -VMSubscriptionID "Sub 2 ID" `
            -VMName "VM-Name" `
            -VMResourceGroup "VM-RG-Name"
#>


param 
(
    [parameter(mandatory)]
    $StorageAccountSubscriptionId,

    [parameter(mandatory)]
    $StorageAccountName,

    [parameter(mandatory)]
    $StorageAccountResourceGroup,

    [parameter(mandatory)]
    $StorageAccountContainer,

    [parameter(mandatory)]
    $VMSubscriptionID,

    [parameter(mandatory)]
    $VMName,

    [parameter(mandatory)]
    $VMResourceGroup
)

#region functions
<#
    .SYNOPSIS
        Connects to Azure and sets the provided subscription.
 
    .PARAMETER SubscriptionId
        ID of subscription to use
 
    .PARAMETER AutomationConnection
        Azure automation connection object for using a AA run as account
#>

function Set-AzureConnection
{
    Param
    (
        [parameter(mandatory = $true)]
        $SubscriptionId,

        $AutomationConnection,

        $AzureEnvironment = "AzureCloud"
    )

    $context = Get-AzureRmContext

    if($null -eq $context.Account)
    {
        $envARM = Get-AzureRmEnvironment -Name $AzureEnvironment

        if($null -ne $AutomationConnection)
        {
            $context = Add-AzureRmAccount `
                -ServicePrincipal `
                -Tenant $Conn.TenantID `
                -ApplicationId $Conn.ApplicationID `
                -CertificateThumbprint $Conn.CertificateThumbprint `
                -Environment $envARM
        }
        else # if no connection info, log in using the web prompts
        {
            $context = Add-AzureRmAccount -Environment $envARM -ErrorAction Stop
        }
    }

    $null = Set-AzureRmContext -Subscription $SubscriptionId -ErrorAction Stop
}
#endregion

Set-AzureConnection -SubscriptionId $VMSubscriptionID

# Make sure the VM is generalized.
Write-Output "Generalizing VM $VMName"
$null = Set-AzureRmVm -ResourceGroupName $VMResourceGroup -Name $VMName -Generalized

# get info about the VM disk so we can DL them
Write-Output "Obtaining VM $VMName disk SAS access"
$vm = Get-AzureRMVM -Name $VMName -ResourceGroupName $VMResourceGroup

$diskList = @($vm.StorageProfile.OsDisk)
$diskList += $vm.StorageProfile.DataDisks

$SASList = @()
foreach($disk in $diskList)
{
    $SAS = Grant-AzureRmDiskAccess `
        -ResourceGroupName $VMResourceGroup `
        -DiskName $disk.Name `
        -Access 'Read' `
        -DurationInSecond (60 * 60)

    $SASList += @{
        AccessSAS = $SAS.AccessSAS
        DiskName = $disk.Name
    }
}

# Set these storage account variables
Set-AzureConnection -SubscriptionId $StorageAccountSubscriptionId

$storageAccount = Get-AzureRmStorageAccount `
    -Name $StorageAccountName `
    -ResourceGroupName $StorageAccountResourceGroup

Write-Output "Starting the disk copy to $StorageAccountName"
$copyJob = @()
foreach($diskSAS in $SASList)
{
    $copyJob += Start-AzureStorageBlobCopy `
        -AbsoluteUri $diskSAS.AccessSAS `
        -DestContainer $StorageAccountContainer `
        -DestContext $storageAccount.Context `
        -DestBlob "$($diskSAS.DiskName).vhd"
}

# wait for the copy job to complete
foreach($copy in $copyJob)
{
    Write-Output "Waiting for disk $($copy.Name) copy to complete"

    Get-AzureStorageBlobCopyState `
        -Blob $copy.Name `
        -Container $StorageAccountContainer `
        -Context $storageAccount.Context `
        -WaitForComplete
}