Use PowerShell to Refresh CenturyLink Cloud VM Snapshots

Summary: VMs created in the CenturyLink Cloud, can have a snapshot (only 1 per VM), and that snapshot has a maximum life of 10 days. Therefore if it’s necessary to maintain a perpetual snapshot (like for test VMs that have a baseline configuration), VMs need to have their snapshots routinely refreshed. The following PowerShell script leverages the powerful CenturyLink Cloud REST v2 API to automate this process. This script relies on the presence of a ‘bearer token’ for authentication into the CLC API. Details on how to create this ‘bearer token’ can be found in this blog post (as well as details on how to discover your Group ID). The complete reference to CenturyLink’s Cloud REST API can be found here: https://www.ctl.io/api-docs/v2/

This script basically leverages CLC APIs to restore, delete, and create a VM snapshot. After each API is called, the script will wait until the task is complete, by querying the status of the task requested.

Note: This script sample requires PowerShell v4+

Refresh-Snapshot.ps1

#
# Name:: Refresh-Snapshot.ps1
# Version:: 0.1.2 (3/5/2016)
# Script Description:: Refreshes a server's snapshot by restoring the snapshot, deleting it, then creating a new one.
#
# API Documentation:: https://www.ctl.io/api-docs/v2/
#
# Author(s):: Otto Helweg
#

param($s)

# Display help
if (($Args -match "-\?|--\?|-help|--help|/\?|/help") -or !($s)) {
  Write-Host "Usage: Refresh-Snapshot.ps1"
  Write-Host "     -s [server name]       (Required) Server's name according to Centurylink Cloud"
  Write-Host "     -r                     Revert VM to existing snapshot before refreshing"
  Write-Host ""
  Write-Host "EXAMPLES:"
  Write-Host "     Refresh-Snapshot.ps1 -s CA3TESTTEST01 -r"
  Write-Host ""
  exit
}

$goodStatus = @("notStarted","executing","succeeded","resumed")

# PowerShell v4 is required for the REST cmdlets
if (!($psversiontable.PSVersion.Major -ge 4)) {
  Write-Host -ForegroundColor "red" "Requires PowerShell v.4 or greater. Please install the Windows Management Framework 4 or above."
  exit
}

# Check to make sure the Bearer Token is less than 2 weeks old
if (!(Test-Path .\bearerToken.txt)) {
  Write-Host -ForegroundColor Red "Error: Bearer Token file is missing. Run Save-BearerToken.ps1"
  exit
} else {
  $fileInfo = dir .\bearerToken.txt
  if ($fileInfo.LastWriteTime -lt (Get-Date).AddDays(-11)) {
    Write-Host -ForegroundColor Yellow "Warning: Bearer Token file is almost out of date. Run Save-BearerToken.ps1"
  }
  if ($fileInfo.LastWriteTime -lt (Get-Date).AddDays(-13)) {
    Write-Host -ForegroundColor Red "Error: Bearer Token file is out of date. Run Save-BearerToken.ps1"
    exit
  }
}

$bearerTokenInput = Get-Content ".\bearerToken.txt"
$accountAlias = "SXSW"
$bearerToken = " Bearer " + $bearerTokenInput
$header = @{}
$header["Authorization"] = $bearerToken
$serverName = $s

Write-Host -ForegroundColor Green "Getting server properties for $serverName..."
$requestUri = "https://api.ctl.io/v2/servers/$accountAlias/$serverName"
$serverProperties = Invoke-RestMethod -Method GET -Headers $header -Uri $requestUri -ContentType "application/json"

if (!$serverProperties) {
  Write-Host -ForegroundColor Red "Error: $serverName does not appear to exist!"
  exit
}

if (($Args -contains "-r") -and $serverProperties.details.snapshots) {
  Write-Host -ForegroundColor Green "Restoring snapshot for $serverName..."
  $startTime = Get-Date
  $requestUri = "https://api.ctl.io$($serverProperties.details.snapshots.links.href[0])/restore"
  $restoreResult = Invoke-RestMethod -Method POST -Headers $header -Uri $requestUri -ContentType "application/json"
  $statusUri = "https://api.ctl.io/v2/operations/$accountAlias/status/$($restoreResult.id)"
  $continue = $true
  Write-Host "Waiting for snapshot restore to complete..."
  while ($continue) {
    $statusResult = Invoke-RestMethod -Method GET -Headers $header -Uri $statusUri -ContentType "application/json"
    [int]$elapsedSeconds = ($(Get-Date) - $startTime).TotalSeconds
    Write-Host "   [$elapsedSeconds seconds] $($statusResult.status)"
    if ($statusResult.status -eq "succeeded") {
      $continue = $false
    }
    if ($statusResult.status -notin $goodStatus) {
      Write-Host -ForegroundColor Red "Error: Restoring snapshot for $serverName failed!"
      exit
    }
    Sleep 2
  }
}

if ($serverProperties.details.snapshots) {
  Write-Host -ForegroundColor Green "Deleting snapshot for $serverName..."
  $startTime = Get-Date
  $requestUri = "https://api.ctl.io$($serverProperties.details.snapshots.links.href[0])"
  $restoreResult = Invoke-RestMethod -Method DELETE -Headers $header -Uri $requestUri -ContentType "application/json"
  $statusUri = "https://api.ctl.io/v2/operations/$accountAlias/status/$($restoreResult.id)"
  $continue = $true
  Write-Host "Waiting for snapshot delete to complete..."
  while ($continue) {
    $statusResult = Invoke-RestMethod -Method GET -Headers $header -Uri $statusUri -ContentType "application/json"
    [int]$elapsedSeconds = ($(Get-Date) - $startTime).TotalSeconds
    Write-Host "   [$elapsedSeconds seconds] $($statusResult.status)"
    if ($statusResult.status -eq "succeeded") {
      $continue = $false
    }
    if ($statusResult.status -notin $goodStatus) {
      Write-Host -ForegroundColor Red "Error: Deleting snapshot for $serverName failed!"
      exit
    }
    Sleep 2
  }
}

Write-Host -ForegroundColor Green "Creating snapshot for $serverName..."
$startTime = Get-Date
$createBody = "{
  ""snapshotExpirationDays"":""10"",
  ""serverIds"":[
      ""$serverName""
    ]
}"

$requestUri = "https://api.ctl.io/v2/operations/$accountAlias/servers/createSnapshot"
$restoreResult = Invoke-RestMethod -Method POST -Headers $header -Body $createBody -Uri $requestUri -ContentType "application/json"
$statusUri = "https://api.ctl.io/v2/operations/$accountAlias/status/$($restoreResult.links.id)"

$continue = $true
Write-Host "Waiting for snapshot creation to complete..."
while ($continue) {
  $statusResult = Invoke-RestMethod -Method GET -Headers $header -Uri $statusUri -ContentType "application/json"
  [int]$elapsedSeconds = ($(Get-Date) - $startTime).TotalSeconds
  Write-Host "   [$elapsedSeconds seconds] $($statusResult.status)"
  if ($statusResult.status -eq "succeeded") {
    $continue = $false
  }
  if ($statusResult.status -notin $goodStatus) {
    Write-Host -ForegroundColor Red "Error: Creating snapshot for $serverName failed!"
    exit
  }
  Sleep 2
}

 

Enjoy!