Using PowerShell to Pause CenturyLink Cloud Virtual Servers

Summary

The following script will pause all servers in the specified group for the account alias ‘SXSW’. You can discover your Group ID by querying an existing server (see example towards the end of this blog post). Pausing your Virtual Servers in the CenturyLink Cloud merely saves their state (memory and disk) and can easily be resumed by Starting them (unlike shutting them down or turning them off which does not save the state of their memory). Pausing servers is similar to ‘sleep’ mode supported by most computers. This can be useful for reducing your CLC costs when virtual servers are not being used. This script reads the necessary BearerToken credentials from a text file (bearerToken.txt) that is created by the following script (keep in mind that this token has a life of only 2 weeks and will then need to be regenerated):

Note: All of these script samples are using PowerShell v4 (required)

Example: Save-BearerToken.ps1
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
}

$psCreds = Get-Credential -Message "Use CLC Web Portal Credentials"
$creds = @{
 username = $psCreds.username
 password = $psCreds.GetNetworkCredential().Password
}
$creds = $creds | ConvertTo-Json

Write-Host -ForegroundColor "green" "Getting authentication 'bearerToken'..."
$logonUri = "https://api.ctl.io/v2/authentication/login"
$logonResponse = Invoke-RestMethod -Method Post -Uri $logonUri -Body $creds -ContentType "application/json"

Write-Host -ForegroundColor "green" "Account Summary:"
$logonResponse

Write-Host -ForegroundColor "green" "Bearer Token (2 week TTL):"
$logonResponse.bearerToken
$logonResponse.bearerToken | Set-Content "bearerToken.txt"
Example: Pause-Servers.ps1
$bearerTokenInput = Get-Content ".\bearerToken.txt"
$groupId = "7c3a1aee32241223a1aee32241979a28"
$accountAlias = "SXSW"
$bearerToken = " Bearer " + $bearerTokenInput
$header = @{}
$header["Authorization"] = $bearerToken

# Discover all servers in the specified group
$requestUri = "https://api.ctl.io/v2/groups/$accountAlias/$groupId"
$groupOutput = Invoke-RestMethod -Method GET -Headers $header -Uri $requestUri -ContentType "application/json" -SessionVariable "theSession"

$serverArray = @()
$serverPause = @()
foreach ($item in $groupOutput.links) {
  if ($item.rel -eq "server") {
    $serverName = $item.href.split("/")[($item.href.split("/").count - 1)]
    $serverArray = $serverArray + ($serverName)
  }
}

# Gather server status
$serverList = ""
foreach ($serverName in $serverArray) {
  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" -SessionVariable "theSession"
  $powerState = $serverProperties.details.powerState
  Write-Host -ForegroundColor Green "  PowerState: $powerState"
  if ($serverProperties.details.powerState -eq "started") {
    Write-Host -ForegroundColor Yellow "  Pausing $serverName"
    $serverPause = $serverPause + ($serverName)
    $serverList = "$serverList, $serverName"
  }
}

$serverList = $serverList.TrimStart(", ")
if ($serverPause.Count -eq 1) {
  $servers = "[ ""$serverPause"" ]"
} else {
  $servers = $serverPause | ConvertTo-Json
}

Write-Host -ForegroundColor Green "Sending Server pause request for $serverList"
Write-Host "servers: $servers"
Write-Host "serverPause: $serverPause"
$requestUri = "https://api.ctl.io/v2/operations/$accountAlias/servers/pause"
$pauseRequest = Invoke-RestMethod -Method POST -Headers $header -Uri $requestUri -Body $servers -ContentType "application/json" -SessionVariable "theSession"

Write-Host "Request Output: $pauseRequest"

Getting Your Group ID From Your Server

The following sample script will retrieve your Group ID from an existing server. You will need to incorporate your specific server name in this script (we use ‘CA3TESTTEST01’).

Example: List-ServerGroup.ps1
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
}
$dataCenter = "CA3"
$serverName = "CA3TESTTEST01"
$psCreds = Get-Credential -Message "Use CLC Web Portal Credentials"
$creds = @{
 username = $psCreds.username
 password = $psCreds.GetNetworkCredential().Password
}
$creds = $creds | ConvertTo-Json

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"

Write-Host -ForegroundColor "green" "Getting authentication 'bearerToken'..."
$logonUri = "https://api.ctl.io/v2/authentication/login"
$logonResponse = Invoke-RestMethod -Method Post -Headers $headers -Uri $logonUri -Body $creds -ContentType "application/json" -SessionVariable "theSession"

$bearerToken = " Bearer " + $logonResponse.bearerToken
$accountAlias = $logonResponse.accountAlias

$headers.Add("Authorization",$bearerToken)

Write-Host -ForegroundColor "green" "Getting datacenter capabilities for $dataCenter..."
$RequestUri = "https://api.ctl.io/v2/datacenters/$accountAlias/CA3/deploymentCapabilities"
$deployCapabilities = Invoke-RestMethod -Method GET -Headers $headers -Uri $RequestUri -ContentType "application/json" -SessionVariable "theSession"

Write-Host -ForegroundColor "green" "This datacenter supports the following platform templates:"
$deployCapabilities.templates
Output:

CLC Output 3

Extra: Task Scheduler Rule

The following is a rule that can be imported into Task Scheduler to run the above script on a regular basis.

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2015-06-19T10:56:50.2629119</Date>
<Author>CA3SXSWTEST01\Administrator</Author>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<StartBoundary>2015-06-19T19:00:00</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>CA3SXSWTEST01\Administrator</UserId>
<LogonType>Password</LogonType>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>.\Pause-Servers.ps1</Arguments>
<WorkingDirectory>C:\Users\Administrator\Documents\Scripts</WorkingDirectory>
</Exec>
</Actions>
</Task>

Enjoy!

PowerShell and the CenturyLink Cloud API

Summary

CenturyLink has a powerful REST API for automating the provisioning and management of virtual servers in their environment. And since PowerShell v4 now has cmdlets that support interacting with REST APIs, it is an easy way to work with entities in the CenturyLink Cloud. The following post will cover the steps required (and sample code) to use PowerShell to provision a CenturyLink Cloud virtual server. Plain old Internet connectivity is all that’s required between PowerShell and CenturyLink, since their API is on the open Internet. The complete reference to CenturyLink’s Cloud REST API can be found here: https://www.ctl.io/api-docs/v2/

Steps:

  1. Get a free account for CenturyLink Cloud here: https://www.centurylinkcloud.com/
  2. Get your Account Alias
  3. Get your Group ID (Default Group)
  4. Query for the capabilities of your desired datacenter (you can find a list of datacenters from the CenturyLink Cloud Control Portal here: https://control.ctl.io/)
  5. Create your server!

Note: All of these script samples are using PowerShell v4 (required)

Get Your Account Alias

Once you get your CenturyLink account, you set your account alias to an abbreviation between 2 and 4 characters. You can also get your AccountAlias from the REST API logon response. You also need to get the BearerToken from the logon response to use as your authentication mechanism with communicating with the CenturyLink REST API (a bearer token has a life of 2 weeks, so don’t hard code it into your scripts).

Example: LogonResponse.ps1
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
}
 
$psCreds = Get-Credential -Message "Use CLC Web Portal Credentials"
$creds = @{
 username = $psCreds.username
 password = $psCreds.GetNetworkCredential().Password
}
$creds = $creds | ConvertTo-Json
 
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
 
Write-Host -ForegroundColor "green" "Getting authentication 'bearerToken'..."
$logonUrl = "https://api.ctl.io/v2/authentication/login"
$logonResponse = Invoke-RestMethod -Method Post -Headers $headers -Uri $logonUrl -Body $creds -ContentType "application/json" -SessionVariable "theSession"
 
Write-Host -ForegroundColor "green" "Account Summary:"
$logonResponse
 
Write-Host -ForegroundColor "green" "Bearer Token (2 week TTL):"
$logonResponse.bearerToken
Output:
PS-CLC Output 1

Find the Default Group for Your Server

This example is going to use your account’s datacenter “Default Group” as the destination for your new server, specifically for the datacenter ‘CA3’ (in Calgary, Canada). Any group can be used, but your account will have a “Default Group” for each datacenter your account can access. This group ID is required in order to identify the destination for your new server.

Example:  GetDefaultDatacenter.ps1
$psCreds = Get-Credential -Message "Use CLC Web Portal Credentials"
$creds = @{
 username = $psCreds.username
 password = $psCreds.GetNetworkCredential().Password
}
$creds = $creds | ConvertTo-Json

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"

# Write-Host -ForegroundColor "green" "Getting authentication 'bearerToken'..."
$logonUrl = "https://api.ctl.io/v2/authentication/login"
$logonResponse = Invoke-RestMethod -Method Post -Headers $headers -Uri $logonUrl -Body $creds -ContentType "application/json" -SessionVariable "theSession"

$dataCenter = "CA3"
$bearerToken = " Bearer " + $logonResponse.bearerToken
$accountAlias = $logonResponse.accountAlias

$headers.Add("Authorization",$bearerToken)

Write-Host -ForegroundColor Green "Getting datacenter groups for $dataCenter..."
$requestURL = "https://api.ctl.io/v2/datacenters/$accountAlias/CA3?groupLinks=true"
$datacenterLinks = Invoke-RestMethod -Method GET -Headers $headers -Uri $requestURL -ContentType "application/json" -SessionVariable "theSession"
Write-Host "Available links:"
Write-Host $datacenterLinks.links

foreach ($link in $datacenterLinks.links) {
  if ($link.rel -eq "group") {
    Write-Host -ForegroundColor Green "Getting default datacenter group for group $($link.id)..."
    $requestURL = "https://api.ctl.io/v2/groups/$accountAlias/$($link.id)"
    $datacenterGroups = Invoke-RestMethod -Method GET -Headers $headers -Uri $requestURL -ContentType "application/json" -SessionVariable "theSession"
    foreach ($group in $datacenterGroups.Groups) {
      if ($group.type -eq "default") {
        Write-Host "Found group:"
        Write-Host $group
        Write-Host "`n"
        Write-Host -ForegroundColor Green "Default Group for $($dataCenter): $($group.id)"
      }
    }
  }
}
Output:

PS-CLC Output 2

Query for the Capabilities of Your Desired Datacenter

You can also get a list of available datacenters through the API with the following URI: https://api.ctl.io/v2/datacenters/yourAccountAlias. For this example we’re going to query CA3 (Canada – Toronto). As of this writing, the list includes: CA1, CA2, CA3, DE1, GB1, GB3, IL1, NY1, SG1, UC1, UT1, VA1, WA1. You will want to select the desired platform for your virtual server from the datacenter’s available templates as well as the ID of your destination network. If you don’t have any networks yet, the easiest way to create on is to first deploy a virtual server through the Control Portal web site. This can also be created through the API, but for the sake of brevity, we’ll use a network that already exists.

Partial Example (using the results from logonResponse.ps1): Query the Datacenter
$bearerToken = " Bearer " + $logonResponse.bearerToken
$accountAlias = $logonResponse.accountAlias
 
$headers.Add("Authorization",$bearerToken)
 
Write-Host -ForegroundColor "green" "Getting datacenter capabilities for $dataCenter..."
$RequestURL = "https://api.ctl.io/v2/datacenters/$accountAlias/CA3/deploymentCapabilities"
$deployCapabilities = Invoke-RestMethod -Method GET -Headers $headers -Uri $RequestURL -ContentType "application/json" -SessionVariable "theSession"
 
Write-Host -ForegroundColor "green" "This datacenter supports the following platform templates:"
$deployCapabilities.templates
 
Write-Host -ForegroundColor "green" "This datacenter supports the following networks:"
$deployCapabilities.deployableNetworks
Output

CLC Output 2

Finally, Create Your Server

After gathering the destination group (default group for this example), provisioning template (sourceServerId) and the destination network, you can use the following script to create your virtual server in the CenturyLink Cloud.

Note: There is a character limitation for the ‘name’ field (6 I believe but this example uses a 4 character name).

Example: CreateServer.ps1
$server = @{
  name = "test"
  description = "My test server"
  groupId = "7c39b8a1aee32241979a282241979a28"
  sourceServerId = "WIN2012R2DTC-64"
  isManagedOS = "false"
  networkId = "7J3aCCm0GGF5ev4cmh6U58279b6f056a"
  password = "SomePassword!"
  cpu = 2
  memoryGB = 4
  type = "standard"
  storageType = "standard"
}
$server = $server | ConvertTo-Json
 
$psCreds = Get-Credential -Message "Use CLC Web Portal Credentials"
$creds = @{
 username = $psCreds.username
 password = $psCreds.GetNetworkCredential().Password
}
$creds = $creds | ConvertTo-Json
 
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
 
Write-Host -ForegroundColor "green" "Getting authentication 'bearerToken'..."
$logonUrl = "https://api.ctl.io/v2/authentication/login"
$logonResponse = Invoke-RestMethod -Method Post -Headers $headers -Uri $logonUrl -Body $creds -ContentType "application/json" -SessionVariable "theSession"
 
$bearerToken = " Bearer " + $logonResponse.bearerToken
$accountAlias = $logonResponse.accountAlias
 
$headers.Add("Authorization",$bearerToken)
 
Write-Host -ForegroundColor "green" "Sending Server build request..."
$requestURL = "https://api.ctl.io/v2/servers/$accountAlias"
$buildRequest = Invoke-RestMethod -Method POST -Headers $headers -Uri $requestURL -Body $server -ContentType "application/json" -SessionVariable "theSession"
 
Write-Host -ForegroundColor "green" "Request Output:"
Write-Host -ForegroundColor "green" "Server Name:"
$buildRequest.server
Write-Host -ForegroundColor "green" "Is Queued?"
$buildRequest.isQueued
Write-Host -ForegroundColor "green" "Queue Status Links:"
$buildRequest.links
Output

PS-CLC Output 4

Enjoy!