Thursday, September 19, 2013

PowerShell & BitsTransfer

I had need to pull down a very large file (127 GB) from Windows Azure.  In order to do so, I needed to be able to pause/resume as I moved my work laptop between locations (and internet connections).

To accomplish this, I used BitsTransfer PowerShell functionality.

(Note: Under the azure management console, I made sure the file was public, and grabbed the url to use as the source)

First step is to load the module:
Import-Module BitsTransfer

Next, I declare a few variables:
$source = "http://my.url"
$destination = "C:\localFolder\newFile.name"
$transferName = "MyTransfer"

I need the credentials for logging into Azure to pull the file.
$cred = Get-Credential

Being the asynchronous download:
Start-BitsTransfer -Source $source -Destination $destination -DisplayName $transferName -Asynchronous

The command prompt returns.  I can close the PowerShell console window at this point.  (If I do, I have to remember the Import-Module command when re-opening in the future).  If I want to take some actions on the job, I will want to capture it in an object variable.
$myJob = Get-BitsTransfer $transferName

I can pause the transfer process with a simple command:
Suspend-BitsTransfer $job

I can resume it just as easily:
Resume-BitsTranfer -BitsJob $job -Asynchronous

For me, the biggest thing was to have my own status reporting.  I wanted a simple, and clean message to let me know how much data has been transferred.  And let me know when the process stops.

I start with a helper function.  The BitsJob is going to report in number of bytes, and that means BIG numbers.  So my function converts bytes into a friendlier string.

function Convert-DataSize([long]$numOfBytes)
{
     [int] $cnt = 0
     [double] $dBytes = $numOfBytes

     for($i = 0; $i -lt 5; $i++)
     {
          $cnt = $i
          [double] $results = [double]($dBytes /1024)
          if ($results -lt 1) { break; }
          $dBytes = $results
     }

     switch($cnt)
     {
          1 { return $dBytes.ToString("0.00") + " KB" }
          2 { return $dBytes.ToString("0.00") + " MB" }
          3 { return $dBytes.ToString("0.00") + " GB" }
          4 { return $dBytes.ToString("0.00") + " TB" }
     }

     return $numOfBytes.ToString() + " B"
}

With my helper function established, I create a loop to display my message:

do
{
     cls
     $percentComplete = "(" + ($job.BytesTransferred / $job.BytesTotal * 100).ToString("0.00%") + ") "
     $bytesTrans = Convert-DataSize($job.BytesTransferred)
     $bytesTotal = Convert-DataSize($job.BytesTotal)
     Write-Host $percentComplete $bytesTrans " of " $bytesTotal
     Start-Sleep -s 5
   
     if ($job.JobState -ne "Transferring") { break; }
}
while ($job.BytesTransferred -lt $job.BytesTotal)

$job  #Displaying promoted properties of the BitsJob object.

The last step I need to remember is to complete the transfer process:
Complete-BitsTransfer $job

Just imagine how all this PowerShell looks in a nice clean script file.  Add a little code to check if the job already exists, and some better error handling, I can launch this thing easily with a double-click, and let it run until it completes.  (My big job took almost 40 hours based on the variety of connections I ended up attached too).