Working with Akamai
Certificate Provisioning System (CPS)

CPS

The CPS API has been around for a long time and is the most common interface customers use to interact with certificates. The API itself, and by extension the PowerShell module, has support for DV, OV and EV certificates, even though the Akamai Terraform provider only supports DV. As such, PowerShell is often considered for interacting with OV or EV certificates, pushing changes to existing enrollments, or for auditing purposes.

Enrollments

In CPS, the most common object you interact with is the enrollment.

Retrieving Enrollments

You can list enrollments like this:

$Enrollments = Get-CPSEnrollment

You could also use this function to perform certificate auditing. For example, if you wanted to find only those enrollments which are SNI-only, and display just the ID, common name, and network (Standard TLS vs Enhanced TLS), you could do this:

Get-CPSEnrollment | Where-Object { $_.networkConfiguration.sniOnly -eq $true } | Select-Object id, @{Name="CN";Expression={$_.csr.cn}}, @{Name="Network";Expression={$_.networkConfiguration.secureNetwork}}

Typically, you would simply want to filter your list of enrollments to find a specific one by its common name, like this:

$Enrollment = Get-CPSEnrollment | Where-Object { $_.csr.cn -eq "www.example.com" }

Alternatively, the same command can also be used to retrieve a single enrollment by its ID, if known:

$Enrollment = Get-CPSEnrollment -EnrollmentId 123456
$Enrollment
 
id                             : 12345
productionSlots                : {30000}
stagingSlots                   : {30000}
assignedSlots                  : {30000}
location                       : /cps/v2/enrollments/12345
ra                             : lets-encrypt
validationType                 : dv
certificateType                : san
certificateChainType           : default
networkConfiguration           : @{geography=core; secureNetwork=standard-tls; mustHaveCiphers=ak-akamai-2018q3; preferredCiphers=ak-akamai-2018q3;
                                 fipsMode=False; disallowedTlsVersions=System.Object[]; sniOnly=True; quicEnabled=False; dnsNameSettings=; ocspStapling=on;
                                 clientMutualAuthentication=}
signatureAlgorithm             : SHA-256
changeManagement               : False
csr                            : @{cn=www.example.com; c=GB; st=London; l=London; o=Akamai; ou=; sans=System.Object[]; preferredTrustChain=}
org                            : @{name=Akamai; addressLineOne=7 Air St; addressLineTwo=; city=London; region=London; postalCode=W1B5AD; country=GB;
                                 phone=+44800800800}
orgId                          :
adminContact                   : @{firstName=Bob; lastName=Loblaw; phone=+44 203 800 800; email=bob@loblaws.com; addressLineOne=;
                                 addressLineTwo=; city=; country=; organizationName=; postalCode=; region=; title=}
techContact                    : @{firstName=Tom; lastName=Leighton; phone=+44 203 800 800; email=tomleighton@akamai.com; addressLineOne=; addressLineTwo=;
                                 city=; country=; organizationName=; postalCode=; region=; title=}
thirdParty                     :
enableMultiStackedCertificates : False
autoRenewalStartTime           :
pendingChanges                 : {@{location=/cps/v2/enrollments/12345/changes/2345678; changeType=renewal; changeId=9144360}}
maxAllowedSanNames             : 100
maxAllowedWildcardSanNames     : 25

The CPS API uses a versioned object model approach, which means that depending on the request headers you send, the object you receive may be different. More details can be found here (opens in a new tab), but when using PowerShell we always default to the latest version, so you shouldn't need to worry about it.

Creating Enrollments

As you can see, there are a lot of settings inside this object, which makes creating enrollments difficult. There are no user-friendly parameters for New-CPSEnrollment, purely because there are too many options and they are too complex, so typically we recommend finding a comparable enrollment (evenb on another account), exporting that either to JSON or a PSCustomObject, editing then pushing to the New endpoint. Like this:

$Enrollment = Get-CPSEnrollment -EnrollmentId 123456
$Enrollment.csr.cn = "new.example.com"
$Enrollment.csr.sans = @("new.example.com")
 
$Enrollment | New-CPSEnrollment -ContractID 1-2AB34C

Note: the above command assumes that only the CSR and SANs would differ between your reference certificate and the new one. This is valid, but somewhat simplified. There are many, many other options you might change.

Updating Enrollments

Similar to how we create enrollments, updating enrollments is a pretty simple matter with PowerShell. You can do this generally either with the returned PSCustomObject, which is best for straightforward changes, or with a JSON file if you need a more wholistic view. Let's say you wanted to add a SAN to an existing enrollment, you could do this:

$Enrollment = Get-CPSEnrollment -EnrollmentId 123456
$Enrollment.csr.sans += "new-san.example.com"
$Enrollment | Set-CPSEnrollment

Alternatively, in JSON:

Get-CPSEnrollment -EnrollmentId 123456 | ConvertTo-Json -Depth 10 | Out-File "Enrollment.json"

Then edit the JSON file to add the new SAN, and then push it back:

Set-CPSEnrollment -EnrollmentId 123456 -Body (Get-Content .\Enrollment.json -Raw)

When using the pipeline, the -EnrollmentID parameter gets its data from the piped object. When using -Body, either with JSON data or PSCustomObject, you must provide it with the -EnrollmentID parameter.

This command can often fail, specifically if the enrollment has pending changes, which you can see in the enrollment object. If this is the case, and you wish to update the enrollment anyway, you must add the -AllowCancelPendingChanges parameter.

Deleting Enrollments

Deleting an enrollment is a simple matter of using the Remove-CPSEnrollment command, like this:

Remove-CPSEnrollment -EnrollmentId 123456

Or, more commonly, you would have retrieved your enrollment first, then piped it to the Remove-CPSEnrollment command, like this:

$Enrollment = Get-CPSEnrollment | Where-Object { $_.csr.cn -eq "www.example.com" }
$Enrollment | Remove-CPSEnrollment

or even

Get-CPSEnrollment | Where-Object { $_.csr.cn -eq "www.example.com" } | Remove-CPSEnrollment

The above scenario with -AllowCancelPendingChanges also applies to deleting enrollments. If you have pending changes, you must use that parameter to delete the enrollment.

Working with changes

There are various CPS states which require interaction. Most common would be retrieving a CSR for a Third Party enrollment, and uploading its reciprocal signed certificate, but there are several others you can see below.

Change TypeCommand
Promote a certificate from staging to production when your enrollment has 'Always test on staging' enabled.Complete-CPSChange
Retrieve third party CSR.Get-CPSCSR
Upload third party signed certificate.Add-CPSThirdPartyCert
Retrieve Lets Encrypt challenges for DV enrollments.Get-CPSLetsEncryptChallenges
Instruct CPS that LE challenges are in place.Confirm-CPSLetsEncryptChallengesCompleted
Retrieve pre-verification warning.sGet-CPSPreVerificationWarnings
Acknowledge pre-verification warnings.Confirm-CPSPreVerificationWarnings
Retrieve post-verification warnings.Get-CPSPostVerificationWarnings
Acknowledge post-verification warnings.Confirm-CPSPostVerificationWarnings
Schedule a timed deployment of a change.Set-CPSDeploymentSchedule
Get the schedule for a timed deployment.Get-CPSDeploymentSchedule

Most of these changes in some way require a -ChangeID parameter. There is no API endpoint to list active changes, you can instead find them in the pendingChanges property of the enrollment object. You can also retrieve the status of a specific change by its ID, like this:

Get-CPSChangeStatus -EnrollmentId 123456 -ChangeId 2345678

As way of an example, let's say you haved created a third party enrollment (we'll skip the creation part for brevity).

First, we retrieve the enrollment and hold it in a variable.

$Enrollment = Get-CPSEnrollment | Where-Object { $_.csr.cn -eq "www.example.com" }

Next we need to set it not to proceed to production without approval.

$Enrollment.changeManagement = $true
$Enrollment | Set-CPSEnrollment

Then, we need to retrieve the CSR for the third party CA to sign.

$CSR = Get-CPSCSR -EnrollmentId $Enrollment.id -ChangeId $Enrollment.pendingChanges[0].changeId
$CSR.csrs | Where-Object keyAlgorithm -eq 'RSA' | Select-Object -ExpandProperty csr | Out-File "www.example.com.rsa.csr"
$CSR.csrs | Where-Object keyAlgorithm -eq 'ECDSA' | Select-Object -ExpandProperty csr | Out-File "www.example.com.ecdsa.csr"

Next, once you have retrieved the signed certificate from your CA, you would save them into files in your directory. Those files can then be read into strings as part of the Add-CPSThirdPartyCert command, like this:

$Cert = Get-Content .\www.example.com.crt -Raw
$Chain = Get-Content .\www.example.com.chain.crt -Raw
Add-CPSThirdPartyCert -EnrollmentId $Enrollment.id -ChangeId $Enrollment.pendingChanges[0].changeId -Certificate $Cert -TrustChain $Chain

This should kick off the process to get your cert as far as the staging network, as we configured. Then, you would test the cert works as expected, and finally promote it to production, like this:

Complete-CPSChange -EnrollmentId $Enrollment.id -ChangeId $Enrollment.pendingChanges[0].changeId