{"id":4862,"date":"2023-11-01T22:35:18","date_gmt":"2023-11-01T22:35:18","guid":{"rendered":"https:\/\/www.robhabraken.nl\/?p=4862"},"modified":"2023-11-30T22:37:49","modified_gmt":"2023-11-30T22:37:49","slug":"xm-cloud-devops-pipeline","status":"publish","type":"post","link":"https:\/\/www.robhabraken.nl\/index.php\/4862\/xm-cloud-devops-pipeline\/","title":{"rendered":"XM Cloud DevOps pipeline"},"content":{"rendered":"\n<p>The easiest way to deploy your project to your XM Cloud instance is manually via the Sitecore Cloud Portal. If you go to the XM Cloud Deploy dashboard, you can not only create projects and environments, but also start a &#8220;Build and deploy&#8221; process for a specific environment. While that might seem convenient and perfectly okay to do, it doesn&#8217;t really fit in a proper deployment strategy where you would want to script both the infrastructure provisioning as well as the application deployments to create a fully automated, repeatable and robust deployment process.<\/p>\n\n\n\n<p>If you want to have a little more control, you can opt for starting a deployment via the CLI from your workstation. The Sitecore Command Line Interface is a command-line tool that lets you perform different tasks against your Sitecore instance, of which the Cloud command is one:  <a rel=\"noreferrer noopener\" href=\"https:\/\/doc.sitecore.com\/xmc\/en\/developers\/xm-cloud\/the-cli-cloud-command.html\" target=\"_blank\">https:\/\/doc.sitecore.com\/xmc\/en\/developers\/xm-cloud\/the-cli-cloud-command.html<\/a>. This command has several useful subcommands that help you manage your Sitecore XM Cloud projects, and starting a new deployment requires only one command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet sitecore cloud deployment create --environment-id &lt;id&gt; --upload --working-dir &lt;path&gt;<\/code><\/pre>\n\n\n\n<p>Although you now have more options and control, it still is not desirable to run this manually from your development workstation. But we can use this CLI command in our pipelines and build a decent deployment process that fits well into the bigger picture of our deployment strategy (we have multiple pipelines for multiple applications within our landscape, from front-end to individual microservices).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Don&#8217;t reuse the deployment command<\/h2>\n\n\n\n<p>As you probably already know when reading this blog post, there&#8217;s a hierarchy to XM Cloud environments. A &#8220;Project&#8221; defines as the top-level entity, containing multiple &#8220;Environments&#8221;, usually at least two non-production environments (like test and acceptance or UAT) and one production environment. When you start a deployment, you always deploy to a specific environment: as you can see in the above example, the <code>cloud deployment<\/code> command takes a (required) parameter named <code>--environment-id<\/code>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>While this implies that you can use that deployment command to deploy to each environment of your XM Cloud project individually, that isn&#8217;t the way to go!<\/p>\n<\/blockquote>\n\n\n\n<p>Script-wise it is very easy to use the above deployment command to deploy to each of your environments, only passing the corresponding environment ID into the pipeline and you&#8217;re good. But for a decent deployment strategy, I would go the extra mile of utilizing the build promotion mechanism that is built into XM Cloud.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Promoting deployments<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Why?<\/h3>\n\n\n\n<p>There are two important reasons to promote an existing build to other environments instead of creating a new deployment per environment:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Saving time: promoting is even quicker than deploying as it skips the build step<\/li>\n\n\n\n<li>Guaranteed the same build &amp; version, which is important for a representative (acceptance) test preceding a production deployment, ensuring a proper DTAP process<\/li>\n<\/ul>\n\n\n\n<p>When you check out the deployment logs in the Sitecore Cloud portal you can see promotion skips the build step (as it is re-using the same build), saving about 25% of the initial deployment time:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"753\" src=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/10\/deployment-promotion-1024x753.png\" alt=\"\" class=\"wp-image-4876\" srcset=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/10\/deployment-promotion-1024x753.png 1024w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/10\/deployment-promotion-300x220.png 300w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/10\/deployment-promotion-768x564.png 768w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/10\/deployment-promotion-1536x1129.png 1536w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/10\/deployment-promotion.png 1939w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"> <\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">The mechanism<\/h3>\n\n\n\n<p>When you are using the CLI you need to take the following steps to be able promote your builds:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create a deployment without starting it (using the <code>--no-start<\/code> option)<\/li>\n\n\n\n<li>Retrieve and store the deployment ID<\/li>\n\n\n\n<li>Execute a <code>cloud deployment start<\/code> command referencing the ID<\/li>\n\n\n\n<li>When you want to promote your build to another environment, use the <code>environment promote<\/code> command with the deployment ID as input for the <code>--source-id<\/code> option<\/li>\n<\/ul>\n\n\n\n<p>If you would start your deployment right away, as shown in the above example, you won&#8217;t retrieve a deployment ID and thus are not able to promote that build <em>using the CLI<\/em> (of course, you can always do that via the Sitecore Cloud portal but that&#8217;s not what we&#8217;re after).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The promote command<\/h3>\n\n\n\n<p>While promoting deployments is a deployment in way, this CLI command actually isn&#8217;t a subcommand of <code>cloud deployment<\/code> but of the <code>cloud environment<\/code> command. This makes it less apparent when you are creating a deployment script using the deployment command as a guide in the documentation. The environment command is used to create, update and list environments, but also promote a build from one environment to another: <a rel=\"noreferrer noopener\" href=\"https:\/\/doc.sitecore.com\/xmc\/en\/developers\/xm-cloud\/the-cloud-environment-command.html#the-promote-subcommand\" target=\"_blank\">https:\/\/doc.sitecore.com\/xmc\/en\/developers\/xm-cloud\/the-cloud-environment-command.html#the-promote-subcommand<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Storing and passing the deployment ID<\/h3>\n\n\n\n<p>So, the theory is quite simple. But getting the YAML pipeline to actually return and store the deployment ID using powershell steps to execute Sitecore CLI commands is a bit of a challenge. Thanks to my colleague Rinus Leenders for figuring out the works, we can now share our fully scripted deployment pipeline for Azure DevOps in YAML. We are utilizing the <code>##vso[task.setvariable]<\/code> command, which is a special syntax recognized by Azure DevOps environments, informing the system to set a variable accordingly.<\/p>\n\n\n\n<p>This leads to the following PowerShell script to store the deployment ID in your pipeline when creating a new deployment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$deployment = dotnet sitecore cloud deployment create --environment-id $env:XMC_ENVIRONMENT_ID --no-watch --no-start --upload --working-dir $(Build.SourcesDirectory) --json | ConvertFrom-Json\nWrite-Host \"$deployment\"\n$deploymentId = $deployment.id\nWrite-Host \"##vso&#91;task.setvariable variable=deploymentId]$deploymentId\"\nWrite-Host \"##vso&#91;task.setvariable variable=deploymentId;isOutput=true]$deploymentId\"<\/code><\/pre>\n\n\n\n<p>After which we can use that variable to start the deployment right away:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet sitecore cloud deployment start --deployment-id $(deploymentId) --json | ConvertFrom-Json<\/code><\/pre>\n\n\n\n<p>And subsequently promoting that build to another environment as part of the deployment pipeline to our acceptance and production environments (encompassing more parts and pipelines, as we simultaniously and \/ or individually would want to deploy our front-end and microservices as well, but that&#8217;s outside the scope of this blog post):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet sitecore cloud environment promote --environment-id $env:XMC_ENVIRONMENT_ID --source-id $(deploymentId)<\/code><\/pre>\n\n\n\n<p>I have shared the full YAML pipeline for Azure DevOps on my GitHub account over here: <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/robhabraken\/xmcloud-devops-deployment\" target=\"_blank\">https:\/\/github.com\/robhabraken\/xmcloud-devops-deployment<\/a>. It contains separate templates for both the deployment creation as well as the deployment promotion steps. Feel free to use this as a starting point for your own deployment pipeline in Azure DevOps and share your input via a Pull Request or an Issue if you have a suggestion or improvement.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"594\" src=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/11\/github-xmcloud-deploy-1024x594.png\" alt=\"\" class=\"wp-image-4892\" srcset=\"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/11\/github-xmcloud-deploy-1024x594.png 1024w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/11\/github-xmcloud-deploy-300x174.png 300w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/11\/github-xmcloud-deploy-768x445.png 768w, https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2023\/11\/github-xmcloud-deploy.png 1047w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"> <\/figcaption><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>The easiest way to deploy your project to your XM Cloud instance is manually via the Sitecore Cloud Portal. If you go to the XM Cloud Deploy dashboard, you can not only create projects and environments, but also start a &#8220;Build and deploy&#8221; process for a specific environment. While that might seem convenient and perfectly [&hellip;]<\/p>\n","protected":false},"author":9,"featured_media":4897,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[94],"tags":[96,56,24,42,20],"class_list":["post-4862","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-xm-cloud","tag-azure-devops","tag-cloud","tag-deployments","tag-how-to","tag-sitecore"],"jetpack_featured_media_url":"https:\/\/www.robhabraken.nl\/wp-content\/uploads\/2015\/01\/pipelines-cloud.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts\/4862","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/comments?post=4862"}],"version-history":[{"count":43,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts\/4862\/revisions"}],"predecessor-version":[{"id":4910,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/posts\/4862\/revisions\/4910"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/media\/4897"}],"wp:attachment":[{"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/media?parent=4862"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/categories?post=4862"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.robhabraken.nl\/index.php\/wp-json\/wp\/v2\/tags?post=4862"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}