Application Gateway: Incorrect certificate chain or order

SSL management is always a pain. We should check SSL certificates periodically or implement a solution that carries all management tasks for us (let’s encrypt and cert-manager, for instance). And if there is an issue with a certificate, it’s a always a subject of downtime, so we have to find a solution as quickly as possible. Furthermore, all websites should meet requirements to complete tests and get a “green” mark from mozilla observatory or ssl shopper checker, for example. In this post, we’ll discuss possible issues you may face during the ssl check: “incorrect certificate chain” or “incorrect order. contains anchor”

Please note that my setup includes azure application gateway and azure kubernetes service. The following steps are general, however, may require using different certificate formats or signature algorithms. Check your environment’s requirements beforehand.

  • In my case, it was a wrong intermediate certificate provided by GoDaddy. So, I went to the godaddy site, clicked on certificate and copied intermediate certificate to cer file intermediate.cer > Intermediate certificate
  • Make sure you have openssl on your computer and create a new pfx that contains a certificate, private key and intermediate certificate:
    openssl pkcs12 -export -out appgw-cert.pfx -inkey .\pk.key -in .\ssl.crt -certfile .\intermediate.cer
  • If you have an old pfx with a valid certificate and key, do these commands:
    openssl pkcs12 -in old.pfx -nocerts -nodes -out pk.key
    openssl pkcs12 -in old.pfx -clcerts -nokeys -out cert.crt
    openssl pkcs12 -export -out new.pfx -inkey .\pk.key -in .\cert.crt -certfile .\intermediate.cer
  • Type password for the pfx, and then update azure application gateway if needed:
    $appGW = Get-AzApplicationGateway -Name "ApplicationGatewayName"ResourceGroupName "ResourceGroupName"
    $password = ConvertTo-SecureString $passwordPlainString -AsPlainText -Force
    $cert = Set-AzApplicationGatewaySslCertificate -ApplicationGateway $AppGW -Name "CertName" -CertificateFile "D:\certname.pfx" -Password $password
  • Also, export pfx certificate to your personal certificate store and make sure that the correct chain is used or use for already updated certificate. and certificate chain
  • ..and finally my certificate is “green” and overall rating

Azure DevOps: Update service connection expired secret

If you’re reading this post, you’re trying to find a way to edit an existing service connection with a new service principal secret/key.

It’s a weird that UI and devops cli don’t allow us to quickly change service connection details if it was created automatically by Azure DevOps (“creationMode”: “Automatic”; will talk about it a bit later).

So, how to change a secret? Answer: Azure DevOps REST API.
Note: if you have correct permissions, try out the steps at the bottom of the post. The steps below are for those who don’t have Owner permissions.

  • Create a new Personal Access Token (full access and all scope, expiration 1 day)
  • Go to Project Settings – Service Connections, choose your connection and click on Manage Service Principal. Add a new secret and note it’s value.
  • Choose a tool to work with REST API. It could be either PowerShell or Postman, for instance. I will show both.
  • [Postman] Install Postman and create a new HTTP Request
Postman – File – New – HTTP Request
  • [Postman] Go to Authorization and paste PAT token to the password field
PAT token should be used as password for any REST API requests
  • [Postman] Using the following GET request, get a service endpoint details in JSON format. Organization Name, Project Name and Endpoint Name are parts of the URI (can be taken from service connections list in the azure devops ui) :<orgName>/<ProjectName>/_apis/serviceendpoint/endpoints?endpointNames=<Endpoint Name> &api-version=6.0-preview.4
  • [Postman] Copy everything from the response under the value as shown below
            "data": {
                "subscriptionId": "",
                "subscriptionName": "",
                "environment": "AzureCloud",
                "scopeLevel": "Subscription",
                "creationMode": "Automatic",
                "azureSpnRoleAssignmentId": ""
  • [Postman] Using a PUT request update the service connection. Make sure you set Body – Raw to JSON , and then Paste JSON copied in the previous step to the Body
Body – RAW should be set to JSON
  • Here is a tricky part. Prior to sending PUT request, change creationMode from “Automatic” to “Manual”. Also, in my case, I had to delete the following parameters spnObjectId and appObjectId (data section). Plus, I added serviceprincipalkey with a value set to a new secret (authorization section)
    A short excerpt is provided below:
    "data": {
        "subscriptionId": "",
        "subscriptionName": "",
        "environment": "AzureCloud",
        "scopeLevel": "Subscription",
        "creationMode": "Manual",  # changed
        "azureSpnRoleAssignmentId": "",
        "azureSpnPermissions": ""
         spnObjectId # deleted
         appObjectId # deleted
    "description": "",
    "authorization": {
        "parameters": {
            "tenantid": "",
            "serviceprincipalid": "",
            "authenticationType": "spnKey",
            "serviceprincipalkey": "secret here" # added
        "scheme": "ServicePrincipal"
  • [Postman] URI used for a PUT request:
  • [Postman] Go back to Azure DevOps and make sure that service connections has been updated and ready to use.

  • [PowerShell] Use the following example
$token ="PAT Token"
$orgName = "Organization Name"
$projectName = "Project Name"
$endpointName = "your endpoint"
$endpointId = "your endpoint ID, use GET request or UI"
$header = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($token)")) }

# Get Endpoint details

Invoke-RestMethod -Method GET -URI "$($orgName)/$($projectName)/_apis/serviceendpoint/endpoints?endpointNames=$($endpointName)&api-version=6.0-preview.4" -Headers $header -ContentType "application\json"

# Update Endpoint
$json = @{ json here } | ConvertTo-Json -Depth <your depth>
Invoke-RestMethod -Method PUT -URI "$($OrgName)/_apis/serviceendpoint/endpoints/$($endpointId)?api-version=6.0-preview.4" -Body -Headers $header -ContentType "application\json" -Body $json

That’s it. Now you know how to change a service connection with a new secret without removing a connection and customizing all pipelines in a project.

P.S. If you have Owners permissions on the app registration/service principal used by the connection, try to edit the connection by adding a description, and then click on Save. Azure DevOps should create a new secret and update the connection automatically.

%d bloggers like this: