Automate Sitecore XM Cloud Webhook Registration with Azure DevOps Pipeline & PowerShell


🚀 Automate Azure Webhook Registration for Sitecore XM Cloud: A CI/CD Approach
In today's fast-moving digital world, keeping content in sync in real-time is key to providing dynamic and engaging user experiences. Sitecore XM Cloud, with its Experience Edge platform, offers strong tools for managing and sharing content. As mentioned before, using Azure Functions and webhooks with XM Cloud is important for starting actions when content is updated.
My previous blog, Azure Function Webhook XM Cloud Integration, covered the basics of generating client credentials, acquiring access tokens, registering webhooks with Experience Edge, and understanding its execution modes, along with key considerations when working with XM Cloud versus Experience Edge webhooks. In earlier blogs, we discussed integrating
Azure Functions with Sitecore XM Cloud using webhooks
to achieve real-time content sync between XM Cloud and Sitecore Search. However, manually setting up and managing these webhooks, particularly generating tokens and registering them repeatedly, can be time-consuming
and error-prone.
In this article, we’ll walk you through how to automate the registration of a custom Azure webhook with Sitecore XM Cloud using an Azure DevOps pipeline and a custom PowerShell script, improving deployment efficiency across environments. 🔝
🔧 Why Automate XM Cloud Webhook Registration?
Manually registering webhooks involves:
✔ Generating client credentials
✔ Fetching JWT access tokens (expiring in 24 hours)
✔ Repeat the same steps for different environments
✔ Calling the Experience Edge Admin API
This process can cause delays, missed updates, and wasted developer hours, especially when working across development, staging, and production environments. 🔝
Automation ensures:
✅ No manual token refreshes
✅ Reusable across environments
✅ Seamless pipeline integration
✅ Reduced human error and faster deployment cycles
⚙️ Solution: Automation Process
In this blog post, I will explain how to automate the process using two methods: one with a PowerShell script and the other with PowerShell and Azure DevOps to register a custom Azure webhook with Sitecore XM Cloud. 🔝
Option 1: Automate Using PowerShell
In this approach, I will show you how to use a PowerShell script along with the .env (environment) file to execute the necessary steps from your LOCAL
or DEV
environment. Alternatively, you can replace the .env
file environment variables with your preferred secret manager
or configuration management
tool.
Step 1: Set Up Environment Variables in a .env File
To effectively organize essential environment variables in a .env
file, ensure you have all necessary values ready, such as client credentials and access tokens. These prerequisites are crucial for initiating the automation process to register the XM Cloud webhook, streamlining your deployment workflow. 🔝
BEARER_TOKEN=<If available>
CLIENT_ID=<Your Client ID, check https://enlightenwithamit.hashnode.dev/azure-function-webhook-xm-cloud-integration for more details>
CLIENT_SECRET=<Your Client Secret, check https://enlightenwithamit.hashnode.dev/azure-function-webhook-xm-cloud-integration for more details>
AUDIENCE=https://api.sitecorecloud.io
GRANT_TYPE=client_credentials
SITECORE_EDGE_API=https://edge.sitecorecloud.io/api/admin/v1/webhooks
SITECORE_AUTH_API=https://auth.sitecorecloud.io/oauth/token
Step 2: How to Read Environment Variables from a .env File
To read environment variables from a .env
file in a PowerShell script, I wrote a custom PowerShell function
that gives you the value of an environment variable in the script. This method lets you manage settings separately from your main code, making it more modular and flexible. By using environment variables, you can easily change settings between different environments (like development, staging, production) without changing the code. This also helps keep the code cleaner and reduces the risk of exposing sensitive information.
I made a custom GetEnvVariable function that takes the path of the .env
file and the name of the environment variable you want the value for. 🔝
function GetEnvVariable {
param(
[Parameter(Mandatory = $true, HelpMessage = "Specifies the path of the .env file.")]
[string]$filePath = ".env", # Default path for the .env file
[Parameter(Mandatory = $true, HelpMessage = "Specifies the .env name.")]
[string]$varName # Name of the environment variable to retrieve
)
Write-Host "Inside GetEnvVariable" # Log entry into the function
$envFilePath = Resolve-Path "$PSScriptRoot\$filePath" # Resolve the full path of the .env file
if (Test-Path $envFilePath) { # Check if the .env file exists
if ($varName -ne "" -and $envFilePath -ne "") { # Ensure parameters are not empty
Write-Host "Using .env file: $envFilePath" -ForegroundColor Cyan # Log the file being used
# Read the contents of the .env file
$envFileContent = Get-Content -Path $envFilePath # Load the file content into an array
# Iterate through each line to find the variable
foreach ($line in $envFileContent) {
if ($line -match "^\s*$varName\s*=\s*(.+)\s*$") { # Check if the line matches the variable name
$varValue = $matches[1].Trim() # Extract the variable value
Write-Host "Environment variable '$varName' found with value '$varValue'." # Log success message
return $varValue # Return the found value
}
}
Write-Host "Environment variable '$varName' not found in the .env file." -ForegroundColor Yellow # Log if not found
return $null # Return null if not found
} else {
Write-Host "Invalid parameters" -ForegroundColor Red # Log error for invalid parameters
return $null # Return null for invalid parameters
}
} else {
Write-Error "The .env file does not exist at the specified path: $envFilePath" # Log error if file doesn't exist
return $null # Return null if file doesn't exist
}
}
Using the syntax below, you can call this function:
GetEnvVariable -filePath $EnvFileName -varName "BEARER_TOKEN"
Step 3: Requesting a JWT for Sitecore XM Cloud APIs
Automating the process of requesting a JWT for the Authoring and Management API or the XM Cloud Deploy API is crucial for maintaining seamless operations, especially when tokens expire or haven't been generated yet. A custom PowerShell script function GetAccessToken can handle JWT generation automatically, ensuring uninterrupted access. Before requesting a JWT, it's essential to create client credentials, including a client ID and client secret, tailored to the access level required. For detailed steps on setting up these credentials, refer to my previous blog on Azure Function Webhook XM Cloud Integration. This approach not only enhances security but also streamlines the authentication process across different environments. 🔝
function GetAccessToken {
param(
[Parameter(Mandatory = $true, HelpMessage = "Specifies the path of the .env file.")]
[string]$filePath = ".env" # Default path for the .env file
)
Write-Host "Inside GetAccessToken" # Log entry into the function
$statusCodeAccessToken = 0 # Initialize status code
$envFilePath = Resolve-Path "$PSScriptRoot\$filePath" # Resolve the full path of the .env file
if (Test-Path $envFilePath) { # Check if the .env file exists
Write-Host "Using .env file: $envFilePath" -ForegroundColor Cyan # Log the file being used
# Read the contents of the .env file
$audience = GetEnvVariable -filePath $EnvFileName -varName "AUDIENCE"
$grantType = GetEnvVariable -filePath $EnvFileName -varName "GRANT_TYPE"
$clientId = GetEnvVariable -filePath $EnvFileName -varName "CLIENT_ID"
$clientSecret = GetEnvVariable -filePath $EnvFileName -varName "CLIENT_SECRET"
$sitecoreAuthApi = GetEnvVariable -filePath $EnvFileName -varName "SITECORE_AUTH_API"
$authBody = @{
audience = $audience
grant_type = $grantType
client_id = $clientId
client_secret = $clientSecret
} | ConvertTo-Json # Prepare the body for the authentication request
try {
# Make the API call
$response = Invoke-WebRequest -Uri $sitecoreAuthApi -Method POST -Body $authBody -ContentType "application/json" -ErrorAction Stop
# Extract the status code
$statusCodeAccessToken = $response.StatusCode
# Parse the response content
$authResponse = $response.Content | ConvertFrom-Json
# Debug logs
# Write-Host "Status Code Access Token: $statusCodeAccessToken"
# Write-Host "Response Content GetAccessToken:"
# Write-Host $authResponse
} catch {
Write-Host "Error occurred while getting new access token: $($_.Exception.Message)" -ForegroundColor Red
if ($_.Exception.Response -ne $null) {
$statusCodeAccessToken = $_.Exception.Response.StatusCode
$errorContent = $_.Exception.Response.GetResponseStream() | %{ $_.ReadToEnd() }
Write-Host "Error Response Content:"
Write-Host $errorContent
} else {
Write-Host "No response received from the server." -ForegroundColor Yellow
}
throw "Unexpected error occurred while getting new access token: $($_.Exception.Message)"
}
# Handle the status code and execute the next step
switch ($statusCodeAccessToken) {
200 {
if ($authResponse -and $authResponse.access_token) {
$bearerToken = $authResponse.access_token
Write-Host "Successfully retrieved new access token with Status Code 200." # Log success message
Write-Host "Access Token Retrieved: $bearerToken"
} else {
Write-Error "Status Code 200 - Access token not found in the response."
}
return $bearerToken # Return the new access token
}
"OK" {
if ($authResponse -and $authResponse.access_token) {
$bearerToken = $authResponse.access_token
Write-Host "Successfully retrieved new access token with Status Code 200 OK" # Log success message
# Write-Host "Access Token Retrieved: $bearerToken"
} else {
Write-Error "Status Code OK - Access token not found in the response."
}
return $bearerToken # Return the new access token
}
default {
if ($statusCodeAccessToken -match "OK") {
if ($authResponse -and $authResponse.access_token) {
$bearerToken = $authResponse.access_token
Write-Host "Successfully retrieved new access token with Status Code Default OK IF block" # Log success message
# Write-Host "Access Token Retrieved: $bearerToken"
} else {
Write-Error "Status Code Default ELSE - Access token not found in the response."
}
return $bearerToken # Return the new access token
} else {
Write-Error "Failed to retrieve new access token in Default - ELSE switch. Status code: $statusCodeAccessToken" # Log error if not successful
# Add your next step for other errors here
}
}
}
if (-not $authResponse.access_token) {
Write-Error "Failed to retrieve new access token."
exit 1
}
} else {
Write-Error "The .env file does not exist at the specified path: $envFilePath" # Log error if file doesn't exist
return $null # Return null if file doesn't exist
}
}
Using the syntax below, you can call this function:
$bearerToken=GetAccessToken -filePath $EnvFileName
You need to give the path to your .env file, and it will return the new JWT token for you to use. 🔝
Step 4: Registering Webhooks with Sitecore Experience Edge
A custom script makes it easy to register webhooks with Sitecore Experience Edge by automating the needed API calls. This reduces manual work and mistakes. The script takes care of authentication, gets access tokens, and registers the webhook, making sure it works smoothly with Sitecore's content delivery network. By using this automation, you can quickly set up webhooks to trigger actions like ISR revalidation when content is published, improving the responsiveness of your Sitecore XM Cloud apps. This method saves time and ensures consistent and reliable webhook management across different environments.
I created a custom RegisterWebhook function that uses the following parameters: 🔝
✅ filePath: Specifies the path of the .env file.
✅ webHookName: Specifies the webhook name.
✅ webHookURL: Specifies the webhook URL.
✅ createdBy: Specifies the Created By Name.
✅ functionKey: Specifies the security key for your webhook.
function RegisterWebhook {
param(
[Parameter(Mandatory = $true, HelpMessage = "Specifies the path of the .env file.")]
[string]$filePath = ".env" , # Default path for the .env file
[Parameter(Mandatory = $true, HelpMessage = "Specifies the webhook name.")]
[string]$webHookName, # Webhook Name to register
[Parameter(Mandatory = $true, HelpMessage = "Specifies the webhook url.")]
[string]$webHookURL, # URL to register
[Parameter(Mandatory = $true, HelpMessage = "Specifies the Created By Name.")]
[string]$createdBy, # Registration done by
[Parameter(Mandatory = $true, HelpMessage = "Specifies the Security Key for your Webhook.")]
[string]$functionKey # Security key for the webhook
)
Write-Host "Inside RegisterWebhook" # Log entry into the function
$envFilePath = Resolve-Path "$PSScriptRoot\$filePath" # Resolve the full path of the .env file
if (Test-Path $envFilePath) { # Check if the .env file exists
if ($varName -ne "" -and $envFilePath -ne "") { # Ensure parameters are not empty
Write-Host "Using .env file: $envFilePath" -ForegroundColor Cyan # Log the file being used
# Read the contents of the .env file
# Define the API endpoint
$sitecoreEdgeApi = GetEnvVariable -filePath $EnvFileName -varName "SITECORE_EDGE_API"
# Write-Host "Local variable bearerToken set with value '$bearerToken'."
Write-Host "Calling Sitecore Edge API to register webhooks..."
# Prepare the headers
$headers = @{ Authorization = "Bearer $bearerToken" }
# Prepare the body
$body = @{
label = $webHookName
uri = $webHookURL
method = "POST"
headers = @{
"x-functions-key" = $functionKey
}
createdBy = $createdBy
executionMode = "OnUpdate"
} | ConvertTo-Json -Depth 10 # Convert the body to JSON
Write-Host "Calling Sitecore Edge API to register webhook..."
Write-Host "Request Body: $body"
try {
$response = Invoke-WebRequest -Uri $sitecoreEdgeApi -Headers $headers -Method POST -Body $body -ContentType "application/json" -ErrorAction Stop
$statusCode = $response.StatusCode
# Print the raw response content
# Write-Host "Response Content GetWebhookListing:"
# Write-Host $response.Content
} catch {
Write-Host "Error occurred in RegisterWebhook: $($_.Exception.Message)" -ForegroundColor Red
if ($_.Exception.Response -ne $null) {
$statusCode = $_.Exception.Response.StatusCode
} else {
throw "Unexpected error occurred in RegisterWebhook: $($_.Exception.Message)"
}
}
# Handle the status code and execute the next step
switch ($statusCode) {
201 {
Write-Host "Webhook registered successfully. Response:"
Write-Host $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 10
}
401 {
Write-Host "Unauthorized access 401. Please check your bearer token."
Write-Host "Response content: $($response.Content)"
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for unauthorized access here
$bearerToken=GetAccessToken -filePath $EnvFileName
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
RegisterWebhook -filePath $filePath -webHookName $webHookName -webHookURL $webHookURL -createdBy $createdBy -functionKey $functionKey
}
default {
if ($statusCode -match "Unauthorized") {
Write-Host "Unauthorized error detected. Please verify your credentials or token."
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for handling unauthorized errors here
$bearerToken=GetAccessToken -filePath $EnvFileName
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
RegisterWebhook -filePath $filePath -webHookName $webHookName -webHookURL $webHookURL -createdBy $createdBy -functionKey $functionKey
} else {
Write-Host "Failed to register webhook. Status code: $statusCode"
Write-Host "Response content: $($response.Content)"
# Add your next step for other errors here
}
}
}
return $null # Return null if not found
}
} else {
Write-Error "The .env file does not exist at the specified path: $envFilePath" # Log error if file doesn't exist
return $null # Return null if file doesn't exist
}
}
You can call this function using the syntax below: 🔝
RegisterWebhook -filePath $EnvFileName -webHookName "Amit Kumar GetUpdate" -webHookURL "https://amitkumar.com/GetUpdate" -createdBy "Amit Kumar" -functionKey "12345"
The RegisterWebhook function also provides useful information through logs, allowing you to understand what is happening during the process. It also returns the registered webhook details, which can be helpful for debugging issues. You can enhance the logging according to your needs. Some of the log details include: 🔝
Inside RegisterWebhook
Using .env file: C:\.env
Inside GetEnvVariable
Using .env file: C:\.env
Environment variable 'SITECORE_EDGE_API' found with value 'https://edge.sitecorecloud.io/api/admin/v1/webhooks'.
Calling Sitecore Edge API to register webhooks...
Request Body: {
"label": "Amit Kumar GetUpdate",
"createdBy": "Amit Kumar",
"executionMode": "OnUpdate",
"method": "POST",
"uri": "https://amitkumar.com/GetUpdate",
"headers": {
"x-functions-key": "12345"
}
}
Webhook registered successfully. Response:
{"id":"57614d29-e3e2-43f2-8d8e-04041eac1151","tenantId":"amitkumar-xmc123-amitiukumar","label":"Amit Kumar GetUpdate","uri":"https://amitkumar.com/GetUpdate","method":"POST","headers":{"x-functions-key":"12345"},"body":"","createdBy":"Amit Kumar","created":"2025-01-15T11:15:50.7956347+00:00","bodyInclude":null,"executionMode":"OnUpdate","lastRuns":[],"disabled":null}
The switch statement in the RegisterWebhook function is designed to handle various HTTP response status codes effectively, ensuring seamless webhook registration. When the response status code
is 401 (Unauthorized
), it indicates that the existing JWT token has expired or is invalid. In this case, the script intelligently calls the GetAccessToken function to fetch a new bearer token dynamically. Once the new token is retrieved, the RegisterWebhook
function is invoked again with the updated token to retry the webhook registration process. This robust error-handling
mechanism ensures uninterrupted API communication and automates token renewal for secure and efficient webhook management. 🔝
switch ($statusCode) {
201 {
Write-Host "Webhook registered successfully. Response:"
Write-Host $response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 10
}
401 {
Write-Host "Unauthorized access 401. Please check your bearer token."
Write-Host "Response content: $($response.Content)"
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for unauthorized access here
$bearerToken=GetAccessToken -filePath $EnvFileName
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
RegisterWebhook -filePath $filePath -webHookName $webHookName -webHookURL $webHookURL -createdBy $createdBy -functionKey $functionKey
}
default {
if ($statusCode -match "Unauthorized") {
Write-Host "Unauthorized error detected. Please verify your credentials or token."
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for handling unauthorized errors here
$bearerToken=GetAccessToken -filePath $EnvFileName
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
RegisterWebhook -filePath $filePath -webHookName $webHookName -webHookURL $webHookURL -createdBy $createdBy -functionKey $functionKey
} else {
Write-Host "Failed to register webhook. Status code: $statusCode"
Write-Host "Response content: $($response.Content)"
# Add your next step for other errors here
}
}
}
Option 2: Automate Using Azure Pipeline + PowerShell
This option shows how to automate Azure webhook registration for Sitecore XM Cloud using a PowerShell script and Azure Pipelines. It makes it easier to generate access tokens, set up webhooks, and connect with the Experience Edge API for better content management. 🔝
Step 1: Set Up Environment Variables in Azure Pipelines
To securely manage sensitive data, store the following in your Azure Pipelines environment variables:
BEARER_TOKEN=<If available>
CLIENT_ID=<Your Client ID, check https://enlightenwithamit.hashnode.dev/azure-function-webhook-xm-cloud-integration for more details>
CLIENT_SECRET=<Your Client Secret, check https://enlightenwithamit.hashnode.dev/azure-function-webhook-xm-cloud-integration for more details>
AUDIENCE=https://api.sitecorecloud.io
GRANT_TYPE=client_credentials
SITECORE_EDGE_API=https://edge.sitecorecloud.io/api/admin/v1/webhooks
SITECORE_AUTH_API=https://auth.sitecorecloud.io/oauth/token
Step 2: PowerShell Script to Handle Token Generation
The PowerShell script GetAccessToken performs requests a new JWT access token using the client credentials flow if the current token is expired or missing.
This script differs from Option 1 because we are not using the .env
file to get the environment variables. Instead, we will retrieve the necessary environment variables from the Azure Pipelines Environment Variables. 🔝
function GetAccessToken {
Write-Host "Inside GetAccessToken" # Log entry into the function
$statusCodeAccessToken = 0 # Initialize status code
# Check if the .env file exists
Write-Host "Read Environment Variables" -ForegroundColor Cyan # Log the file being used
# Read the contents of the .env file
$audience = ${env:AUDIENCE}
$grantType = ${env:GRANT_TYPE}
$clientId = ${env:CLIENT_ID}
$clientSecret = ${env:CLIENT_SECRET}
$sitecoreAuthApi = ${env:SITECORE_AUTH_API}
$authBody = @{
audience = $audience
grant_type = $grantType
client_id = $clientId
client_secret = $clientSecret
} | ConvertTo-Json # Prepare the body for the authentication request
try {
# Make the API call
$response = Invoke-WebRequest -Uri $sitecoreAuthApi -Method POST -Body $authBody -ContentType "application/json" -ErrorAction Stop
# Extract the status code
$statusCodeAccessToken = $response.StatusCode
# Parse the response content
$authResponse = $response.Content | ConvertFrom-Json
# Debug logs
# Write-Host "Status Code Access Token: $statusCodeAccessToken"
# Write-Host "Response Content GetAccessToken:"
# Write-Host $authResponse
} catch {
Write-Host "Error occurred while getting new access token: $($_.Exception.Message)" -ForegroundColor Red
if ($_.Exception.Response -ne $null) {
$statusCodeAccessToken = $_.Exception.Response.StatusCode
$errorContent = $_.Exception.Response.GetResponseStream() | %{ $_.ReadToEnd() }
Write-Host "Error Response Content:"
Write-Host $errorContent
} else {
Write-Host "No response received from the server." -ForegroundColor Yellow
}
throw "Unexpected error occurred while getting new access token: $($_.Exception.Message)"
}
# Handle the status code and execute the next step
switch ($statusCodeAccessToken) {
200 {
if ($authResponse -and $authResponse.access_token) {
$bearerToken = $authResponse.access_token
Write-Host "Successfully retrieved new access token with Status Code 200." # Log success message
# Write-Host "Access Token Retrieved: $bearerToken"
} else {
Write-Error "Status Code 200 - Access token not found in the response."
}
return $bearerToken # Return the new access token
}
"OK" {
if ($authResponse -and $authResponse.access_token) {
$bearerToken = $authResponse.access_token
Write-Host "Successfully retrieved new access token with Status Code 200 OK" # Log success message
# Write-Host "Access Token Retrieved: $bearerToken"
} else {
Write-Error "Status Code OK - Access token not found in the response."
}
return $bearerToken # Return the new access token
}
default {
if ($statusCodeAccessToken -match "OK") {
if ($authResponse -and $authResponse.access_token) {
$bearerToken = $authResponse.access_token
Write-Host "Successfully retrieved new access token with Status Code Default OK IF block" # Log success message
# Write-Host "Access Token Retrieved: $bearerToken"
} else {
Write-Error "Status Code Default ELSE - Access token not found in the response."
}
return $bearerToken # Return the new access token
} else {
Write-Error "Failed to retrieve new access token in Default - ELSE switch. Status code: $statusCodeAccessToken" # Log error if not successful
# Add your next step for other errors here
}
}
}
if (-not $authResponse.access_token) {
Write-Error "Failed to retrieve new access token."
exit 1
}
}
Step 3: Register Webhook via Experience Edge API
This PowerShell script function GetAccessToken uses the JWT token to call the Experience Edge admin API (https://edge.sitecorecloud.io/api/admin/v1/webhooks) to register webhook or update webhook or get the listing of existing webhooks 🔝
function RegisterWebhook {
param(
[Parameter(Mandatory = $true, HelpMessage = "Specifies the webhook name.")]
[string]$webHookName, # Webhook Name to register
[Parameter(Mandatory = $true, HelpMessage = "Specifies the webhook url.")]
[string]$webHookURL, # URL to register
[Parameter(Mandatory = $true, HelpMessage = "Specifies the Created By Name.")]
[string]$createdBy, # Registration done by
[Parameter(Mandatory = $true, HelpMessage = "Specifies the Security Key for your Webhook.")]
[string]$functionKey # Security key for the webhook
)
Write-Host "Inside RegisterWebhook" # Log entry into the function
# Ensure parameters are not empty
Write-Host "Read Environment Variables" -ForegroundColor Cyan # Log the file being used
# Read the contents of the .env file
$sitecoreEdgeApi = ${env:SITECORE_EDGE_API}
Write-Host "Local variable bearerToken set with value '$bearerToken'."
Write-Host "Calling Sitecore Edge API to regsiter the webhook"
$headers = @{ Authorization = "Bearer $bearerToken" }
# Prepare the body
$body = @{
label = $webHookName
uri = $webHookURL
method = "POST"
headers = @{
"x-functions-key" = $functionKey
}
createdBy = $createdBy
executionMode = "OnUpdate"
} | ConvertTo-Json -Depth 10 # Convert the body to JSON
Write-Host "Request Body: $body"
Write-Host "Headers: $headers"
try {
$response = Invoke-WebRequest -Uri $sitecoreEdgeApi -Headers $headers -Method POST -Body $body -ContentType "application/json" -ErrorAction Stop
$statusCode = $response.StatusCode
# Print the raw response content
# Write-Host "Response Content GetWebhookListing:"
# Write-Host $response.Content
} catch {
Write-Host "Error occurred in RegisterWebhook: $($_.Exception.Message)"
if ($_.Exception.Response -ne $null) {
$statusCode = $_.Exception.Response.StatusCode
} else {
throw "Unexpected error occurred: $($_.Exception.Message)"
}
}
# Handle the status code and execute the next step
switch ($statusCode) {
201 {
Write-Host "Webhook registered successfully. Response:"
$response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 10
# Add your next step for success here
}
401 {
Write-Host "Unauthorized access 401. Please check your bearer token."
Write-Host "Response content: $($response.Content)"
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for unauthorized access here
$bearerToken=GetAccessToken
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
RegisterWebhook -webHookName $webHookName -webHookURL $webHookURL -createdBy $createdBy -functionKey $functionKey
}
default {
if ($statusCode -match "Unauthorized") {
Write-Host "Unauthorized error detected. Please verify your credentials or token."
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for handling unauthorized errors here
$bearerToken=GetAccessToken
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
RegisterWebhook -webHookName $webHookName -webHookURL $webHookURL -createdBy $createdBy -functionKey $functionKey
} else {
Write-Host "Failed to retrieve webhooks. Status code: $statusCode"
Write-Host "Response content: $($response.Content)"
# Add your next step for other errors here
}
}
}
return $null # Return null if not found
}
Step 4: Integrate Script into Azure Pipeline
Configure your Azure DevOps pipeline to execute the PowerShell script as a task. Trigger this task manually or automatically during deployment or environment changes. The pipeline will:
Inject environment variables securely
Run the script to ensure the webhook is registered with a valid token
Enable quick switching between environments by changing only
CLIENT_ID
andCLIENT_SECRET
Here’s an example YAML configuration: 🔝
# Starter pipeline
trigger:
- feature/REGISTER-WEBHOOK
pool:
vmImage: ubuntu-latest
variables:
system.debug: true
- script: $PSVersionTable
displayName: 'Check PowerShell Version'
- task: PowerShell@2
displayName: 'Run PowerShell Script with Windows PowerShell 5.1'
inputs:
filePath: './Register-XMCloud-Webhook.ps1'
failOnStderr: true
pwsh: false # Use Windows PowerShell instead of PowerShell Core
This ensures your webhooks are always registered and ready—with no manual steps required.
🏁Final Step: Test and Validate
After running the pipeline, verify that the webhook is registered by checking the Experience Edge API response or testing the webhook with a sample OnUpdate event from XM Cloud.
I have created a script GetWebhookListing that easily checks the list of webhooks registered with Experience Edge, removing the need for manual work. This automated solution simplifies the process, allowing you to quickly verify webhook registrations. By using this script, you improve efficiency and maintain smooth integration with Experience Edge 🔝
function GetWebhookListing {
param(
[Parameter(Mandatory = $true, HelpMessage = "Specifies the path of the .env file.")]
[string]$filePath = ".env" # Default path for the .env file
)
Write-Host "Inside GetWebhookListing" # Log entry into the function
$envFilePath = Resolve-Path "$PSScriptRoot\$filePath" # Resolve the full path of the .env file
if (Test-Path $envFilePath) { # Check if the .env file exists
if ($varName -ne "" -and $envFilePath -ne "") { # Ensure parameters are not empty
Write-Host "Using .env file: $envFilePath" -ForegroundColor Cyan # Log the file being used
# Read the contents of the .env file
$sitecoreEdgeApi = GetEnvVariable -filePath $EnvFileName -varName "SITECORE_EDGE_API"
# Write-Host "Local variable bearerToken set with value '$bearerToken'."
Write-Host "Calling Sitecore Edge API to get webhooks..."
$headers = @{ Authorization = "Bearer $bearerToken" }
try {
$response = Invoke-WebRequest -Uri $sitecoreEdgeApi -Headers $headers -Method GET -ErrorAction Stop
$statusCode = $response.StatusCode
# Print the raw response content
# Write-Host "Response Content GetWebhookListing:"
# Write-Host $response.Content
} catch {
Write-Host "Error occurred: $($_.Exception.Message)"
if ($_.Exception.Response -ne $null) {
$statusCode = $_.Exception.Response.StatusCode
} else {
throw "Unexpected error occurred: $($_.Exception.Message)"
}
}
# Handle the status code and execute the next step
switch ($statusCode) {
200 {
Write-Host "Successfully retrieved webhooks:"
$response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 10
# Add your next step for success here
}
401 {
Write-Host "Unauthorized access 401. Please check your bearer token."
Write-Host "Response content: $($response.Content)"
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for unauthorized access here
$bearerToken=GetAccessToken -filePath $EnvFileName
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
GetWebhookListing -filePath $EnvFileName
}
default {
if ($statusCode -match "Unauthorized") {
Write-Host "Unauthorized error detected. Please verify your credentials or token."
Write-Warning "JWT verification failed. Requesting new token..."
# Add your next step for handling unauthorized errors here
$bearerToken=GetAccessToken -filePath $EnvFileName
Write-Host "Local variable bearerToken received from GetAccessToken function is '$bearerToken'." # Log success message
GetWebhookListing -filePath $EnvFileName
} else {
Write-Host "Failed to retrieve webhooks. Status code: $statusCode"
Write-Host "Response content: $($response.Content)"
# Add your next step for other errors here
}
}
}
return $null # Return null if not found
}
} else {
Write-Error "The .env file does not exist at the specified path: $envFilePath" # Log error if file doesn't exist
return $null # Return null if file doesn't exist
}
}
You can use this function with the following syntax: 🔝
GetWebhookListing -filePath $EnvFileName
🔄 Bonus: Real-Time Sync with Sitecore Search
In your previous blog on real-time sync, you discussed: 🔝
Listening to OnUpdate events
Pulling updated content from Experience Edge using GraphQL
Sending it to Sitecore Search via Ingestion API
This automated webhook setup is the missing piece
that makes such syncs robust and self-sustaining.
🔚Conclusion: Save Time, Reduce Errors, Stay Synced
Manually registering Sitecore Experience Edge webhooks is no longer necessary. With a CI/CD pipeline and PowerShell script, you can:
Automate token creation
Reuse your webhook setup
Easily switch between different environments
This automation makes deployment faster, ensures everything works smoothly, and lets your team focus on new ideas instead of setting up infrastructure. 🔝
🙌 Engage With Us and Get the Code!
Have thoughts or suggestions 🔠 on automating Sitecore XM Cloud webhooks? Share your feedback 💬 in the comments or contribute to our GitHub repo! Scan the QR code below to access the full PowerShell
script and Azure Pipelines
YAML code in the Sitecore-Automation
repository.
And if you enjoy this content, consider subscribing 📰 for more updates and insights. Your engagement means the world to me and helps me continue providing valuable resources! 🌟
🧾Credit/References
🔗Pingback
Sitecore Blog: Real-Time Integrations with XM Cloud | Sitecore Community: Automating XM Cloud Deployments | Sitecore & Azure Functions Best Practices 🔝 |
Querying Experience Edge via GraphQL | Automating Sitecore Deployments with PowerShell | Automate Sitecore XM Cloud Webhooks with Azure Pipelines: Discover how to streamline Sitecore XM Cloud webhook registration using Azure Pipelines and a PowerShell script. Learn to automate access token generation and integrate with the Experience Edge API for efficient content management. |
Automate Sitecore XM Cloud Webhooks with Azure Pipelines: Discover how to streamline Sitecore XM Cloud webhook registration using Azure Pipelines and a PowerShell script. Learn to automate access token generation and integrate with the Experience Edge API for efficient content management. | Simplify Webhook Setup for Sitecore XM Cloud with PowerShell : Save time by automating Sitecore XM Cloud webhook registration with a custom PowerShell script. This guide covers Azure Pipelines integration and Experience Edge API for seamless webhook management. | Azure Webhook Integration for Sitecore XM Cloud Made Easy: Learn how to integrate Azure webhooks with Sitecore XM Cloud using Azure Pipelines. Automate token generation and webhook registration with this step-by-step PowerShell solution. |
How to Register Webhooks in Sitecore XM Cloud Automatically: Automate webhook registration for Sitecore XM Cloud using PowerShell and Azure Pipelines. This tutorial explains how to manage access tokens and connect with the Experience Edge API effortlessly. | Streamline Access Token Automation for Sitecore XM Cloud: Simplify Sitecore XM Cloud webhook setup by automating access token generation with PowerShell. Learn how to use Azure Pipelines to register webhooks with the Experience Edge API. | PowerShell and Azure Pipelines for Sitecore Webhook Automation: Boost efficiency with a PowerShell script to automate Sitecore XM Cloud webhook registration. This guide shows how to use Azure Pipelines for seamless Experience Edge integration. |
Guide to Sitecore Experience Edge Webhook Integration: Explore how to automate webhook registration for Sitecore XM Cloud using Azure Pipelines and PowerShell. Connect with the Experience Edge API for real-time content updates. | Automating Client Credentials for Sitecore XM Cloud Webhooks: Learn to automate client credentials and webhook registration for Sitecore XM Cloud. This PowerShell and Azure Pipelines solution simplifies integration with the Experience Edge API. | Real-Time Webhook Sync with Sitecore XM Cloud: Discover how to automate webhook registration for real-time content sync in Sitecore XM Cloud. Use PowerShell and Azure Pipelines to streamline Experience Edge API integration. 🔝 |
Subscribe to my newsletter
Read articles from Amit Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Amit Kumar
Amit Kumar
My name is Amit Kumar. I work as a hands-on Solution Architect. My experience allows me to provide valuable insights and guidance to organizations looking to leverage cutting edge technologies for their digital solutions.As a Solution Architect, I have extensive experience in designing and implementing robust and scalable solutions using server-side and client-side technologies. My expertise lies in architecting complex systems, integrating various modules, and optimizing performance to deliver exceptional user experiences. Additionally, I stay up-to-date with the latest industry trends and best practices to ensure that my solutions are always cutting-edge and aligned with business objectives.