Sample backup script

As an alternative to using Settings > Actions > Create backup from the appliance UI, you can write and run a script to automatically create and download an appliance backup file.

The Sample backup.ps1 script shown below provides a sample PowerShell script that uses REST calls to create and download an appliance backup file. Cut and paste this sample script into a file on a Windows system that runs PowerShell version 3.0, and edit the script to customize it for your environment.

You can schedule the backup script to run automatically in interactive or batch mode on a regular basis (Hewlett Packard Enterprise recommends daily backups). Only a user with Backup administrator or Infrastructure administrator privileges can run the script interactively.

  • To run the script interactively, do not include any parameters. The script prompts you to enter the appliance host name, appliance user name and password, and the name of a file to store these parameters for batch mode executions. Enter the name and password of a user with the Backup administrator or Infrastructure administrator role. The user name and password are stored encrypted.

    Hewlett Packard Enterprise recommends that you run the script interactively the first time. Then, you can schedule the script to run automatically in the background using the parameter file created by the first run.

  • To run the script in batch mode, specify the name of the file containing the parameters on the command line.

Hewlett Packard Enterprise recommends that you install cURL with the SSL option to improve performance. The sample script works without cURL, but it might take several hours to download a large backup file. To download cURL, see:

http://curl.haxx.se/download.html

NOTE:

You might also need to install Microsoft Visual C++ Redistributable, the MSVCR100.dll file, available here:

Make sure the path environment variable includes the path for cURL.

Sample script

The sample script makes the following calls to create and download a backup file:

  1. Calls queryfor-credentials() to get the appliance host name, user name, and password by either prompting the user or reading the values from a file.

  2. Calls login-appliance() to issue a REST request to obtain a session ID used to authorize backup REST calls.

  3. Calls backup-appliance() to issue a REST request to start a backup.

  4. Calls waitFor-completion() to issue REST requests to poll for backup status until the backup completes.

  5. Calls get-backupResource() to issue a REST request to get the download URI.

  6. Calls download-backup() to issue a REST request to download the backup.

Sample backup.ps1 script

# (C) Copyright 2012-2014 Hewlett Packard Enterprise Development LP
###########################################################################################################################
# Name:     backup.ps1
# Usage:    {directory}\backup.ps1 or {directory}\backup.ps1 filepath
# Parameter: $filepath: optional, uses the file in that path as the login credentials. ie: host address, username,
#           password, and, optionally, the Active Directory domain name
# Purpose:  Runs the backup function on the appliance and downloads it onto your machine's drive
#           in current user's home directory
# Notes:    To improve performance, this script uses the curl command if it is installed.  The curl command must
#           be installed with the SSL option.
#           Windows PowerShell 3.0 must be installed to run the script
###########################################################################################################################

#tells the computer that this is a trusted source that we are connecting to (brute force, could be refined)
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

$global:interactiveMode = 0

# The scriptApiVersion is the default Api version (if the appliance supports this level
# or higher).  This variable may be changed if the appliance is at a lower Api level.
$global:scriptApiVersion = 3
# Using this Api version or greater requires a different interaction when creating a backup.
Set-Variable taskResourceV2ApiVersion -option Constant -value 3

try {
  #this log must be added if not already on your computer
  New-EventLog -LogName Application -Source backup.ps1 -ErrorAction stop
}
catch [System.Exception]
{
  #this is just to keep the error "already a script" from showing up on the screen if it is already created
}

##### Querying user for login info #####
function queryfor-credentials ()
{
  <#
        .DESCRIPTION
            Gathers information from User if in manual entry mode (script ran with zero arguments) or
            runs silently and gathers info from specified path (script ran with 1 argument)

        .INPUTS
            None, this function does not take inputs.

        .OUTPUTS
            Returns an object that contains the login name, password, hostname and
            ActiveDirectory domain to connect to.

        .EXAMPLE
            $variable = queryfor-credentials #runs function, saves json object to variable.
    #>

  if ($args[0] -eq $null)
  {

    Write-Host "Enter appliance name (https://ipaddress)"
    $appliance = Read-Host

    # Correct some common errors
    $appliance = $appliance.Trim().ToLower()
    if (!$appliance.StartsWith("https://"))
    {
       if ($appliance.StartsWith("http://"))
       {
          $appliance = $appliance.Replace("http","https")
       } else {
          $appliance = "https://" + $appliance
       }
    }

    Write-Host "Enter username"
    $username = Read-Host -AsSecureString | ConvertFrom-SecureString

    Write-Host "Enter password"
    $SecurePassword = Read-Host -AsSecureString | ConvertFrom-SecureString

    Write-Host "If using Active Directory, enter the Active Directory domain"
    Write-Host "  (Leave this field blank if not using Active Directory.)"
    $ADName = Read-Host

    Write-Host "Would you like to save these credentials to a file? (username and password encrypted)"
    $saveQuery = Read-Host

    $loginVals = [pscustomobject]@{ userName = $username; password = $SecurePassword;
                  hostname = $appliance; authLoginDomain = $ADName }
    $loginJson = $loginVals | convertTo-json

    $global:interactiveMode = 1

    if ($saveQuery[0] -eq "y") #enters into the mode to save the credentials
    {
      Write-Host "Enter file path and file name to save credentials  (example: C:\users\bob\machine1.txt)"
      $storagepath = Read-Host

      try
      {
        $loginJson | Out-File $storagepath -NoClobber -ErrorAction stop
      }
      catch [System.Exception]
      {
        Write-Host $_.Exception.message
        if ($_.Exception.getType() -eq [System.IO.IOException]) # file already exists throws an IO exception
        {
          do
          {
            Write-Host "Overwrite existing credentials for this machine?"
            [string]$overwriteQuery = Read-Host
            if ($overwriteQuery[0] -eq 'y')
            {
              $loginJson | Out-File $storagepath -ErrorAction stop
              $exitquery = 1
            }
            elseif ($overwriteQuery[0] -eq 'n')
            {
              $exitquery = 1
            }
            else
            {
              Write-Host "Please respond with a y or n"
              $exitquery = 0
            }

          } while ($exitquery -eq 0)
        }
        else
        {
          Write-Host "Improper filepath or no permission to write to given directory"
          Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Improper filepath, $storagepath " $_.Exception.message
          return
        }
      }

      $savedLoginJson = Get-Content $storagepath

      Write-Host "Run backup?"

      $continue = 0
      do
      {
        $earlyExit = Read-Host
        if ($earlyExit[0] -eq 'n')
        {
          return
        }
        elseif ($earlyExit[0] -ne 'y')
        {
          Write-Host "Please respond with a y or n"
        }
        else
        {
          $continue = 1
        }

      } while ($continue -eq 0)

    }
    else
    {
      return $loginJson
    }
  }
  elseif ($args.count -ne 1)
  {
    Write-Host "Incorrect number of arguments, use either filepath parameter or no parameters."
    return

  }
  else
  {
    foreach ($arg in $args)
    {
      $storagepath = $arg
    }
    try
    {
      $savedLoginJson = Get-Content $storagepath -ErrorAction stop
    }
    catch [System.Exception]
    {
      Write-Host "Login credential file not found. Please run script without arguments to access manual entry mode."
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Login credential file not found. Please run script without arguments to access manual entry mode."
      return

    }
  }
  return $savedloginJson
}

##### getApiVersion: Get X_API_Version #####
function getApiVersion ([int32] $currentApiVersion,[string]$hostname)
{
  <#
        .DESCRIPTION
            Sends a web request to the appliance to obtain the current Api version.
      Returns the lower of: Api version supported by the script and Api version
      supported by the appliance.

        .PARAMETER currentApiVersion
      Api version that the script is currently using

        .PARAMETER hostname
            The appliance address to send the request to (in https://{ipaddress} format)

        .INPUTS
            None, does not accept piping

        .OUTPUTS
            Outputs the new active Api version

        .EXAMPLE
            $global:scriptApiVersion = getApiVersion()
    #>

  # the particular Uri on the Appliance to reqest the Api Version
  $versionUri = "/rest/version"

  # append the Uri to the end of the IP address to obtain a full Uri
  $fullVersionUri = $hostname + $versionUri

  # use setup-request to issue the REST request api version and get the response
  try
  {
    $applianceVersionJson = setup-request -Uri $fullVersionUri -method "GET" -accept "application/json" -contentType "application/json"
    if ($applianceVersionJson -ne $null)
    {
      $applianceVersion = $applianceVersionJson | convertFrom-Json
      $currentApplianceVersion = $applianceVersion.currentVersion
      if ($currentApplianceVersion -lt $currentApiVersion)
      {
        return $currentApplianceVersion
      }
      return $currentApiVersion
    }
  }
  catch [System.Exception]
  {
    if ($global:interactiveMode -eq 1)
    {
      Write-Host $error[0].Exception.Message
    }
    else
    {
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message $error[0].Exception.Message
    }
  }
}

##### Sending login info #####
function login-appliance ([string]$username,[string]$password,[string]$hostname,[string]$ADName)
{
  <#
        .DESCRIPTION
            Attempts to send a web request to the appliance and obtain an authorized sessionID.

        .PARAMETER username
            The username to log into the remote appliance

        .PARAMETER password
            The correct password associated with username

        .PARAMETER hostname
            The appliance address to send the request to (in https://{ipaddress} format)

        .PARAMETER ADName
            The Active Directory name (optional)

        .INPUTS
            None, does not accept piping

        .OUTPUTS
            Outputs the response body containing the needed session ID.

        .EXAMPLE
            $authtoken = login-appliance $username $password $hostname $ADName
    #>

  # the particular Uri on the Appliance to reqest an "auth token"
  $loginUri = "/rest/login-sessions"

  # append the Uri to the end of the IP address to obtain a full Uri
  $fullLoginUri = $hostname + $loginUri
  # create the request body as a hash table, then convert it to json format
  if ($ADName)
  {
    $body = @{ userName = $username; password = $password; authLoginDomain = $ADName } | convertTo-json
  }
  else    # null or empty
  {
    $body = @{ userName = $username; password = $password } | convertTo-json
  }

  # use setup-request to issue the REST request to login and get the response
  try
  {
    $loginResponse = setup-request -Uri $fullLoginUri -method "POST" -accept "application/json" -contentType "application/json" -Body $body
    if ($loginResponse -ne $null)
    {
      $loginResponse | convertFrom-Json
    }
  }
  catch [System.Exception]
  {
    if ($global:interactiveMode -eq 1)
    {
      Write-Host $error[0].Exception.Message
    }
    else
    {
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message $error[0].Exception.Message
    }
  }
}

##### Executing backup ######
function backup-Appliance ([string]$authValue,[string]$hostname)
{
  <#
        .DESCRIPTION
            Gives the appliance the command to start creating a backup

        .PARAMETER authValue
            The authorized sessionID given by login-appliance

        .PARAMETER hostname
            The location of the appliance to connect to (in https://{ipaddress} format)

        .INPUTS
            None, does not accept piping

        .OUTPUTS
            The task Resource returned by the appliance, converted to a hashtable object

        .EXAMPLE
            $taskResource = backup-Appliance $sessionID $hostname
    #>

  # append the REST Uri for backup to the IP address of the Appliance
  $bkupUri = "/rest/backups/"
  $fullBackupUri = $hostname + $bkupUri

  # create a new webrequest and add the proper headers (new header, auth, is needed for authorization
  # in all functions from this point on)
  try
  {
    if ($global:scriptApiVersion -lt $taskResourceV2ApiVersion)
    {
      $taskResourceJson = setup-request -Uri $fullBackupUri -method "POST" -accept "application/json" -contentType "application/json" -authValue $authValue
    }
    else
    {
      $taskUri = setup-request -Uri $fullBackupUri -method "POST" -accept "application/json" -contentType "application/json" -authValue $authValue -returnLocation $true
      if ($taskUri -ne $null)
      {
        $taskResourceJson = setup-request -Uri $taskUri -method "GET" -accept "application/json" -contentType "application/json" -authValue $authValue
      }
    }
    if ($taskResourceJson -ne $null)
    {
      return $taskResourceJson | ConvertFrom-Json
    }
  }
  catch [System.Exception]
  {
    if ($global:interactiveMode -eq 1)
    {
      Write-Host $error[0].Exception.Message
    }
    else
    {
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message $error[0].Exception.Message
    }
  }
}

##### Polling to see if backup is finished ######
function waitFor-completion ([object]$taskResource,[string]$authValue,[string]$hostname)
{
  <#
        .DESCRIPTION
            Checks the status of the backup every twenty seconds, stops when status changes from running to a different status

        .PARAMETER taskResource
            The response object from the backup-appliance method

        .PARAMETER authValue
            The authorized session ID

        .PARAMETER hostname
            The appliance to connect to (in https://{ipaddress} format)

        .INPUTS
            None, does not accept piping

        .OUTPUTS
            The new task resource object, which contains the Uri to get the backup resource in the next function

        .EXAMPLE
            $taskResource = waitFor-Completion $taskResource $sessionID $hostname
    #>

  # extracts the Uri of the task Resource from itself, to poll repeatedly
  $taskResourceUri = $taskResource.uri
  if ($taskResourceUri -eq $null)
  {
    # Caller will provide the error message
    return
  }

  # appends the Uri to the hostname to create a fully-qualified Uri
  $fullTaskUri = $hostname + $taskResourceUri

  # retries if unable to get backup progress information
  $errorCount = 0
  $errorMessage = ""

  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Backup initiated."
    Write-Host "Checking for backup completion, this may take a while."
  }

  # a while loop to determine when the backup process is finished
  do
  {

    try
    {
      # creates a new webrequest with appropriate headers
      $taskResourceJson = setup-request -Uri $fullTaskUri -method "GET" -accept "application/json" -authValue $authValue -isSilent $true
      # converts the response from the Appliance into a hash table
      $taskResource = $taskResourceJson | convertFrom-Json
      # checks the status of the task manager
      $status = $taskResource.taskState
    }
    catch
    {
      $errorMessage = $error[0].Exception.Message
      $errorCount = $errorCount + 1
      $status = "RequestFailed"
      Start-Sleep -s 15
      continue
    }

    # Update progress bar
    if ($global:interactiveMode -eq 1)
    {
      $trimmedPercent = ($taskResource.completedSteps) / 5
      $progressBar = "[" + "=" * $trimmedPercent + " " * (20 - $trimmedPercent) + "]"
      Write-Host "`r Backup progress: $progressBar " $taskResource.completedSteps "%" -NoNewline
    }

    # Reset the error count since progress information was successfully retrieved
    $errorCount = 0

    # If the backup is still running, wait a bit, and then check again
    if ($status -eq "Running")
    {
      Start-Sleep -s 20
    }

  } while (($status -eq "Running" -or $status -eq "RequestFailed") -and $errorCount -lt 20);

  # if the backup reported an abnormal state, report the state and exit function
  if ($status -ne "Completed")
  {
    if ($global:interactiveMode -eq 1)
    {
      Write-Host "`n"
      Write-Host "Backup stopped abnormally"
      Write-Host $errorMessage
    }
    else
    {
      #log error message
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Backup stopped abnormally"
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message $errorMessage
    }
    return $null
  }

  # upon successful completion of task, outputs a hash table which contains task resource
  else
  {
    Write-Host "`n"
    $taskResource
    return
  }
}

##### Gets the backup resource #####
function get-backupResource ([object]$taskResource,[string]$authValue,[string]$hostname)
{
  <#
        .DESCRIPTION
            Gets the Uri for the backup resource from the task resource and gets the backup resource

        .PARAMETER taskResource
            The task resource object that we use to get the Uri for the backup resource

        .PARAMETER authValue
            The authorized sessionID

        .PARAMETER hostname
            the appliance to connect to (in https://{ipaddress} format)

        .INPUTS
            None, does not accept piping

        .OUTPUTS
            The backup resource object

        .EXAMPLE
            $backupResource = get-BackupResource $taskResource $sessionID $applianceName
    #>

  # the backup Resource Uri is extracted from the task resource
  if ($global:scriptApiVersion -lt $taskResourceV2ApiVersion)
  {
    $backupUri = $taskResource.associatedResourceUri
  }
  else
  {
    $backupUri = $taskResource.associatedResource.resourceUri
  }
  if ($backupUri -eq $null)
  {
    # Caller will provide the error message
    return
  }
  # construct the full backup Resource Uri from the hostname and the backup resource uri
  $fullBackupUri = $hostname + $backupUri

  # get the backup resource that contains the Uri for downloading
  try
  {
    # creates a new webrequest with appropriate headers
    $backupResourceJson = setup-request -Uri $fullBackupUri -method "GET" -accept "application/json" -auth $authValue
    if ($backupResourceJson -ne $null)
    {
       $resource = $backupResourceJson | convertFrom-Json
       if ($global:interactiveMode -eq 1)
       {
         Write-Host "Obtained backup resource.  Now downloading.  This may take a while ..."
       }
       $resource
       return
    }
  }
  catch [System.Exception]
  {
    if ($global:interactiveMode -eq 1)
    {
      Write-Host $error[0].Exception.Message
    }
    else
    {
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message $error[0].Exception.Message
    }
  }
}

##### Function to download the backup file #####
function download-Backup ([PSCustomObject]$backupResource,[string]$authValue,[string]$hostname)
{
  <#
        .DESCRIPTION
            Downloads the backup file from the appliance to the local system.  Tries to use the
            curl command.  The curl command has significantly better performance especially for
            large backups.  If curl isn't installed, invokes download-Backup-without-curl to
            download the backup.

        .PARAMETER backupResource
            Backup resource containing Uri for downloading

        .PARAMETER authValue
            The authorized sessionID

        .PARAMETER hostname
            The IP address of the appliance

        .INPUTS
            None, does not accept piping

        .OUTPUTS
            The absolute path of the download file

        .EXAMPLE
            download-backup $backupResource $sessionID https://11.111.11.111
    #>

  $downloadUri = $hostname + $backupResource.downloadUri
  $fileDir = [environment]::GetFolderPath("Personal")
  $filePath = $fileDir + "\" + $backupResource.id + ".bkp"
  $curlDownloadCommand = "curl -o " + $filePath + " -s -f -L -k -X GET " +
    "-H 'accept: application/octet-stream' " +
    "-H 'auth: " + $authValue + "' " +
    "-H 'X-API-Version: $global:scriptApiVersion' " +
    $downloadUri
  $curlGetDownloadErrorCommand = "curl -s -k -X GET " +
    "-H 'accept: application/json' " +
    "-H 'auth: " + $authValue + "' " +
    "-H 'X-API-Version: $global:scriptApiVersion' " +
    $downloadUri

  try
  {
    $testCurlSslOption = curl -V
    if ($testCurlSslOption -match "SSL")
    {
        invoke-expression $curlDownloadCommand
    }
    else
    {
        if ($global:interactiveMode -eq 1)
        {
          Write-Host "Version of curl must support SSL to get improved download performance."
        }
        else
        {
          Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Version of curl must support SSL to get improved download performance"
        }

        return download-Backup-without-curl $backupResource $authValue $hostname
    }

    if ($LASTEXITCODE -ne 0)
    {
        $errorResponse = invoke-expression $curlGetDownloadErrorCommand
        if ($global:interactiveMode -eq 1)
        {
          Write-Host "Download using curl error: $errorResponse"
        }
        else
        {
          Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Download error: $errorResponse"
        }

        if (Test-Path $filePath)
        {
           Remove-Item $filePath
        }
        return
    }

    if ($global:interactiveMode -eq 1)
    {
      Write-Host "Backup download complete!"
    }
  }
  catch [System.Management.Automation.CommandNotFoundException]
  {
    return download-Backup-without-curl $backupResource $authValue $hostname
  }
  catch [System.Exception]
  {
    Write-Host "Not able to download backup"
    Write-Host $error[0].Exception
    return
  }

  return $filePath
}

##### Function to download the Backup file without using the curl command #####
function download-Backup-without-curl ([PSCustomObject]$backupResource,[string]$authValue,[string]$hostname)
{
  <#
        .DESCRIPTION
            Downloads the backup file from the appliance to the local system (without using curl)

        .PARAMETER backupResource
            Backup resource containing Uri for downloading

        .PARAMETER authValue
            The authorized sessionID

        .PARAMETER hostname
            The IP address of the appliance

        .INPUTS
            None, does not accept piping

        .OUTPUTS
            The absolute path of the download file

        .EXAMPLE
            download-backup-without-curl $backupResource $sessionID https://11.111.11.111
    #>

  # appends Uri ( obtained from previous function) to IP address
  $downloadUri = $hostname + $backupResource.downloadUri
  $downloadTimeout = 43200000 # 12 hours
  $bufferSize = 65536 # bytes

  # creates a new webrequest with appropriate headers
  [net.httpsWebRequest]$downloadRequest = [net.webRequest]::create($downloadUri)
  $downloadRequest.method = "GET"
  $downloadRequest.AllowAutoRedirect = $TRUE
  $downloadRequest.Timeout = $downloadTimeout
  $downloadRequest.ReadWriteTimeout = $downloadTimeout
  $downloadRequest.Headers.Add("auth", $authValue)
  $downloadRequest.Headers.Add("X-API-Version", $global:scriptApiVersion)
  # accept either octet-stream or json to allow the response body to contain either the backup or an exception
  $downloadRequest.accept = "application/octet-stream;q=0.8,application/json"

  # creates a variable that stores the path to the file location. Note: users may change this to other file paths.
  $fileDir = [environment]::GetFolderPath("Personal")

  try
  {
    # connects to the Appliance, creates a new file with the content of the response
    [net.httpsWebResponse]$response = $downloadRequest.getResponse()
    $responseStream = $response.getResponseStream()
    $responseStream.ReadTimeout = $downloadTimeout

    #saves file as the name given by the backup ID
    $filePath = $fileDir + "\" + $backupResource.id + ".bkp"
    $sr = New-Object System.IO.FileStream ($filePath,[System.IO.FileMode]::create)
    $responseStream.CopyTo($sr,$bufferSize)
    $response.close()
    $sr.close()
    if ($global:interactiveMode -eq 1)
    {
      Write-Host "Backup download complete!"
    }
  }
  catch [Net.WebException]
  {
    $errorMessage = $error[0].Exception.message

    #Try to get more information about the error
    try {
      $errorResponse = $error[0].Exception.InnerException.Response.getResponseStream()
      $sr = New-Object IO.StreamReader ($errorResponse)
      $rawErrorStream = $sr.readtoend()
      $error[0].Exception.InnerException.Response.close()
      $errorObject = $rawErrorStream | convertFrom-Json
      if (($errorObject.message.length -gt 0) -and
          ($errorObject.recommendedActions.length -gt 0))
      {
        $errorMessage = $errorObject.message + " " + $errorObject.recommendedActions
      }
    }
    catch [System.Exception]
    {
      #Use exception message
    }

    if ($global:interactiveMode -eq 1)
    {
      Write-Host $errorMessage
    }
    else
    {
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message $errorMessage
    }
    return
  }

  return $filePath
}

function setup-request ([string]$uri,[string]$method,[string]$accept,[string]$contentType = "",[string]$authValue = "",[object]$body = $null,[bool]$isSilent=$false, [bool]$returnLocation=$false)
{
  try
  {
    [net.httpsWebRequest]$request = [net.webRequest]::create($uri)
    $request.method = $method
    $request.accept = $accept

    $request.Headers.Add("Accept-Language: en-US")
    if ($contentType -ne "")
    {
      $request.ContentType = $contentType
    }
    if ($authValue -ne "")
    {
      $request.Headers.Item("auth") = $authValue
    }
    $request.Headers.Item("X-API-Version") = $global:scriptApiVersion
    if ($body -ne $null)
    {
      $requestBodyStream = New-Object IO.StreamWriter $request.getRequestStream()
      $requestBodyStream.WriteLine($body)
      $requestBodyStream.flush()
      $requestBodyStream.close()
    }

    # attempt to connect to the Appliance and get a response
    [net.httpsWebResponse]$response = $request.getResponse()

    if ($returnLocation)
    {
        $taskUri = $response.getResponseHeader("Location")

        $response.close()
        return $taskUri
    }
    else
    {
        # response stored in a stream
        $responseStream = $response.getResponseStream()
        $sr = New-Object IO.StreamReader ($responseStream)

        #the stream, which contains a json object, is read into the storage variable
        $rawResponseContent = $sr.readtoend()
        $response.close()
        return $rawResponseContent
    }
  }
  catch [Net.WebException]
  {
    $errorMessage = $error[0].Exception.message

    #Try to get more information about the error
    try {
      $errorResponse = $error[0].Exception.InnerException.Response.getResponseStream()
      $sr = New-Object IO.StreamReader ($errorResponse)
      $rawErrorStream = $sr.readtoend()
      $error[0].Exception.InnerException.Response.close()
      $errorObject = $rawErrorStream | convertFrom-Json
      if (($errorObject.message.length -gt 0) -and
          ($errorObject.recommendedActions.length -gt 0))
      {
        $errorMessage = $errorObject.message + " " + $errorObject.recommendedActions
      }
    }
    catch [System.Exception]
    {
      #Use exception message
    }

    if ($isSilent) {
        throw $errorMessage
    }
    elseif ($global:interactiveMode -eq 1)
    {
      Write-Host $errorMessage
    }
    else
    {
      Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message $errorMessage
    }

    #No need to rethrow since already recorded error
    return
  }
}


##### Start of function calls #####

#gets the credentials from user, either manual entry or from file
$savedLoginJson = queryfor-credentials $args[0]
if ($savedLoginJson -eq $null)
{
  #if an error occurs, it has already been logged in the queryfor-credentials function
  return
}

#extracts needed information from the credential json
try
{
  $savedLoginJson = "[" + $savedLoginJson + "]"
  $savedloginVals = $savedLoginJson | convertFrom-Json
  $SecStrLoginname = $savedloginVals.userName | ConvertTo-SecureString -ErrorAction stop
  $loginname =
  [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecStrLoginName))
  $hostname = $savedloginVals.hostname
  $SecStrPassword = $savedloginVals.password | ConvertTo-SecureString -ErrorAction stop
  $password =
  [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecStrpassword))
  $adname = $savedloginVals.authLoginDomain
}
catch [System.Exception]
{
  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Failed to get credentials: " + $error[0].Exception.Message
  }
  else
  {
    Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Failed to get credentials: " + $error[0].Exception.Message
  }
}

#determines the active Api version
$global:scriptApiVersion = getApiVersion $global:scriptApiVersion $hostname
if ($global:scriptApiVersion -eq $null)
{
  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Could not determine appliance Api version"
  }

  Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Could not determine appliance Api version"
  return
}

#sends the login request to the machine, gets an authorized session ID if successful
$authValue = login-appliance $loginname $password $hostname $adname
if ($authValue -eq $null)
{
  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Failed to receive login session ID."
  }
  Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Failed to receive login session ID."
  return
}

#sends the request to start the backup process, returns the taskResource object
$taskResource = backup-Appliance $authValue.sessionID $hostname
if ($taskResource -eq $null)
{
  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Could not initialize backup"
  }

  Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Could not initialize backup"
  return
}

#loops to keep checking how far the backup has gone
$taskResource = waitFor-completion $taskResource $authValue.sessionID $hostname
if ($taskResource -eq $null)
{
  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Could not fetch backup status"
  }

  Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Could not fetch backup status"
  return
}

#gets the backup resource
$backupResource = get-backupResource $taskResource $authValue.sessionID $hostname
if ($backupResource -eq $null)
{
  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Could not get the Backup Resource"
  }
  Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Could not get the Backup Resource"
  return
}

#downloads the backup file to the local drive
$filePath = download-Backup $backupResource $authValue.sessionID $hostname
if ($filePath -eq $null)
{
  if ($global:interactiveMode -eq 1)
  {
    Write-Host "Could not download the backup"
  }
  Write-EventLog -EventId 100 -LogName Application -Source backup.ps1 -Message "Could not download the backup"
  return
}

if ($global:interactiveMode -eq 1)
{
  Write-Host "Backup can be found at $filePath"
  Write-Host "If you wish to automate this script in the future and re-use login settings currently entered,"
  Write-Host "then provide the file path to the saved credentials file when running the script."
  Write-Host "ie: " $MyInvocation.MyCommand.Definition " filepath"
}
else
{
  Write-Host "Backup completed successfully."
  Write-Host "The backup can be found at $filePath."
}
Write-EventLog -EventId 0 -LogName Application -Source backup.ps1 -Message "script completed successfully"