Akamai - CPCode Fast Purge
Octopus.Script exported 02/11/2019 by ajwightm belongs to 'Akamai' category.
Allows to purge CP codes using the Content Control Utility (CCU) v3 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
Authentication token used in client authentication. Available in Luna Portal.
Client Access Token
Authentication token used in client authentication. Available in Luna Portal.
Secret
Authentication password used in client authentication. Available in Luna Portal.
CPCode
The CPCode for which to execute the purge operation
Host
Akamai Host (no HTTP/HTTPS). Available in Luna Portal.
Action
The action to execute on the purge operation
Domain
The Akamai domain to perform the purge operation on
Script body
$clientToken = $OctopusParameters['AkamaiClientToken']
$clientAccessToken = $OctopusParameters['AkamaiClientAccessToken']
$clientSecret = $OctopusParameters['AkamaiSecret']
$cpcode = $OctopusParameters['AkamaiCPCode']
$akhost = $OctopusParameters['AkamaiHost']
$action = $OctopusParameters['AkamaiAction']
$domain = $OctopusParameters['AkamaiDomain']
# NOTICE : PowerShell EdgeGrid Client has been deprecated and will reach End of Life soon. For more information, please see https://developer.akamai.com/blog/2018/11/13/akamai-powershell-edgegrid-client-end-life-notice
# Copied from https://github.com/akamai-open/AkamaiOPEN-powershell/blob/master/Invoke-AkamaiOPEN.ps1
function Invoke-AkamaiOpenRequest {
param(
[Parameter(Mandatory=$true)]
[ValidateSet("GET", "PUT", "POST", "DELETE")]
[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,
[Parameter(Mandatory=$false)][string]$MaxBody = 131072
)
#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 "akamaiapis.net")
{
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]
#Add body to signature. Truncate if body is greater than max-body (Akamai default is 131072). PUT Medthod does not require adding to signature.
if ($Body -and $Method -eq "POST")
{
$Body_SHA256 = [System.Security.Cryptography.SHA256]::Create()
if($Body.Length -gt $MaxBody){
$Post_Hash = [System.Convert]::ToBase64String($Body_SHA256.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($Body.Substring(0,$MaxBody))))
}
else{
$Post_Hash = [System.Convert]::ToBase64String($Body_SHA256.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($Body)))
}
$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 ($Body)
{
# 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
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
if ($Method -eq "PUT" -or $Method -eq "POST") {
if ($Body) {
try{
Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -Body $Body -ContentType 'application/json'
}
catch{
Write-Host $_ -fore green
}
}
else {
Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -ContentType 'application/json'
}
}
else {
#Invoke API call with GET or DELETE and return
Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers
}
}
function Perform-AkamaiRequest {
param (
[string]$request,
[string]$method="Get",
[int]$expectedStatusCode=200,
$body)
$baseUrl = "https://" + $akhost
$uri = "{0}{1}" -f $baseUrl,$request
$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
}
$response
}
function Request-Purge {
param ([Int]$cpcode,[string]$action="remove",[string]$domain="production")
$body = @{
objects = @($cpcode)
}
Perform-AkamaiRequest "/ccu/v3/$action/cpcode/$domain" "Post" 201 $body
}
$purge = Request-Purge $cpcode $action $domain
Write-Output "Purge request created"
Write-Output "PurgeId: $($purge.purgeId)"
Write-Output "SupportId: $($purge.supportId)" To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
Show JSON{
"Id": "ee4a6957-7d98-4dcf-8f94-78f19ab1c6e0",
"Name": "Akamai - CPCode Fast Purge",
"Description": "Allows to purge CP codes using the Content Control Utility (CCU) v3 REST API.",
"Version": 2,
"ExportedAt": "2019-02-11T18:51:20.358Z",
"ActionType": "Octopus.Script",
"Author": "ajwightm",
"Parameters": [
{
"Id": "293e2cc1-e471-4801-8a9c-42633a3c9122",
"Name": "AkamaiClientToken",
"Label": "Client Token",
"HelpText": "Authentication token used in client authentication. Available in Luna Portal.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "e0edcbea-bfdd-4781-9a8a-55b08eba6ed5",
"Name": "AkamaiClientAccessToken",
"Label": "Client Access Token",
"HelpText": "Authentication token used in client authentication. Available in Luna Portal.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "c1ea0502-f68e-4890-99a2-3c721d16b7f0",
"Name": "AkamaiSecret",
"Label": "Secret",
"HelpText": "Authentication password used in client authentication. Available in Luna Portal.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
},
"Links": {}
},
{
"Id": "f54e1b4a-3960-483b-88c9-5ea7a94698a0",
"Name": "AkamaiCPCode",
"Label": "CPCode",
"HelpText": "The CPCode for which to execute the purge operation",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "a334eabb-8a36-4c24-b728-52f834f8a893",
"Name": "AkamaiHost",
"Label": "Host",
"HelpText": "Akamai Host (no HTTP/HTTPS). Available in Luna Portal.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "8482001d-a6fc-4e72-b9be-8cc584a39b36",
"Name": "AkamaiAction",
"Label": "Action",
"HelpText": "The action to execute on the purge operation",
"DefaultValue": "invalidate",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "invalidate\nremove"
},
"Links": {}
},
{
"Id": "e1c3dfa0-7118-4e23-9315-c03ea3662125",
"Name": "AkamaiDomain",
"Label": "Domain",
"HelpText": "The Akamai domain to perform the purge operation on",
"DefaultValue": "production",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "production\nstaging"
},
"Links": {}
}
],
"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$cpcode = $OctopusParameters['AkamaiCPCode']\n$akhost = $OctopusParameters['AkamaiHost']\n$action = $OctopusParameters['AkamaiAction']\n$domain = $OctopusParameters['AkamaiDomain']\n\n# NOTICE : PowerShell EdgeGrid Client has been deprecated and will reach End of Life soon. For more information, please see https://developer.akamai.com/blog/2018/11/13/akamai-powershell-edgegrid-client-end-life-notice\n# Copied from https://github.com/akamai-open/AkamaiOPEN-powershell/blob/master/Invoke-AkamaiOPEN.ps1\nfunction Invoke-AkamaiOpenRequest {\n\tparam(\n\t\t[Parameter(Mandatory=$true)]\n\t\t[ValidateSet(\"GET\", \"PUT\", \"POST\", \"DELETE\")]\n\t\t[string]$Method,\n\t\t[Parameter(Mandatory=$true)][string]$ClientToken,\n\t\t[Parameter(Mandatory=$true)][string]$ClientAccessToken,\n\t\t[Parameter(Mandatory=$true)][string]$ClientSecret,\n\t\t[Parameter(Mandatory=$true)][string]$ReqURL,\n\t\t[Parameter(Mandatory=$false)][string]$Body,\n\t\t[Parameter(Mandatory=$false)][string]$MaxBody = 131072\n\t\t)\n\n\t#Function to generate HMAC SHA256 Base64\n\tFunction Crypto ($secret, $message)\n\t{\n\t\t[byte[]] $keyByte = [System.Text.Encoding]::ASCII.GetBytes($secret)\n\t\t[byte[]] $messageBytes = [System.Text.Encoding]::ASCII.GetBytes($message)\n\t\t$hmac = new-object System.Security.Cryptography.HMACSHA256((,$keyByte))\n\t\t[byte[]] $hashmessage = $hmac.ComputeHash($messageBytes)\n\t\t$Crypt = [System.Convert]::ToBase64String($hashmessage)\n\n\t\treturn $Crypt\n\t}\n\n\t#ReqURL Verification\n\tIf (($ReqURL -as [System.URI]).AbsoluteURI -eq $null -or $ReqURL -notmatch \"akamaiapis.net\")\n\t{\n\t\tthrow \"Error: Ivalid Request URI\"\n\t}\n\n\t#Sanitize Method param\n\t$Method = $Method.ToUpper()\n\n\t#Split $ReqURL for inclusion in SignatureData\n\t$ReqArray = $ReqURL -split \"(.*\\/{2})(.*?)(\\/)(.*)\"\n\n\t#Timestamp for request signing\n\t$TimeStamp = [DateTime]::UtcNow.ToString(\"yyyyMMddTHH:mm:sszz00\")\n\n\t#GUID for request signing\n\t$Nonce = [GUID]::NewGuid()\n\n\t#Build data string for signature generation\n\t$SignatureData = $Method + \"`thttps`t\"\n\t$SignatureData += $ReqArray[2] + \"`t\" + $ReqArray[3] + $ReqArray[4]\n\n\t#Add body to signature. Truncate if body is greater than max-body (Akamai default is 131072). PUT Medthod does not require adding to signature.\n\t\n\tif ($Body -and $Method -eq \"POST\")\n\t{\n\t $Body_SHA256 = [System.Security.Cryptography.SHA256]::Create()\n\t if($Body.Length -gt $MaxBody){\n\t\t$Post_Hash = [System.Convert]::ToBase64String($Body_SHA256.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($Body.Substring(0,$MaxBody))))\n\t }\n\t else{\n\t\t$Post_Hash = [System.Convert]::ToBase64String($Body_SHA256.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($Body)))\n\t }\n\n\t $SignatureData += \"`t`t\" + $Post_Hash + \"`t\"\n\t}\n\telse\n\t{\n\t $SignatureData += \"`t`t`t\"\n\t}\n\n\t$SignatureData += \"EG1-HMAC-SHA256 \"\n\t$SignatureData += \"client_token=\" + $ClientToken + \";\"\n\t$SignatureData += \"access_token=\" + $ClientAccessToken + \";\"\n\t$SignatureData += \"timestamp=\" + $TimeStamp + \";\"\n\t$SignatureData += \"nonce=\" + $Nonce + \";\"\n\n\t#Generate SigningKey\n\t$SigningKey = Crypto -secret $ClientSecret -message $TimeStamp\n\n\t#Generate Auth Signature\n\t$Signature = Crypto -secret $SigningKey -message $SignatureData\n\n\t#Create AuthHeader\n\t$AuthorizationHeader = \"EG1-HMAC-SHA256 \"\n\t$AuthorizationHeader += \"client_token=\" + $ClientToken + \";\"\n\t$AuthorizationHeader += \"access_token=\" + $ClientAccessToken + \";\"\n\t$AuthorizationHeader += \"timestamp=\" + $TimeStamp + \";\"\n\t$AuthorizationHeader += \"nonce=\" + $Nonce + \";\"\n\t$AuthorizationHeader += \"signature=\" + $Signature\n\n\t#Create IDictionary to hold request headers\n\t$Headers = @{}\n\n\t#Add Auth header\n\t$Headers.Add('Authorization',$AuthorizationHeader)\n\n\t#Add additional headers if POSTing or PUTing\n\tIf ($Body)\n\t{\n\t # turn off the \"Expect: 100 Continue\" header\n\t # as it's not supported on the Akamai side.\n\t [System.Net.ServicePointManager]::Expect100Continue = $false\n\t}\n\t\n\t#Check for valid Methods and required switches\n\t[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12\n\tif ($Method -eq \"PUT\" -or $Method -eq \"POST\") {\n\t\tif ($Body) {\n\t\t\ttry{\n\t\t\t\tInvoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -Body $Body -ContentType 'application/json'\n\t\t\t}\n\t\t\tcatch{\n\t\t\t\tWrite-Host $_ -fore green\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t Invoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers -ContentType 'application/json'\n\t\t}\n\t}\n\telse {\n\t\t#Invoke API call with GET or DELETE and return\n\t\tInvoke-RestMethod -Method $Method -Uri $ReqURL -Headers $Headers\n\t}\n}\n\nfunction Perform-AkamaiRequest {\n param (\n [string]$request, \n [string]$method=\"Get\", \n [int]$expectedStatusCode=200, \n $body)\n\n $baseUrl = \"https://\" + $akhost\n $uri = \"{0}{1}\" -f $baseUrl,$request\n\n $json = ConvertTo-Json $body -Compress\n $response = Invoke-AkamaiOpenRequest -Method $method -ClientToken $clientToken -ClientAccessToken $clientAccessToken -ClientSecret $clientSecret -ReqURL $uri -Body $json\n\t\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 $response\n}\n\nfunction Request-Purge {\n param ([Int]$cpcode,[string]$action=\"remove\",[string]$domain=\"production\")\n\n $body = @{\n objects = @($cpcode)\n }\n\n Perform-AkamaiRequest \"/ccu/v3/$action/cpcode/$domain\" \"Post\" 201 $body\n}\n\n$purge = Request-Purge $cpcode $action $domain\n\nWrite-Output \"Purge request created\"\nWrite-Output \"PurgeId: $($purge.purgeId)\"\nWrite-Output \"SupportId: $($purge.supportId)\" ",
"Octopus.Action.RunOnServer": "false",
"Octopus.Action.Script.ScriptFileName": null,
"Octopus.Action.Package.FeedId": null,
"Octopus.Action.Package.PackageId": null
},
"Category": "Akamai",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates/akamai-cpcode-fastpurge.json",
"Website": "/step-templates/ee4a6957-7d98-4dcf-8f94-78f19ab1c6e0",
"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"
}
}Provided under the Apache License version 2.0.