AzureFunctions.ps1

Function AuthAzure([string]$azureClientId, [string]$azureSecret, [string]$azureTenantId, [string]$azureSubscriptionId)
{
    if ($azureClientId -And $azureSecret -And $azureTenantId)
    {
        Log "Authenticating to Azure as service principal $azureClientId in subscription $azureSubscriptionId"
        $secret = ConvertTo-SecureString $azureSecret -AsPlainText -Force
        $pscredential = New-Object -TypeName System.Management.Automation.PSCredential($azureClientId, $secret)
        $null = Connect-AzAccount -ServicePrincipal -Credential $pscredential -Tenant $azureTenantId -Subscription $azureSubscriptionId `
          -ErrorAction Stop
    }
    else
    {
        $context = Get-AzContext
        if ($null -eq $context -or ($context.Subscription.Id -ne $azureSubscriptionId))
        {
            Log "Authenticating to Azure interactively in subscription $azureSubscriptionId"
            $null = Connect-AzAccount -Subscription $azureSubscriptionId -ErrorAction Stop
        }
        else
        {
            Log "Already connected to subscription $azureSubscriptionId" $False
        }
    }
}

Function GetAzureDisk([string]$cloudDiskName, [string]$targetResourceGroup)
{
    $err = ""
    $disk = Get-AzDisk -ResourceGroupName $targetResourceGroup -DiskName $cloudDiskName -ErrorAction SilentlyContinue -ErrorVariable err
    if ($null -ne $disk) {
        return $disk
    }
    if (ErrorIsResourceNotFound $err)
    {
        return $null
    }

    $msg = "Failure trying to find managed disk '$cloudDiskName' in resource group $targetResourceGroup"
    ThrowError ([UploaderException]::new($msg, $err.Exception))
}

Function CleanUpAzureDisk([string]$cloudDiskName, [string]$targetResourceGroup)
{
    $disk = GetAzureDisk $cloudDiskName $targetResourceGroup
    if ($null -ne $disk)
    {
        Log "Deleting existing managed disk '$cloudDiskName' in resource group $targetResourceGroup"
        $err = ""
        $result = Remove-AzDisk -ResourceGroupName $targetResourceGroup -DiskName $disk.Name -Force -ErrorAction SilentlyContinue -ErrorVariable err
        if ($null -eq $result)
        {
            ThrowError ([UploaderException]::new("Failed to delete existing managed disk '$($disk.Name)'", $err.Exception))
        }
        Log "Remove-AzDisk status $($result.status)" $False
        Log "Deleted managed disk '$($disk.Name)'"
    }
}

Function CreateManagedDisk([long]$sizeInBytes, [int]$uploadTimeout, [string]$azureStorageType, [string]$azureLocation, [string]$targetResourceGroup,
                           [string]$cloudDiskName, [HashTable]$tags = @{})
{
    $sasExpiryDuration = $uploadTimeout
    $azDiskConfigArgs = @{
        AccountType = $azureStorageType
        Location = $azureLocation
        UploadSizeInBytes = $sizeInBytes
        CreateOption = 'Upload'
        OsType = 'Windows'
        HyperVGeneration = 'V2'
    }

    if ($tags.Count -ne 0) {
        $azDiskConfigArgs["Tag"] = $tags
    }

    $diskConfig = New-AzDiskConfig @azDiskConfigArgs
    Log "Creating managed disk '$cloudDiskName' with size $sizeInBytes bytes in resource group $targetResourceGroup location $azureLocation"
    $err = ""
    $disk = New-AzDisk -ResourceGroupName $targetResourceGroup -DiskName $cloudDiskName -Disk $diskConfig -ErrorAction SilentlyContinue `
      -ErrorVariable err
    if ($null -eq $disk)
    {
        ThrowError ([UploaderException]::new("Failed to create managed disk '$cloudDiskName' in resource group $targetResourceGroup", $err.Exception))
    }

    Log "Granting access to managed disk '$cloudDiskName' for $sasExpiryDuration seconds" $False
    $err = ""
    $access = Grant-AzDiskAccess -ResourceGroupName $targetResourceGroup -DiskName $cloudDiskName -DurationInSecond $sasExpiryDuration `
      -Access 'Write' -ErrorAction SilentlyContinue -ErrorVariable err
    if ($null -eq $access)
    {
        ThrowError ([UploaderException]::new("Failed to create SAS for mananged disk '$cloudDiskName'", $err.Exception))
    }
    $sas = $access.AccessSAS
    Log "Created managed disk '$cloudDiskName' with SAS $sas" $False
    return $sas
}

Function DoAzureUpload([ScriptBlock]$uploadScript, [string]$diskName, [int]$threads, [string]$targetResourceGroup, [string]$cloudDiskName)
{
    Log ("Copying disk '$diskName' to managed disk '$cloudDiskName' " + $(if ($threads -le 0) {"(threads=default)"} else {"(threads=$threads)"}))
    $ex = $null
    try
    {
        & $uploadScript
        Log "Revoking Azure disk access" $False
        $err = ""
        $r = Revoke-AzDiskAccess -ResourceGroupName $targetResourceGroup -DiskName $cloudDiskName -ErrorAction SilentlyContinue -ErrorVariable err
        if ($null -eq $r)
        {
            ThrowError ([UploaderException]::new("Revoke-AzDiskAccess failed", $err.Exception)) $False
        }
        Log "Revoke-AzDiskAccess status '$($r.status)'" $False
    }
    catch
    {
        $ex = $_.Exception
    }
    finally
    {
        if ($null -eq $ex)
        {
            Log "Copied disk to Azure managed disk '$cloudDiskName'"
        }
        else
        {
            Log "Deleting managed disk '$cloudDiskName' because upload failed"
            $null = Remove-AzDisk -ResourceGroupName $targetResourceGroup -DiskName $cloudDiskName -Force
            throw $ex
        }
    }
}

Function UploadToAzure([string]$destination, [string]$path, [int]$threads, [string]$targetResourceGroup, [string]$cloudDiskName, [bool] $debug, [string]$logFileName)
{
    $uploadScript = {
        $parameters = @{
            File = $path
            Sas = $destination
            Threads = $threads
            LogFileName = $logFileName
            BlobDebug = $debug
        }
        Copy-ToAzDisk @parameters
    }
    DoAzureUpload $uploadScript $path $threads $targetResourceGroup $cloudDiskName
}