<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><language>en</language><title>Blog posts by Joona Immonen</title> <link>https://world.optimizely.com/blogs/joona-immonen/</link><description></description><ttl>60</ttl><generator>Optimizely World</generator><item> <title>Continuous delivery with the Episerver DXC Service</title>            <link>http://dev.solita.fi/episerver/2018/01/04/Continuous_delivery_with_episerver_dxcs</link>            <description>&lt;p&gt;I have put a lot of hours into building up a continuous delivery environment which I can be proud of. I already wrote &lt;a href=&quot;http://dev.solita.fi/episerver/2017/11/21/Installing-Jenkins-with-PowerShell-DSC.html&quot;&gt;in my previous blog&lt;/a&gt; about how to setup a Jenkins for this purpose. It was a ground work where we start building in this blog post. This blog post is about how to do continuous Delivery with Episerver Digital Experiance Cloud Service but nothing stops you to use this information in some other place.&lt;/p&gt;

&lt;h2 id=&quot;continuous-delivery-cd-and-continuous-integration-ci&quot;&gt;Continuous delivery (CD) and continuous integration (CI)&lt;/h2&gt;

&lt;p&gt;For sure there are better references about CI and CD than following will be. Actually, if you have hard time in convincing your product owner to continuous delivery we have &lt;a href=&quot;http://dev.solita.fi/2016/09/06/CD-for-UX-and-PO.html&quot;&gt;a blog&lt;/a&gt; for that purpose as well. For me continuous integration means merging work in git repository hourly with coworkers working on the same things. On the other hand continuous delivery means pushing the work to the customer as fast as we can. Developers work on the codebase and then deploy it once they are confident with it. When you speed up the cycle of your deployments, your customer will have new features faster, and you they can steer the direction of your development in a truly agile way. Characteristics of CI and CD are below.&lt;/p&gt;

&lt;h4 id=&quot;continuous-integration&quot;&gt;Continuous integration:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Integrate early and often&lt;/li&gt;
  &lt;li&gt;Make the code available to other developers&lt;/li&gt;
  &lt;li&gt;Make sure the code builds after merging to the works of other people&lt;/li&gt;
  &lt;li&gt;Track the quality of your code&lt;/li&gt;
  &lt;li&gt;Test your code&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;continuous-delivery&quot;&gt;Continuous delivery:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Push changes to production early and often&lt;/li&gt;
  &lt;li&gt;Make the code available to your end-users&lt;/li&gt;
  &lt;li&gt;Make sure that you can deploy at any time&lt;/li&gt;
  &lt;li&gt;Track the quality of your runtime&lt;/li&gt;
  &lt;li&gt;Test your deployments with automated release process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I feel that continuous integration is something that software business is doing already with high adaptation rate. Continuous delivery in the other hand is something that many are still learning. Continuous delivery definitely needs more maturity from the development team since you need to automate a lot of things to be able to deploy automatically. You will need a process that makes all kind of sanity checks across the line to trust the deployments on the hand of the automated system. The good thing is once you get on the road it will be a lot easier to adjust the process when you have ability to repeat it multiple times a day.&lt;/p&gt;

&lt;p&gt;The hardest question asked about continuous delivery is often: “How can your customer decide when a feature is good enough for release?”. This is something that you need to decide per feature. With Episerver creating new content features is easy. Customer tests the new content types and decides when they serve them well enough to be published. Changing existing content can be done with feature toggles. Of course creating a feature toggle is a change too and can be misimplemented. From this emerges the need for automatic testing. You need to catch errors before they get on the production. The hardest kind of changes are cross-cutting concerns like authentication or database schema changes. You need to be super careful in how do you implement and roll these kind of features to the production. In the end the most important thing is that your development organization (developers and business people) have the correct working habits and the mindset for failing fast and failing often. Some day you will fail and with CD you will be most likely to recover faster.&lt;/p&gt;

&lt;p&gt;Having said scary things about deploying features there is the positive side of the coin. When you deploy often you have only a small changeset at the time. It is a lot easier to find the problems on your patch and fix them. Installing the fix is also easy when you have process to take care of all and when manual work is not involved.&lt;/p&gt;

&lt;h2 id=&quot;the-dxc-service&quot;&gt;the DXC Service&lt;/h2&gt;

&lt;p&gt;In case you don’t know what the DXC Service is I will describe it briefly. The DXC Service is a managed platform from Episerver to host Episerver CMS in the Azure cloud. Instead of working directly with the Azure platform you will get the services the Episerver thinks that you need. In case that you wish to have more services. Well tough luck. You will need to setup an additional Azure subscription because you won’t be adding them to the DXC Service subscription. Actually you won’t be able to see your production stuff in Azure portal at all. The DXC Service comes with three environments: integration, preproduction and production. The architecture of the DXC service is as following:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_architecture.png&quot; alt=&quot;The DXC Service architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The architecture picture was taken directly from their &lt;a href=&quot;https://www.episerver.com/legal/episerver-dxc-service-description/&quot;&gt;service description&lt;/a&gt;. What I would like to highlight here is that they have CDN, they are using Azure Web apps, Azure Storage, Azure Service Bus, Azure SQL and a smtp service. This is a pretty recommended setup when building web applications to Azure.&lt;/p&gt;

&lt;p&gt;The DXC Service architecture is actually pretty nice as it is. If you are building up simple CMS solution you won’t be needing much else to be honest. In case you are building extranet, intranet or commerce site then you might end up in a different situation. The main problem is that you won’t be able to add services like Azure Functions, Azure AD, Azure B2C AD or even use the full potential of underlying infrastructure since WebJobs are not supported. You will also have limited visibility on the services you have like Azure Service Bus, Azure Storage and Azure SQL. Episerver does not provide you the technological details about the services you are running but you can of course implement functionality to your production site that tells you how they have been configured (of course there will be security issues with this). Things that you will do with this information is of course in the danger zone. I have been doublechecking what I can do and what I can’t do constantly from Episerver. If you are going to implement a complex project on top of the Episerver DXC Service then you should plan on having an Azure Subscription for deploying all the additional services you might need.&lt;/p&gt;

&lt;p&gt;You might be able to read between the lines that I’m not perfectly happy about lost control on the Azure services in the DXC Service. Another thing I’m not perfectly satisfied about is the deployment process. Episerver has decided to reinvent the wheel and implement their own deployment pipeline for the DXC Service. You can do deployments with WebDeploy only to integration environment. From that point onwards you will need to use either their own portal or make service requests (via phone or email) to get deployment into preproduction and finally you need a service request to get the software into the production. Effectively this means that you will have hard times to control the exact moment of a deployment for preproduction and production via continuous delivery. To preproduction environment you will be able to do manual deployments via their own portal and they have been promising to open that for production too. Unfortunately there is no API for that or no supported devops way whatsoever. This means that you either fall back to emailing them or make some serious browser scripting to bypass these problems. We went to the email way because I think that it pushes Episerver towards finding the right solutions for this problem. The problem with email deployment is 24h SLA for registering a ticket (Episerver does not do this automatically) and 24h SLA for fulfilling request which sums up to total of 48h to get the deployment done once email is sent. Which effectively would mean that we need to ask the deployment 48h before if we want it to take place at the exact moment of time. Here is the The Episerver DXC Service description vision about the deployments.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_deployments.png&quot; alt=&quot;The DXC Service deployments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I guess the noteworthy thing is that code swims towards production and content swims towards development. Content arrow towards production means once only in the case of the first deployment. This means that you can replenish your integration and preproduction environment data with the production data. If you wish to do so you need to implement your solution so that you can recover integration and preproduction sites to the correct site settings etc. Episerver won’t do that for you. One another thing to notice is all the stored data from the Episerver Forms and other personal data storages. I would highly encourage to delete completely or scramble that data on the replenish process.&lt;/p&gt;

&lt;h2 id=&quot;our-team-and-the-process&quot;&gt;Our team and the process&lt;/h2&gt;

&lt;p&gt;We are working on a project that has multiple CMS sites on the same DXC Service platform. It is not a simple project and our developer team is somewhat big for an Episerver project. Currently we have seven developers working on the project most of them being full-time and few doing split with some other project. This means that in a busy day there will be a lot of stuff going on on our Git repository. Our working habits wary on the problem at hand. If we are doing straightward and simple things just do it and push it. If we are doing tricky things then creating a branch and having other developer reviewing your work is advised. That is enough background information. Now we should focus on the process that starts on the git push to the master branch. Here is an overall picture of our Jenkins build pipeline.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_deployment_pipeline.png&quot; alt=&quot;The DXC Service deployment pipeline&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Jenkins build pipeline has been separated to few stages that somehow represents where things are happening. For most of the stuff those are self-explanatatory but there are few scheduled tasks that are more or less just thrown somewhere. These are long-running or heavy tasks that needs time more than we are willing to give for each deployment. For example ZAP scans can take hours and we want to publish fast and often. So instead of scanning every build we are doing daily scanning. Here is few explanations what is happening.&lt;/p&gt;

&lt;h5 id=&quot;build-phase&quot;&gt;Build phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Build trigger task is triggered by git commit&lt;/li&gt;
  &lt;li&gt;Build tasks builds the software and makes a release to Octopus Deploy&lt;/li&gt;
  &lt;li&gt;Unit-tests are run after the build and making the release&lt;/li&gt;
  &lt;li&gt;OWASP-Dependenchy-Check scans nightly if we have any known vulnerabilities in our binaries&lt;/li&gt;
  &lt;li&gt;Sonar-Tests makes nightly a static code analysis of our current codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;dev-phase&quot;&gt;Dev phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy makes a deployment with Octopus deploy to our internal test server once unit tests are passed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are run to see if the site is fine&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;test-phase&quot;&gt;Test phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy makes a deployment with Octopus deploy to the DXC Service integration environment once the Dev smoke-tests have passed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are ran to see if the site is fine&lt;/li&gt;
  &lt;li&gt;Performance-Tests are run nightly to track on the performance of the website&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;staging-phase&quot;&gt;Staging phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy sends an email to the Episerver support for an deployment once the Test smoke-tests have passed&lt;/li&gt;
  &lt;li&gt;Smoke-Tests-Polling-Trigger constantly polls if the version of the site has changed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are ran whenever polling trigger notices a change in the version of the site&lt;/li&gt;
  &lt;li&gt;ZAP-scans are run nightly to see if there are any low-hanging security enhancements to be made&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;prod-phase&quot;&gt;Prod phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy sends an email to the Episerver support for an deployment once per day or in demand&lt;/li&gt;
  &lt;li&gt;Smoke-Tests-Polling-Trigger constantly polls if the version of the site has changed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are ran whenever polling trigger notices a change in the version of the site&lt;/li&gt;
  &lt;li&gt;Data to staging and data to test sends an email to the Episerver support to refresh data of integration and preproduction environment weekly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So from quality perspective to get the software into production it has been surely passed unit-tests and smoke-tests. In addition we have SonarQube, OWASP Dependency Check, OWASP ZAP and jMeter gathering information that needs to be digested. On top of that we of course follow what is happening in the Episerver and in the .NET versions. Here is some kind of picture about different kind of information sources that we are leveraging when building towards top quality.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_infosources.png&quot; alt=&quot;The DXC Service information sources&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;building-the-build-pipeline&quot;&gt;Building the build pipeline&lt;/h2&gt;

&lt;p&gt;After the long introductionary presentation of deployment philosophy we finally get the point were I can show some code. We are using &lt;a href=&quot;https://github.com/jenkinsci/job-dsl-plugin/wiki&quot;&gt;the Jenkins Job DSL plugin&lt;/a&gt; in building our build pipeline. This means that we have a second repository for infrastructure where we have a groovy script for the Jenkins pipeline. Then we have one job in the Jenkins that builds up the other Jenkins jobs with the groovy script. So let’s see how our phases are from that perspective. Starting from easy side this is how the build trigger looks like.&lt;/p&gt;

&lt;h2 id=&quot;build-trigger&quot;&gt;Build trigger&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Trigger&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;deliveryPipelineConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Build Trigger&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;wrappers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deliveryPipelineVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\$GIT_REVISION&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;scm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;remote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gitRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gitCredentialsId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branchName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;* * * * *&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;quietPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;downstream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;SUCCESS&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So the job has cron scheduler that constantly polls if there are changes on the git repository and triggers a downstream job if there are. There are a lot of variables used to make it easier to change the naming practices of the whole pipeline. From this point onward I will present only parts of the job DSL to reduce the amount of noise.&lt;/p&gt;

&lt;h2 id=&quot;build-and-octopus-release&quot;&gt;Build and octopus release&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builders&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;builders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.msbuild.MsBuildBuilder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;msBuildName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msBuildInstallationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;msBuildFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cmdLineArgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/t:Build /p:RunOctoPack=true /p:OctoPackPackageVersion=1.0.${BUILD_NUMBER} /p:OctoPackPublishPackageToHttp=&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oServerUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;nuget/packages /p:CmdLineInMemoryStorage=true /p:Configuration=Release /p:OctoPackPublishApiKey=${OCTOPUS_API_KEY} /p:DeployOnBuild=false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;buildVariablesAsProperties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pubs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pubs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.octopusdeploy.OctopusDeployReleaseRecorder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;serverId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oServerId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oProject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;tenant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;waitForDeployment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;1.0.\${BUILD_NUMBER}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseNotes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseNotesSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;scm&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oChannel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseNotesJenkinsLinkback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;deployThisRelease&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;packageConfigs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
			&lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.octopusdeploy.PackageConfiguration&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;packageName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oPackage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;packageVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;1.0.\${BUILD_NUMBER}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultPackageVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;1.0.\${BUILD_NUMBER}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In case you wondered why I made release before running unit tests. The answer is that I like to have unit-tests and build separated in the pipeline. Still I want to be able to easily track octopus deploy build version in Jenkins and the easiest way is to use build number as a version number. This way every build will have a unique number and I can find out which Octopus release is which Jenkins build. In addition to all this at this point we will do few funny things. We package the git commit hash and the build number into the folder structure and archive the application. Archiving means that later on other Jenkins jobs can access this job from downstream and use the same binaries.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\$path = &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildVersionPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;If(!(test-path \$path)){ New-Item -ItemType Directory -Force -Path \$path }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;git rev-parse --verify HEAD | Set-Content &quot;\$path/ver.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;\$env:BUILD_NUMBER | Set-Content &quot;\$path/rel.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the groovy script to generate a PowerShell script that is able to parse the information that we need and put it on to the folder structure. Finally we archive the whole thing.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;archiveArtifacts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;**/*&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;**/.git/**,**/static/node_modules/**,**/static/src/**,**/static/styleguide/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;onlyIfSuccessful&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There some excludes added to archiving for getting better IO out of the build server. Without those excludes every build step that uses this archive seemed to last 20-30 minutes longer since we have a lot of node modules and our repository is becoming a rather large one.&lt;/p&gt;

&lt;h2 id=&quot;unit-tests&quot;&gt;Unit tests&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;copyArtifacts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Build&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;buildSelector&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;upstreamBuild&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;allowUpstreamDependencies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;fallbackToLastSuccessful&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;includePatterns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;**/*&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mstest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;org.jenkinsci.plugins.MsTestBuilder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;msTestName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msTestVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;testFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationPrefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testDllPostFix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\\bin\\Release\\&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationPrefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testDllPostFix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;.dll&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;resultFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;continueOnFail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;categories&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;cmdLineArgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mstestPublish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ publishers /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.mstest.MSTestPublisher&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;testResultsFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running unit tests is not especially interesting. First we copy the archived information from the upstream job. Then we just run the tests and make sure that there alerts about failure. We have found having chatbots the most convenient solution for the team.&lt;/p&gt;

&lt;h2 id=&quot;deployment-with-octopus-deploy&quot;&gt;Deployment with Octopus Deploy&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;$ErrorActionPreference = &quot;Stop&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;\$path = &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildVerPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;\$releaseNumber = Get-Content &quot;\$path/rel.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;octo deploy-release --project &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oProject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --channel &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oChannel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --releaseNumber 1.0.\$releaseNumber --deployto &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oEnvironment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --server &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oServerUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --progress --waitfordeployment --apiKey \$Env:OCTOPUS_API_KEY&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From the Jenkins perspective the deployment process is quite easy. Just make a call for octo.exe. Of course you need to setup your deployment processes into the Octopus Deploy. For us they are rather simple. For our internal testing environment (we are calling it CI) we use octopus deploy agent and do just some configuration transformation beside normal WebDeploy. For the DXC Service integration environment we deploy to a deployment slot. Clean up few files and finally swap slots. The files we need to clean up are the configuration transformation files for the CI and postdeploy scripts. They are not cleaned up automatically with the Azure Web App as they are with the on-premise IIS server.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_octopus_deployment_process.png&quot; alt=&quot;The DXC Service deployments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The nice thing our setup is the build promotion. Even though we are rebuilding software for unit-tests and smoke-tests we are building only once for deployments. Binary compilation and configuration are separated from each other which means that we can trust that our binaries are the same in each environment and they won’t change because of time relative issues like some 3rd party dependency not being fetched or somebody patching server. Even when we are doing deployments from the Jenkins we can still see and do build promotions from the Octopus Deploy too.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_octopus_build_promotion.png&quot; alt=&quot;The DXC Service build promotions&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;smoke-testing&quot;&gt;Smoke testing&lt;/h2&gt;

&lt;p&gt;Smoke testing is done with the same kind of DSL as the unit testing as we are using unit tests for smoke testing too. We just have two separated projects in our solution for different kind of tests. Smoke tests are tests that needs something to be deployed before we can test. We have few basic tests that we do.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Urls in the sitemap returns 200&lt;/li&gt;
  &lt;li&gt;Different archetypes of pages render with JS ok&lt;/li&gt;
  &lt;li&gt;Robots.txt is found&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could see that all the pages would render with JS but it would take a really long time. The only point where smoke testing differs from the unit tests is that since Episerver does not always startup so fast we have a wait routine for waiting the server to respond with reasonable http statuscodes after full startup.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\$request = [system.Net.WebRequest]::Create(&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;)&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;$result = &quot;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;for (\$i = 1; \$i -le 100; \$i++) {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  try {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    \$result = \$request.GetResponse()&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    if (\$result -is &quot;System.Net.HttpWebResponse&quot; -and \$result.StatusCode -ne &quot;&quot;) {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;      break&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  } catch [System.Net.WebException] {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    \$result = \$_.Exception.Response &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  Start-Sleep -s 2&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Write-Host &quot;Status was: \$(\$result.StatusCode)&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;email-deployments&quot;&gt;Email deployments&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailSubject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Pre-production deployment \$PIPELINE_VERSION&#39;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailContent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Schedule: ASAP&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nPriority: Medium&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nPipeline revision: \$PIPELINE_VERSION&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nFrom: Integration&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nTo: Pre-production&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nDeploy: &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tCode: Yes&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; 
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tDatabase: No&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tBlobs: No&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; 
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tDeployment verification url:&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\t\thttps://www.google.fi?q=figure+out&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\nBest Regards: Jenkins&#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;extendedEmail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;recipientList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailRecipientList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;replyToList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailReplyTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;text/plain&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;preSendScript&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;$DEFAULT_PRESEND_SCRIPT\nmsg.addHeader(&quot;X-Priority&quot;, &quot;1 (Highest)&quot;);\nmsg.addHeader(&quot;Importance&quot;, &quot;High&quot;);\nmsg.setFrom(new javax.mail.internet.InternetAddress(&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailSender&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;))&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;beforeBuild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is pretty close that we got into. We use some Java inside groovy script to setup email being sent as a high priority and to rewrite the sender to be something that we actually want to get responses to. We also have a verification url packaged into emails that we wish the Episerver Support to use to see if the software is fine and rollback if the page does not render correctly. We have same kind of emails for the data replenish too. Instead code we just say deploy data.&lt;/p&gt;

&lt;h2 id=&quot;triggering-smoke-tests&quot;&gt;Triggering smoke tests&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;./persistenthash.txt&#39;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;./temphash.txt&#39;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;./test_if_this_exists.txt&#39;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;if(!(test-path &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;)) { &quot;init_first_non_existent_hash&quot; | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;rm -force &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; -ErrorAction SilentlyContinue&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;rm -force &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; -ErrorAction SilentlyContinue&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;curl https://www.google.fi?q=ver.txt -UseBasicParsing | % { $_.Content | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;if((Get-FileHash &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;).hash -ne (Get-FileHash &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;).hash) { &quot;true&quot; | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;conditionalSteps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;fileExists&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;WORKSPACE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;DontRun&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;curl https://www.google.fi?q=ver.txt -UseBasicParsing | % { $_.Content | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;downstreamParameterized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stagingEnvironmentName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Smoke-Tests&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The idea of this PowerShell script is to store a commit hash into our jobs working folder. Then see on every 5 minutes if the hash we have is matching with the one that is on the production server (yeah, I replaced the actual url with google urls). If it does match then nothing happens. If it does not then we create a temporary file. Then we have a conditional step that from Jenkins which checks if the temporary file is present or not. If there is a file it updates the hash in working folder and triggers smoke tests for corresponding environment. This way our smoke tests start running within 5 minutes after the Episerver support has done the deployment.&lt;/p&gt;

&lt;h2 id=&quot;the-nightly-stuff&quot;&gt;The nightly stuff&lt;/h2&gt;

&lt;p&gt;Here is how I run the OWASP ZAP from Jenkins via PowerShell. I use &lt;a href=&quot;https://github.com/solita/powershell-zap&quot;&gt;my own PowerShell modules for managing ZAP&lt;/a&gt;. There are Jenkins plugins too but I had my own version before they existed so I’m kind of stuck on my own baby. I tried the plugin but it just didn’t do the same thing so I left it and went back to my own scripts.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zapReport&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;(\$env:WORKSPACE+&quot;\\&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapReportFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;)&#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parentJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Import-Module C:\\tools\\powershell-zap-master\\PowerShell-ZAP\\PowerShell-ZAP.psm1&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapLocation &quot;C:\\Program Files\\OWASP\\Zed Attack Proxy\\&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapReportLocation &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapReport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapUrlToScan &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapUrlToScan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Ensure that daemon is running&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Start-Zap&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Configure policies, this just enables all scanners atm&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapScanPolicies&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Do spidering against the url&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Invoke-ZapSpidering &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Do ajax spidering against the url&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Invoke-ZapAjaxSpidering&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Do scanning against the url&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Invoke-ZapScanning&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Save report&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Save-ZapReport&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Destroy scans&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Remove-ZapCurrentSpider&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Remove-ZapCurrentScan&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parentJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;archiveJunit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapReportFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;allowEmptyResults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;retainLongStdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;testDataPublishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;publishTestStabilityData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For performance monitoring we use my coworkers &lt;a href=&quot;https://github.com/jounihuttunen/jmeter-perfotrator&quot;&gt;jmeter-perfotrator&lt;/a&gt; which is just a template for jMeter. From the Jenkins the usage is quite simple.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	 &lt;span class=&quot;n&quot;&gt;batchFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;call &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jmeterPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; -n -t &quot;%WORKSPACE%\\Solution Items\\jmeter\\TestUrls.jmx&quot; -j &quot;%WORKSPACE%\\log.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
		&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perfPublish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.performance.PerformancePublisher&#39;&lt;/span&gt; 
		&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perfPublish&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ &#39;parsers&#39; /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.performance.JMeterParser&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;glob&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;results\\results.jtl&#39;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the dependency checks we use a plugin. In addition you need to setup a job to update the dependency data. After you have the fresh dependency data then you can run the check with DSL like this:&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owaspCheck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;org.jenkinsci.plugins.DependencyCheck.DependencyCheckBuilder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;skipOnScmChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;skipOnUpstreamChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;scanpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;outdir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;datadir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\\OWASP_DEPENDENCY_DATA&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;suppressionFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;isAutoupdateDisabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;includeHtmlReports&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owaspPublish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ publishers /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;org.jenkinsci.plugins.DependencyCheck.DependencyCheckPublisher&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;healthy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;unHealthy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;thresholdLimit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;low&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pluginName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[DependencyCheck] &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultEncoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;canRunOnFailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;usePreviousBuildAsReference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;useStableBuildAsReference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;useDeltaValues&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;shouldDetectModules&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dontComputeNew&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;doNotResolveRelativePaths&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the static code analysis we use the SonarQube. It is fairly easy to setup with the Jenkins. Just wrap your build between the SonarQube begin and end analysis.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Configure begin analysis&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
		&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sonarBegin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.sonar.MsBuildSQRunnerBegin&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;projectKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarProjectKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;projectName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarProjectName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;projectVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarProjectVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;msBuildScannerInstallationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarScannerName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ... Your build goes here&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Configure end analysis&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
		&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sonarEnd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.sonar.MsBuildSQRunnerEnd&#39;&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;show-it-on-the-tv&quot;&gt;Show it on the TV&lt;/h2&gt;

&lt;p&gt;Finally one of the biggest strenghts of this setup is the ability to show it on the screen with relatively simple DSL (and of course with some Jenkins plugins).&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;deliveryPipelineView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; delivery Pipeline&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pipelineInstances&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;showAvatars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;showChangeLog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;showTotalBuildTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;enableManualTriggers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;allowRebuild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;showAggregatedPipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Trigger&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;buildMonitorView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; build Monitor&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; jobs&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;jobs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;.*&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;.*&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two views. The pipeline view shows the view that was in the beginning of the blog post where you could see different phases of the continuous delivery process. The build monitor view builds up a screen full of rectangles that are green / yellow / red depending on your jobs status.&lt;/p&gt;

&lt;p&gt;We are asking the production deployment in the early morning hours so it usually takes place just before we got to the work. It is kind of peaceful time to do deployments and we have few earlybirds that will notice if something strange is going on when they get to work.&lt;/p&gt;

&lt;h2 id=&quot;summarizing&quot;&gt;Summarizing&lt;/h2&gt;

&lt;p&gt;Some characteristics about our project:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;More than 100 deployments per month&lt;/li&gt;
  &lt;li&gt;Go live every workday morning (~20 times a month)&lt;/li&gt;
  &lt;li&gt;Half a year of live site hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have had a few issues but nothing that would have upset our customer badly. Most of the times if there is an issue our smoke-tests notices it earlier than the Episerver Support does even when they are the ones who are deploying it. Usually the problems have been on the configuration transformation that is done by the Episerver Support with a configuration that we are not aware of. Once or twice we have been forced to make a fix patch immediately after going live. On these rare occasions it takes usually less time to implement the patch than to get episerver installing it. They tell us to call them if we have problem but it is really hard to call and solve the problem at the same time. If we call them, then we need double the people to figure out the situation since the telephone consumes one guy.&lt;/p&gt;

&lt;p&gt;In the DXC environment the problem remains that after a failed deployment it takes too much time to get the fixing deployment on the way. Immediate rollbacks do help, but not if we need to wait for a hour or two for that to happen. We have a fix ready on that time and then we would rather go forward-only deployment with the fixed version.&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2018/01/04/Continuous_delivery_with_episerver_dxcs</guid>            <pubDate>Thu, 04 Jan 2018 08:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Continuous delivery with the Episerver DXC Service</title>            <link>http://dev.solita.fi/episerver/2018/01/04/Continuous_delivery_with_episerver_dxcs</link>            <description>&lt;p&gt;I have put a lot of hours into building up a continuous delivery environment which I can be proud of. I already wrote &lt;a href=&quot;http://dev.solita.fi/episerver/2017/11/21/Installing-Jenkins-with-PowerShell-DSC.html&quot;&gt;in my previous blog&lt;/a&gt; about how to setup a Jenkins for this purpose. It was a ground work where we start building in this blog post. This blog post is about how to do continuous Delivery with Episerver Digital Experiance Cloud Service but nothing stops you to use this information in some other place.&lt;/p&gt;

&lt;h2 id=&quot;continuous-delivery-cd-and-continuous-integration-ci&quot;&gt;Continuous delivery (CD) and continuous integration (CI)&lt;/h2&gt;

&lt;p&gt;For sure there are better references about CI and CD than following will be. Actually, if you have hard time in convincing your product owner to continuous delivery we have &lt;a href=&quot;http://dev.solita.fi/2016/09/06/CD-for-UX-and-PO.html&quot;&gt;a blog&lt;/a&gt; for that purpose as well. For me continuous integration means merging work in git repository hourly with coworkers working on the same things. On the other hand continuous delivery means pushing the work to the customer as fast as we can. Developers work on the codebase and then deploy it once they are confident with it. When you speed up the cycle of your deployments, your customer will have new features faster, and you they can steer the direction of your development in a truly agile way. Characteristics of CI and CD are below.&lt;/p&gt;

&lt;h4 id=&quot;continuous-integration&quot;&gt;Continuous integration:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Integrate early and often&lt;/li&gt;
  &lt;li&gt;Make the code available to other developers&lt;/li&gt;
  &lt;li&gt;Make sure the code builds after merging to the works of other people&lt;/li&gt;
  &lt;li&gt;Track the quality of your code&lt;/li&gt;
  &lt;li&gt;Test your code&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;continuous-delivery&quot;&gt;Continuous delivery:&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Push changes to production early and often&lt;/li&gt;
  &lt;li&gt;Make the code available to your end-users&lt;/li&gt;
  &lt;li&gt;Make sure that you can deploy at any time&lt;/li&gt;
  &lt;li&gt;Track the quality of your runtime&lt;/li&gt;
  &lt;li&gt;Test your deployments with automated release process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I feel that continuous integration is something that software business is doing already with high adaptation rate. Continuous delivery in the other hand is something that many are still learning. Continuous delivery definitely needs more maturity from the development team since you need to automate a lot of things to be able to deploy automatically. You will need a process that makes all kind of sanity checks across the line to trust the deployments on the hand of the automated system. The good thing is once you get on the road it will be a lot easier to adjust the process when you have ability to repeat it multiple times a day.&lt;/p&gt;

&lt;p&gt;The hardest question asked about continuous delivery is often: “How can your customer decide when a feature is good enough for release?”. This is something that you need to decide per feature. With Episerver creating new content features is easy. Customer tests the new content types and decides when they serve them well enough to be published. Changing existing content can be done with feature toggles. Of course creating a feature toggle is a change too and can be misimplemented. From this emerges the need for automatic testing. You need to catch errors before they get on the production. The hardest kind of changes are cross-cutting concerns like authentication or database schema changes. You need to be super careful in how do you implement and roll these kind of features to the production. In the end the most important thing is that your development organization (developers and business people) have the correct working habits and the mindset for failing fast and failing often. Some day you will fail and with CD you will be most likely to recover faster.&lt;/p&gt;

&lt;p&gt;Having said scary things about deploying features there is the positive side of the coin. When you deploy often you have only a small changeset at the time. It is a lot easier to find the problems on your patch and fix them. Installing the fix is also easy when you have process to take care of all and when manual work is not involved.&lt;/p&gt;

&lt;h2 id=&quot;the-dxc-service&quot;&gt;the DXC Service&lt;/h2&gt;

&lt;p&gt;In case you don’t know what the DXC Service is I will describe it briefly. The DXC Service is a managed platform from Episerver to host Episerver CMS in the Azure cloud. Instead of working directly with the Azure platform you will get the services the Episerver thinks that you need. In case that you wish to have more services. Well tough luck. You will need to setup an additional Azure subscription because you won’t be adding them to the DXC Service subscription. Actually you won’t be able to see your production stuff in Azure portal at all. The DXC Service comes with three environments: integration, preproduction and production. The architecture of the DXC service is as following:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_architecture.png&quot; alt=&quot;The DXC Service architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The architecture picture was taken directly from their &lt;a href=&quot;https://www.episerver.com/legal/episerver-dxc-service-description/&quot;&gt;service description&lt;/a&gt;. What I would like to highlight here is that they have CDN, they are using Azure Web apps, Azure Storage, Azure Service Bus, Azure SQL and a smtp service. This is a pretty recommended setup when building web applications to Azure.&lt;/p&gt;

&lt;p&gt;The DXC Service architecture is actually pretty nice as it is. If you are building up simple CMS solution you won’t be needing much else to be honest. In case you are building extranet, intranet or commerce site then you might end up in a different situation. The main problem is that you won’t be able to add services like Azure Functions, Azure AD, Azure B2C AD or even use the full potential of underlying infrastructure since WebJobs are not supported. You will also have limited visibility on the services you have like Azure Service Bus, Azure Storage and Azure SQL. Episerver does not provide you the technological details about the services you are running but you can of course implement functionality to your production site that tells you how they have been configured (of course there will be security issues with this). Things that you will do with this information is of course in the danger zone. I have been doublechecking what I can do and what I can’t do constantly from Episerver. If you are going to implement a complex project on top of the Episerver DXC Service then you should plan on having an Azure Subscription for deploying all the additional services you might need.&lt;/p&gt;

&lt;p&gt;You might be able to read between the lines that I’m not perfectly happy about lost control on the Azure services in the DXC Service. Another thing I’m not perfectly satisfied about is the deployment process. Episerver has decided to reinvent the wheel and implement their own deployment pipeline for the DXC Service. You can do deployments with WebDeploy only to integration environment. From that point onwards you will need to use either their own portal or make service requests (via phone or email) to get deployment into preproduction and finally you need a service request to get the software into the production. Effectively this means that you will have hard times to control the exact moment of a deployment for preproduction and production via continuous delivery. To preproduction environment you will be able to do manual deployments via their own portal and they have been promising to open that for production too. Unfortunately there is no API for that or no supported devops way whatsoever. This means that you either fall back to emailing them or make some serious browser scripting to bypass these problems. We went to the email way because I think that it pushes Episerver towards finding the right solutions for this problem. The problem with email deployment is 24h SLA for registering a ticket (Episerver does not do this automatically) and 24h SLA for fulfilling request which sums up to total of 48h to get the deployment done once email is sent. Which effectively would mean that we need to ask the deployment 48h before if we want it to take place at the exact moment of time. Here is the The Episerver DXC Service description vision about the deployments.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_deployments.png&quot; alt=&quot;The DXC Service deployments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I guess the noteworthy thing is that code swims towards production and content swims towards development. Content arrow towards production means once only in the case of the first deployment. This means that you can replenish your integration and preproduction environment data with the production data. If you wish to do so you need to implement your solution so that you can recover integration and preproduction sites to the correct site settings etc. Episerver won’t do that for you. One another thing to notice is all the stored data from the Episerver Forms and other personal data storages. I would highly encourage to delete completely or scramble that data on the replenish process.&lt;/p&gt;

&lt;h2 id=&quot;our-team-and-the-process&quot;&gt;Our team and the process&lt;/h2&gt;

&lt;p&gt;We are working on a project that has multiple CMS sites on the same DXC Service platform. It is not a simple project and our developer team is somewhat big for an Episerver project. Currently we have seven developers working on the project most of them being full-time and few doing split with some other project. This means that in a busy day there will be a lot of stuff going on on our Git repository. Our working habits wary on the problem at hand. If we are doing straightward and simple things just do it and push it. If we are doing tricky things then creating a branch and having other developer reviewing your work is advised. That is enough background information. Now we should focus on the process that starts on the git push to the master branch. Here is an overall picture of our Jenkins build pipeline.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_deployment_pipeline.png&quot; alt=&quot;The DXC Service deployment pipeline&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Jenkins build pipeline has been separated to few stages that somehow represents where things are happening. For most of the stuff those are self-explanatatory but there are few scheduled tasks that are more or less just thrown somewhere. These are long-running or heavy tasks that needs time more than we are willing to give for each deployment. For example ZAP scans can take hours and we want to publish fast and often. So instead of scanning every build we are doing daily scanning. Here is few explanations what is happening.&lt;/p&gt;

&lt;h5 id=&quot;build-phase&quot;&gt;Build phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Build trigger task is triggered by git commit&lt;/li&gt;
  &lt;li&gt;Build tasks builds the software and makes a release to Octopus Deploy&lt;/li&gt;
  &lt;li&gt;Unit-tests are run after the build and making the release&lt;/li&gt;
  &lt;li&gt;OWASP-Dependenchy-Check scans nightly if we have any known vulnerabilities in our binaries&lt;/li&gt;
  &lt;li&gt;Sonar-Tests makes nightly a static code analysis of our current codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;dev-phase&quot;&gt;Dev phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy makes a deployment with Octopus deploy to our internal test server once unit tests are passed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are run to see if the site is fine&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;test-phase&quot;&gt;Test phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy makes a deployment with Octopus deploy to the DXC Service integration environment once the Dev smoke-tests have passed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are ran to see if the site is fine&lt;/li&gt;
  &lt;li&gt;Performance-Tests are run nightly to track on the performance of the website&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;staging-phase&quot;&gt;Staging phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy sends an email to the Episerver support for an deployment once the Test smoke-tests have passed&lt;/li&gt;
  &lt;li&gt;Smoke-Tests-Polling-Trigger constantly polls if the version of the site has changed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are ran whenever polling trigger notices a change in the version of the site&lt;/li&gt;
  &lt;li&gt;ZAP-scans are run nightly to see if there are any low-hanging security enhancements to be made&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;prod-phase&quot;&gt;Prod phase&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Deploy sends an email to the Episerver support for an deployment once per day or in demand&lt;/li&gt;
  &lt;li&gt;Smoke-Tests-Polling-Trigger constantly polls if the version of the site has changed&lt;/li&gt;
  &lt;li&gt;Smoke-tests are ran whenever polling trigger notices a change in the version of the site&lt;/li&gt;
  &lt;li&gt;Data to staging and data to test sends an email to the Episerver support to refresh data of integration and preproduction environment weekly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So from quality perspective to get the software into production it has been surely passed unit-tests and smoke-tests. In addition we have SonarQube, OWASP Dependency Check, OWASP ZAP and jMeter gathering information that needs to be digested. On top of that we of course follow what is happening in the Episerver and in the .NET versions. Here is some kind of picture about different kind of information sources that we are leveraging when building towards top quality.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_infosources.png&quot; alt=&quot;The DXC Service information sources&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;building-the-build-pipeline&quot;&gt;Building the build pipeline&lt;/h2&gt;

&lt;p&gt;After the long introductionary presentation of deployment philosophy we finally get the point were I can show some code. We are using &lt;a href=&quot;https://github.com/jenkinsci/job-dsl-plugin/wiki&quot;&gt;the Jenkins Job DSL plugin&lt;/a&gt; in building our build pipeline. This means that we have a second repository for infrastructure where we have a groovy script for the Jenkins pipeline. Then we have one job in the Jenkins that builds up the other Jenkins jobs with the groovy script. So let’s see how our phases are from that perspective. Starting from easy side this is how the build trigger looks like.&lt;/p&gt;

&lt;h2 id=&quot;build-trigger&quot;&gt;Build trigger&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Trigger&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;deliveryPipelineConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Build Trigger&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;wrappers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deliveryPipelineVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\$GIT_REVISION&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;scm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;remote&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gitRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gitCredentialsId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branchName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;* * * * *&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;quietPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;downstream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;SUCCESS&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So the job has cron scheduler that constantly polls if there are changes on the git repository and triggers a downstream job if there are. There are a lot of variables used to make it easier to change the naming practices of the whole pipeline. From this point onward I will present only parts of the job DSL to reduce the amount of noise.&lt;/p&gt;

&lt;h2 id=&quot;build-and-octopus-release&quot;&gt;Build and octopus release&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builders&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;builders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.msbuild.MsBuildBuilder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;msBuildName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msBuildInstallationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;msBuildFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cmdLineArgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/t:Build /p:RunOctoPack=true /p:OctoPackPackageVersion=1.0.${BUILD_NUMBER} /p:OctoPackPublishPackageToHttp=&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oServerUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;nuget/packages /p:CmdLineInMemoryStorage=true /p:Configuration=Release /p:OctoPackPublishApiKey=${OCTOPUS_API_KEY} /p:DeployOnBuild=false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;buildVariablesAsProperties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pubs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pubs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.octopusdeploy.OctopusDeployReleaseRecorder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;serverId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oServerId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oProject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;tenant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;waitForDeployment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;1.0.\${BUILD_NUMBER}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseNotes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseNotesSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;scm&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oChannel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;releaseNotesJenkinsLinkback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;deployThisRelease&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;packageConfigs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
			&lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.octopusdeploy.PackageConfiguration&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;packageName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oPackage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;packageVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;1.0.\${BUILD_NUMBER}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultPackageVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;1.0.\${BUILD_NUMBER}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In case you wondered why I made release before running unit tests. The answer is that I like to have unit-tests and build separated in the pipeline. Still I want to be able to easily track octopus deploy build version in Jenkins and the easiest way is to use build number as a version number. This way every build will have a unique number and I can find out which Octopus release is which Jenkins build. In addition to all this at this point we will do few funny things. We package the git commit hash and the build number into the folder structure and archive the application. Archiving means that later on other Jenkins jobs can access this job from downstream and use the same binaries.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\$path = &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildVersionPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;If(!(test-path \$path)){ New-Item -ItemType Directory -Force -Path \$path }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;git rev-parse --verify HEAD | Set-Content &quot;\$path/ver.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;\$env:BUILD_NUMBER | Set-Content &quot;\$path/rel.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We use the groovy script to generate a PowerShell script that is able to parse the information that we need and put it on to the folder structure. Finally we archive the whole thing.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;archiveArtifacts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;**/*&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;**/.git/**,**/static/node_modules/**,**/static/src/**,**/static/styleguide/**&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;onlyIfSuccessful&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There some excludes added to archiving for getting better IO out of the build server. Without those excludes every build step that uses this archive seemed to last 20-30 minutes longer since we have a lot of node modules and our repository is becoming a rather large one.&lt;/p&gt;

&lt;h2 id=&quot;unit-tests&quot;&gt;Unit tests&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;copyArtifacts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Build&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;buildSelector&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;upstreamBuild&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;allowUpstreamDependencies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;fallbackToLastSuccessful&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;includePatterns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;**/*&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mstest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;org.jenkinsci.plugins.MsTestBuilder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;msTestName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msTestVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;testFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationPrefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testDllPostFix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\\bin\\Release\\&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationPrefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testDllPostFix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;.dll&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;resultFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;continueOnFail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;false&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;categories&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;cmdLineArgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mstestPublish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ publishers /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.mstest.MSTestPublisher&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;testResultsFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running unit tests is not especially interesting. First we copy the archived information from the upstream job. Then we just run the tests and make sure that there alerts about failure. We have found having chatbots the most convenient solution for the team.&lt;/p&gt;

&lt;h2 id=&quot;deployment-with-octopus-deploy&quot;&gt;Deployment with Octopus Deploy&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;$ErrorActionPreference = &quot;Stop&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;\$path = &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildVerPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;\$releaseNumber = Get-Content &quot;\$path/rel.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;octo deploy-release --project &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oProject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --channel &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oChannel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --releaseNumber 1.0.\$releaseNumber --deployto &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oEnvironment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --server &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oServerUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; --progress --waitfordeployment --apiKey \$Env:OCTOPUS_API_KEY&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From the Jenkins perspective the deployment process is quite easy. Just make a call for octo.exe. Of course you need to setup your deployment processes into the Octopus Deploy. For us they are rather simple. For our internal testing environment (we are calling it CI) we use octopus deploy agent and do just some configuration transformation beside normal WebDeploy. For the DXC Service integration environment we deploy to a deployment slot. Clean up few files and finally swap slots. The files we need to clean up are the configuration transformation files for the CI and postdeploy scripts. They are not cleaned up automatically with the Azure Web App as they are with the on-premise IIS server.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_octopus_deployment_process.png&quot; alt=&quot;The DXC Service deployments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The nice thing our setup is the build promotion. Even though we are rebuilding software for unit-tests and smoke-tests we are building only once for deployments. Binary compilation and configuration are separated from each other which means that we can trust that our binaries are the same in each environment and they won’t change because of time relative issues like some 3rd party dependency not being fetched or somebody patching server. Even when we are doing deployments from the Jenkins we can still see and do build promotions from the Octopus Deploy too.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/continuous-delivery-with-episerver-dxcs/dxcs_octopus_build_promotion.png&quot; alt=&quot;The DXC Service build promotions&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;smoke-testing&quot;&gt;Smoke testing&lt;/h2&gt;

&lt;p&gt;Smoke testing is done with the same kind of DSL as the unit testing as we are using unit tests for smoke testing too. We just have two separated projects in our solution for different kind of tests. Smoke tests are tests that needs something to be deployed before we can test. We have few basic tests that we do.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Urls in the sitemap returns 200&lt;/li&gt;
  &lt;li&gt;Different archetypes of pages render with JS ok&lt;/li&gt;
  &lt;li&gt;Robots.txt is found&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could see that all the pages would render with JS but it would take a really long time. The only point where smoke testing differs from the unit tests is that since Episerver does not always startup so fast we have a wait routine for waiting the server to respond with reasonable http statuscodes after full startup.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;\$request = [system.Net.WebRequest]::Create(&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;targetAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;)&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;$result = &quot;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;for (\$i = 1; \$i -le 100; \$i++) {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  try {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    \$result = \$request.GetResponse()&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    if (\$result -is &quot;System.Net.HttpWebResponse&quot; -and \$result.StatusCode -ne &quot;&quot;) {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;      break&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  } catch [System.Net.WebException] {&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;    \$result = \$_.Exception.Response &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;  Start-Sleep -s 2&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;}&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Write-Host &quot;Status was: \$(\$result.StatusCode)&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;email-deployments&quot;&gt;Email deployments&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailSubject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Pre-production deployment \$PIPELINE_VERSION&#39;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailContent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Schedule: ASAP&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nPriority: Medium&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nPipeline revision: \$PIPELINE_VERSION&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nFrom: Integration&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nTo: Pre-production&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\nDeploy: &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tCode: Yes&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; 
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tDatabase: No&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tBlobs: No&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; 
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\tDeployment verification url:&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\t\thttps://www.google.fi?q=figure+out&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&#39;\n\nBest Regards: Jenkins&#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;extendedEmail&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;recipientList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailRecipientList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;replyToList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailReplyTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailSubject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;text/plain&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;preSendScript&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;$DEFAULT_PRESEND_SCRIPT\nmsg.addHeader(&quot;X-Priority&quot;, &quot;1 (Highest)&quot;);\nmsg.addHeader(&quot;Importance&quot;, &quot;High&quot;);\nmsg.setFrom(new javax.mail.internet.InternetAddress(&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailSender&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;))&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;triggers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;beforeBuild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is pretty close that we got into. We use some Java inside groovy script to setup email being sent as a high priority and to rewrite the sender to be something that we actually want to get responses to. We also have a verification url packaged into emails that we wish the Episerver Support to use to see if the software is fine and rollback if the page does not render correctly. We have same kind of emails for the data replenish too. Instead code we just say deploy data.&lt;/p&gt;

&lt;h2 id=&quot;triggering-smoke-tests&quot;&gt;Triggering smoke tests&lt;/h2&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;./persistenthash.txt&#39;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;./temphash.txt&#39;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;./test_if_this_exists.txt&#39;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;if(!(test-path &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;)) { &quot;init_first_non_existent_hash&quot; | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;rm -force &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; -ErrorAction SilentlyContinue&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;rm -force &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; -ErrorAction SilentlyContinue&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;curl https://www.google.fi?q=ver.txt -UseBasicParsing | % { $_.Content | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;if((Get-FileHash &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;).hash -ne (Get-FileHash &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;).hash) { &quot;true&quot; | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;conditionalSteps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;fileExists&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;WORKSPACE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;DontRun&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;curl https://www.google.fi?q=ver.txt -UseBasicParsing | % { $_.Content | Set-Content &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persistentHash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot; }&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;downstreamParameterized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stagingEnvironmentName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Smoke-Tests&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The idea of this PowerShell script is to store a commit hash into our jobs working folder. Then see on every 5 minutes if the hash we have is matching with the one that is on the production server (yeah, I replaced the actual url with google urls). If it does match then nothing happens. If it does not then we create a temporary file. Then we have a conditional step that from Jenkins which checks if the temporary file is present or not. If there is a file it updates the hash in working folder and triggers smoke tests for corresponding environment. This way our smoke tests start running within 5 minutes after the Episerver support has done the deployment.&lt;/p&gt;

&lt;h2 id=&quot;the-nightly-stuff&quot;&gt;The nightly stuff&lt;/h2&gt;

&lt;p&gt;Here is how I run the OWASP ZAP from Jenkins via PowerShell. I use &lt;a href=&quot;https://github.com/solita/powershell-zap&quot;&gt;my own PowerShell modules for managing ZAP&lt;/a&gt;. There are Jenkins plugins too but I had my own version before they existed so I’m kind of stuck on my own baby. I tried the plugin but it just didn’t do the same thing so I left it and went back to my own scripts.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;line.separator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zapReport&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;(\$env:WORKSPACE+&quot;\\&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapReportFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;)&#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parentJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;powerShell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Import-Module C:\\tools\\powershell-zap-master\\PowerShell-ZAP\\PowerShell-ZAP.psm1&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapLocation &quot;C:\\Program Files\\OWASP\\Zed Attack Proxy\\&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapReportLocation &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapReport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapUrlToScan &quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapUrlToScan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Ensure that daemon is running&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Start-Zap&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Configure policies, this just enables all scanners atm&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Set-ZapScanPolicies&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Do spidering against the url&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Invoke-ZapSpidering &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Do ajax spidering against the url&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Invoke-ZapAjaxSpidering&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Do scanning against the url&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Invoke-ZapScanning&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Save report&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Save-ZapReport&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;# Destroy scans&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Remove-ZapCurrentSpider&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineSeparator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
		&lt;span class=&quot;s1&quot;&gt;&#39;Remove-ZapCurrentScan&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parentJob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;publishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;archiveJunit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zapReportFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;allowEmptyResults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;retainLongStdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;testDataPublishers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;publishTestStabilityData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For performance monitoring we use my coworkers &lt;a href=&quot;https://github.com/jounihuttunen/jmeter-perfotrator&quot;&gt;jmeter-perfotrator&lt;/a&gt; which is just a template for jMeter. From the Jenkins the usage is quite simple.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	 &lt;span class=&quot;n&quot;&gt;batchFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;call &#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jmeterPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; -n -t &quot;%WORKSPACE%\\Solution Items\\jmeter\\TestUrls.jmx&quot; -j &quot;%WORKSPACE%\\log.txt&quot;&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
		&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perfPublish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.performance.PerformancePublisher&#39;&lt;/span&gt; 
		&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perfPublish&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ &#39;parsers&#39; /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.performance.JMeterParser&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;glob&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;results\\results.jtl&#39;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the dependency checks we use a plugin. In addition you need to setup a job to update the dependency data. After you have the fresh dependency data then you can run the check with DSL like this:&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owaspCheck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;org.jenkinsci.plugins.DependencyCheck.DependencyCheckBuilder&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;skipOnScmChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;skipOnUpstreamChange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;scanpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;outdir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;datadir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\\OWASP_DEPENDENCY_DATA&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;suppressionFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;isAutoupdateDisabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;includeHtmlReports&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;owaspPublish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ publishers /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;org.jenkinsci.plugins.DependencyCheck.DependencyCheckPublisher&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;healthy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;unHealthy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;thresholdLimit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;low&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pluginName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[DependencyCheck] &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;defaultEncoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;canRunOnFailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;usePreviousBuildAsReference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;useStableBuildAsReference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;useDeltaValues&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;shouldDetectModules&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dontComputeNew&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;doNotResolveRelativePaths&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the static code analysis we use the SonarQube. It is fairly easy to setup with the Jenkins. Just wrap your build between the SonarQube begin and end analysis.&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Configure begin analysis&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
		&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sonarBegin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.sonar.MsBuildSQRunnerBegin&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;projectKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarProjectKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;projectName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarProjectName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;projectVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarProjectVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;msBuildScannerInstallationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sonarScannerName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ... Your build goes here&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Configure end analysis&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
		&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sonarEnd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/ builders /&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;hudson.plugins.sonar.MsBuildSQRunnerEnd&#39;&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;show-it-on-the-tv&quot;&gt;Show it on the TV&lt;/h2&gt;

&lt;p&gt;Finally one of the biggest strenghts of this setup is the ability to show it on the screen with relatively simple DSL (and of course with some Jenkins plugins).&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;deliveryPipelineView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; delivery Pipeline&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pipelineInstances&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;showAvatars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;showChangeLog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;showTotalBuildTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;enableManualTriggers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;allowRebuild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;showAggregatedPipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildEnvironmentName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; Trigger&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;buildMonitorView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39; build Monitor&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39; jobs&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;jobs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;.*&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;.*&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two views. The pipeline view shows the view that was in the beginning of the blog post where you could see different phases of the continuous delivery process. The build monitor view builds up a screen full of rectangles that are green / yellow / red depending on your jobs status.&lt;/p&gt;

&lt;p&gt;We are asking the production deployment in the early morning hours so it usually takes place just before we got to the work. It is kind of peaceful time to do deployments and we have few earlybirds that will notice if something strange is going on when they get to work.&lt;/p&gt;

&lt;h2 id=&quot;summarizing&quot;&gt;Summarizing&lt;/h2&gt;

&lt;p&gt;Some characteristics about our project:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;More than 100 deployments per month&lt;/li&gt;
  &lt;li&gt;Go live every workday morning (~20 times a month)&lt;/li&gt;
  &lt;li&gt;Half a year of live site hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have had a few issues but nothing that would have upset our customer badly. Most of the times if there is an issue our smoke-tests notices it earlier than the Episerver Support does even when they are the ones who are deploying it. Usually the problems have been on the configuration transformation that is done by the Episerver Support with a configuration that we are not aware of. Once or twice we have been forced to make a fix patch immediately after going live. On these rare occasions it takes usually less time to implement the patch than to get episerver installing it. They tell us to call them if we have problem but it is really hard to call and solve the problem at the same time. If we call them, then we need double the people to figure out the situation since the telephone consumes one guy.&lt;/p&gt;

&lt;p&gt;In the DXC environment the problem remains that after a failed deployment it takes too much time to get the fixing deployment on the way. Immediate rollbacks do help, but not if we need to wait for a hour or two for that to happen. We have a fix ready on that time and then we would rather go forward-only deployment with the fixed version.&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2018/01/04/Continuous_delivery_with_episerver_dxcs</guid>            <pubDate>Thu, 04 Jan 2018 07:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Installing Jenkins with PowerShell DSC</title>            <link>http://dev.solita.fi/episerver/2017/11/21/Installing-Jenkins-with-PowerShell-DSC</link>            <description>&lt;p&gt;I have been struggling to find a good reference about how to setup a Jenkins environment in Microsoft environment with an automatic installation script. So, I decided to write a blog post about it. I also wanted to write about how to do continuous integration with Episerver DXC but felt that I need to tell first how to setup Jenkins. Our aim is to put up a Jenkins server that can build .NET FrameWork MVC application and front end. If you are not a reader type of a person, then &lt;a href=&quot;https://github.com/solita/powershell-dsc-jenkins&quot;&gt;here&lt;/a&gt; is a GitHub link to example scripts.&lt;/p&gt;

&lt;h2 id=&quot;about-the-powershell-dsc&quot;&gt;About the PowerShell DSC&lt;/h2&gt;

&lt;p&gt;You might not be familiar with the PowerShell DSC. The acronym comes from Desired State Configuration. DSC has been there now for years. Still, in my experience most .NET developers are unfamiliar with DSC. DSC exists for various reasons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make scripting less complex&lt;/li&gt;
  &lt;li&gt;Make scripting look the same over various environments&lt;/li&gt;
  &lt;li&gt;Make scripts and their parts to be more reusable&lt;/li&gt;
  &lt;li&gt;Make installation scripts to be idempotent (repeatable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The DSC is all about setting the state of a machine to be certain. Most used example is to make sure that a service is running or that specific file is found on given location. If you dig further into this world you will find concepts of pull and push servers that would help you to set a farm of machines into certain state. We will not use those but we run the script locally with the help of LCM which is “local configuration manager”. If you are looking for basics of PowerShell DSC then this &lt;a href=&quot;https://red-gate.com/simple-talk/sysadmin/powershell/powershell-desired-state-configuration-the-basics/&quot;&gt;blog&lt;/a&gt; was a well-written one.&lt;/p&gt;

&lt;h2 id=&quot;dsc-resources&quot;&gt;DSC resources&lt;/h2&gt;

&lt;p&gt;Before we start configuration we need to talk about DSC resources. Resources provide you functionality for DSC. For our purpose we are needing at least one that helps us to grab software from &lt;a href=&quot;https://chocolatey.org/&quot;&gt;Chocolatey&lt;/a&gt; (a package repository for windows). These resources would normally be where you start the script from (which might take you back to push and pull servers). In this scenario I just install them locally. &lt;a href=&quot;https://github.com/solita/powershell-dsc-jenkins/blob/master/install-modules.ps1&quot;&gt;Here&lt;/a&gt; is what my Install-Modules.ps1 script has inside.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Module cChoco -f
Install-Module xNetworking -f
Install-Module xWebAdministration -f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Three modules that provide you three types of functionality. Something to get stuff from Choco, configuring network things and managing IIS. If you run this for the first time you might be asking whether you want to install nuget package provider for PowerShell. You should, if you want to follow this path. It grabs you the wanted modules from &lt;a href=&quot;https://www.powershellgallery.com/packages/cChoco/2.3.1.0&quot;&gt;PowerShellGallery&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;our-objective&quot;&gt;Our objective&lt;/h2&gt;

&lt;p&gt;We want to have a Jenkins server running in the end with the following requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Install all the needed dependencies&lt;/li&gt;
  &lt;li&gt;Install Jenkins itself&lt;/li&gt;
  &lt;li&gt;Configure Jenkins startup parameters&lt;/li&gt;
  &lt;li&gt;Setup authentication for Jenkins&lt;/li&gt;
  &lt;li&gt;Install plugins for Jenkins&lt;/li&gt;
  &lt;li&gt;Protect Jenkins by settings IIS in front of it&lt;/li&gt;
  &lt;li&gt;Make sure that all traffic is HTTPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once our objective is now clear we can take a look at how I managed to do it.&lt;/p&gt;

&lt;h2 id=&quot;starting-the-scripting&quot;&gt;Starting the scripting&lt;/h2&gt;

&lt;p&gt;In the following paraghraps, we have the basic structure of my PowerShell script. There are a few important parts in the script. The most important one is the Configuration JENKINS_CI which states that here is my DSC configuration. It has a few steps in it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;State the name of the configuration (JENKINS_CI)&lt;/li&gt;
  &lt;li&gt;Declare parameters (JenkinsPort with default value of 8080)&lt;/li&gt;
  &lt;li&gt;Import necessary DscResources (cChoco)&lt;/li&gt;
  &lt;li&gt;For all the nodes make sure of the following&lt;/li&gt;
  &lt;li&gt;Make sure NetFrameworkCore WindowsFeature is present&lt;/li&gt;
  &lt;li&gt;Make sure Choco is installed&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;Configuration &lt;/span&gt;JENKINS_CI
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$JenkinsPort&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 8080
	&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	
	Import-DscResource -ModuleName &lt;span class=&quot;s1&quot;&gt;&#39;cChoco&#39;&lt;/span&gt;
	
	Node &lt;span class=&quot;nv&quot;&gt;$AllNodes&lt;/span&gt;.NodeName &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;       
		&lt;span class=&quot;c1&quot;&gt;# Install .NET 3.5&lt;/span&gt;
		WindowsFeature NetFrameworkCore 
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			Ensure    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt; 
			Name      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;NET-Framework-Core&quot;&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;c1&quot;&gt;# Install Chocolatey&lt;/span&gt;
		cChocoInstaller installChoco
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			InstallDir &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;c:\choco&quot;&lt;/span&gt;
			DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]NetFrameworkCore&quot;&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should notice from above that you can mark one resource to be dependent on another with DependsOn. This is super important since we have multiple requirements for actually installing most of the stuff in our configuration later on. Now when we have the basic DSC configuration in place we can call it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We need to define the AllNodes.NodeName that we referred in the script.&lt;/li&gt;
  &lt;li&gt;We also need to set the parameters we want for the configuration.&lt;/li&gt;
  &lt;li&gt;Finally we start the configuration with Start-DscConfiguraiton&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ConfigData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    AllNodes &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 
    @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            NodeName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LocalHost&quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

JENKINS_CI -JenkinsPort 8080 -ConfigurationData &lt;span class=&quot;nv&quot;&gt;$ConfigData&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Start-DscConfiguration&lt;/span&gt; -Path .\JENKINS_CI -Wait -Verbose -Force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;all-the-needed-dependencies&quot;&gt;All the needed dependencies&lt;/h2&gt;

&lt;p&gt;We will need a bunch of stuff to be able to build a .NET application.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Java for the Jenkins&lt;/li&gt;
  &lt;li&gt;Visual Studio and MSBuild for all the build targets and compilation&lt;/li&gt;
  &lt;li&gt;NodeJs for the front end compilation&lt;/li&gt;
  &lt;li&gt;Git for grabbing few more resources&lt;/li&gt;
  &lt;li&gt;Firefox for OWASP Zap&lt;/li&gt;
  &lt;li&gt;Notepad++ for comfort&lt;/li&gt;
  &lt;li&gt;Nuget.exe (I like to have a fixed version of this)&lt;/li&gt;
  &lt;li&gt;Python for … things&lt;/li&gt;
  &lt;li&gt;Jenkins for the actual goal&lt;/li&gt;
  &lt;li&gt;Zap for web application security testing automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scripting the installation of the software is fairly simple.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Install Visual Studio, todo: optional features (F#) with param --includeOptional&lt;/span&gt;
cChocoPackageInstaller installVisualStudio
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;visualstudio2017professional&quot;&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Install Visual Studio Web tools &lt;/span&gt;
cChocoPackageInstaller installVisualStudioWebWorkload
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;visualstudio2017-workload-netweb&quot;&lt;/span&gt;
	Params &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--includeOptional&quot;&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installVisualStudio&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Install NuGet&lt;/span&gt;
File installNuget 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	DestinationPath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:\tools\nuget\nuget.exe&quot;&lt;/span&gt;
	SourcePath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$InstallConfDirectory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;nuget.exe&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	Ensure &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;File&quot;&lt;/span&gt;
	Checksum &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;modifiedDate&quot;&lt;/span&gt;
	Force &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
	MatchSource &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I think that the above script is quite self-explanatory although I made it shorter for easier reading. The full script can be found &lt;a href=&quot;https://github.com/solita/powershell-dsc-jenkins/blob/master/jenkins_dsc.ps1&quot;&gt;here&lt;/a&gt; We have a bunch of resources, most of them depending on something earlier. Afterwards we want to have all those resources in place. The only odd thing is in the installVisualStudioWebWorkload part where I add includeOptional parameter for the choco. This is to get F# installed on the target machine as well. It does not come by default. If you need to dig on to those additional options in nuget packages you should head to chocolateys web page and investigate the packages you need to figure out if there is any customization options.&lt;/p&gt;

&lt;h2 id=&quot;installing-custom-made-stuff&quot;&gt;Installing custom made stuff&lt;/h2&gt;

&lt;p&gt;We have something in our GitHub repositories that we would like to use with Jenkins. Those are marvelous libraries jmeter-perfotrator and powershell-zap. We can fetch files from a git repository directly into Jenkins server with the help of the git client we just installed. Here is an example resource for that.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Install powershell-zap module 		&lt;/span&gt;
Script installPowershellZap 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; Result &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gci&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:\tools\powershell-zap-master&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		mkdir &lt;span class=&quot;s2&quot;&gt;&quot;C:\tools\powershell-zap-master&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$gitexe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Git\cmd\git.exe&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$arguments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;clone https://github.com/solita/powershell-zap.git &quot;C:\tools\powershell-zap-master&quot;&#39;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;start-process&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$gitexe&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arguments&lt;/span&gt; 
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Test-Path&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:\tools\powershell-zap-master&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installJdk8&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installGit&quot;&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-ugly&quot;&gt;The ugly&lt;/h2&gt;

&lt;p&gt;The only big letdown that I have had with DSC is manipulating environments PATH variable. You can do it really easily but only once.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Environment setVS2017ToolsPath 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;PATH&#39;&lt;/span&gt;
	Ensure &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Present&#39;&lt;/span&gt;
	Path &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installVisualStudio&quot;&lt;/span&gt;
	Value &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Microsoft Visual Studio\2017\Professional\Common7\IDE&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After you have done that once and try to manipulate the exactly same named environment variable in the same configuration you will get an error. The same applies also for creating and deleting the same file in the same configuration. Instead, you can do a workaround by having multiple configurations or by using script resources in DSC. I have used script resources for this and well it is a bit complex but not worse than it is with traditional scripts.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Set Java to path&lt;/span&gt;
Script SetJavaToPath 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; Result &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;:Path &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Try to find Java bin path and force the result to string &lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$javaBinPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gci&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Env&lt;/span&gt;:ProgramFiles&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Java&quot;&lt;/span&gt; -r -filter java.exe | &lt;span class=&quot;nb&quot;&gt;Select &lt;/span&gt;Directory | &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; -first 1 | % &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Directory.FullName &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Adds javaBinPath to path variable &lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$newPathValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;:Path + &lt;span class=&quot;s2&quot;&gt;&quot;;&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$javaBinPath&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# You might need to reset your console after this &lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Environment]::SetEnvironmentVariable&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Path&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;$newPathValue&lt;/span&gt;, &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;EnvironmentVariableTarget]::Machine&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Add also path to current session&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;:Path &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$newPathValue&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Try to find Java bin path and force the result to string &lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$javaBinPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gci&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Env&lt;/span&gt;:ProgramFiles&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Java&quot;&lt;/span&gt; -r -filter java.exe | &lt;span class=&quot;nb&quot;&gt;Select &lt;/span&gt;Directory | &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; -first 1 | % &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Directory.FullName &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;-not &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;:Path.Contains&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$javaBinPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;# Do update&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$False&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Don&#39;t update&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installJdk8&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Script resources are separated into three stages.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;GetScript which returns something to be printed&lt;/li&gt;
  &lt;li&gt;SetScript which sets the value&lt;/li&gt;
  &lt;li&gt;TestScript which finds out if we need to set or not.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test is like unit test, it fails with false which means that DSC needs to run the SetScript. I have multiple copies in my script of this so check them from GitHub repository.&lt;/p&gt;

&lt;h2 id=&quot;setup-jenkins-startup-parameters&quot;&gt;Setup Jenkins startup parameters&lt;/h2&gt;

&lt;p&gt;Here are all the variables that I have for Jenkins configuration in my script.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$JenkinsPort&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 8080,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsPlugins&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsUsername&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsPassword&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsXmx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 1024,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsMaxPermSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 128,
	&lt;span class=&quot;nv&quot;&gt;$InstallConfDirectory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;./&quot;&lt;/span&gt;,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsInitScriptPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsUsernameTemplate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;,
	&lt;span class=&quot;nv&quot;&gt;$JenkinsPasswordTemplate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that you have seen all the variables we will introduce some mindblowing PowerShell magic and use them to setup Jenkins (it was already installed earlier). We can refer to those parameters with $Using:ParamName.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Script SetJenkinsServiceArguments
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$argString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-Xrs -Xmx&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsXmx+&lt;span class=&quot;s2&quot;&gt;&quot;m -XX:MaxPermSize=&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsMaxPermSize+&lt;span class=&quot;s2&quot;&gt;&quot;m -Djenkins.install.runSetupWizard=false -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%BASE%\jenkins.war&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; --httpPort=&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPort+&lt;span class=&quot;s2&quot;&gt;&quot; --webroot=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%BASE%\war&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; -Verbose &lt;span class=&quot;s2&quot;&gt;&quot;Setting jenkins service arguments to &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$argString&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
		
		&lt;span class=&quot;nv&quot;&gt;$Config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Path &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Jenkins\Jenkins.xml&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$NewConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Config&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-replace &lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;arguments&amp;gt;[\s\S]*?&amp;lt;\/arguments&amp;gt;&#39;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;arguments&amp;gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;argString&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/arguments&amp;gt;&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Set-Content&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Path &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Jenkins\Jenkins.xml&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Value &lt;span class=&quot;nv&quot;&gt;$NewConfig&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Force
		&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; -Verbose &lt;span class=&quot;s2&quot;&gt;&quot;Restarting Jenkins&quot;&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$Config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Path &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Jenkins\Jenkins.xml&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$Matches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;regex]::matches&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Config&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;arguments&amp;gt;[\s\S]*?&amp;lt;\/arguments&amp;gt;&quot;&lt;/span&gt;, &lt;span class=&quot;s1&quot;&gt;&#39;IgnoreCase&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$currentMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Matches&lt;/span&gt;.Groups[1].Value
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;s1&quot;&gt;&#39;Result&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$currentMatch&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
		&lt;span class=&quot;nv&quot;&gt;$Config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Path &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Jenkins\Jenkins.xml&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$Matches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;regex]::matches&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Config&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;arguments&amp;gt;[\s\S]*?&amp;lt;\/arguments&amp;gt;&quot;&lt;/span&gt;, &lt;span class=&quot;s1&quot;&gt;&#39;IgnoreCase&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$argString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-Xrs -Xmx&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsXmx+&lt;span class=&quot;s2&quot;&gt;&quot;m -XX:MaxPermSize=&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsMaxPermSize+&lt;span class=&quot;s2&quot;&gt;&quot;m -Djenkins.install.runSetupWizard=false -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%BASE%\jenkins.war&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; --httpPort=&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPort+&lt;span class=&quot;s2&quot;&gt;&quot; --httpListenAddress=127.0.0.1 --webroot=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%BASE%\war&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$currentMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Matches&lt;/span&gt;.Groups[1].Value
		
		&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Current service arguments: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$currentMatch&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Should be service arguments: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$argString&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
		
		&lt;span class=&quot;k&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$argString&lt;/span&gt; -ne &lt;span class=&quot;nv&quot;&gt;$currentMatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;# Jenkins port must be changed&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$False&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Jenkins is already on correct port&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installJenkins&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I admit. The script could be cleaner. Nevertheless it sets startup arguments in jenkins.xml file to have given memorylimit parameters and listen only localhost on the given port. We listen only localhost because I don’t feel Jenkins should be open in any network. I’ll later on show how to setup IIS to be a reverse proxy in front of the Jenkins.&lt;/p&gt;

&lt;h2 id=&quot;initial-authentication-setup-for-jenkins&quot;&gt;Initial authentication setup for Jenkins&lt;/h2&gt;

&lt;p&gt;If we would startup the Jenkins at this moment it would not be ready to get api requests from us which we will need to install plugins. So what we need to do is to setup a initialization script to setup authentication. We can do it by making sure that there is groovy script under Jenkins/init.groovy.d/ folder. All the groovy scripts are run on that folder are run on the Jenkins startup. Yes. That is a bit scary. The actual script setup is easy one:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;File JenkinsAuthenticationSetup 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	DestinationPath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$JenkinsInitScriptPath&lt;/span&gt;
	SourcePath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$InstallConfDirectory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;solita_jenkins_security_realm.groovy&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	Ensure &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;File&quot;&lt;/span&gt;
	Checksum &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;modifiedDate&quot;&lt;/span&gt;
	Force &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
	MatchSource &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installJenkins&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So we put a groovy script there and it is set. That script checks the authorization strategy and sets it to be full control once logged in authorization strategy. It also creates a user with given username and password. The script file has placeholders for username and password so we just use again some scripting to replace them with given parameters.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Script SetJenkinsAuthenticationUsername
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsUsername
		&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.Replace&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsUsernameTemplate,&lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Set-Content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get-content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; | % &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -match &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsUsernameTemplate &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; | ? &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -contains &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$aResult&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; -eq &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;s1&quot;&gt;&#39;Result&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$aResult&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get-content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; | % &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -match &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsUsernameTemplate &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; | ? &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -contains &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; -eq &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;# needs configuration&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$False&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
Script SetJenkinsAuthenticationPassword
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPassword
		&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.Replace&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPasswordTemplate,&lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Set-Content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath 
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get-content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; | % &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -match &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPasswordTemplate &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; | ? &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -contains &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$aResult&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; -eq &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;s1&quot;&gt;&#39;Result&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$aResult&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get-content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsInitScriptPath&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; | % &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -match &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPasswordTemplate &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; | ? &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -contains &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$containsReplacaple&lt;/span&gt; -eq &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;# needs configuration&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$False&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;start-the-jenkins&quot;&gt;Start the Jenkins&lt;/h2&gt;

&lt;p&gt;Nothing fancy here. This is the most common example of DSC usage. Script is really simple. Make sure that Jenkins is started automatically and that it is running.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Service JenkinsService
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Name        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Jenkins&quot;&lt;/span&gt;
	StartupType &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Automatic&quot;&lt;/span&gt;
	State       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Running&quot;&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installJenkins&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[Script]SetJenkinsServiceArguments&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[File]JenkinsAuthenticationSetup&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[Script]SetJenkinsAuthenticationUsername&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[Script]SetJenkinsAuthenticationPassword&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;install-plugins&quot;&gt;Install plugins&lt;/h2&gt;

&lt;p&gt;My coworker argued that the Jenkins is bad build server because it does not even give you timestamps on the console log by default. For all of this kind of arguments there is always the same counterargument. There is a plugin for that! We will need plugins. A lot of plugins. Actually more than 40 in our setup which would make with dependencies … well a huge amount of foreign code running on your build server. A full list of plugins can be found at &lt;a href=&quot;https://github.com/solita/powershell-dsc-jenkins/blob/master/misc/jenkins_plugins.txt&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now when we have happily setup the Jenkins and made sure that it is running we can start using its API to install some plugins. Here is a script that expects to have a list of plugins as a parameter.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Script InstallJenkinsPlugins
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$plugins&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPlugins
		&lt;span class=&quot;nv&quot;&gt;$port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPort
		&lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPassword
		&lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsUsername
		
		&lt;span class=&quot;c1&quot;&gt;# Make sure that Jenkins is in the configurated state&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Restart-Service&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Name Jenkins
		&lt;span class=&quot;nb&quot;&gt;Start-Sleep&lt;/span&gt; -s 15
		
		&lt;span class=&quot;c1&quot;&gt;# Wait a bit for Jenkins to get online &lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;system.Net.WebRequest]::Create&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 1; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; -le 10; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;++&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				   &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;.GetResponse&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Net.WebException] &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				   &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Exception.Response
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
			
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; -is &lt;span class=&quot;s2&quot;&gt;&quot;System.Net.HttpWebResponse&quot;&lt;/span&gt; -and &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;.StatusCode -ne &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;nv&quot;&gt;$done&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Got status&quot;&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
			&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
			
			&lt;span class=&quot;nb&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Get status attempt number &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; failed. Retrying...&quot;&lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;Start-Sleep&lt;/span&gt; -s 5
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		
		&lt;span class=&quot;c1&quot;&gt;# Install plugins&lt;/span&gt;
		
		&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$jplug&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$plugins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;installing &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$jplug&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
			java -jar &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;\Jenkins\war\WEB-INF\jenkins-cli.jar  -s &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; install-plugin &lt;span class=&quot;nv&quot;&gt;$jplug&lt;/span&gt; --username &lt;span class=&quot;nv&quot;&gt;$username&lt;/span&gt; --password &lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;# Wait a bit, Jenkins is kind of slow &lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;Start-Sleep&lt;/span&gt; -s 5
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; -Verbose &lt;span class=&quot;s2&quot;&gt;&quot;Restarting Jenkins&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Restart-Service&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
			-Name Jenkins
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; Result &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Jenkins\plugins&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Select &lt;/span&gt;Name &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Check if there are plugins&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$directoryInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENV&lt;/span&gt;:ProgramFiles&lt;span class=&quot;p&quot;&gt;(x86)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Jenkins\plugins&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Measure-Object&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Directory is empty, do the update&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$directoryInfo&lt;/span&gt;.Count -eq 0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$False&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# Do not make update &lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]installJenkins&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[Script]SetJenkinsServiceArguments&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[File]JenkinsAuthenticationSetup&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[Service]JenkinsService&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[Script]SetJenkinsAuthenticationUsername&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[Script]SetJenkinsAuthenticationPassword&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we have actual build server running that listens localhost. We have installed all the tools, all the plugins and setup the authentication so that we can start working on creating build jobs. At this point you might want to delete the authentication initialization script. You can’t do it with the same DSC script because it disallows manipulating same file twice in same configuration. Although you can do it afterwards with a single liner.&lt;/p&gt;

&lt;h2 id=&quot;install-iis&quot;&gt;Install IIS&lt;/h2&gt;
&lt;p&gt;I created separated script for this just because I felt that this script is a reusable one. So let’s start from the beginning and introduce some Windows Features that we want to use in a brand new DSC script. The modules that we are using are the same as stated in the beginning so you would need to import them first. Here are the resources that we need:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Check the windowsfeature names with Get-WindowsFeature&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Install .NET 3.5&lt;/span&gt;
WindowsFeature NetFrameworkCore 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Ensure    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt; 
	Name      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;NET-Framework-Core&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Install Chocolatey&lt;/span&gt;
cChocoInstaller installChoco
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	InstallDir &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;c:\choco&quot;&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]NetFrameworkCore&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Install the IIS role&lt;/span&gt;
WindowsFeature IIS
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Ensure          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt;
	Name            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Web-Server&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Install the ASP .NET 4.5 role&lt;/span&gt;
WindowsFeature AspNet45
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Ensure          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt;
	Name            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Web-Asp-Net45&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Install the web management console&lt;/span&gt;
WindowsFeature WebManagementConsole
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Ensure          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt;
	Name            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Web-Mgmt-Console&quot;&lt;/span&gt;
	DependsOn 		&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]IIS&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Basically we installed IIS WebServer with some tooling. Yet again the full script can be found at &lt;a href=&quot;https://github.com/solita/powershell-dsc-jenkins/blob/master/iis_reverse_proxy_dsc.ps1&quot;&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;setup-a-website-for-proxying&quot;&gt;Setup a website for proxying&lt;/h2&gt;

&lt;p&gt;The next thing would be to get rid of the “Default Web Site” just because I hate having it laying around. This is really simple task to do.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Make sure to get rid of default web site&lt;/span&gt;
xWebsite DefaultWebSite
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Ensure          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Absent&quot;&lt;/span&gt;
	Name            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Default Web Site&quot;&lt;/span&gt;
	State           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Stopped&quot;&lt;/span&gt;
	PhysicalPath    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:\inetpub\wwwroot&quot;&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]AspNet45&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]IIS&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once we have got rid of that ugly thing we can create our own physical folder for the website and also the actual website. I have index.html ready for the proxy site just to make recognizing problems easier.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;File JenkinsProxyFolder 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	DestinationPath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:\inetpub\JenkinsProxy\index.html&quot;&lt;/span&gt;
	SourcePath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$InstallConfDirectory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;index.html&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	Ensure &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;File&quot;&lt;/span&gt;
	Checksum &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;modifiedDate&quot;&lt;/span&gt;
	Force &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
	MatchSource &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Create jenkins proxywebsite&lt;/span&gt;
xWebsite JenkinsProxyWebSite
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Ensure          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Present&quot;&lt;/span&gt;
	Name            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;JenkinsProxyWebSite&quot;&lt;/span&gt;
	State           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Started&quot;&lt;/span&gt;
	PhysicalPath    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:\inetpub\JenkinsProxy&quot;&lt;/span&gt;
	BindingInfo     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
		MSFT_xWebBindingInformation
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			Protocol              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;HTTPS&quot;&lt;/span&gt;
			Port                  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 443
			CertificateThumbprint &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$thumbPrint&lt;/span&gt;
			CertificateStoreName  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;My&quot;&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]AspNet45&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]IIS&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[File]JenkinsProxyFolder&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You might have noticed that the site was setup with HTTPS and it has a thumbprint as a parameter. I was lazy and did just some manual script step before DSC for installing the certificate into the machine. It requires that you know how to get a .pfx for TLS.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$thumbPrint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;your_thumbprint&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$thumb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-ChildItem &lt;/span&gt;cert:\localmachine\my | &lt;span class=&quot;nb&quot;&gt;Where&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Thumbprint -like &lt;span class=&quot;nv&quot;&gt;$thumbPrint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;Select &lt;/span&gt;Thumbprint
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$thumb&lt;/span&gt; -eq &lt;span class=&quot;nv&quot;&gt;$NULL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$mypwd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Read-Host&lt;/span&gt; -AsSecureString &lt;span class=&quot;s2&quot;&gt;&quot;Give password for certificate&quot;&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;Import-PfxCertificate&lt;/span&gt; -FilePath .\your_certificate.pfx cert:\localMachine\my -Password &lt;span class=&quot;nv&quot;&gt;$mypwd&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you have no idea how to get a certificate. Then you might want to try this script. It creates a self-signed certificate and gives you the thumbprint that my script is asking.&lt;/p&gt;
&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;New-SelfSignedCertificate&lt;/span&gt; -DnsName &lt;span class=&quot;s2&quot;&gt;&quot;www.jenkinstest.fi&quot;&lt;/span&gt; -CertStoreLocation &lt;span class=&quot;s2&quot;&gt;&quot;cert:\LocalMachine\My&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this setup we should have in place a website that is running and responding in 443 port. If you want to support redirects from 80 to 443 too then you need to allow the 80 port too.&lt;/p&gt;

&lt;h2 id=&quot;urlrewrite-and-applicationrequestrouting&quot;&gt;UrlRewrite and ApplicationRequestRouting&lt;/h2&gt;

&lt;p&gt;To be able to finish the setup we will need a few more dependencies. Those are UrlRewrite and IIS-ApplicationRequestRouting. Here is a yet again easy setup for those two.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Install UrlRewrite&lt;/span&gt;
cChocoPackageInstaller UrlRewrite
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;urlrewrite&quot;&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Install UrlRewrite&lt;/span&gt;
cChocoPackageInstaller ApplicationRequestRouting
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;iis-arr&quot;&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then the fun ends and we will fall back to the ScriptResources and do some configuration madness. First of all we need to make sure that certain server variables are allowed. We can do it in this way:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Script ReWriteRules
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;#Adds rewrite allowedServerVariables to applicationHost.config&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]UrlRewrite&quot;&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-WebConfiguration&lt;/span&gt; /system.webServer/rewrite/allowedServerVariables | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty collection | ?&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.ElementTagName -eq &lt;span class=&quot;s2&quot;&gt;&quot;add&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty name
		&lt;span class=&quot;nv&quot;&gt;$expected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HTTPS&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;HTTP_X_FORWARDED_FOR&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;HTTP_X_FORWARDED_PROTO&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;REMOTE_ADDR&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$missing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$expected&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$current&lt;/span&gt; -notcontains &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;Start-WebCommitDelay&lt;/span&gt; 
			&lt;span class=&quot;nv&quot;&gt;$missing&lt;/span&gt; | %&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Add-WebConfiguration&lt;/span&gt; /system.webServer/rewrite/allowedServerVariables -atIndex 0 -value @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; -Verbose &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;nb&quot;&gt;Stop-WebCommitDelay&lt;/span&gt; -Commit &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt; 
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; 
		&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Exception]
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
			&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Out-String&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-WebConfiguration&lt;/span&gt; /system.webServer/rewrite/allowedServerVariables | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty collection | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty name
		&lt;span class=&quot;nv&quot;&gt;$expected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HTTPS&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;HTTP_X_FORWARDED_FOR&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;HTTP_X_FORWARDED_PROTO&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;REMOTE_ADDR&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; -not @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$expected&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$current&lt;/span&gt; -notcontains &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -first 1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.Count
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$allowedServerVariables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-WebConfiguration&lt;/span&gt; /system.webServer/rewrite/allowedServerVariables | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty collection
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$allowedServerVariables&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then once we have that setupped we can make the rewrite rules for routing all the traffic from 443 port to jenkins running in different port.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Script JenkinsReverseProxy
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]UrlRewrite&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]ApplicationRequestRouting&quot;&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;HTTPS Reverse Proxy to Jenkins&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$proxyTargetPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:&quot;&lt;/span&gt;+&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;:JenkinsPort+&lt;span class=&quot;s2&quot;&gt;&quot;/{R:0}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;nb&quot;&gt;Clear-WebConfiguration&lt;/span&gt; -pspath &lt;span class=&quot;nv&quot;&gt;$PsPath&lt;/span&gt; -filter &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Filter&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/rule[@name=&#39;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;]&quot;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$Filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;system.webserver/rewrite/rules&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Clear-WebConfiguration&lt;/span&gt; -location &lt;span class=&quot;nv&quot;&gt;$Site&lt;/span&gt; -pspath &lt;span class=&quot;nv&quot;&gt;$PsPath&lt;/span&gt; -filter &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Filter&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/rule[@name=&#39;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;]&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Add-WebConfigurationProperty&lt;/span&gt; -location &lt;span class=&quot;nv&quot;&gt;$Site&lt;/span&gt; -pspath &lt;span class=&quot;nv&quot;&gt;$PsPath&lt;/span&gt; -filter &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Filter&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; -name &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt; -value @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;; &lt;span class=&quot;nv&quot;&gt;patternSyntax&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;ECMAScript&#39;&lt;/span&gt;; &lt;span class=&quot;nv&quot;&gt;stopProcessing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;True&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Set-WebConfigurationProperty&lt;/span&gt; -location &lt;span class=&quot;nv&quot;&gt;$Site&lt;/span&gt; -pspath &lt;span class=&quot;nv&quot;&gt;$PsPath&lt;/span&gt; -filter &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Filter&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/rule[@name=&#39;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;]/match&quot;&lt;/span&gt; -name url -value &lt;span class=&quot;s2&quot;&gt;&quot;(.*)&quot;&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Set-WebConfigurationProperty&lt;/span&gt; -location &lt;span class=&quot;nv&quot;&gt;$Site&lt;/span&gt; -pspath &lt;span class=&quot;nv&quot;&gt;$PsPath&lt;/span&gt; -filter &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Filter&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/rule[@name=&#39;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;]/action&quot;&lt;/span&gt; -name &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; -value &lt;span class=&quot;s2&quot;&gt;&quot;Rewrite&quot;&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;# R:0 Is full phase, R:1 Is the domain with the port and R:2 is the querypart&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;Set-WebConfigurationProperty&lt;/span&gt; -location &lt;span class=&quot;nv&quot;&gt;$Site&lt;/span&gt; -pspath &lt;span class=&quot;nv&quot;&gt;$PsPath&lt;/span&gt; -filter &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Filter&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/rule[@name=&#39;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;]/action&quot;&lt;/span&gt; -name &lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt; -value &lt;span class=&quot;nv&quot;&gt;$proxyTargetPath&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-WebConfiguration&lt;/span&gt; /system.webServer/rewrite/rules | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty collection | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty name
		&lt;span class=&quot;nv&quot;&gt;$expected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HTTPS to Jenkins&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; -not @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$expected&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$current&lt;/span&gt; -notcontains &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;| &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -first 1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.Count
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$rules&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-WebConfiguration&lt;/span&gt; /system.webServer/rewrite/rules | &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; -ExpandProperty collection
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rules&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are looking UrlRewrite rules first time that might look confusing. Basic idea is just to make a xml configuration for IIS. Next up we will need to setup an ARR proxy to actually get the proxy functionality enabled. Yet again we go with the ScriptResource and make some XML configuration.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Script EnableARRProxy
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]WebManagementConsole&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[WindowsFeature]IIS&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]UrlRewrite&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;[cChocoPackageInstaller]ApplicationRequestRouting&quot;&lt;/span&gt;
	SetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$assembly&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Reflection.Assembly]::LoadFrom&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new-object &lt;/span&gt;Microsoft.Web.Administration.ServerManager
		&lt;span class=&quot;nv&quot;&gt;$sectionGroupConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.GetApplicationHostConfiguration&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;

		&lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;proxy&#39;&lt;/span&gt;;

		&lt;span class=&quot;nv&quot;&gt;$webserver&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sectionGroupConfig&lt;/span&gt;.RootSectionGroup.SectionGroups[&lt;span class=&quot;s1&quot;&gt;&#39;system.webServer&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$webserver&lt;/span&gt;.Sections[&lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;])&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;nv&quot;&gt;$proxySection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$webserver&lt;/span&gt;.Sections.Add&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;;
			&lt;span class=&quot;nv&quot;&gt;$proxySection&lt;/span&gt;.OverrideModeDefault &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Deny&quot;&lt;/span&gt;;
			&lt;span class=&quot;nv&quot;&gt;$proxySection&lt;/span&gt;.AllowDefinition&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;AppHostOnly&quot;&lt;/span&gt;;
			&lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.CommitChanges&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new-object &lt;/span&gt;Microsoft.Web.Administration.ServerManager
		&lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.GetApplicationHostConfiguration&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt;.GetSection&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;system.webServer/&#39;&lt;/span&gt; + &lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$enabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt;.GetAttributeValue&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enabled&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;;
		&lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt;.SetAttributeValue&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enabled&#39;&lt;/span&gt;, &lt;span class=&quot;s1&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;;
		&lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.CommitChanges&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	TestScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$assembly&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Reflection.Assembly]::LoadFrom&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;proxy&#39;&lt;/span&gt;;
		&lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new-object &lt;/span&gt;Microsoft.Web.Administration.ServerManager
		&lt;span class=&quot;nv&quot;&gt;$sectionGroupConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.GetApplicationHostConfiguration&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.GetApplicationHostConfiguration&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt;.GetSection&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;system.webServer/&#39;&lt;/span&gt; + &lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt; -eq &lt;span class=&quot;nv&quot;&gt;$null&lt;/span&gt; -and &lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt;.GetAttributeValue&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enabled&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; -eq &lt;span class=&quot;nv&quot;&gt;$False&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	GetScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$assembly&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Reflection.Assembly]::LoadFrom&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;proxy&#39;&lt;/span&gt;;
		&lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new-object &lt;/span&gt;Microsoft.Web.Administration.ServerManager
		&lt;span class=&quot;nv&quot;&gt;$sectionGroupConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.GetApplicationHostConfiguration&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$manager&lt;/span&gt;.GetApplicationHostConfiguration&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt;.GetSection&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;system.webServer/&#39;&lt;/span&gt; + &lt;span class=&quot;nv&quot;&gt;$sectionName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$section&lt;/span&gt;.GetAttributeValue&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enabled&#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Only last thing is to actually run the script.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ConfigData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	AllNodes &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 
	@&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
		@&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
			NodeName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LocalHost&quot;&lt;/span&gt;
		&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$thumbPrint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;certificate_thumbprint&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$currentPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;split-path&lt;/span&gt; -parent &lt;span class=&quot;nv&quot;&gt;$MyInvocation&lt;/span&gt;.MyCommand.Definition&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$installConfPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;join-path&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$currentPath&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;misc&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
IIS_REVERSE_PROXY -ThumbPrint &lt;span class=&quot;nv&quot;&gt;$thumbPrint&lt;/span&gt; -InstallConfDirectory &lt;span class=&quot;nv&quot;&gt;$installConfPath&lt;/span&gt; -JenkinsPort 8080 -ConfigurationData &lt;span class=&quot;nv&quot;&gt;$ConfigData&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Start-DscConfiguration&lt;/span&gt; -Path .\IIS_REVERSE_PROXY -Wait -Verbose -Force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I really like how the PowerShell DSC makes some things to be so easy and self-explanatory. What I didn’t like was the need to step down on to creating ScriptResources. Creating a script resource takes time and a lot of testing. You will need to run your script multiple times. This is of course there the idempotent nature of the DSC comes really handy. Although I was able to corrupt my PATH variable (in a VM) into horrible state while testing. Protip would be to get a VM for developing scripts. My usual setup is something like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Windows 2016 server running on Hyper-V&lt;/li&gt;
  &lt;li&gt;Snapshot before running any scripts&lt;/li&gt;
  &lt;li&gt;Another snapshot when all the software is installed (downloading all the stuff takes ages)&lt;/li&gt;
  &lt;li&gt;Develop on your own machine, throw in the new script for the VM&lt;/li&gt;
  &lt;li&gt;Run the script&lt;/li&gt;
  &lt;li&gt;Repeat 4 and 5 and if you screw up badly then go back to snapshot from point 3.&lt;/li&gt;
  &lt;li&gt;Finally when you think you are ready go back to snapshot from point 2.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope you liked it. All the script is available at our &lt;a href=&quot;https://github.com/solita/powershell-dsc-jenkins&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;known-problems&quot;&gt;Known problems&lt;/h2&gt;

&lt;p&gt;Few things that might be broken at the repository. If you are trying to setup the ZAP as a daemon you need to start it manually and change the port to something else since it have same default (8080) as my scripts have. Another thing that might be broken is that later on if you would like to use OWASP dependency check module that was installed. Then you might hit the problem (or not) that Java cacerts does not have let’s encrypt intermediatory certificate. A  &lt;a href=&quot;https://github.com/solita/powershell-dsc-jenkins/tree/master/certs&quot;&gt;fix&lt;/a&gt; is in the repository in case you need it.&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2017/11/21/Installing-Jenkins-with-PowerShell-DSC</guid>            <pubDate>Tue, 21 Nov 2017 14:40:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Achieving Azure certification after ARM refresh</title>            <link>http://dev.solita.fi/episerver/2017/03/01/Achieving-Azure-certification-after-ARM-refresh</link>            <description>&lt;p&gt;I started developing on Azure when the current “old portal” did not yet exist. Back then Azure resources were managed with Silverlight portal. After being around in Azure for years I decided to give a shot for the certifications while I can still earn both MCSE and MCSD with the same trouble. MCSD is expiring at the end of march 2017. Here are some observations from the certification journey.&lt;/p&gt;

&lt;h2 id=&quot;overall-feeling-about-the-exams&quot;&gt;Overall feeling about the exams&lt;/h2&gt;

&lt;p&gt;They reflected real life quite well. I was actually surprised that there were only few questions that got me angry. The upsetting ones were either confusing or asked about numbers that I don’t feel like wanting to memorize. If you are not familiar with the Microsoft exams, then you should know how they are structured. There are four things that you will find in every question.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Business problem (enterprise x is developing y to achieve business goal z)&lt;/li&gt;
  &lt;li&gt;Goal statement (you need to solve issue x)&lt;/li&gt;
  &lt;li&gt;One or more correct answers&lt;/li&gt;
  &lt;li&gt;DISTRACTERS (stuff that are not related to problem scenario but rings bells in your head as they are familiar)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try to extract the essence of the question out and then eliminate the distractors. Some of the distractors were so bad that I was still wondering about them 10 questions after.&lt;/p&gt;

&lt;p&gt;Were the questions easy? No. Having years of experience from three generation of Azure portals I just barely passed the first one. Second one went through with a bit better score and the third one with the highest score. I took them in an order that I thought was most familiar to me: development, architecture, infrastructure. What does this tell? They overlap a lot so each round was easier because of more studying on the same subject.&lt;/p&gt;

&lt;p&gt;How to mentally prepare yourself to the quetions? Ask yourself questions like “There is a problem with x, where would I find logs?”, “How do i scale this?” or “How do i secure the connection?”. If you can answer those, then you will most likely be successful in eliminating the distractors out of the scenery. Even if the options you are left are nearly the same you have still atleast improved your odds!&lt;/p&gt;

&lt;h2 id=&quot;about-the-certificates-and-the-arm-refresh&quot;&gt;About the certificates and the ARM refresh&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/img/azure-certification/mcsd_pyramid.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is how the old pyramid looked like. And the new one is actually with the exact same format. The lowest tier is the technology specialist tier which you get if you pass a certain kind of exam. The middle tier is solution associate tier which you will get by passing two exams. The highest tier is solution expert tier, which you will get after passing one test and having the solution associate already.&lt;/p&gt;

&lt;p&gt;Before studying any further notice that when redesigning the tiers they actually changed the contents of the few relevant exams. Before paying for any training material make sure that the ARM refresh is mentioned. For example books that were preparing for 70-532, 70-533 and 70-534 tests have not been updated. In my experience, you can pretty safely forget the “old portal” and focus on the newer one.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/learning/mcsd-azure-architect-certification.aspx&quot;&gt;MCSD certification&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/learning/mcse-cloud-platform-infrastructure.aspx&quot;&gt;MCSE certification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that there are also other routes to the same MCSE! You don’t need to take any of the exams that I took.&lt;/p&gt;

&lt;h2 id=&quot;no-more-recertification&quot;&gt;No more recertification&lt;/h2&gt;

&lt;p&gt;I have always hated the recertifications. The new tier of Microsoft certificates seems to be lacking of expiration date. I love that. As a developer I get nothing out of recertification for the subject that I have already been certificated. If I have forgotten something during the 2-3 year period then I’m sure that it has not been important. Renewing the internals of the exam won’t help the core of the exam mostly remains the same still. I would rather expand my view of sight by taking a new certificate.&lt;/p&gt;

&lt;p&gt;Hint to Episerver: you could learn from the security scene. There you can keep your qualification alive by gathering points from events. Like doing a blog post or speaking in a seminar. That would work for Episerver certifications too. This does not prevent that you could take recertification if you wish. There are much more like assessments, projects, forums, support tickets, and GitHub repositories that you could track. Nothing in this prevents from taking the certification test again.&lt;/p&gt;

&lt;h2 id=&quot;three-exams-seven-certificates&quot;&gt;Three exams, seven certificates&lt;/h2&gt;

&lt;p&gt;At the edge of certification era change I was able to get seven different certificates with three exams. How is this possible? Well, technology specialist certification is granted from each exam. After two exams solution associate certification is granted. Finally, I was granted solution expert and solution developer certificates from passing all three exams. As a bonus, I also got MCSD App Builder by having MCSA: web applications already in my pocket. The full list is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft Specialist: Developing Microsoft Azure Solutions&lt;/li&gt;
  &lt;li&gt;Microsoft Specialist: Architecting Microsoft Azure Solutions&lt;/li&gt;
  &lt;li&gt;Microsoft Specialist: Implementing Microsoft Azure Infrastructure solutions&lt;/li&gt;
  &lt;li&gt;Microsoft Certified Solutions Associate: Cloud Platform&lt;/li&gt;
  &lt;li&gt;Microsoft Certified Solutions Developer: Azure Solutions Architecting&lt;/li&gt;
  &lt;li&gt;Microsoft Certified Solutions Developer: App Builder&lt;/li&gt;
  &lt;li&gt;Microsoft Certified Solutions Expert: Cloud Platform and Infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expect to earn at least five certificates by targetting to MCSE.&lt;/p&gt;

&lt;h2 id=&quot;70-53x-overlaps-with-each-other&quot;&gt;70-53x overlaps with each other&lt;/h2&gt;

&lt;p&gt;Despite which number of exams you are going to take, I strongly suggest that you check the materials of each three exam. There were lots of stuff even in Ignite cert preparation videos that would have been beneficial for me in the first place. Here are the list of similarities:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ARM templates&lt;/li&gt;
  &lt;li&gt;ARM virtual machines&lt;/li&gt;
  &lt;li&gt;ARM Networking&lt;/li&gt;
  &lt;li&gt;Storage solutions (especially Azure Storage)&lt;/li&gt;
  &lt;li&gt;Web apps&lt;/li&gt;
  &lt;li&gt;Managing identities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t mean to say that the tests are the same. Each exam certainly has its owng angle to the subject. Expect to see code in developer exam, expect to see command-line stuff in infrastructure exam and expect to see design problems in architecture exam. Still, there are lots of questions like “Which is the best for job x in circumstances y?” and the answer would be same regardless of the exam the question was in.&lt;/p&gt;

&lt;p&gt;The thing is that you can learn from many of those subjects by learning the ARM templates. So make sure that you study at least this &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-template-walkthrough&quot;&gt;walkthrough&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;hot-topics&quot;&gt;Hot topics&lt;/h2&gt;

&lt;p&gt;Where to focus? What to learn the best? It’s hard to go wrong with these:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Azure PowerShell (try to setup environments with it directly creating the resources and with arm templates)&lt;/li&gt;
  &lt;li&gt;ARM templates (use the automation button in portal and try to understand the json)&lt;/li&gt;
  &lt;li&gt;Storage (I made example clients and I did not need to regret it)&lt;/li&gt;
  &lt;li&gt;Web apps (especially the pricing tiers, scaling and monitoring)&lt;/li&gt;
  &lt;li&gt;Virtual machines (pricing tiers, scaling and monitoring)&lt;/li&gt;
  &lt;li&gt;Hybrid cloud (how to connect on-premise stuff to cloud and vice versa)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-to-expect-from-70-532-developing-microsoft-azure-solutions&quot;&gt;What to expect from 70-532 Developing Microsoft Azure solutions&lt;/h2&gt;

&lt;p&gt;Case studies and code examples. You don’t need to be a good programmer to pass this. You need to know the services listed in the exam and how to do design decisions with them. To be honest, I felt that this was more of a DevOps test than a developer test. There were code yes, but if you think what are the services that needs specific kind of coding to work you don’t have that much to learn. Those things are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;WebJobs&lt;/li&gt;
  &lt;li&gt;CloudServices (role entrypoints and stuff)&lt;/li&gt;
  &lt;li&gt;Storage clients&lt;/li&gt;
  &lt;li&gt;Azure Service Bus clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing is that you need to know a lot about things like DNS, HTTPS, deployments, monitoring and scaling to pass this test.  In some organizations this might not be typical for developer. The DevOps-loving Solita of course knows this all!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Link to the &lt;a href=&quot;https://www.microsoft.com/en-us/learning/exam-70-532.aspx&quot;&gt;exam&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Link to the preparation &lt;a href=&quot;https://channel9.msdn.com/Events/Ignite/2016/BRK3261&quot;&gt;video&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-to-expect-from-70-533-implementing-microsoft-azure-infrastructure-solutions&quot;&gt;What to expect from 70-533 Implementing Microsoft Azure Infrastructure solutions&lt;/h2&gt;

&lt;p&gt;I was not entirely sure what to expect from this one. I watched The Ignite videos and studied the docs. It was helpful. Notice that the exam materials say that you need to know how to implement Windows and Linux systems. Yes, Linux in Microsoft certification. Luckily, I had used Linux enough that I was able to answer the questions without preparing to answer to them.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Here are a lots of useful stuff at &lt;a href=&quot;https://aka.ms/Azure/IaaSOpsGuide&quot;&gt;IaaSOpsGuide&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;You might be also interested in &lt;a href=&quot;http://aka.ms/Azure/tools&quot;&gt;tools&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Link to the &lt;a href=&quot;https://www.microsoft.com/en-us/learning/exam-70-533.aspx&quot;&gt;exam&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Link to the preparation &lt;a href=&quot;https://channel9.msdn.com/Events/Ignite/2016/BRK3262&quot;&gt;video&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-to-expect-from-70-534-architecting-microsoft-azure-solutions&quot;&gt;What to expect from 70-534 Architecting Microsoft Azure solutions&lt;/h2&gt;

&lt;p&gt;All I can say is that try to understand purpose of every thing in this picture:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/azure-certification/mcsd_pyramid.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Make extra sure that you understand the purpose of different Azure connectivity options (p2p, p2s, ExpressRoute) and that you study hybrid cloud solutions. There were questions related to nearly every service I service I could think of and it was up to luck if the questions were detailed and hard or merely just about knowing the purpose of a service.&lt;/p&gt;

&lt;p&gt;Picture was borrowed from &lt;a href=&quot;https://channel9.msdn.com/Blogs/The-Game-Blog/A-Quick-overview-into-typical-Architecture-for-a-Cloud-Based-Gaming-Services&quot;&gt;channel9&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Link to the &lt;a href=&quot;https://www.microsoft.com/en-us/learning/exam-70-534.aspx&quot;&gt;exam&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Link to the preparation &lt;a href=&quot;https://techcommunity.microsoft.com/t5/Microsoft-Ignite-Content/BRK3264-Cert-Exam-Prep-Exam-70-534-Architecting-Azure-Solutions/td-p/9675&quot;&gt;video&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Udemy course that helped me to get the &lt;a href=&quot;https://www.udemy.com/70534-azure/&quot;&gt;overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-study&quot;&gt;How to study&lt;/h2&gt;

&lt;p&gt;Everyone has their own way to learn. I would still mainly focus on all the materials on under the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/&quot;&gt;Azure docs&lt;/a&gt;. Then I would definitely watch the preparation videos from Ignite 2016, all three for every certification (with 1.5x speed). And finally I would go out to the portal. I would press buttons N (for new popup) and B (for browse) and setup all the stuff that are mentioned on the exam. I would also definetly press the “automation” button that would show me the json template of the stuff I was about to create. I would also study resource groups json template after I would have set all in place.&lt;/p&gt;

&lt;p&gt;There are also a lot of “reading lists” available for the certifications. I checked a few but so many things have changed so recently and so many links were broken that I can’t really recommend those.&lt;/p&gt;

&lt;p&gt;Braindumps are no-no for me. But I think that there is kind of “official” practice tests available. You should learn explanations from them if you buy them. I didn’t want to invest to those because I felt confident even without them. Measureup had refreshed atleast some of their tests to the ARM era.&lt;/p&gt;

&lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;

&lt;p&gt;Why would you want to certificate? Well, the usual list is kind of boring. Partnerships, CV, sales, test your limits etc etc. I took the exams because my company encouraged me to do this. My company also supports certifications by giving a chance to study during worktime. Of course, I also got a chance to refresh my knowledge on the areas that are not so familiar to me.&lt;/p&gt;

&lt;h2 id=&quot;how-was-this-related-to-episerver&quot;&gt;How was this related to Episerver?&lt;/h2&gt;

&lt;p&gt;Most of the time the customer is heavily influencing where to host Episerver. Azure is one option. DXC also runs on top of Azure. There is really no harm to understand in depth Azure as a hosting option or as an option to implement microservices-related to Episerver projects. Azure AD is also a possibility for identity federation.&lt;/p&gt;

&lt;h2 id=&quot;how-much-effort-for-this-all&quot;&gt;How much effort for this all?&lt;/h2&gt;

&lt;p&gt;From 10-15 hours preparation for each test. Then, a few hours to take the actual exam. That was it for me. I got the whole thing done during February 2017.&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2017/03/01/Achieving-Azure-certification-after-ARM-refresh</guid>            <pubDate>Wed, 01 Mar 2017 16:20:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Episerver developer meetup at Solita</title>            <link>http://dev.solita.fi/episerver/2016/10/10/episerver-developer-meetup-solita</link>            <description>&lt;p&gt;Solita organized a meetup for Finnish Episerver developers in Helsinki on the 29th of September. The meetup consisted of food, beer and presentations. The event was a successor to the spring’s Episerver dev meetup and was as fun and cozy as the first one. This time we had a bit more time in the end for the networking than last time!&lt;/p&gt;

&lt;h2 id=&quot;commerce&quot;&gt;Commerce&lt;/h2&gt;

&lt;p&gt;Johan Hedberg from the Commerce development team visited us and gave a presentation about how to use Commerce to your daily household finance governance. He showed us how to invent your own currency in Commerce, how to give users credit to be used inside the Commerce and how to use the credit for shopping. In addition to that he showed how to use promotion engine in household to give stuff cheaper to household members when they do laundry or clean toilets. It was a very nice presentation. There were not many slides but Johan promised that the fun and educational demo application will be available in some format later. Looking forward for that!&lt;/p&gt;

&lt;h2 id=&quot;performance&quot;&gt;Performance&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/img/episerver-meetup/tommi.jpg&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Tommi Raunio from Solita had a presentation about performance. He first talked about what performance is and why it matters and then divided the topic into three different segments: design &amp;amp; architecture, implementation, and measurement. The slides can be found &lt;a href=&quot;http://www.slideshare.net/Solita_Oy/episerver-dev-meetup-performance-in-episerver-solutions-tommi-raunio-solita&quot;&gt;here&lt;/a&gt;
.&lt;/p&gt;

&lt;h2 id=&quot;devops&quot;&gt;DevOps&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/img/episerver-meetup/joona.jpg&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also had a presentation of my own about DevOps with Episerver. First I talked about how to help new guys in installing development environments. Then I talked about how to script server installations with PowerShell. Then I went a bit deeper and talked about how to build a build pipeline and finally I talked about something about NuGets. The &lt;a href=&quot;http://www.slideshare.net/Solita_Oy/epihelsinki-episerverdevops-joonaimmonen-solita&quot;&gt;Slides&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, thanks for everyone for the great evening!&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2016/10/10/episerver-developer-meetup-solita</guid>            <pubDate>Mon, 10 Oct 2016 15:45:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Episerver developer meetup at Helsinki</title>            <link>http://dev.solita.fi/episerver/2016/04/25/episerver-developer-meetup-helsinki</link>            <description>&lt;p&gt;Last week Episerver organized a meetup for Finnish developers in Helsinki. Meetup consisted of pizza, beer and presentations. I loved the meetup and the format was a perfect match to have a cozy atmosphere around the Episerver topic. Only thing that could have been better (next time maybe?) would be to have more time at the end for the networking! Which just means that the actual event was great success!&lt;/p&gt;

&lt;h2 id=&quot;rip-xforms&quot;&gt;R.I.P XForms&lt;/h2&gt;

&lt;p&gt;Mikko Huilaja from Solita had a hour long presentation about new Episerver Forms. He went through how they work, how you can create them, edit them, and also how to extend them. I haven’t had the time before to take look on the new Episerver Forms before, but now I feel I would be ready to implement them any day. Thanks Mikko for this great deep dive into the new Episerver Forms!
&lt;a href=&quot;http://www.slideshare.net/huilaaja/Episerver-forms-fi&quot;&gt;Slides&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;commerce-promotion-engine&quot;&gt;Commerce promotion engine&lt;/h2&gt;

&lt;p&gt;Quan Mai gave us a sneak peek into the upcomming promotion engine enhancement. If I understood correctly, the promotion engine might be included in next Commerce major release. The new system is IContent based and it was promised to be a lot faster than the old system. Debugging and extending it seemed to be thought in a good manner. All this means that there will be even less need to use the old Commerce Manager in the future.&lt;/p&gt;

&lt;p&gt;At Solita our Commerce developers absolutely love this news!&lt;/p&gt;

&lt;h2 id=&quot;webhooks&quot;&gt;Webhooks&lt;/h2&gt;

&lt;p&gt;James Stout (the egandalf himself) had a cool presentation about webhooks. With just a few lines of code he showed us how to hook into form post event and push its content through Zapier into google spreadsheets. It looked so easy and so painless I wish I would be as cool magician as egandalf is!&lt;/p&gt;

&lt;h2 id=&quot;cyberaware-development-with-episerver&quot;&gt;Cyberaware development with Episerver&lt;/h2&gt;

&lt;p&gt;I also had a presentation of my own about secure development. I talked about how developers should also be hobbyist hackers (white hat ones), what secure development lifecycle means, threat modeling and how this all is linked to our Episerver projects. I had only half of the time I thought I had so I hope that people got something out of my fast-forwarded slideshow. 
&lt;a href=&quot;http://www.slideshare.net/JoonaImmonen/secure-development-in-net-with-Episerver-solita&quot;&gt;Slides&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, thanks Episerver for the great evening!&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2016/04/25/episerver-developer-meetup-helsinki</guid>            <pubDate>Mon, 25 Apr 2016 02:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Installing server environment with PowerShell</title>            <link>http://dev.solita.fi/episerver/2016/02/11/installing-server-environment-with-powershell</link>            <description>&lt;p&gt;Most likely, as a reader you are mostly interested in my scripts. Here are my scripts for setting up Windows Server 2012 R2 ready for WebDeployments and running EPiServer ASP.NET site!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/solita/powershell-webdevelopertools/blob/master/scripts/solita_example_server_install.ps1&quot;&gt;Example script using modules below&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/solita/powershell-webdevelopertools/blob/master/scripts/solita_example_server_install_config.xml&quot;&gt;Config used by example script&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/solita/powershell-webdevelopertools/blob/master/solita-iistools.psm1&quot;&gt;IIS tools module&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/solita/powershell-webdevelopertools/blob/master/solita-servertools.psm1&quot;&gt;Server tools module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-automate-the-installation&quot;&gt;Why automate the installation?&lt;/h2&gt;

&lt;p&gt;This blog is about how to make your brand new Windows Server ready for WebDeployments with just pressing enter once. Cloud services can make your infrastructure lifecycle handling very easy; still people often encounter situations where they need to host our ASP.NET applications in virtual machines or directly on physical hardware. On those situations installation procedures in Windows operating systems are often done with some “clickety click” magic that won’t take too long. Still the “clickety click” installations have lots of long-term problems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Installations can’t be reproduced&lt;/li&gt;
  &lt;li&gt;Only the installer knows how he did it&lt;/li&gt;
  &lt;li&gt;If you have multiple servers you are doing same manual steps multiple times&lt;/li&gt;
  &lt;li&gt;Base of the installations does not differ much from project to project&lt;/li&gt;
  &lt;li&gt;After few years when your windows server needs upgrade you will need to repeat this&lt;/li&gt;
  &lt;li&gt;There is no way to test “clickety click” installations&lt;/li&gt;
  &lt;li&gt;Windows Server Core installations are rare because people is not used to manage Windows Servers without GUI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One could argue that documentation and clear processes would take care of all the problems above. Maybe they could but I have never seen installation documentation that has 100% coverage over how the installation has been done. Installation script works as document and developers are more likely to update it!&lt;/p&gt;

&lt;h2 id=&quot;what-needs-to-be-installed-on-a-fresh-windows-server&quot;&gt;What needs to be installed on a fresh Windows server&lt;/h2&gt;
&lt;p&gt;Here is short version of my list what I would do for new Windows Server&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Install IIS&lt;/li&gt;
  &lt;li&gt;Install WebPI&lt;/li&gt;
  &lt;li&gt;Install newest .NET&lt;/li&gt;
  &lt;li&gt;Install webdeploy&lt;/li&gt;
  &lt;li&gt;Install various modules for IIS from windows feature list or with WebPI&lt;/li&gt;
  &lt;li&gt;Install tools for administration (7zip, text editor etc)&lt;/li&gt;
  &lt;li&gt;Create IIS site and application pool and change some defaults for better&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;is-there-packagemanagement-for-windows&quot;&gt;Is there PackageManagement for Windows?&lt;/h2&gt;
&lt;p&gt;Oh yes there is! It is called &lt;a href=&quot;http://blogs.technet.com/b/packagemanagement/archive/2015/04/29/introducing-packagemanagement-in-windows-10.aspx&quot;&gt;Windows PackageManagement&lt;/a&gt; (aka OneGet). Unfortunately it is not available for Windows Servers yet. Production preview of Windows Management Framework 5 is already &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=48729&quot;&gt;available&lt;/a&gt;. Windows PackageManagement is actually a manager that can access multiple type of repositories to have unified way to download and install software from multiple sources. Microsoft MSI installers, Windows Features and software could be all installed with Windows PackageManagement in the future. The thing still missing is grown up economy where all the toys would be easily available in a secure manner. Chocolatey is pretty good already but it is missing a lot of software and I’m still sceptical about security of software packages. Also Chocolatey is not the main delivery channel for many software companies and there might be a huge delay before software is available from there. So instead of using Chocolatey I provide examples how to do silent installations with different type of software packages.&lt;/p&gt;

&lt;h2 id=&quot;how-stuff-is-installed-without-chocolatey&quot;&gt;How stuff is installed without Chocolatey&lt;/h2&gt;
&lt;p&gt;If you are familiar with Windows Server installation you might notice that there are multiple different types of installation involved in the process. Windows features, executables, msi files and software specific plugins are all in the same process. This is the part where I wish that Windows PackageManagement (aka OneGet) or Chocolatey can one day solve unifying installation.&lt;/p&gt;

&lt;h4 id=&quot;windows-features-eg-iis&quot;&gt;Windows features (e.g. IIS)&lt;/h4&gt;
&lt;p&gt;The first question you should be looking at is “what are the windows features”? To be able to install the features you must first learn how to find them. Here is a oneliner that will give the available features to you.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-WindowsOptionalFeature -Online 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Great! Now we know how to find the windows features. Now we just need to change the oneliner a bit to be able to Install them. This is a oneliner for .NET:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Enable-WindowsOptionalFeature -Online -All -FeatureName NetFx4
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;All-flag means that it will also install required dependencies if any. The .NET delivered among the operating system might not be latest, but the feature needs to be enabled all the same. IIS will need it among some other features. Full list of my users can be found in our example config file at the bottom of the article.&lt;/p&gt;

&lt;h4 id=&quot;executables&quot;&gt;Executables&lt;/h4&gt;
&lt;p&gt;With windows feature we were able to install the .NET version that is delivered with the operating system. Although that is unlikely the newest one. Instead we need to fetch the newest one from &lt;a href=&quot;https://download.microsoft.com/download/E/4/1/E4173890-A24A-4936-9FC9-AF930FE3FA40/NDP461-KB3102436-x86-x64-AllOS-ENU.exe&quot;&gt;the internet&lt;/a&gt;. With executables you never can be quite sure how they should be installed because there is no strictly unified commandline parameters. Here is an example how your executable installation might look like.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$arguments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;/qn&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# display no interface &lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;/norestart&quot;&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;/passive&quot;&lt;/span&gt;
		&lt;span class=&quot;s2&quot;&gt;&quot;/S&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Start&lt;/span&gt;-Process &lt;span class=&quot;nv&quot;&gt;$exe&lt;/span&gt;  -Wait -PassThru -Verb runAs -ArgumentList &lt;span class=&quot;nv&quot;&gt;$arguments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;I chose an approach where I just guess what kind of parameters the executable will need for silent installation. I found out that /S is pretty common, but sometimes also /passive is used too. Most likely your executables needs to be run elevated, that can be achieved with “-Verb runAs” parameter for Start-Process function. So even if this worked for my executables, this might be terrible mess for yours (for example if there is checks for arguments being eligible).&lt;/p&gt;

&lt;h4 id=&quot;msi-files&quot;&gt;MSI files&lt;/h4&gt;
&lt;p&gt;MSI files are really common way to install stuff in Microsoft environment. They are also unified so we can be more certain that silent installation can be achieved. The installation looks really similar to installation of executables.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$arguments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;/i&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#install product&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$msiFile&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;/qn&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# display no interface &lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;/norestart&quot;&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;/passive&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Start&lt;/span&gt;-Process msiexec.exe -Wait -PassThru -Verb runAs -ArgumentList &lt;span class=&quot;nv&quot;&gt;$arguments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The File to be installed is given as an parameter to msiexec.exe among some other flags. Installer is again run as elevated process to make sure that it can make all the configuration changes it wants.&lt;/p&gt;

&lt;h4 id=&quot;webpi-modules&quot;&gt;WebPI modules&lt;/h4&gt;
&lt;p&gt;To be able to install WebPI modules, you need to first &lt;a href=&quot;http://download.microsoft.com/download/C/F/F/CFF3A0B8-99D4-41A2-AE1A-496C08BEB904/WebPlatformInstaller_amd64_en-US.msi&quot;&gt;install it&lt;/a&gt;. After installation you are able to use it’s cmdline tools to install modules. Before you know what to install, you might need to list the available modules. Here is an example oneliner for listing:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;amp; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:programfiles&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;microsoft&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\W&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;eb Platform Installer&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\w&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ebpicmd.exe&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; /List /ListOption:All
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Once we get all the available modules we can find out that the name of the needed WebDeploy package is WDeploy36NoSmo. Here we install WebDeploy with oneliner:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;amp; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:programfiles&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;microsoft&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\W&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;eb Platform Installer&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\w&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ebpicmd.exe&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; /Install /Products:&lt;span class=&quot;s2&quot;&gt;&quot;WDeploy36NoSmo&quot;&lt;/span&gt; /AcceptEula
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;AcceptEula flag basicly gives you silent installation. You might want to elevate this process too, but I didn’t feel that it was necessary.&lt;/p&gt;

&lt;h2 id=&quot;how-stuff-is-configured&quot;&gt;How stuff is configured&lt;/h2&gt;
&lt;p&gt;Once we got our IIS, .NET, WebPI and WebDeploy successfully installed there are still few steps before machine is ready to host our applications. First we need to make sure that the IIS website is set to be able to make deployments.&lt;/p&gt;

&lt;h4 id=&quot;iis-website&quot;&gt;IIS website&lt;/h4&gt;
&lt;p&gt;First of all we need a folder for IIS website. We can create it like this:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$folderPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\M&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ySite&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;!&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Test-Path&lt;/span&gt; -Path &lt;span class=&quot;nv&quot;&gt;$folderPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)){&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Creating installation folder &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$folderPath&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$Null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;New-Item&lt;/span&gt; -ItemType directory -Path &lt;span class=&quot;nv&quot;&gt;$folderPath&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then we need to setup an application pool for the website. It is also good idea to make sure few defaults are changed to better meet the purpose. We like to disable application pool idle timeout to get rid off unexpected expensive first hits. Instead we are using periodic restart to restart the pool when we are not expecting many customers.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Import-Module WebAdministration
New-WebAppPool -Name &lt;span class=&quot;nv&quot;&gt;$appPoolName&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# .NET runtime &lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Set-ItemProperty&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IIS:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ppPools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;appPoolName&quot;&lt;/span&gt; -Name &lt;span class=&quot;s2&quot;&gt;&quot;managedRuntimeVersion&quot;&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-Value &lt;span class=&quot;nv&quot;&gt;$appPoolDotNetVersion&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# recycling&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Set-ItemProperty&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IIS:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ppPools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;appPoolName&quot;&lt;/span&gt; -Name Recycling.periodicRestart.time &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-value &lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;TimeSpan]::FromMinutes&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1440&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 1 day (default: 1740)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Clear existing values if any&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Clear&lt;/span&gt;-ItemProperty &lt;span class=&quot;s2&quot;&gt;&quot;IIS:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ppPools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;appPoolName&quot;&lt;/span&gt; -Name Recycling.periodicRestart.schedule 
&lt;span class=&quot;nb&quot;&gt;Set-ItemProperty&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IIS:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ppPools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;appPoolName&quot;&lt;/span&gt; -Name Recycling.periodicRestart.schedule &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-Value @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$appPoolRestartTime&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Set-ItemProperty&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IIS:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ppPools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;appPoolName&quot;&lt;/span&gt; -Name processModel.idleTimeout &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-value &lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;TimeSpan]::FromMinutes&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Disabled (default: 20)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# logs from recycling to event log&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$recycleLogEvents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Set-ItemProperty&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IIS:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\A&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ppPools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;appPoolName&quot;&lt;/span&gt; -Name Recycling.logEventOnRecycle &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-Value &lt;span class=&quot;nv&quot;&gt;$recycleLogEvents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;After that we can create the website. Below there is an example how to create website with possible multiple bindings:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;New-WebSite -Name &lt;span class=&quot;nv&quot;&gt;$siteName&lt;/span&gt;  &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-Port 80 &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-HostHeader &lt;span class=&quot;nv&quot;&gt;$siteName&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-PhysicalPath &lt;span class=&quot;nv&quot;&gt;$physicalPath&lt;/span&gt;  
&lt;span class=&quot;nb&quot;&gt;Set-ItemProperty &lt;/span&gt;IIS:&lt;span class=&quot;se&quot;&gt;\S&lt;/span&gt;ites&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;siteName -name applicationPool &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-value &lt;span class=&quot;nv&quot;&gt;$appPoolName&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# set bindings (go through all the bindings and create new webbinding for each)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$binding&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$bindings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Adding binding &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$binding&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$bindingProtocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http&quot;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$bindingIP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$bindingPort&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;80&quot;&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$bindingHostHeader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$binding&lt;/span&gt;.Hostname
	&lt;span class=&quot;nv&quot;&gt;$bindingCreationInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; New-WebBinding -Protocol &lt;span class=&quot;nv&quot;&gt;$binding&lt;/span&gt;.protocol &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
		-Name &lt;span class=&quot;nv&quot;&gt;$siteName&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
		-IPAddress &lt;span class=&quot;nv&quot;&gt;$bindingIP&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
		-Port &lt;span class=&quot;nv&quot;&gt;$bindingPort&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
		-HostHeader &lt;span class=&quot;nv&quot;&gt;$bindingHostHeader&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;webdeploy&quot;&gt;WebDeploy&lt;/h4&gt;
&lt;p&gt;Once our website is happily set we can easily configure it for WebDeploy with the WebPI plugin we installed earlier. Scripts for WebDeploy can be found under the installation directory of the product. The scripts take care of problems like giving permissions to the iis site folder for webdeploy, creating user if it does not exist and setting up the WebDeploy configurations. Here is how to use the script with one long oneliner:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;amp; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:programfiles&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IIS&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\M&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icrosoft Web Deploy V3&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cripts&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\S&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;etupSiteForPublish.ps1&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-siteName &lt;span class=&quot;nv&quot;&gt;$siteName&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-siteAppPoolName &lt;span class=&quot;nv&quot;&gt;$appPoolName&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-deploymentUserName &lt;span class=&quot;nv&quot;&gt;$wdeployUser&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-deploymentUserPassword &lt;span class=&quot;nv&quot;&gt;$wdeployUserPw&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
	-managedRuntimeVersion &lt;span class=&quot;nv&quot;&gt;$appPoolDotNetVersion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;summing-up&quot;&gt;Summing up&lt;/h2&gt;
&lt;p&gt;Instead of huge scripts I like to put my PowerShell stuff to modules and also I like to be able to configure my installations. Thus I created few modules, an example script and an example configuration. I am using XML configuration because it was the way to go earlier with the PowerShell (it was easy to load unlike JSON). Now newer PowerShell has “ConvertFrom-JSON” function that makes it possible to use also JSON configuration.&lt;/p&gt;

&lt;p&gt;Here is how to load an XML file:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;xml]&lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;get-content&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$configFile&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;After the variable with XML file contents is loaded it is easy to access different elements inside it. For example if you would like to access the name of the WebDeploy user it might look something like this:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nv&quot;&gt;$config&lt;/span&gt;.Root.IIS.WebDeploy.GetAttribute&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;With all this done you could also enable PowerShell remoting on your server and do all the installations without using any RDP connection at all.&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2016/02/11/installing-server-environment-with-powershell</guid>            <pubDate>Thu, 11 Feb 2016 01:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Security controls in continuous integration</title>            <link>http://dev.solita.fi/episerver/2015/12/11/ci-security-controls</link>            <description>&lt;p&gt;Continuous integration and continuous delivery have been trendy topics lately. Among other things it is necessary to take care of security in your development process. I want to share few ideas how you can enhance your security in continuous integration in EPiServer CMS projects. &lt;a href=&quot;http://www.n4s.fi/en/&quot;&gt;Need for speed&lt;/a&gt; is a Finnish foundation and its purpose is to find new ways to deliver real time value for customers of Finnish software companies. There was a Q4 review this week at Rovaniemi and my blog post will be about &lt;a href=&quot;http://www.slideshare.net/Solita_Oy/continuous-integration-and-security-testing-with-net?related=1&quot;&gt;my presentation&lt;/a&gt; at there.&lt;/p&gt;

&lt;h2 id=&quot;problem-domain&quot;&gt;Problem domain&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/owasp_problem_domain.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this picture from OWASP it is important to understand that when talking about security, the threat agents in the picture will be trying to effect your business. By understanding the motive of the attacker it will be much easier to estimate what he or she is trying to achieve. Of course there can be natural disasters that are mindless threats but for example botnets are always built for some purpose.&lt;/p&gt;

&lt;p&gt;In continuous integration we want to find possible security vulnerabilities and make the build fail to get instant feedback about security problem for the developer.&lt;/p&gt;

&lt;h2 id=&quot;security-testing-as-whole&quot;&gt;Security testing as whole&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/owasp_sdlc.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Diagram above is from &lt;a href=&quot;https://www.owasp.org/images/5/52/OWASP_Testing_Guide_v4.pdf&quot;&gt;OWASP Testing guide 4&lt;/a&gt;. It presents secure development lifecycle (Microsoft has its &lt;a href=&quot;https://www.microsoft.com/en-us/sdl/&quot;&gt;own&lt;/a&gt; too). There are many things that can’t be handled with automatisation like going through customer security policies, learning industrial standards and creating security architecture but nevertheless there are areas that can be partly automated.&lt;/p&gt;

&lt;h2 id=&quot;different-tools-tested&quot;&gt;Different tools tested&lt;/h2&gt;

&lt;p&gt;We tested various tools and divided them to different categories.&lt;/p&gt;

&lt;h5 id=&quot;static-code-analysis&quot;&gt;Static code analysis&lt;/h5&gt;
&lt;p&gt;In this category there are tools that go through source code and possibly also the application configuration. They look for problems in code and are meant for ensuring the code quality. They are capable of finding SQL injection possibilities, unvalidated parameters, memory leaks and security misconfigurations but still security testing is not the core idea behind these tools.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;FxCop&lt;/li&gt;
  &lt;li&gt;VisualCodeGrepper&lt;/li&gt;
  &lt;li&gt;SonarQube&lt;/li&gt;
  &lt;li&gt;ReSharper commandlinetools&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;code-quality-metrics&quot;&gt;Code quality metrics&lt;/h5&gt;
&lt;p&gt;Code quality metrics is all about code quality. By having good quality enhances your security indirectly. Complex software is harder to test and bad code is more likely to contain security holes. Duplicated code easily leads to fact that problems are only fixed in one place and left open to another place. Having bugs in your code affects integrity and availability of the software and data. As you might know, the security is often modeled as CIA triangle (confidentiality, integrity and availability).&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SonarQube&lt;/li&gt;
  &lt;li&gt;Code metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;configuration-and-deployment-analysis&quot;&gt;Configuration and deployment analysis&lt;/h5&gt;
&lt;p&gt;These tools will test how your system has been set up. MBSA checks if your installation follows best practices and ASA checks what is the attack surface of your application.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft Baseline Security Analyzer&lt;/li&gt;
  &lt;li&gt;Attack Surface Analyzer&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;vulnerability-scanning&quot;&gt;Vulnerability scanning&lt;/h5&gt;
&lt;p&gt;These tools are meant for security professionals. Nessus is a network scanner that shines in finding known vulnerabilities in operating systems and application servers. OWASP ZAP is meant for web application security testing. Both of these test directly the security of your application. You should be careful when using these since you might be acting against the law if you scan targets without their permission.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Nessus&lt;/li&gt;
  &lt;li&gt;OWASP Zed Attack Proxy&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;performance-testing&quot;&gt;Performance testing&lt;/h4&gt;
&lt;p&gt;Since availability is in the core of security, the performance testing will help you in finding bottlenecks that attacker could use to amplify his or her DOS Denial of Service attack.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;jMeter&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-do-these-tools-sit-on-secure-development-lifecycle&quot;&gt;How do these tools sit on secure development lifecycle?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/tools_in_sdlc.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As it can be clearly seen in the picture the tools can help you in development, deployment and maintenance.&lt;/p&gt;

&lt;h2 id=&quot;in-which-layers-these-tools-work&quot;&gt;In which layers these tools work&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/tools_in_did.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This picture tries to tell you something about defence in depth. Choosing tools only from one category will not be enought to cover all aspects of the security. Also keep in mind that the attacker won’t care if the vulnerability is in the host or in the application. This means that seemless cooperation between development and operations is needed; thus you should embrace DevOps in your workplace to cover these gaps.&lt;/p&gt;

&lt;h2 id=&quot;how-do-these-tools-mitigate-owasp-top-10-threats&quot;&gt;How do these tools mitigate OWASP TOP 10 threats&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/tools_owasp_top_10.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://owasptop10.googlecode.com/files/OWASP%20Top%2010%20-%202013.pdf&quot;&gt;OWASP TOP 10&lt;/a&gt; is a list of common web application vulnerabilities. As it can be seen in the picture the OWASP ZAP is a tool that is created to mitigate these vulnerabilities. Nessus and static code analysis tools can also be helpful. Truth to be told some of these threats are really generic ones. Security misconfiguration for example can mean pretty much anything (network devices, operating systems, application servers, other services in the operating system, etc). Missing function level authorization instead is so tightly coupled to application logic that it is hard to test automatically; the only option is to write your own tests against that.&lt;/p&gt;

&lt;h2 id=&quot;how-do-these-tools-mitigate-cloud-security-alliances-notorious-nine&quot;&gt;How do these tools mitigate Cloud Security Alliances “Notorious Nine”&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/tools_csa_notorious_nine.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://downloads.cloudsecurityalliance.org/initiatives/top_threats/The_Notorious_Nine_Cloud_Computing_Top_Threats_in_2013.pdf&quot;&gt;Notorious nine&lt;/a&gt; is Cloud Security Alliance’s list of most common threats for cloud computing. In the picture above you can pretty clearly see that most of the threats can’t be mitigated in CI with automized tools. Instead you should read carefully how CSA suggests you to mitigate the threats. Most of the threats are handled with security policies inside companies and only way to truly mitigate them is to educate your employees to be aware of these threats.&lt;/p&gt;

&lt;h2 id=&quot;how-useful-our-few-example-projects-felt-that-the-tools-are-in-security-perspective&quot;&gt;How useful our few example projects felt that the tools are in security perspective&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/tools_usefulness.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This one is interesting. You could think that OWASP ZAP and Nessus would have been better here. But the truth is that the amount of vulnerabilities found with these tools was not big when compared the effort you need to have to use them continuously in continuous integration. Nessus for example was out of the case in projects because hosting was done by 3rd party in these example projects and it makes it hard to make continuous network scans since monitoring of the 3rd party company will be disturbed by this kind of network activity. OWASP ZAP instead reports a great deal of false positives and it is meant to be used manually so its usage in CI is harder than you would think. Example for using it from Jenkins is available at &lt;a href=&quot;https://github.com/solita/powershell-zap/tree/master/PowerShell-ZAP&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also when using only OWASP TOP 10 and CSA Notorious nine the usefulness of tools is twisted. The accuracy is great deal better in static code analysis, they can be run a lot faster and tuning them for your needs is easier. OWASP-ZAP can take a lot of time if you run it in a manner that spiders your website, scans the found urls and gathers the report. We have projects that have several thousands of pages which means that going through all of them will really take some time.&lt;/p&gt;

&lt;h2 id=&quot;how-does-it-feel-to-use-these-tools&quot;&gt;How does it feel to use these tools&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/n4s-q4-and-ci-security-controls/sonarqube.png&quot; alt=&quot;Tools&quot; /&gt;
In the picture there is example of SonarQube dashboard. You might think that there is a great deal of major issues in the code in the project that was scanned with SonarQube. I used three different static code analysis to get that dashboard (ReSharper commandlinetools, FxCop and SonarQubes own static code analysis). 600 of major issues are because of our software didn’t have good enough commentation ratio (above 25% of code should be comments). I personally don’t trust that having that much comments is a good thing; instead readability of code should be embraced and only necessary things should be commented. This way the comments won’t become noise in your eyes and you will keep on reading them.&lt;/p&gt;

&lt;p&gt;Well the bottom line is that from the around 6000 reported issues there was maybe few dozen real ones. With all the automized tools you need to spare some time to tune the tools to only report about issues you are really interested on.&lt;/p&gt;

&lt;h2 id=&quot;recommendation-for-implementing-continuous-security&quot;&gt;Recommendation for implementing continuous security&lt;/h2&gt;
&lt;p&gt;Try to run static code analysis with few different tools. See what they can find and reflect that to your security needs. Take atleast one into your CI build after tuning it into your needs.&lt;/p&gt;

&lt;p&gt;Calculate code metrics and see what they say about your software. My recommendation would be to use SonarQube as a code quality platform that keeps at track how your code quality develops. It will be a difficult task to truly bring down the CI build with these metrics instead you will like to watch how the metrics develop over time.&lt;/p&gt;

&lt;p&gt;Try to scan your application manually with few different tools (MSBA, OWASP ZAP, Burp suite, nmap, nessus) and again see what they can find and think if you want to make these checks continuous. You should for example create nightly builds and/or continuous security checks separated from development if they are taking too much time. By learning the tools you can also make only specific tests in CI which would make sure that critical parts of application are secure.&lt;/p&gt;

&lt;p&gt;Above all else remember to educate your personnel!&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2015/12/11/ci-security-controls</guid>            <pubDate>Fri, 11 Dec 2015 01:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Installing development environment with PowerShell</title>            <link>http://dev.solita.fi/episerver/2015/12/04/installing-development-environment-with-powershell</link>            <description>&lt;p&gt;I will not lie: I love PowerShell! Lately I have been trying to figure out how we can ease the life of .NET developers in EPiServer projetcs. PowerShell and NuGet has been keywords on that topic. Now I want to show you how PowerShell can ease your life! Open your hearth and let the PowerShell flow within!&lt;/p&gt;

&lt;h2 id=&quot;what-are-the-identified-problems&quot;&gt;What are the identified problems&lt;/h2&gt;
&lt;p&gt;We have few things that are happening all the time.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We are getting new people on our team&lt;/li&gt;
  &lt;li&gt;We are upgrading our development machines to new ones&lt;/li&gt;
  &lt;li&gt;We are changing from one project to another&lt;/li&gt;
  &lt;li&gt;Creation of virtual machines for various reasons is common, they need some tools too&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these above will have some kind of impact of installating software, updating components and configuration of various things. I really wanted to cut down the costs that our company and/or our customers are having because of this.&lt;/p&gt;

&lt;h2 id=&quot;enhancement-1-workstation-installation-script&quot;&gt;Enhancement 1: Workstation installation script&lt;/h2&gt;
&lt;p&gt;Our awesome IT department (you guys are really awesome!) has of course workstation images that they use when they deliver workstations to all employees. Nevertheless there are so many kind of development happening that they don’t have time to support all the variations that different developer archetypes would like to have. In some cases developers won’t even use same tools in same project.&lt;/p&gt;

&lt;p&gt;To solve this issue we created a PowerShell Chocolatey script that installs all the wanted tools for .NET developers (others can do their own scripts). Developer can himself comment out unwatend tools or add some others.&lt;/p&gt;

&lt;p&gt;Here is an example of it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/powershell-is-backbone-of-dotnet-devops/chocoscript.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;enchancement-2-project-specific-environment-setup&quot;&gt;Enchancement 2: Project specific environment setup&lt;/h2&gt;
&lt;p&gt;We have currently many EPiServer projects going on. Common for these projects is the need to support multiple different bindings for the same IIS site. If we bind to *:80 then we are able to develop only one project per machine and we have need to support multiple projects at the same time. Some of the sites have also need to install some certificates for integrations and to create test sites and all other stuff. To achieve this we created a PowerShell Module that can be used.&lt;/p&gt;

&lt;h4 id=&quot;list-of-needed-functionality&quot;&gt;List of needed functionality&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;IIS site creation&lt;/li&gt;
  &lt;li&gt;Application pool creation&lt;/li&gt;
  &lt;li&gt;IIS site bindings creation&lt;/li&gt;
  &lt;li&gt;Certificate creation (e.g. for encryption)&lt;/li&gt;
  &lt;li&gt;Certificate installation&lt;/li&gt;
  &lt;li&gt;Test that all mandatory .NET and IIS stuff is installed correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here is how it looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/powershell-is-backbone-of-dotnet-devops/sitecreation.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next&lt;/h2&gt;
&lt;p&gt;As having one PowerShell Module that all projects are dependant might be burdensome; we are inspecting possiblity to deliver this module via NuGet for each project independently. Project could then decide to which version of Module it is dependant. But that would be then a subject for another blog post.&lt;/p&gt;

&lt;h2 id=&quot;cool-i-want-to-do-that-too&quot;&gt;Cool! I want to do that too!&lt;/h2&gt;
&lt;p&gt;Because we are such a nice guys we put all the scripts into GitHub for everyone to access. You can get them from our &lt;a href=&quot;https://github.com/solita/powershell-webdevelopertools&quot;&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/episerver/2015/12/04/installing-development-environment-with-powershell</guid>            <pubDate>Fri, 04 Dec 2015 01:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Hello BadUSB</title>            <link>http://dev.solita.fi/2015/09/21/Hello-BadUSB</link>            <description>&lt;p&gt;There was a big security flaw in a 2014 Jeep Cherokee. Chrysler didn&amp;#39;t have a way to patch cars over the air so &lt;a href=&quot;http://www.wired.com/2015/09/chrysler-gets-flak-patching-hack-via-mailed-usb/&quot;&gt;they mailed 1.4 million USB drives&lt;/a&gt; via the US Postal Service. By doing that they teached their customers to trust USB drives that are delivered by mail. What security professionals are fearing is of course malicious USB devices. Let&amp;#39;s peek into world of BadUSB.&lt;/p&gt;

&lt;h2&gt;What is BadUSB?&lt;/h2&gt;

&lt;p&gt;BadUSB can for example upload malware to your computer, redirect your internet traffic or pretend to be a keyboard. BadUSB can be any USB device. It is possible for hackers to reprogram micro-controllers of normal devices or cloak their malicious devices to look like regular ones.  Probably the most famous use of USB drives in a cyberattack was &lt;a href=&quot;http://arstechnica.com/tech-policy/2011/07/how-digital-detectives-deciphered-stuxnet-the-most-menacing-malware-in-history/&quot;&gt;stuxnet&lt;/a&gt;. USB drives were used for getting worm into computers that were not connected to the other world. &lt;/p&gt;

&lt;h2&gt;How do I get one?&lt;/h2&gt;

&lt;p&gt;I ordered my BadUSB from &lt;a href=&quot;http://hakshop.myshopify.com/&quot;&gt;hakshop&lt;/a&gt;. It is called USB Rubber Ducky Deluxe and it is build for penetration testing purposes for security professionals. In short it is a USB drive that acts as a USB Human Interface Device. When you plug it into your device it presents itself as a keyboard. Within a package there is the BadUSB itself, microSD card and microSD card reader. Because it is a keyboard it works with any device that supports USB keyboard (Windows, Mac, Linux, Android, etc). The payload has to be chosen by knowing the target system because the same keyboard injection won&amp;#39;t do desired things in different operating systems. &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/hello-badusb/ducky_usb.jpg&quot; alt=&quot;Tools&quot; /&gt;
The device itself looks just like an USB drive&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/hello-badusb/ducky_embedded_microsd.jpg&quot; alt=&quot;Tools&quot; /&gt;
From inside the Ducky is actually embedded device with microSD reader&lt;/p&gt;

&lt;h2&gt;How to script Ducky&lt;/h2&gt;

&lt;p&gt;Ducky has its own script language called &lt;a href=&quot;https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Duckyscript&quot;&gt;DuckyScript&lt;/a&gt;. It is simple language which allows you to define what keyboard actions you want to script. There is no actual IDE for it but community has created an User Defined Language for Notepad++ which can be found from &lt;a href=&quot;https://forums.hak5.org/index.php?/topic/21045-encoder-duckyscript-notepad-userdefinedlanguage/&quot;&gt;hak5 forums&lt;/a&gt;. My first payload script simply opened PowerShell with Win-R shortcut and downloaded an executable which was then executed. &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/hello-badusb/duckyscript.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The script itself is quite self-explanatory. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;At the top of the script there are few lines of comments with REM keyword. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then DELAY for 10s is added because Ducky was anxious to start executing the script before Windows has finished installing HID drivers thus my payload failed without it. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WINDOWS r opens the run prompt and yet again delay is added for waiting the prompt to truly open. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then PowerShell is written to prompt following with ENTER. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PowerShell is used to invoke a webclient command to download a file and store it under temp folder. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally the executable is started with Start-Process command in PowerShell. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are plenty of ready scripts available. &lt;a href=&quot;https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Payloads&quot;&gt;Here&lt;/a&gt; are few samples. There is also &lt;a href=&quot;http://www.ducktoolkit.com&quot;&gt;community made toolkit&lt;/a&gt; available. You should check it out since it allows you to generate scripts with certain payload. Toolkit also allows you to compile scripts on the internet but my precautioness didn&amp;#39;t let me to test it out. &lt;/p&gt;

&lt;h2&gt;Building the script for Ducky&lt;/h2&gt;

&lt;p&gt;This was for me the trickiest part. Wiki tells you to download &lt;a href=&quot;https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Downloads&quot;&gt;Duck Encoder&lt;/a&gt;. It was straightforwart to use.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span class=&quot;n&quot;&gt;java&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-jar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duckencoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exploit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;txt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;microsdcard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Although there was an annoying problem. It only supported US layout. The script didn&amp;#39;t work unless you chose the correct keyboard layout from Windows first. Luckily community has solved this problem. There were newer versions of &lt;a href=&quot;https://github.com/midnitesnake/USB-Rubber-Ducky&quot;&gt;commandline compiler&lt;/a&gt; available. I just needed to pass new parameter for the encoder!&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span class=&quot;n&quot;&gt;java&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-jar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exploit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;txt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-l&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Taking BadUSB into action&lt;/h2&gt;

&lt;p&gt;After that there is only one more thing to do. To upload the payload for your device. You need to take microSD card from your Ducky device and upload inject.bin into device. After inserting microSD card back the device is ready to use. If you plan to buy multiple microSD cards you should know that there are pretty strict limitations what kind of microSD cards are eligible. The device can&amp;#39;t handle big cards at all. &lt;/p&gt;

&lt;p&gt;After injecting the BadUSB to a Windows machine the following things will happen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/hello-badusb/ducky_cmd.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Run prompt is shown and &amp;quot;powershell&amp;quot; is typed into it&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/hello-badusb/ducky_powershell.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;PowerShell runs script to download an executable and then runs it&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/hello-badusb/ducky_exe.png&quot; alt=&quot;Tools&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The executable itself was this time a pretty harmless one that opens notepad and prints an adorable ASCII dragon.&lt;/p&gt;

&lt;h2&gt;Why BadUSB is so dangerous?&lt;/h2&gt;

&lt;p&gt;Dangerousness of Human Interface Device USB is that all the user actions are always trusted. How do you bypass UAC in Windows? With user action. Also if the malicious code is injected into USB micro-controller there is no way to antivirus software to be able to scan it.&lt;/p&gt;

&lt;p&gt;This kind of BadUSB device is pretty easy to notice because it didn&amp;#39;t do what user expected (it wasn&amp;#39;t an USB drive). With Ducky there are community made firmwares that can be both USB Drive and USB Keyboard at the same time. This way the victim could download e.g. PDF document from USB drive and after that you could just disrupt him when the HID keyboard starts to build a backdoor for you. &lt;/p&gt;

&lt;h2&gt;How to protect yourself from BadUSB?&lt;/h2&gt;

&lt;p&gt;Once your car manufacturer sends you an USB drive via postal service do not plug it in! It is simple as that. You should go to meet your local car service company and ask them to do the needed job. Also you should of course always lock your computer when not using it and never leave it into unsafe location unattended. In an open-plan office it could be pretty easy to add for example keylogger in between your keyboard and computer without you ever noticing it. Modern keyloggers are able to broadcast keypresses through WiFi to the attacker.&lt;/p&gt;

&lt;p&gt;One way around would be to whitelist the allowed devices and deny all others. This might be burdensome especially if user does not have privileges to bypass this restriction. It is also common action to disable USB ports from devices that do not need them (e.g. servers). Although that is a bit extreme action and if somebody is in your server room with malicious USB devices the USB device itself is most likely least of your concerns. &lt;/p&gt;

&lt;p&gt;Nevertheless the most important thing is to educate your personnel to be aware of this kind of threats. You should also support secure ways to transfer from files from computer to another to ensure that use of USB drives is minimized.&lt;/p&gt;
</description>            <guid>http://dev.solita.fi/2015/09/21/Hello-BadUSB</guid>            <pubDate>Mon, 21 Sep 2015 02:00:00 GMT</pubDate>           <category>Blog post</category></item></channel>
</rss>