Octopus.Script exported 2016-07-11 by roberto-mardeni belongs to ‘Akamai’ category.
Allows to purge content using the Content Control Utility (CCU) v2 REST API.
Parameters
When steps based on the template are included in a project’s deployment process, the parameters below can be set.
Client Token
AkamaiClientToken
Authentication token used in client authentication. Available in Luna Portal.
Client Access Token
AkamaiClientAccessToken
Authentication token used in client authentication. Available in Luna Portal.
Secret
AkamaiSecret
Authentication password used in client authentication. Available in Luna Portal.
Queue
AkamaiQueue = default
Purge requests queue
Objects
AkamaiObjects
A comma separated list of objects to purge, either URLs or CPCODEs
Type
AkamaiType = arl
The type associated with the items in the Objects parameter
Action
AkamaiAction = invalidate
The action to execute on the purge operation
Domain
AkamaiDomain = production
The Akamai domain to perform the purge operation on
Proxy User
ProxyUser
Optional, a user name for the proxy if required in the network
Proxy Password
ProxyPassword
Optional, the password for the account to use if a Proxy User was specified
Wait
WaitForPurgeToComplete = false
Indicates if the step should wait for the purge operation to complete
Check Limit
CheckLimit = 2
Maximum number of times to check for the purge operation to complete if set to Wait
Script body
Steps based on this template will execute the following PowerShell script.
$clientToken = $OctopusParameters['AkamaiClientToken']
$clientAccessToken = $OctopusParameters['AkamaiClientAccessToken']
$clientSecret = $OctopusParameters['AkamaiSecret']
$queueName = $OctopusParameters['AkamaiQueue']
$objects = $OctopusParameters['AkamaiObjects'] -split ","
$type = $OctopusParameters['AkamaiType']
$action = $OctopusParameters['AkamaiAction']
$domain = $OctopusParameters['AkamaiDomain']
$proxyUser = $OctopusParameters['ProxyUser']
$proxyPassword = $OctopusParameters['ProxyPassword']
$wait = [bool]::Parse($OctopusParameters['WaitForPurgeToComplete'])
$maxChecks = [int]::Parse($OctopusParameters['CheckLimit'])
if ($proxyUser) {
$securePassword = ConvertTo-SecureString $proxyPassword -AsPlainText -Force
$proxyCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $proxyUser,$securePassword
(New-Object System.Net.WebClient).Proxy.Credentials=$proxyCredential
}
# Copied from https://github.com/akamai-open/AkamaiOPEN-powershell/blob/master/Invoke-AkamaiOPEN.ps1
function Invoke-AkamaiOpenRequest {
param(
[Parameter(Mandatory=$true)][string]$Method,
[Parameter(Mandatory=$true)][string]$ClientToken,
[Parameter(Mandatory=$true)][string]$ClientAccessToken,
[Parameter(Mandatory=$true)][string]$ClientSecret,
[Parameter(Mandatory=$true)][string]$ReqURL,
[Parameter(Mandatory=$false)][string]$Body)
#Function to generate HMAC SHA256 Base64
Function Crypto ($secret, $message)
{
[byte[]] $keyByte = [System.Text.Encoding]::ASCII.GetBytes($secret)
[byte[]] $messageBytes = [System.Text.Encoding]::ASCII.GetBytes($message)
$hmac = new-object System.Security.Cryptography.HMACSHA256((,$keyByte))
[byte[]] $hashmessage = $hmac.ComputeHash($messageBytes)
$Crypt = [System.Convert]::ToBase64String($hashmessage)
return $Crypt
}
#ReqURL Verification
If (($ReqURL -as [System.URI]).AbsoluteURI -eq $null -or $ReqURL -notmatch "akamai.com")
{
throw "Error: Ivalid Request URI"
}
#Sanitize Method param
$Method = $Method.ToUpper()
#Split $ReqURL for inclusion in SignatureData
$ReqArray = $ReqURL -split "(.*\/{2})(.*?)(\/)(.*)"
#Timestamp for request signing
$TimeStamp = [DateTime]::UtcNow.ToString("yyyyMMddTHH:mm:sszz00")
#GUID for request signing
$Nonce = [GUID]::NewGuid()
#Build data string for signature generation
$SignatureData = $Method + "`thttps`t"
$SignatureData += $ReqArray[2] + "`t" + $ReqArray[3] + $ReqArray[4]
if (($Body -ne $null) -and ($Method -ceq "POST"))
{
$Body_SHA256 = [System.Security.Cryptography.SHA256]::Create()
$Post_Hash = [System.Convert]::ToBase64String($Body_SHA256.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($Body.ToString())))
$SignatureData += "`t`t" + $Post_Hash + "`t"
}
else
{
$SignatureData += "`t`t`t"
}
$SignatureData += "EG1-HMAC-SHA256 "
$SignatureData += "client_token=" + $ClientToken + ";"
$SignatureData += "access_token=" + $ClientAccessToken + ";"
$SignatureData += "timestamp=" + $TimeStamp + ";"
$SignatureData += "nonce=" + $Nonce + ";"
#Generate SigningKey
$SigningKey = Crypto -secret $ClientSecret -message $TimeStamp
#Generate Auth Signature
$Signature = Crypto -secret $SigningKey -message $SignatureData
#Create AuthHeader
$AuthorizationHeader = "EG1-HMAC-SHA256 "
$AuthorizationHeader += "client_token=" + $ClientToken + ";"
$AuthorizationHeader += "access_token=" + $ClientAccessToken + ";"
$AuthorizationHeader += "timestamp=" + $TimeStamp + ";"
$AuthorizationHeader += "nonce=" + $Nonce + ";"
$AuthorizationHeader += "signature=" + $Signature
#Create IDictionary to hold request headers
$Headers = @{}
#Add Auth header
$Headers.Add('Authorization',$AuthorizationHeader)
#Add additional headers if POSTing or PUTing
If (($Method -ceq "POST") -or ($Method -ceq "PUT"))
{
$Body_Size = [System.Text.Encoding]::UTF8.GetByteCount($Body)
$Headers.Add('max-body',$Body_Size.ToString())
# turn off the "Expect: 100 Continue" header
# as it's not supported on the Akamai side.
[System.Net.ServicePointManager]::Expect100Continue = $false
}
#Check for valid Methods and required switches
If (($Method -ceq "POST") -and ($Body -ne $null))
{
Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -Body $Body -ContentType 'application/json'
}
elseif (($Method -ceq "PUT") -and ($Body -ne $null))
{
#Invoke API call with PUT and return
Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -Body $Body -ContentType 'application/json'
}
elseif (($Method -ceq "GET") -or ($Method -ceq "DELETE"))
{
#Invoke API call with GET or DELETE and return
Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers
}
else
{
throw "Error: Invalid -Method specified or missing required parameter"
}
}
function Perform-AkamaiRequest {
param (
[string]$request,
[string]$method="Get",
[int]$expectedStatusCode=200,
$body)
$baseUrl = "http://private-anon-3934daf8d-akamaiopen2purgeccuv2production.apiary-mock.com"
# $baseUrl = "https://api.ccu.akamai.com"
$uri = "{0}{1}" -f $baseUrl,$request
if ($uri -match "mock"){
$requestHeaders = @{'Cache-Control'='no-cache,proxy-revalidate'}
$response = Invoke-RestMethod -Uri $uri -Method $method -DisableKeepAlive -Headers $requestHeaders -Body $body
} else {
$json = ConvertTo-Json $body -Compress
$response = Invoke-AkamaiOpenRequest -Method $method -ClientToken $clientToken -ClientAccessToken $clientAccessToken -ClientSecret $clientSecret -ReqURL $uri -Body $json
}
if ($response.httpStatus -ne $expectedStatusCode){
Write-Error "Request not processed correctly: $($response.detail)"
} elseif ($response.detail) {
Write-Verbose $response.detail
}
Write-Verbose $response
$response
}
function Get-QueueSize {
param ([string]$queueName)
$queueSize = Perform-AkamaiRequest "/ccu/v2/queues/$queueName"
$queueSize
}
function Request-Purge {
param ($objects,[string]$type="arl",[string]$action="remove",[string]$domain="production")
$body = @{
objects = $objects
action = $action
type = $type
domain = $domain
}
Perform-AkamaiRequest "/ccu/v2/queues/$queueName" "Post" 201 $body
}
function Get-PurgeStatus {
param ([string]$purgeId)
$status = Perform-AkamaiRequest "/ccu/v2/purges/$purgeId"
Write-Host "Purge status: $($status.purgeStatus)"
$status
}
$queueSize = Get-QueueSize $queueName
Write-Output "$($queueName) queue size is $($queueSize.queueLength)"
$purge = Request-Purge $objects $type $action $domain
Write-Output "Purge request created"
Write-Output "PurgeId: $($purge.purgeId)"
Write-Output "SupportId: $($purge.supportId)"
if ($wait) {
$check = 1
$purgeStatus = "Unknown"
do {
if ($check -gt 1) {
Write-Output "Waiting $($purge.pingAfterSeconds) seconds before checking purge status again"
Start-Sleep -Seconds $purge.pingAfterSeconds
}
$status = Get-PurgeStatus $purge.purgeId
$purgeStatus = $status.purgeStatus
$check = $check + 1
if ($check -gt $maxChecks) {
Write-Output "Maximum number of checks reached"
}
} while ($status.purgeStatus -ne "Done" -and $check -le $maxChecks)
if ($status.purgeStatus -ne "Done") {
Write-Error "Purge status is not Done"
}
}
Provided under the Apache License version 2.0.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "0bfe41dc-50b0-44a7-abe3-e6ba2a6d317a",
"Name": "Akamai - Content Purge",
"Description": "Allows to purge content using the Content Control Utility (CCU) v2 REST API.",
"Version": 1,
"ExportedAt": "2016-07-11T14:27:34.748+00:00",
"ActionType": "Octopus.Script",
"Author": "roberto-mardeni",
"Parameters": [
{
"Name": "AkamaiClientToken",
"Label": "Client Token",
"HelpText": "Authentication token used in client authentication. Available in Luna Portal.",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Name": "AkamaiClientAccessToken",
"Label": "Client Access Token",
"HelpText": "Authentication token used in client authentication. Available in Luna Portal.",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Name": "AkamaiSecret",
"Label": "Secret",
"HelpText": "Authentication password used in client authentication. Available in Luna Portal.",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
}
},
{
"Name": "AkamaiQueue",
"Label": "Queue",
"HelpText": "Purge requests queue",
"DefaultValue": "default",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "default\nemergency"
}
},
{
"Name": "AkamaiObjects",
"Label": "Objects",
"HelpText": "A comma separated list of objects to purge, either URLs or CPCODEs",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Name": "AkamaiType",
"Label": "Type",
"HelpText": "The type associated with the items in the Objects parameter",
"DefaultValue": "arl",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "arl\ncpcode"
}
},
{
"Name": "AkamaiAction",
"Label": "Action",
"HelpText": "The action to execute on the purge operation",
"DefaultValue": "invalidate",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "invalidate\nremove"
}
},
{
"Name": "AkamaiDomain",
"Label": "Domain",
"HelpText": "The Akamai domain to perform the purge operation on",
"DefaultValue": "production",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "production\nstaging"
}
},
{
"Name": "ProxyUser",
"Label": "Proxy User",
"HelpText": "Optional, a user name for the proxy if required in the network",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
}
},
{
"Name": "ProxyPassword",
"Label": "Proxy Password",
"HelpText": "Optional, the password for the account to use if a Proxy User was specified",
"DefaultValue": null,
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
}
},
{
"Name": "WaitForPurgeToComplete",
"Label": "Wait",
"HelpText": "Indicates if the step should wait for the purge operation to complete",
"DefaultValue": "false",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "false|No\ntrue|Yes"
}
},
{
"Name": "CheckLimit",
"Label": "Check Limit",
"HelpText": "Maximum number of times to check for the purge operation to complete if set to **Wait**",
"DefaultValue": "2",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText",
"Octopus.SelectOptions": "0\n5\n10"
}
}
],
"Properties": {
"Octopus.Action.Script.Syntax": "PowerShell",
"Octopus.Action.Script.ScriptSource": "Inline",
"Octopus.Action.Script.ScriptBody": "$clientToken = $OctopusParameters['AkamaiClientToken']\n$clientAccessToken = $OctopusParameters['AkamaiClientAccessToken']\n$clientSecret = $OctopusParameters['AkamaiSecret']\n$queueName = $OctopusParameters['AkamaiQueue']\n$objects = $OctopusParameters['AkamaiObjects'] -split \",\"\n$type = $OctopusParameters['AkamaiType']\n$action = $OctopusParameters['AkamaiAction']\n$domain = $OctopusParameters['AkamaiDomain']\n$proxyUser = $OctopusParameters['ProxyUser']\n$proxyPassword = $OctopusParameters['ProxyPassword']\n\n$wait = [bool]::Parse($OctopusParameters['WaitForPurgeToComplete'])\n$maxChecks = [int]::Parse($OctopusParameters['CheckLimit'])\n\nif ($proxyUser) {\n $securePassword = ConvertTo-SecureString $proxyPassword -AsPlainText -Force\n $proxyCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $proxyUser,$securePassword\n\n (New-Object System.Net.WebClient).Proxy.Credentials=$proxyCredential\n}\n\n# Copied from https://github.com/akamai-open/AkamaiOPEN-powershell/blob/master/Invoke-AkamaiOPEN.ps1\nfunction Invoke-AkamaiOpenRequest {\n param(\n [Parameter(Mandatory=$true)][string]$Method, \n [Parameter(Mandatory=$true)][string]$ClientToken, \n [Parameter(Mandatory=$true)][string]$ClientAccessToken, \n [Parameter(Mandatory=$true)][string]$ClientSecret, \n [Parameter(Mandatory=$true)][string]$ReqURL, \n [Parameter(Mandatory=$false)][string]$Body)\n\n #Function to generate HMAC SHA256 Base64\n Function Crypto ($secret, $message)\n {\n\t [byte[]] $keyByte = [System.Text.Encoding]::ASCII.GetBytes($secret)\n\t [byte[]] $messageBytes = [System.Text.Encoding]::ASCII.GetBytes($message)\n\t $hmac = new-object System.Security.Cryptography.HMACSHA256((,$keyByte))\n\t [byte[]] $hashmessage = $hmac.ComputeHash($messageBytes)\n\t $Crypt = [System.Convert]::ToBase64String($hashmessage)\n\n\t return $Crypt\n }\n\n #ReqURL Verification\n If (($ReqURL -as [System.URI]).AbsoluteURI -eq $null -or $ReqURL -notmatch \"akamai.com\")\n {\n\t throw \"Error: Ivalid Request URI\"\n }\n\n #Sanitize Method param\n $Method = $Method.ToUpper()\n\n #Split $ReqURL for inclusion in SignatureData\n $ReqArray = $ReqURL -split \"(.*\\/{2})(.*?)(\\/)(.*)\"\n\n #Timestamp for request signing\n $TimeStamp = [DateTime]::UtcNow.ToString(\"yyyyMMddTHH:mm:sszz00\")\n\n #GUID for request signing\n $Nonce = [GUID]::NewGuid()\n\n #Build data string for signature generation\n $SignatureData = $Method + \"`thttps`t\"\n $SignatureData += $ReqArray[2] + \"`t\" + $ReqArray[3] + $ReqArray[4]\n\n if (($Body -ne $null) -and ($Method -ceq \"POST\"))\n {\n\t $Body_SHA256 = [System.Security.Cryptography.SHA256]::Create()\n\t $Post_Hash = [System.Convert]::ToBase64String($Body_SHA256.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($Body.ToString())))\n\n\t $SignatureData += \"`t`t\" + $Post_Hash + \"`t\"\n }\n else\n {\n\t $SignatureData += \"`t`t`t\"\n }\n\n $SignatureData += \"EG1-HMAC-SHA256 \"\n $SignatureData += \"client_token=\" + $ClientToken + \";\"\n $SignatureData += \"access_token=\" + $ClientAccessToken + \";\"\n $SignatureData += \"timestamp=\" + $TimeStamp + \";\"\n $SignatureData += \"nonce=\" + $Nonce + \";\"\n\n #Generate SigningKey\n $SigningKey = Crypto -secret $ClientSecret -message $TimeStamp\n\n #Generate Auth Signature\n $Signature = Crypto -secret $SigningKey -message $SignatureData\n\n #Create AuthHeader\n $AuthorizationHeader = \"EG1-HMAC-SHA256 \"\n $AuthorizationHeader += \"client_token=\" + $ClientToken + \";\"\n $AuthorizationHeader += \"access_token=\" + $ClientAccessToken + \";\"\n $AuthorizationHeader += \"timestamp=\" + $TimeStamp + \";\"\n $AuthorizationHeader += \"nonce=\" + $Nonce + \";\"\n $AuthorizationHeader += \"signature=\" + $Signature\n\n #Create IDictionary to hold request headers\n $Headers = @{}\n\n #Add Auth header\n $Headers.Add('Authorization',$AuthorizationHeader)\n\n #Add additional headers if POSTing or PUTing\n If (($Method -ceq \"POST\") -or ($Method -ceq \"PUT\"))\n {\n\t $Body_Size = [System.Text.Encoding]::UTF8.GetByteCount($Body)\n\t $Headers.Add('max-body',$Body_Size.ToString())\n\n # turn off the \"Expect: 100 Continue\" header\n # as it's not supported on the Akamai side.\n [System.Net.ServicePointManager]::Expect100Continue = $false\n }\n\n #Check for valid Methods and required switches\n If (($Method -ceq \"POST\") -and ($Body -ne $null))\n {\n Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -Body $Body -ContentType 'application/json'\n }\n elseif (($Method -ceq \"PUT\") -and ($Body -ne $null))\n {\n\t #Invoke API call with PUT and return\n\t Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -Body $Body -ContentType 'application/json'\n }\n elseif (($Method -ceq \"GET\") -or ($Method -ceq \"DELETE\"))\n {\n\t #Invoke API call with GET or DELETE and return\n\t Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers\n }\n else\n {\n\t throw \"Error: Invalid -Method specified or missing required parameter\"\n }\n}\n\nfunction Perform-AkamaiRequest {\n param (\n [string]$request, \n [string]$method=\"Get\", \n [int]$expectedStatusCode=200, \n $body)\n\n $baseUrl = \"http://private-anon-3934daf8d-akamaiopen2purgeccuv2production.apiary-mock.com\"\n # $baseUrl = \"https://api.ccu.akamai.com\"\n $uri = \"{0}{1}\" -f $baseUrl,$request\n\n if ($uri -match \"mock\"){\n $requestHeaders = @{'Cache-Control'='no-cache,proxy-revalidate'}\n $response = Invoke-RestMethod -Uri $uri -Method $method -DisableKeepAlive -Headers $requestHeaders -Body $body\n } else {\n $json = ConvertTo-Json $body -Compress\n $response = Invoke-AkamaiOpenRequest -Method $method -ClientToken $clientToken -ClientAccessToken $clientAccessToken -ClientSecret $clientSecret -ReqURL $uri -Body $json\n }\n\n if ($response.httpStatus -ne $expectedStatusCode){\n Write-Error \"Request not processed correctly: $($response.detail)\"\n } elseif ($response.detail) {\n Write-Verbose $response.detail\n }\n\n Write-Verbose $response\n\n $response\n}\n\nfunction Get-QueueSize {\n param ([string]$queueName)\n\n $queueSize = Perform-AkamaiRequest \"/ccu/v2/queues/$queueName\"\n\n $queueSize \n}\n\nfunction Request-Purge {\n param ($objects,[string]$type=\"arl\",[string]$action=\"remove\",[string]$domain=\"production\")\n\n $body = @{\n objects = $objects\n action = $action\n type = $type\n domain = $domain\n }\n\n Perform-AkamaiRequest \"/ccu/v2/queues/$queueName\" \"Post\" 201 $body\n}\n\nfunction Get-PurgeStatus {\n param ([string]$purgeId)\n\n $status = Perform-AkamaiRequest \"/ccu/v2/purges/$purgeId\"\n\n Write-Host \"Purge status: $($status.purgeStatus)\"\n\n $status\n}\n\n$queueSize = Get-QueueSize $queueName\nWrite-Output \"$($queueName) queue size is $($queueSize.queueLength)\"\n\n$purge = Request-Purge $objects $type $action $domain\n\nWrite-Output \"Purge request created\"\nWrite-Output \"PurgeId: $($purge.purgeId)\"\nWrite-Output \"SupportId: $($purge.supportId)\" \n\nif ($wait) {\n $check = 1\n $purgeStatus = \"Unknown\"\n\n do {\n if ($check -gt 1) {\n Write-Output \"Waiting $($purge.pingAfterSeconds) seconds before checking purge status again\"\n Start-Sleep -Seconds $purge.pingAfterSeconds\n }\n $status = Get-PurgeStatus $purge.purgeId \n $purgeStatus = $status.purgeStatus\n $check = $check + 1\n if ($check -gt $maxChecks) { \n Write-Output \"Maximum number of checks reached\"\n }\n } while ($status.purgeStatus -ne \"Done\" -and $check -le $maxChecks)\n\n if ($status.purgeStatus -ne \"Done\") {\n Write-Error \"Purge status is not Done\"\n }\n}",
"Octopus.Action.Script.ScriptFileName": null,
"Octopus.Action.Package.NuGetFeedId": null,
"Octopus.Action.Package.NuGetPackageId": null
},
"Category": "Akamai",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/akamai-content-purge.json",
"Website": "/step-templates/0bfe41dc-50b0-44a7-abe3-e6ba2a6d317a",
"Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABhQTFRFseDvTrjc1+/3f8zlKqrU////AJnM7/n8E0I9ngAABR5JREFUeNrsndly6zAIQAFJ1v//8c16GydGK3IgA9O3tqOcsCNZhvQjAg7iIA7iIA7iIA7iIA7iIA7iIA7iIA7iIA7iIA5yIAQUnkI2QS4AGLedmAPJEN4QbhJsgdAhxFKMBSAcBYIpZ4djigjZUtTKYTtWBqX1IgeS8Rgj5JQMgdB3MaRAOKM6DUMIhPON8zBEQNhIxWdLoOePHhDWOY7zPX78ecQQ4PsgjFXF/Fk74rHmHv8wCzP377lRHVy6F0z+UyDAfCJ4M6gWitncCfIc+2BF2I4xo5YJkBazAty6JcKpIJx7vJaHNIAxijIKkmPVPfIgxpivgCwHVQNzq4RTQOocFLdJibQepM4RNgEJq0GqHDPeMVp1wgKOebPiKx1BkCoHbHLS7igglQdhBcc+DsqC1PJ5Iwdeq/c/QWQrGVoDwjWD7RyXgp37bHA0n2z0k04QKDtlhmpJeIHgP9n1N5fma4SkD4Qq6geZwjbvmzCUB6kUWFC0qL5RxGvdDNIgFQcB2S6DQo/Dg4Bh5fKvx5ul/y6HsiBlw8orWvHn5C9IglQMKzY18KMNXJYDyWXDWjc0hSaVQK+SmZQO7T0rwS2R7374JPlUSpYC4VJhybDwaEzHhwRee5eEH6RAyrViaPGO+oArchvYhCgEUp5hUUO11zgZGt2lAxGFYLUn6pg2bkPnCmBOIWwq3LsHdPaMA7EO1igEB2e/43vybSBUDFlU5DjAuMdc2WkjrFAIHk5Nbzs674P663kVkX3UJhAuqTMKwc+yBsv7OPTRTXWP6JpAykk9MDnyWSe1bkbRJwwIg5QHQGzcvQ2OkHos5H1/LoiCUDH2Aj83xZGMsE+cKAkSijshKFq1fwQ6lAPhRouH1YnQmayXYIdiIGXLCoyjz8pf2xCkxkGhI4mIHmh6TuuCEEjRsvLSw35BchxULk9gjWG9GZjIOAiK2RClI9Zhn9swNa2vXa6z4kjM7w/GKAASS3VWXubpb64C0yC5Oa1jWia52rLXQaBYY4fPAdeXBBojYD2LYNINgsXKF0/wkLUgH5Hgywqpg5QHjGoUUgUpB620MKkLg1ROyZ3zSMVCkPCuEdIOUp47kBrLmgQBNZYlBULpR0wrqQfBJo2gdZCkxkWEQEA9SG4Dyb+ikfQjIGgApBx+H5yYfwQk2NdI+BUQMANS3mB/1ChgAIQqx2PNgKTK8Xs0DrLtfMgESHkcdPchMgwCrxozoZFQAUErINTwdIIJkMqo8TbByyZAinuhd9tKJkBqTgJWQKB2xC1GGyC54iQpkw2QVH+a1QhIEDo++XUQqtmWFRAbtjVx8CyYAzFhWwKHMw2BVBp3OyCVp5LsgFhwd4lD/oZAqg2vGZCsXiUyjyYZAlHvJbOP7wVzINpzSbuRK88lHd6qu+LqefhHtb/3xE/V/t4DkjfFxtWV0TQbV19qVhy5OmuMqLZS6fwIetNi73dJ0zd6KQFR27/3W3fQSTLgpjpbk5HlVZIMra6RZGxxhSSDazdcLWsDRJ9OhlfWVtOPf4XKMuOELYCqamXGqKn3Cn+tIM3XxqsH4S/yJ2sg7N2+wRoIezfd2UpZ99qOM9+ikta+SOXUPC+0Vti+HYmlVmJf/QTGQK4o8ZsooqswNxjGM9xe+Oti31cHxkB4lhjIGEjKLAvYArnTHF6PB/ZAbpqB93dt2gR50Ly8/ZSSXRBzmd1BHMRBHMRBHMRBHMRBHMRBHMRBHMRBHMRBHORM+SfAAMOMGvrIfh0UAAAAAElFTkSuQmCC",
"$Meta": {
"Type": "ActionTemplate"
}
}
Page updated on Monday, July 11, 2016