Akamai - CPCode Fast Purge

Octopus.Script exported 2019-02-11 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

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.

CPCode

AkamaiCPCode =

The CPCode for which to execute the purge operation

Host

AkamaiHost =

Akamai Host (no HTTP/HTTPS). Available in Luna Portal.

Action

AkamaiAction = invalidate

The action to execute on the purge operation

Domain

AkamaiDomain = production

The Akamai domain to perform the purge operation on

Script body

Steps based on this template will execute the following PowerShell script.

$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)" 

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "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//opt/buildagent/work/75443764cd38076d/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"
  }
}

History

Page updated on Monday, February 11, 2019