Having worked on headless Sitecore projects for the last year and a half or so involving Experience Edge and/or XM Cloud, I’ve run into some…interesting things. In this post, I hope to share a few “lessons from the front” in the hopes that other teams out there can avoid the same mistakes and potentially save themselves time and headache.
I won’t be going into great detail on Experience Edge or XM Cloud as products in this post. There is, of course, documentation out there from Sitecore as well as a ton of excellent content from the many helpful experts in the Sitecore community on these two SaaS products from Sitecore. For more information on Experience Edge and/or XM Cloud, I recommend starting with the official documentation here:
I’d also encourage the reader to check out other blog posts from my colleagues here on the Perficient blog. For example, David San Filippo’s The Big Deal with Headless post and Martin Miles’s Mastering XM Cloud series.
Experience Edge Image URL Casing
A typical task for a Sitecore developer is to ensure that URLs are consistently cased and, usually, this means making URLs uniformly lowercase regardless of the item’s path in Sitecore. This is done for several reasons including SEO and aesthetics. There are several configurations for Sitecore that can affect URL generation with respect to casing.
On a recent project, a pull request was completed that added a patch config to set the lowercaseUrls
attribute to true
on the Sitecore.XA.Foundation.Multisite.LinkManagers.LocalizableLinkProvider
element (yes, we were using SXA). It looked something like this:
<linkManager defaultProvider="switchableLinkProvider" patch:source="Sitecore.XA.Foundation.Multisite.config"> <providers> <add name="localizedProvider" type="Sitecore.XA.Foundation.Multisite.LinkManagers.LocalizableLinkProvider, Sitecore.XA.Foundation.Multisite" cacheExpiration="5" addAspxExtension="false" alwaysIncludeServerUrl="false" encodeNames="true" languageEmbedding="never" languageLocation="filePath" shortenUrls="true" useDisplayName="false" lowercaseUrls="true" /> <!-- HERE, THIS THING --> </providers> </linkManager>
While this worked in that link manager URLs were lowercased as expected, the change had the unintended side effect of forcing media item URLs to be lowercased before they were published to Experience Edge. In other words, this configuration setting caused lowercased media item URLs to be published to Experience Edge. However, only the “native” (non-lowercased) URLs would correctly resolve when browsed on Experience Edge–media item URLs within Experience Edge are case-sensitive as it relates to the path of the item.
For example, if a content page included, say, a logo named logo.png
that resided in the Foo
folder (note: uppercase “F”) in the media library root, then the layout JSON returned via the headless application’s GraphQL query would be:
... "Logo": { "value": { "src": "https://edge.sitecorecloud.io/<environment-token>/media/foo/logo.png?h=123&iar=0&w=400", "alt": "Logo", "width": "400", "height": "123" } } ...
When browsed directly, the newly lowercased URL wouldn’t resolve. Only when the casing of the folder in the URL was updated to match the path of the media item in Sitecore did the request resolve correctly.
- ❌ https://edge.sitecorecloud.io/<environment-token>/media/foo/logo.png (
404
) - ✅ https://edge.sitecorecloud.io/<environment-token>/media/Foo/logo.png (
200
)
Sitecore support acknowledged this issue and registered a bug (reference number 597185).
The takeaway: If you’re publishing images to Experience Edge and you are adding patch configurations to manipulate the casing of URLs, be sure to verify that, after publishing, the media item URLs that hit Experience Edge resolve as you’d expect them to. We ended up rolling back the link manager configuration change.
XM Cloud Deployment via the Sitecore CLI
On a recent XM Cloud project, the task in our Azure DevOps YAML pipeline responsible for deploying to XM Cloud initially looked something like this:
... - script: | dotnet sitecore cloud login --client-credentials --client-id ${{ parameters.xmcClientId }} --client-secret ${{ parameters.xmcClientSecret }} --allow-write echo 'Logged in - Deploying to XM Cloud' dotnet sitecore cloud deployment create --environment-id ${{ parameters.environmentId }} --upload echo 'Deployment to XM Cloud Completed.' ...
The login
command would, unsurprisingly, log in to XM Cloud ahead of the deployment and the deployment create
command would package up the code in the working directory and kick off the deployment. This worked fine for a while. However, we noticed that, when there was an issue with the deployment (according to the XM Cloud Deploy interface), the deployment task in the pipeline would run for ~20+ minutes before reporting a successful completion…despite there being an underlying deployment issue being reported in XM Cloud Deploy. Moreover, the pipeline logs even cited an issue, but the task still succeeded meaning that we had a false positive.
We came learn that, at least for the versions of the Sitecore CLI and tooling we were using (Sitecore CLI 5.2.113
and Sitecore.DevEx.Extensibility.XMCloud 1.1.30
), the deployment command returns successfully if the deployment was submitted successfully, not necessarily that the deployment itself succeeded.
We soon came across this issue in the official Sitecore XM Cloud Introduction GitHub repository which pointed to this commit in which the result of the dotnet sitecore cloud deployment create
command was captured and parsed to determine the result of the deployment (and reflect the same outcome as seen in XM Cloud Deploy). We changed our pipeline task (a bash script task) to something like this:
... - script: | dotnet sitecore cloud login --client-credentials --client-id ${{ parameters.xmcClientId }} --client-secret ${{ parameters.xmcClientSecret }} --allow-write echo 'Logged in - deploying to XM Cloud' result=$(dotnet sitecore cloud deployment create --environment-id ${{ parameters.environmentId }} --upload --json) echo $result isTimedOut=$(echo $result | jq ' .IsTimedOut') isCompleted=$(echo $result | jq ' .IsCompleted') if [ $isTimedOut = true ] then echo "Deployment Timed Out." exit -1 fi if ! [ $isCompleted = true ] then echo "Deployment Failed." exit -1 fi echo "Deployment Completed" ...
Note the --json
flag passed to the cloud deployment create
command and that the return value of the command is set to a variable. The deployment now ran to completion (successfully or otherwise) and the result was parsed to determine the status of the deployment which was then correctly reported in the Azure DevOps pipeline. The tradeoff with this is that we no longer got the default “progression” output (with completion percentages, etc.) while the original command executed. Ideally, the command would do both return detailed progression information as it executed and accurately report the result of the deployment (meaning: a non-zero return code for failed deployments).
The takeaway: If you’re using the Sitecore CLI to deploy to XM Cloud, it’s better to parse the result of the dotnet sitecore cloud deployment create
command to get the result of the deployment rather than risk a false positive, even if it means not seeing the fancy deployment progress as the command executes.
Provisioning Experience Edge Admin Credentials in XM Cloud Deploy
Sitecore administrators manage Experience Edge instances using the Admin API. This API allows for things like clearing cache, deleting content, managing cache TTL and auto-clear settings, and managing webhooks. For a PaaS solution using Experience Edge, Sitecore typically provides the credentials for managing the Experience Edge instance(s) via the Admin API. The process of obtaining similar credentials in XM Cloud is a bit different. This blog post (and others, I’d assume) describes the issue and the solution. The net-out, essentially, is that extra steps are required to obtain an access token with the appropriate Experience Edge scopes (permissions) with which to then call the Admin API.
After learning this, someone on our team (…it’s me, hi, I’m the problem, it’s me) created a Postman collection to fully automate the series of calls necessary to obtain an access token to ultimately call the Admin API (mostly to clear cache for troubleshooting purposes). These automated requests were made several times across different environments over the span of a month or so. The calls included a POST request to the https://xmclouddeploy-api.sitecorecloud.io/api/clients/v1/edge endpoint with the following payload:
{ "projectId": "<PROJECT_ID_HERE>", "environmentId": "<ENVIRONMENT_ID_HERE>", "name": "EDGE_CI_CD", "description": "Credentials for interacting with the Experience Edge Admin API in <ENVIRONMENT>." }
What wasn’t immediately clear to this person (me) was that this request creates a permanent credential in XM Cloud Deploy–it does not create a transient, single-use credential. In other words, when this request is made successfully, a new item is added to this list in the XM Cloud Deploy interface:
This iterative (and entirely superfluous) provisioning of credentials came to a head when, seemingly all of a sudden, the call to this endpoint stopped working and began returning a cryptic error that, at least to this person (yeah, still me) wasn’t incredibly telling. The request would fail with a 504 - Gateway Timeout
which had the following response signature:
HTTP/1.1 504 Gateway Timeout ****: Wed, 15 Nov 2023 18:29:44 GMT Content-Type: application/json; charset=utf-8 Content-Length: 51 Connection: close CF-Cache-Status: DYNAMIC Server: cloudflare CF-RAY: 82698e3fbad653db-ATL alt-svc: h3=":443"; ma=86400 { "message": "The upstream server is timing out" }
After working with Sitecore support, it became known that, because the XM Cloud Deploy endpoint to provision new Experience Edge credentials had been called so many times, we had run afoul of an undocumented upper limit on the number of Experience Edge credentials allowed for a given project: 10. When the request was made to create the 11th credential, it failed with the 504
.
The Sitecore support engineer registered the behavior as a bug, if only to get the documentation updated to include verbiage describing the hard limit (reference number 603658).
The takeaway: Do not automate calls to the XM Cloud Deploy API’s https://xmclouddeploy-api.sitecorecloud.io/api/clients/v1/edge endpoint to provision credentials. It’s better to provision one set of Experience Edge administration credentials for each environment and distribute those credentials securely to your team using, say, an Azure Key Vault or [insert your favorite secrets management tool here].
Thanks for the read! 🙏