<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mark Harrison</title>
    <description>The latest articles on DEV Community by Mark Harrison (@harrisonmeister).</description>
    <link>https://dev.to/harrisonmeister</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F290901%2F1c1406de-fdfd-4288-8c5e-56f7809e71f2.jpg</url>
      <title>DEV Community: Mark Harrison</title>
      <link>https://dev.to/harrisonmeister</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harrisonmeister"/>
    <language>en</language>
    <item>
      <title>Better multi-tenancy with Octopus Deploy</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Mon, 26 Jul 2021 10:15:10 +0000</pubDate>
      <link>https://dev.to/octopus/better-multi-tenancy-with-octopus-deploy-2c4h</link>
      <guid>https://dev.to/octopus/better-multi-tenancy-with-octopus-deploy-2c4h</guid>
      <description>&lt;p&gt;Most people using Octopus will deploy projects to one or more environment. For customers providing Software as a Service (SaaS) applications, they typically need to deploy multiple instances of the application for each of their customers.&lt;/p&gt;

&lt;p&gt;Fortunately, there's a feature that's been around since &lt;a href="https://octopus.com/blog/whats-new-multi-tenant-deployments" rel="noopener noreferrer"&gt;Octopus 3.4&lt;/a&gt; designed exactly for these types of deployment, &lt;a href="https://octopus.com/docs/tenants" rel="noopener noreferrer"&gt;multi-tenancy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I look at two approaches to deploying applications without tenants, and discuss the benefits of using the multi-tenancy feature. &lt;/p&gt;

&lt;h2&gt;
  
  
  In this post
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Deploying without tenants

&lt;ul&gt;
&lt;li&gt;
Using multiple projects

&lt;ul&gt;
&lt;li&gt;Multiple projects pros&lt;/li&gt;
&lt;li&gt;Multiple projects cons&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Using multiple environments

&lt;ul&gt;
&lt;li&gt;Multiple environment pros&lt;/li&gt;
&lt;li&gt;Multiple environment cons&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Deploying with tenants

&lt;ul&gt;
&lt;li&gt;Tenants&lt;/li&gt;
&lt;li&gt;Tenant tags&lt;/li&gt;
&lt;li&gt;
Tenant variables

&lt;ul&gt;
&lt;li&gt;Project variable templates&lt;/li&gt;
&lt;li&gt;Common variables&lt;/li&gt;
&lt;li&gt;Variable snapshot&lt;/li&gt;
&lt;li&gt;Missing variables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Tenanted deployment targets&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Learn more&lt;/li&gt;

&lt;li&gt;Register for the webinar: Better multi-tenancy deployments using Octopus Deploy&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This post assumes knowledge of some key Octopus concepts, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;li&gt;Environments&lt;/li&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Lifecycles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're new to Octopus, I recommend reading our &lt;a href="https://octopus.com/docs/getting-started" rel="noopener noreferrer"&gt;Getting started with Octopus guide&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To demonstrate how you can model deployments of multiple instances of an application with Octopus, I use a fictitious company called &lt;strong&gt;Vet Clinic&lt;/strong&gt;, deploying the Java application, &lt;a href="https://github.com/spring-projects/spring-petclinic" rel="noopener noreferrer"&gt;Pet Clinic&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying without tenants &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;There are two main implementations we see when deploying multiple instances of the same application for each customer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using multiple projects
&lt;/li&gt;
&lt;li&gt;Using multiple environments
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While easy to set up, they don't scale well and can result in duplication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using multiple projects &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In this scenario, you configure Octopus with multiple projects, each one representing one of your customers. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5nddsr992zbzvlf1slh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5nddsr992zbzvlf1slh.png" alt="Multi-tenancy using multiple projects" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Onboarding a new customer typically requires creating all of the resources in Octopus needed for a successful deployment for the customer, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new set of &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets" rel="noopener noreferrer"&gt;deployment targets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Common &lt;a href="https://octopus.com/docs/projects/variables" rel="noopener noreferrer"&gt;project variables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Any customer specific "paid-for" &lt;a href="https://octopus.com/docs/infrastructure/environments" rel="noopener noreferrer"&gt;environments&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition, any common steps across the application's deployment process need to be duplicated in the new project. These are usually manual intervention and notification steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  Multiple projects pros &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;So why choose multiple Octopus projects to deploy instances of an application to each customer?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clear customer release dashboard overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This approach allows you to see which release has been deployed to which environment, for each customer on the dashboard overview.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Variable and Deployment process isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Multiple projects allow for complete isolation of variables, and deployment process for a customer. For example, making a change to one project's process only affects that one customer. You can also tailor the deployment process for the customer depending on the features they've signed up for. &lt;/p&gt;

&lt;p&gt;In the below example, only &lt;strong&gt;Capital Animal Hospital&lt;/strong&gt; has a step for applying custom branding:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xtviyj17sxf39d0waac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xtviyj17sxf39d0waac.png" alt="Multi-tenancy multiple projects customised deployment process" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simpler environment and variable scoping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Environments don't need to be duplicated per customer, resulting in simpler lifecycle configuration. Variables can also be scoped to each environment without risk of choosing the wrong "customer" scoping.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hf203oitmjxok72esa4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hf203oitmjxok72esa4.png" alt="Multi-tenancy multiple projects variable scoping" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Multiple projects cons &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;While multiple projects can be used to deploy customer instances separately, there are a number of problems with this approach.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Duplicated project configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With every customer project, you end up duplicating project configuration. These include variables, deployment process steps, runbooks, channels and lifecycles. With duplication comes inconsistency and the overhead of managing that. For example, if you want to amend the deployment process for all of your customers, you need to change multiple projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Different deployment target roles per customer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your customers have isolated infrastructure, you need a unique way for Octopus to know which deployment targets belong to the customer you're deploying to. This results in each customers' deployment targets needing target roles that include a differentiator per customer. This is typically a customer name, code or ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydy5nsa5jri4k1q777l5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydy5nsa5jri4k1q777l5.png" alt="Multi-tenancy multiple projects customer target roles" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multiple projects per customer doesn't scale&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The per-project approach doesn't scale well when you have more than a handful of customers. This can be problematic if you deploy many more applications per customer. Each application needs to be modeled &lt;code&gt;n times&lt;/code&gt;, where &lt;code&gt;n&lt;/code&gt; is the number of customers you have.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No guard rails to ensure variables provided&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using multiple projects, there are no guard rails to ensure all project configuration is set correctly. For example, if a variable is not added (or has an incorrect value), you might not find out about the issue until the deployment of the customer's instance.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Using multiple environments &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An alternative to deploying without tenants is using a single Octopus project per application, and modeling each customer with a set of environments they deploy to. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsz3pr3odzui8wgrw59rr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsz3pr3odzui8wgrw59rr.png" alt="Multi-tenancy using multiple environments" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Onboarding a new customer typically involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a new set of &lt;a href="https://octopus.com/docs/infrastructure/environments" rel="noopener noreferrer"&gt;environments&lt;/a&gt;, named after the customer.&lt;/li&gt;
&lt;li&gt;Creating a new set of &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets" rel="noopener noreferrer"&gt;deployment targets&lt;/a&gt;, or re-using existing ones and tagging them with the associated customer environments.&lt;/li&gt;
&lt;li&gt;Adding new &lt;a href="https://octopus.com/docs/projects/variables/#scoping-variables" rel="noopener noreferrer"&gt;environment-scoped variables&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Updating the project lifecycle to include the new customer environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Multiple environment pros &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;So why choose one or more environments to represent your customers in Octopus?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Single set of project resources to manage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In contrast to multiple projects, with this approach there's just one project, one deployment process, one set of variables, and one lifecycle to manage. When a change is required, it can be made once instead of per project. &lt;/p&gt;

&lt;p&gt;For example, if you want to add a step that's required for all customers, such as a &lt;strong&gt;Manual intervention step&lt;/strong&gt; before deployments to production, it can be added quickly and easily. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Customer environments are modeled explicitly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With this approach customer environments have to be modeled explicitly. When a new customer is added, the environment they deploy to has to be created to allow deployment for that customer. It's also possible to see in a single row on the dashboard overview which environment a customer can deploy to.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Multiple environment cons &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Although you can use multiple customer environments, there are usually many problems associated with this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multiple environments created per customer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For each customer, you need to create a new environment record for each customer environment, which doesn't scale. For example, if you have 10 customers, and 4 environments (Development, Test, Staging and Production), you need to create 40 customer environments.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complicated variable scoping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As there's a single project per application, handling multiple variable values per customer needs to be achieved using different environment scopes. This can quickly become overwhelming. Adding new values, or editing existing ones has to be done carefully to ensure correct scopes are applied to each value. There's a high risk of cross tenant communication in this model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmd6cw0nmevrrat2wcmsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmd6cw0nmevrrat2wcmsd.png" alt="Multi-tenancy multiple environments variable scoping" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rigid deployment process&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Customizing the deployment process in a project using multiple customers requires you to add &lt;a href="https://octopus.com/docs/projects/steps/conditions#environments" rel="noopener noreferrer"&gt;environment run conditions&lt;/a&gt; for each step that needs to be run for specific customers. This isn't flexible and doesn't scale as you need to modify these conditions per step, when you add a new customer or change your environments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wy4lrc7fs8kes0lq1sf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wy4lrc7fs8kes0lq1sf.png" alt="Multi-tenancy multiple environments run conditions" width="797" height="745"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unclear customer release dashboard overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In contrast to the per-project customer model, it's difficult to see at a glance which release has been deployed to which customer on the dashboard and project overview screens, without endless scrolling.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complicated, unwieldy Lifecycle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As you onboard new customers, you typically add new customer environments to the project's lifecycle. You then have to define awkward lifecycle phases that fit the environments that are required for each customer (both existing and new). Typically this is handled with a phase that allows &lt;em&gt;any 1&lt;/em&gt; customer environment to be deployed to before progressing to the next phase. As the number of customers grows, so does the complexity of the lifecycle and its phases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdko91cpdkaqx55zysvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdko91cpdkaqx55zysvv.png" alt="Multi-tenancy multiple environments lifecycles" width="299" height="454"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Deploying with tenants &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Using tenants in Octopus allows you to easily create customer specific deployment pipelines without duplicating project configuration. You can manage separate instances of your application in multiple environments in a single Octopus project.&lt;/p&gt;

&lt;p&gt;Using our &lt;strong&gt;Vet Clinic&lt;/strong&gt; company, here's what the dashboard overview might look like using tenants to model each customer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhp8r61jar2oladsk4c2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhp8r61jar2oladsk4c2k.png" alt="Tenanted dashboard overview" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives us a concise overview showing which release is in which environment. Instead of multiple rows per customer project, this is replaced with a discrete count of tenants who have been deployed to each environment.&lt;/p&gt;

&lt;p&gt;If we navigate to the project, we see a more granular overview, this time showing which tenant has what release in each environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yfcdm1hf88ivadjmpjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yfcdm1hf88ivadjmpjq.png" alt="Tenanted project overview" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sample Octopus project&lt;/strong&gt;&lt;br&gt;
You can see an example of the Vet Clinic tenanted project in our &lt;a href="https://g.octopushq.com/PatternTenantsSampleVetClinic" rel="noopener noreferrer"&gt;samples instance&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are a number of multi-tenancy features working together to make this happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tenants&lt;/li&gt;
&lt;li&gt;Tenant tags&lt;/li&gt;
&lt;li&gt;Tenant variables&lt;/li&gt;
&lt;li&gt;Tenanted deployment targets&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tenants &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Tenants in Octopus are the backbone of the multi-tenancy feature. They usually represent the customers of your application, especially when it comes to SaaS products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxpd8h0fm7yy433grupq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxpd8h0fm7yy433grupq.png" alt="Tenants screen" width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although we discuss the use of tenants to model customers in this post, we designed tenants to be generic so that they can satisfy multiple use cases. Tenants can also represent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Geographical regions or data centers&lt;/li&gt;
&lt;li&gt;Developers, testers, or teams&lt;/li&gt;
&lt;li&gt;Feature branches&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Learn more about types of tenancy in our &lt;a href="https://octopus.com/docs/tenants/tenant-types" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The tenant overview provides a central place to manage which projects are connected to a tenant, and also which environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6w6kn22mblxwaaubtjg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6w6kn22mblxwaaubtjg.png" alt="Tenant overview" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that each project can control its interaction with tenants. By default the multi-tenant deployment features are disabled. You can allow deployments with/without a tenant, which is a hybrid mode that's useful when you're transitioning to a fully multi-tenant project. &lt;/p&gt;

&lt;p&gt;There's also a mode where you can require a tenant for all deployments, which disables untenanted deployments for that project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7937zv7wk24ylvx8hid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7937zv7wk24ylvx8hid.png" alt="Tenant project settings" width="728" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Onboarding a new customer as a tenant in Octopus can be as simple as creating your tenant, connecting your project to each applicable environment and entering your variable values, then deploying.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tenant tags &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In Octopus, &lt;a href="https://octopus.com/docs/tenants/tenant-tags" rel="noopener noreferrer"&gt;tenant tags&lt;/a&gt; help you classify your tenants using custom tags, and tailor tenanted deployments for your projects and environments. &lt;/p&gt;

&lt;p&gt;Tenant tags also make it easier to work with tenants as groups instead of individuals. As tenant tags are fully customizable, you can apply meaningful metadata to tenants. This allows you to describe them using your own terminology, and tailor the deployment process to their needs.&lt;/p&gt;

&lt;p&gt;In the tenant overview below, &lt;strong&gt;Capital Animal Hospital&lt;/strong&gt; has the &lt;code&gt;Branding&lt;/code&gt; tag included:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl217bgv0o77bqqnao3qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl217bgv0o77bqqnao3qh.png" alt="Tenant tag for branding" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This indicates that they've opted-in for customized branding of their instance of the &lt;strong&gt;Vet Clinic&lt;/strong&gt; application.&lt;/p&gt;

&lt;p&gt;When you build out the deployment process, you can include a tenant tag as a run condition to customize the process for your customers. By applying tags to steps, you are able to specify steps that should only run for customers that match selected tenant tags.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9sowhde2dp0il66cd9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd9sowhde2dp0il66cd9d.png" alt="Tenant tag applied to step" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can associate multiple tenants with the same tag. This automatically groups these tenants together and enables any tenant with the &lt;code&gt;Branding&lt;/code&gt; tag to have the custom branding step included as part of any deployment for that tenant.&lt;/p&gt;

&lt;p&gt;Tenant tags can also be used to associate multiple tenants with deployment targets and channels, and even choosing which tenants to deploy to. They're a powerful way to help you simplify and scale your deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tenant variables &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;You often want to define variable values that are different for each customer. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A database server name or connection string&lt;/li&gt;
&lt;li&gt;A tenant-specific URL&lt;/li&gt;
&lt;li&gt;Contact details for a tenant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using an untenanted project, you would define these values in the project itself. With a tenanted project, you can set these values directly on the tenant for any connected projects.&lt;/p&gt;

&lt;p&gt;With tenants, there are two types of variable you can specify: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project variable templates&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common variables&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These both use the &lt;a href="https://octopus.com/docs/projects/variables/variable-templates" rel="noopener noreferrer"&gt;variable templates&lt;/a&gt; feature.&lt;/p&gt;

&lt;h4&gt;
  
  
  Project variable templates &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Project variables allow you to specify a variable which a tenant can change. A perfect example is a connection string or a database server. With project variables you define them at the project level using &lt;a href="https://octopus.com/docs/projects/variables/variable-templates#project-templates" rel="noopener noreferrer"&gt;project templates&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zdmlyeawdomoy6jbl8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zdmlyeawdomoy6jbl8z.png" alt="Project template screen" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can specify the variable type for the project template, just like regular variables. You can also provide a default value which the tenant can overwrite.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttbwgjawslcv8043yy2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttbwgjawslcv8043yy2l.png" alt="Tenant project variable edit" width="610" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, on the tenant variable screen, you can set those variables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcngprctbiz0jmico05jk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcngprctbiz0jmico05jk.png" alt="Tenant variables" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Common variables &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Common variables are similar to project variables. The main difference is that common variables can be used across multiple projects, and they aren't scoped to environments. Common variables are defined using &lt;a href="https://octopus.com/docs/projects/variables/variable-templates#adding-a-variable-template" rel="noopener noreferrer"&gt;Library variable set templates&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, to define an abbreviation for the tenant to use in a deployment or runbook, you can configure a variable template for the library set.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnwk1k4brt4h3orlnkyn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnwk1k4brt4h3orlnkyn.png" alt="Common variable template" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To include common variables for a tenant, you must add the library variable set in the tenant connected project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just like project variables, common variable values are supplied at the tenant level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzmf6rh38k6je8n5dtia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzmf6rh38k6je8n5dtia.png" alt="Common variable tenant value" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Variable snapshot &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;When you &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/create-release" rel="noopener noreferrer"&gt;create a release&lt;/a&gt; Octopus takes a snapshot of the deployment process and the current state of the &lt;a href="https://octopus.com/docs/projects/variables" rel="noopener noreferrer"&gt;project variables&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;However, tenant variables &lt;em&gt;aren't&lt;/em&gt; included in any snapshot. This is helpful as you can add new tenants at any time and deploy to them without creating a new release. &lt;/p&gt;

&lt;p&gt;This also means any changes you make to tenant variables will take immediate effect.&lt;/p&gt;

&lt;h4&gt;
  
  
  Missing variables &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;One of the great things about tenant variables is the guard rails they put in place for your deployments. Defining either a project template or common variable without a default value means any tenant must provide a value for that variable. Octopus won't allow a deployment to occur without one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjfibjn41clu51wvuzdt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjfibjn41clu51wvuzdt.png" alt="Missing tenant variable" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But those guard rails don't start just at deployment. Octopus will also warn you about any missing values in the tenant's variable overview too:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy1ehx08jchj44x8jg8w1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy1ehx08jchj44x8jg8w1.png" alt="Warning of missing tenant variable" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This level of safety reduces the chance of a failed deployment for a tenant due to a missing or incorrect variable value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tenanted deployment targets &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The way you host your infrastructure for multiple instances of the same project usually varies depending on your application and customers. Two common implementations we see are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated hosting&lt;/strong&gt;: You have dedicated deployment targets for each customer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared hosting&lt;/strong&gt;: You create farms or pools of servers to host all of your customers, achieving higher density.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;In this post, we focus on dedicated hosting, but you can design and implement both dedicated and shared multi-tenant hosting models in Octopus using &lt;a href="https://octopus.com/docs/infrastructure/environments" rel="noopener noreferrer"&gt;environments&lt;/a&gt;, &lt;a href="https://octopus.com/docs/infrastructure" rel="noopener noreferrer"&gt;deployment targets&lt;/a&gt;, and &lt;a href="https://octopus.com/docs/tenants/tenant-tags" rel="noopener noreferrer"&gt;tenant tags&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your customers have isolated infrastructure, in an untenanted configuration, you need to define unique target roles in Octopus to ensure there's no cross-customer communication in the form of deploying one customer's application to another customer's infrastructure.&lt;/p&gt;

&lt;p&gt;Using tenants, customer-specific target roles aren't required. You can choose deployments that a target can be involved in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exclude from tenanted deployments (default) - The deployment target will never be included in tenanted deployments.&lt;/li&gt;
&lt;li&gt;Include only in tenanted deployments - the deployment target will only be included in deployments to the associated tenants. It will be excluded from untenanted deployments.&lt;/li&gt;
&lt;li&gt;Include in both tenanted and untenanted deployments - The deployment target will be included in untenanted deployments, and deployments to the associated tenants.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8umg99i6w15uzduxrkv0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8umg99i6w15uzduxrkv0.png" alt="Tenant target restrictions" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To choose which tenants to associate with a deployment target:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Restrictions ➜ Associated Tenants&lt;/strong&gt; section of the deployment target. &lt;/li&gt;
&lt;li&gt;Select one or more tenants to allow to deploy to individually, or choose from any of the configured tenant tags.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hj9sjdoti85orjwd2rv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hj9sjdoti85orjwd2rv.png" alt="Tenant target restrictions" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We recommend keeping tenanted and untenanted deployment targets separate, particularly in Production. You could use the same deployment targets for other environments but it's generally better to avoid this situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This post covers common approaches when customers deploy multiple instances of the same application for each of their customers without tenants. It also details how you can use the multi-tenancy feature to model this too.&lt;/p&gt;

&lt;p&gt;I hope you can see how the Octopus multi-tenancy feature solves some of the problems presented when deploying without tenants, and how it can be leveraged for scalable, reusable, simplified deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://octopus.com/docs/tenants" rel="noopener noreferrer"&gt;Multi-tenancy documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://octopus.com/docs/tenants/guides" rel="noopener noreferrer"&gt;Multi-tenancy guides&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Register for the webinar: Better multi-tenancy deployments using Octopus Deploy &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Join Senior Solutions Architect Mark Harrison and Solutions Architect Adam Close from Octopus Deploy to learn how the multi-tenancy feature in Octopus can be leveraged for scalable, reusable, simplified deployments.&lt;/p&gt;

&lt;p&gt;We're running three sessions of the webinar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wed 28 Jul, 2021&lt;/li&gt;
&lt;li&gt;Thurs 29 Jul, 2021 &lt;/li&gt;
&lt;li&gt;Mon 2 Aug, 2021&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://octopus.com/events/better-multi-tenancy-deployments-using-octopus-deploy" rel="noopener noreferrer"&gt;Register now&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy deployments!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/better-multi-tenancy-with-octopus" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>deployment</category>
      <category>tenants</category>
      <category>multitenancy</category>
    </item>
    <item>
      <title>Using Azure Key Vault with Octopus</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Wed, 23 Jun 2021 11:31:12 +0000</pubDate>
      <link>https://dev.to/octopus/using-azure-key-vault-with-octopus-4e1n</link>
      <guid>https://dev.to/octopus/using-azure-key-vault-with-octopus-4e1n</guid>
      <description>&lt;p&gt;I recently wrote about &lt;a href="https://octopus.com/blog/using-hashicorp-vault-with-octopus-deploy" rel="noopener noreferrer"&gt;extending the functionality of Octopus to integrate with HashiCorp Vault&lt;/a&gt; using step templates. Afterwards, several people asked if I plan to create step templates to integrate with other secret managers.&lt;/p&gt;

&lt;p&gt;In this post, I walk through a new step template, &lt;a href="https://library.octopus.com/step-templates/6f59f8aa-b2db-4f7a-b02d-a72c13d386f0/actiontemplate-azure-key-vault-retrieve-secrets" rel="noopener noreferrer"&gt;Azure Key Vault - Retrieve Secrets&lt;/a&gt;, which is designed to retrieve secrets from an Azure Key Vault for use in your deployments or runbooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This post assumes some familiarity with &lt;a href="https://octopus.com/docs/projects/custom-step-templates" rel="noopener noreferrer"&gt;custom step templates&lt;/a&gt; and the Octopus &lt;a href="https://octopus.com/docs/projects/community-step-templates" rel="noopener noreferrer"&gt;Community Library&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In addition, this post doesn't go into great detail about Azure Key Vault concepts or how to set it up. You can learn more by reading the &lt;a href="https://docs.microsoft.com/en-us/azure/key-vault/general/basic-concepts" rel="noopener noreferrer"&gt;Azure Key Vault basic concepts guide&lt;/a&gt; from Microsoft.&lt;/p&gt;

&lt;p&gt;The step template in this post retrieves secrets from an &lt;a href="https://azure.microsoft.com/en-gb/services/key-vault/" rel="noopener noreferrer"&gt;Azure Key Vault&lt;/a&gt; using the &lt;a href="https://docs.microsoft.com/en-us/powershell/module/az.keyvault/" rel="noopener noreferrer"&gt;Az.KeyVault&lt;/a&gt; PowerShell module. The module must be downloaded and installed on the deployment target or worker before the step can retrieve secrets successfully. The step template has been tested on both Windows and Linux (with &lt;code&gt;PowerShell Core&lt;/code&gt; installed).&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Before you can retrieve secrets from Azure Key Vault, you must authenticate with Azure. In their &lt;a href="https://docs.microsoft.com/en-us/azure/key-vault/general/authentication" rel="noopener noreferrer"&gt;authentication concepts documentation&lt;/a&gt;, Microsoft note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Authentication with Key Vault works in conjunction with Azure Active Directory (Azure AD), which is responsible for authenticating the identity of any given security principal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Octopus, authentication with Azure Key Vault can be achieved with an &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets/azure" rel="noopener noreferrer"&gt;Azure Account&lt;/a&gt;, using a service principal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In addition to accessing resources in Azure, your service principal may need further permissions configured to access and retrieve secrets stored in Azure Key Vault. To learn more, read the &lt;a href="https://docs.microsoft.com/en-us/azure/key-vault/general/rbac-guide" rel="noopener noreferrer"&gt;Azure Key Vault RBAC guide&lt;/a&gt; on how to provide access to keys, certificates, and secrets with an Azure role-based access control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Retrieving secrets &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/6f59f8aa-b2db-4f7a-b02d-a72c13d386f0/actiontemplate-azure-key-vault-retrieve-secrets" rel="noopener noreferrer"&gt;Azure Key Vault - Retrieve Secrets&lt;/a&gt; step template retrieves one or more secrets from an Azure Key Vault and creates sensitive output variables for each one retrieved. &lt;/p&gt;

&lt;p&gt;For each secret, you can optionally choose to retrieve a specific version, and provide a custom output variable name.&lt;/p&gt;

&lt;p&gt;Retrieving a single secret requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Azure account with permission to access the secret.&lt;/li&gt;
&lt;li&gt;The name of the Azure Key Vault to retrieve the secret from.&lt;/li&gt;
&lt;li&gt;The name of the secret to retrieve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An advanced feature of the step template offers support for retrieving multiple secrets at once. This requires entering each secret on a new line.&lt;/p&gt;

&lt;p&gt;For each secret retrieved, a &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; is created for use in subsequent steps. By default, only a count of the number of variables created will be shown in the task log. To see the names of the variables in the task log, change the &lt;strong&gt;Print output variable names&lt;/strong&gt; parameter to &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step template parameters &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The step template uses the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Azure Account&lt;/code&gt;: An Azure account with permissions to retrieve secrets from the Azure Key Vault.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Vault Name&lt;/code&gt;: The name of the Azure Key Vault to retrieve secrets from.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Vault Secrets to retrieve&lt;/code&gt;: Specify the names of the Secrets to be returned from Azure Key Vault, in the format: &lt;code&gt;SecretName SecretVersion | OutputVariableName&lt;/code&gt; where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SecretName&lt;/code&gt; is the name of the Secret to retrieve.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SecretVersion&lt;/code&gt; is the &lt;em&gt;optional&lt;/em&gt; version of the Secret to retrieve. &lt;em&gt;If this value isn't specified, the latest version will be retrieved&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OutputVariableName&lt;/code&gt; is the &lt;em&gt;optional&lt;/em&gt; Octopus &lt;a href="https://octopus.com/docs/projects/variables/output-variables" rel="noopener noreferrer"&gt;output variable&lt;/a&gt; name to store the secret's value in. &lt;em&gt;If this value isn't specified, an output name will be generated dynamically&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Multiple fields can be retrieved by entering each one on a new line.&lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Print output variable names&lt;/code&gt;: Write out the Octopus &lt;a href="https://octopus.com/docs/projects/variables/output-variables" rel="noopener noreferrer"&gt;output variable&lt;/a&gt; names to the task log. Default: &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Az PowerShell Module version (optional)&lt;/code&gt;: If you wish to use a specific version of the &lt;code&gt;Az&lt;/code&gt; PowerShell module (rather than the default), enter the version number here. e.g. &lt;code&gt;5.9.0&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The version specified must exist on the machine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Az PowerShell Install Location (optional)&lt;/code&gt;: If you wish to provide a custom path to the &lt;code&gt;Az&lt;/code&gt; PowerShell module (rather than the default), enter the value here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Module must exist at the specified location on the machine. This step template will not download the Module.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetpxr7a42vpzjzf52psw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetpxr7a42vpzjzf52psw.png" alt="Parameters for the step" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Azure Key Vault - Retrieve Secrets&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsr3vawzsrkhp229lv7h6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsr3vawzsrkhp229lv7h6.png" alt="Azure Key Vault retrieve secrets step used in a process" width="800" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you've filled in the parameters, you can execute the step in a runbook or deployment process. On successful execution, any matching secrets will be stored as sensitive output variables. If you've configured your step to print the variable names, they'll appear in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7b5kgmp7jzxenzvkuli4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7b5kgmp7jzxenzvkuli4.png" alt="Azure Key Vault retrieve secrets step task log" width="800" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, output variables created from matching secrets can be used in your deployment or runbook.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;Azure Key Vault - Retrieve Secrets&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The step template covered in this post demonstrates that it's easy to integrate with Azure Key Vault, and make use of secrets stored there with your Octopus deployments or runbooks.&lt;/p&gt;

&lt;p&gt;Happy deployments!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/using-azure-key-vault-with-octopus" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>deployment</category>
      <category>security</category>
      <category>vault</category>
    </item>
    <item>
      <title>Using HashiCorp Vault with Octopus Deploy</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Mon, 24 May 2021 10:19:57 +0000</pubDate>
      <link>https://dev.to/octopus/using-hashicorp-vault-with-octopus-deploy-e88</link>
      <guid>https://dev.to/octopus/using-hashicorp-vault-with-octopus-deploy-e88</guid>
      <description>&lt;p&gt;Storing sensitive values in Octopus Deploy solves many problems. If your organization has standardized on a secrets manager though, that might mean storing sensitive values twice, making secrets management more complicated.&lt;/p&gt;

&lt;p&gt;Octopus has supported the concept of &lt;a href="https://octopus.com/docs/projects/variables/sensitive-variables" rel="noopener noreferrer"&gt;sensitive variables&lt;/a&gt; since &lt;a href="https://octopus.com/blog/new-in-2.0/sensitive-variables" rel="noopener noreferrer"&gt;Octopus 2.0&lt;/a&gt;, but customers often ask about support for secret managers. One in particular is &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I walk through a number of new &lt;a href="https://library.octopus.com/listing/hashicorp%20vault" rel="noopener noreferrer"&gt;HashiCorp Vault step templates&lt;/a&gt; designed to retrieve secrets from Vault for use in your deployments or runbooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this post
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Authentication

&lt;ul&gt;
&lt;li&gt;
LDAP login step

&lt;ul&gt;
&lt;li&gt;LDAP login parameters&lt;/li&gt;
&lt;li&gt;Using the LDAP login step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

AppRole login step

&lt;ul&gt;
&lt;li&gt;AppRole login parameters&lt;/li&gt;
&lt;li&gt;Using the AppRole login step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;AppRole best practises&lt;/li&gt;

&lt;li&gt;

AppRole Get Wrapped SecretID step

&lt;ul&gt;
&lt;li&gt;AppRole Get Wrapped SecretID parameters&lt;/li&gt;
&lt;li&gt;Using the AppRole Get Wrapped SecretID step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

AppRole Unwrap SecretID step

&lt;ul&gt;
&lt;li&gt;AppRole Unwrap SecretID parameters&lt;/li&gt;
&lt;li&gt;Using the Unwrap SecretID step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

AppRole Unwrap SecretID and Login step

&lt;ul&gt;
&lt;li&gt;AppRole Unwrap SecretID parameters&lt;/li&gt;
&lt;li&gt;Using the Unwrap SecretID step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Retrieving secrets

&lt;ul&gt;
&lt;li&gt;
Retrieve KV (v1) secrets step

&lt;ul&gt;
&lt;li&gt;Retrieve KV (v1) secrets parameters&lt;/li&gt;
&lt;li&gt;Using Retrieve KV (v1) secrets step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Retrieve KV (v2) secrets step

&lt;ul&gt;
&lt;li&gt;Retrieve KV (v2) secrets parameters&lt;/li&gt;
&lt;li&gt;Using Retrieve KV (v2) secrets step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Learn more&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This post assumes some familiarity with &lt;a href="https://octopus.com/docs/projects/custom-step-templates" rel="noopener noreferrer"&gt;custom step templates&lt;/a&gt; and the Octopus &lt;a href="https://octopus.com/docs/projects/community-step-templates" rel="noopener noreferrer"&gt;Community Library&lt;/a&gt;. To learn more about these, you can read Ryan Rousseau's &lt;a href="https://octopus.com/blog/creating-an-octopus-deploy-step-template" rel="noopener noreferrer"&gt;two-part series&lt;/a&gt; on creating your own step template and publishing it to the library.&lt;/p&gt;

&lt;p&gt;In addition, this post doesn't go into great detail about Vault server concepts or how to configure a Vault server.&lt;/p&gt;

&lt;p&gt;The step templates covered in this post perform both &lt;a href="https://www.vaultproject.io/docs/concepts/auth" rel="noopener noreferrer"&gt;Vault authentication&lt;/a&gt; and secret retrieval for both versions 1 and 2 of the &lt;a href="https://www.vaultproject.io/docs/secrets/kv" rel="noopener noreferrer"&gt;Key-Value (kv)&lt;/a&gt; Secrets Engine.&lt;/p&gt;

&lt;p&gt;All of the step templates make use of the Vault &lt;a href="https://www.vaultproject.io/api-docs" rel="noopener noreferrer"&gt;HTTP API&lt;/a&gt; so there are no additional dependencies required to use them, except being able to connect to your Vault server. They've all been tested using Vault version &lt;strong&gt;1.7.1&lt;/strong&gt; and can run on both Windows and Linux (with &lt;code&gt;Powershell Core&lt;/code&gt; installed).&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Before interacting with Vault, you must authenticate against an auth method. Vault offers a number of different authentication options. The following step templates have been created to support Vault authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LDAP login&lt;/li&gt;
&lt;li&gt;AppRole login&lt;/li&gt;
&lt;li&gt;AppRole Get wrapped SecretID&lt;/li&gt;
&lt;li&gt;AppRole Unwrap SecretID&lt;/li&gt;
&lt;li&gt;AppRole Unwrap SecretID and Login&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The AppRole method is the recommended way to authenticate with Vault for servers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upon authentication with Vault, a &lt;a href="https://www.vaultproject.io/docs/concepts/tokens" rel="noopener noreferrer"&gt;token&lt;/a&gt; is generated that can be used in further interactions with Vault.&lt;/p&gt;

&lt;h3&gt;
  
  
  LDAP login step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/de807003-3b05-4649-9af3-11a2c7722b3f/actiontemplate-hashicorp-vault-ldap-login" rel="noopener noreferrer"&gt;HashiCorp Vault - Login with LDAP&lt;/a&gt; step template authenticates with a Vault Server using the &lt;a href="https://www.vaultproject.io/docs/auth/ldap" rel="noopener noreferrer"&gt;LDAP&lt;/a&gt; authentication method. This allows Vault integration without having to duplicate username or password configuration.&lt;/p&gt;

&lt;p&gt;You might choose to authenticate using LDAP if you already have an LDAP server available and use service accounts to control access to sensitive information.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Microsoft's Active Directory supports LDAP using &lt;a href="https://docs.microsoft.com/en-us/previous-versions/windows/desktop/adam/what-is-active-directory-lightweight-directory-services" rel="noopener noreferrer"&gt;Active Directory Lightweight Directory Services&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After authentication, the &lt;code&gt;client_token&lt;/code&gt; from the Vault response will be made available as a &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; named &lt;code&gt;LDAPAuthToken&lt;/code&gt; for use in other steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  LDAP login parameters &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The step template has the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Vault Server URL&lt;/code&gt;: The URL of the Vault instance you are connecting to, including the port (The default is &lt;code&gt;8200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API version&lt;/code&gt;: Choose the API version to use from a drop-down list. Currently, there is only one option: &lt;code&gt;v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LDAP Auth Login path&lt;/code&gt;: The path that the &lt;a href="https://www.vaultproject.io/api-docs/auth/ldap" rel="noopener noreferrer"&gt;LDAP method is mounted at&lt;/a&gt;. The default is &lt;code&gt;/auth/ldap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Username&lt;/code&gt;: The LDAP username.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Password&lt;/code&gt;: The LDAP password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84njkgdhaha5p3y2c8ey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84njkgdhaha5p3y2c8ey.png" alt="Parameters for the Vault LDAP login step" width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using the LDAP login step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;LDAP login&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftd7zmzi2trmgdppnccrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftd7zmzi2trmgdppnccrv.png" alt="Vault LDAP login step used in a process" width="744" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then execute the step in a runbook or deployment process. On successful execution, the sensitive output variable name containing the token is displayed in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F734ykgkb4lhy05nisnmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F734ykgkb4lhy05nisnmr.png" alt="Vault LDAP login step task log" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, the output variable &lt;code&gt;#{Octopus.Action[HashiCorp Vault - Login with LDAP].Output.LDAPAuthToken}&lt;/code&gt; can be used to authenticate and retrieve secrets.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;HashiCorp Vault - Login with LDAP&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AppRole login step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/e04a9cec-f04a-4da2-849b-1aed0fd408f0/actiontemplate-hashicorp-vault-approle-login" rel="noopener noreferrer"&gt;HashiCorp Vault - Login with AppRole&lt;/a&gt; step template authenticates with a Vault Server using the &lt;a href="https://www.vaultproject.io/docs/auth/approle" rel="noopener noreferrer"&gt;AppRole&lt;/a&gt; authentication method. This is perfect for use with Octopus. HashiCorp themselves recommend it for machines or apps:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This auth method is oriented to automated workflows (machines and services), and is less useful for human operators.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With an AppRole, a machine can log in with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RoleID&lt;/code&gt;, think of this as the username in an authentication pair.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SecretID&lt;/code&gt;, think of this as the password in an authentication pair.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't store the SecretID:&lt;/strong&gt;&lt;br&gt;
Storing the RoleID in Octopus as a sensitive variable is a good way to ensure it remains encrypted until required.&lt;/p&gt;

&lt;p&gt;However, the same is &lt;strong&gt;not recommended&lt;/strong&gt; for the SecretID.&lt;/p&gt;

&lt;p&gt;A SecretID, just like a password is &lt;em&gt;designed to expire&lt;/em&gt;. Storing the SecretID could also provide the capability to retrieve all secrets as both the RoleID and SecretID would be available.&lt;/p&gt;

&lt;p&gt;We recommend you use the more secure Get wrapped SecretID and Unwrap SecretID and Login step templates, as they use one of the best practices &lt;strong&gt;response wrapping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you use the AppRole login step template, we recommend you provide the SecretID at execution time using a sensitive &lt;a href="https://octopus.com/docs/projects/variables/prompted-variables" rel="noopener noreferrer"&gt;prompted variable&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once authenticated, the &lt;code&gt;client_token&lt;/code&gt; from the Vault response will be made available as a &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; named &lt;code&gt;AppRoleAuthToken&lt;/code&gt; for use in other steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  AppRole login parameters &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The step template has the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Vault Server URL&lt;/code&gt;: The URL of the Vault instance you are connecting to, including the port (The default is &lt;code&gt;8200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API version&lt;/code&gt;: Choose the API version to use from a drop-down list. Currently, there is only one option: &lt;code&gt;v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;App Role Path&lt;/code&gt;: The path where the &lt;a href="https://www.vaultproject.io/api-docs/auth/approle" rel="noopener noreferrer"&gt;approle auth method is mounted&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Role ID&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/auth/approle#roleid" rel="noopener noreferrer"&gt;RoleID&lt;/a&gt; of the AppRole.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secret ID&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/auth/approle#secretid" rel="noopener noreferrer"&gt;SecretID&lt;/a&gt; of the AppRole.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqd4aer6539lnu8kosi7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqd4aer6539lnu8kosi7.png" alt="Parameters for the Vault AppRole login step" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using the AppRole login step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;AppRole login&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggwiimy542tfp70uyguz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggwiimy542tfp70uyguz.png" alt="Vault AppRole login step used in a process" width="770" height="767"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then execute the step in a runbook or deployment process. On successful execution, the sensitive output variable name containing the token is displayed in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapfu0ygbo6ypyblc31ex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapfu0ygbo6ypyblc31ex.png" alt="Vault AppRole login step task log" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, the output variable &lt;code&gt;#{Octopus.Action[HashiCorp Vault - Login with AppRole].Output.AppRoleAuthToken}&lt;/code&gt; can be used to authenticate and retrieve secrets.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;HashiCorp Vault - Login with AppRole&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AppRole best practices &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.vaultproject.io/docs/auth/approle" rel="noopener noreferrer"&gt;AppRole&lt;/a&gt; authentication method is considered a &lt;em&gt;trusted-broker&lt;/em&gt; method. This means that the onus of trust rests in the system acting as the authentication intermediary (the &lt;em&gt;broker&lt;/em&gt;) between the client (typically an Octopus deployment target) and Vault.&lt;/p&gt;

&lt;p&gt;An important best practice is to avoid storing an AppRole SecretID. Instead, use &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping" rel="noopener noreferrer"&gt;response wrapping&lt;/a&gt; to provide a &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-tokens" rel="noopener noreferrer"&gt;wrapping token&lt;/a&gt; that will provide an access mechanism to retrieve a SecretID when required. This method of obtaining a SecretID is also known as a &lt;a href="https://www.vaultproject.io/docs/auth/approle#pull-and-push-secretid-modes" rel="noopener noreferrer"&gt;Pull mode&lt;/a&gt; as it requires the SecretID to be fetched or &lt;em&gt;pulled&lt;/em&gt; from the AppRole.&lt;/p&gt;

&lt;p&gt;The Vault documentation contains &lt;a href="https://learn.hashicorp.com/tutorials/vault/pattern-approle?in=vault/recommended-patterns" rel="noopener noreferrer"&gt;recommended patterns&lt;/a&gt; when using AppRole authentication.&lt;/p&gt;

&lt;p&gt;Here's a summary of the recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a secured system to act as the broker for retrieving a wrapped SecretID.&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping" rel="noopener noreferrer"&gt;response wrapping&lt;/a&gt; to obtain a SecretID.&lt;/li&gt;
&lt;li&gt;Limit the number of uses and Time-to-live (TTL) value for a SecretID to prevent overuse.&lt;/li&gt;
&lt;li&gt;Avoid &lt;a href="https://learn.hashicorp.com/tutorials/vault/pattern-approle?in=vault/recommended-patterns#anti-patterns" rel="noopener noreferrer"&gt;anti-patterns&lt;/a&gt; such as having the broker retrieve secrets.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Secure the broker:&lt;/strong&gt;&lt;br&gt;
Since the trust rests on the broker, we strongly recommend using the Octopus Server's &lt;a href="https://octopus.com/docs/infrastructure/workers#built-in-worker" rel="noopener noreferrer"&gt;built-in worker&lt;/a&gt;, or a highly-secured &lt;a href="https://octopus.com/docs/infrastructure/workers#external-workers" rel="noopener noreferrer"&gt;external worker&lt;/a&gt; to act as the broker. It would be responsible for retrieving a wrapped SecretID and passing that value to the machine (the client) that authenticates with Vault.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To support these recommended practices, three additional &lt;code&gt;AppRole&lt;/code&gt; step templates have been created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AppRole Get Wrapped SecretID&lt;/li&gt;
&lt;li&gt;AppRole Unwrap SecretID&lt;/li&gt;
&lt;li&gt;AppRole Unwrap SecretID and Login&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AppRole Get Wrapped SecretID step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/76827264-af27-46d0-913a-e093a4f0db48/actiontemplate-hashicorp-vault-approle-get-wrapped-secret-id" rel="noopener noreferrer"&gt;HashiCorp Vault - AppRole Get Wrapped Secret ID&lt;/a&gt; step template generates a &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping" rel="noopener noreferrer"&gt;response-wrapped&lt;/a&gt; SecretID for the &lt;a href="https://www.vaultproject.io/docs/auth/approle" rel="noopener noreferrer"&gt;AppRole&lt;/a&gt; authentication method.&lt;/p&gt;

&lt;p&gt;The step template authenticates with a Vault Server using a token to retrieve a wrapped SecretID. The response contains a &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-tokens" rel="noopener noreferrer"&gt;wrapping token&lt;/a&gt; and other metadata such as the creation path for the token.&lt;/p&gt;

&lt;p&gt;This value can be used to validate &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-token-validation" rel="noopener noreferrer"&gt;no malfeasance&lt;/a&gt; has occurred. The wrapping token can then be used to retrieve the actual SecretID value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The token used to authenticate to retrieve a wrapped SecretID should be of limited scope and should only be allowed to retrieve wrapped SecretIDs. Consider creating a long-lived Vault token as this presents only a minor risk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the response has been received from the Vault server, two &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variables&lt;/a&gt; are created for use in other steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WrappedToken&lt;/code&gt; This is the wrapped &lt;code&gt;token&lt;/code&gt; from the response, used to retrieve the actual SecretID value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WrappedTokenCreationPath&lt;/code&gt; This is the creation path for the token. It allows you to validate &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-token-validation" rel="noopener noreferrer"&gt;no malfeasance&lt;/a&gt; has occurred.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  AppRole Get Wrapped SecretID parameters ### AppRole Get Wrapped SecretID step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The step template uses the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Vault Server URL&lt;/code&gt;: The URL of the Vault instance you are connecting to, including the port (The default is &lt;code&gt;8200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API version&lt;/code&gt;: Choose the API version from a drop-down list. Currently, there is only one option: &lt;code&gt;v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;App Role Path&lt;/code&gt;: The path where the &lt;a href="https://www.vaultproject.io/api-docs/auth/approle" rel="noopener noreferrer"&gt;AppRole auth method is mounted&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Role Name&lt;/code&gt;: The role name of the &lt;a href="https://www.vaultproject.io/api/auth/approle" rel="noopener noreferrer"&gt;AppRole&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Time-to-live (TTL)&lt;/code&gt;: The TTL in seconds of the &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-tokens" rel="noopener noreferrer"&gt;response-wrapping token&lt;/a&gt; itself. The default is: &lt;code&gt;120s&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Auth Token&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/auth/token" rel="noopener noreferrer"&gt;token&lt;/a&gt; used to authenticate with Vault to generate a &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping" rel="noopener noreferrer"&gt;response-wrapped&lt;/a&gt; SecretID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbsp4m64gqvonfs5bkw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbsp4m64gqvonfs5bkw1.png" alt="Parameters for the Vault Get Wrapped SecretID step" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using the AppRole Get Wrapped SecretID step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Get Wrapped SecretID&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3793y1mb5smw9u00i0lb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3793y1mb5smw9u00i0lb.png" alt="Vault Get Wrapped SecretID step used in a process" width="800" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then execute the step in a runbook or deployment process. On successful execution, the sensitive output variable names are displayed in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnsq71yh798jc0j37wqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnsq71yh798jc0j37wqr.png" alt="Vault Get Wrapped SecretID step task log" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, the output variables can be used to validate and retrieve the actual SecretID value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#{Octopus.Action[HashiCorp Vault - AppRole Get Wrapped Secret ID].Output.WrappedToken}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#{Octopus.Action[HashiCorp Vault - AppRole Get Wrapped Secret ID].Output.WrappedTokenCreationPath}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;HashiCorp Vault - AppRole Get Wrapped Secret ID&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AppRole Unwrap SecretID step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/c1f56030-0bcd-458d-bc70-b4f43ec0d30f/actiontemplate-hashicorp-vault-approle-unwrap-secret-id" rel="noopener noreferrer"&gt;HashiCorp Vault - AppRole Unwrap Secret ID&lt;/a&gt; step template retrieves and unwraps a SecretID for an &lt;a href="https://www.vaultproject.io/docs/auth/approle" rel="noopener noreferrer"&gt;AppRole&lt;/a&gt; using a &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-tokens" rel="noopener noreferrer"&gt;wrapping token&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;secret_id&lt;/code&gt; from the Vault response will be made available as a &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; named &lt;code&gt;UnwrappedSecretID&lt;/code&gt; for use in other steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  AppRole Unwrap SecretID parameters &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The step template uses the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Vault Server URL&lt;/code&gt;: The URL of the Vault instance you are connecting to, including the port (The default is &lt;code&gt;8200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API version&lt;/code&gt;: Choose the API version to use from a drop-down list. Currently, there is only one option: &lt;code&gt;v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Wrapped Token&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-tokens" rel="noopener noreferrer"&gt;wrapping token&lt;/a&gt; used to retrieve the actual Secret ID from Vault.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Token Creation Path&lt;/code&gt;: &lt;em&gt;Optional&lt;/em&gt; The creation path for the wrapped token. If this value is provided, the step template will perform a &lt;a href="https://www.vaultproject.io/api-docs/system/wrapping-lookup" rel="noopener noreferrer"&gt;wrapping lookup&lt;/a&gt; to &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-token-validation" rel="noopener noreferrer"&gt;validate no malfeasance&lt;/a&gt; has occurred.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq15uje8lc4yferyf9ca6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq15uje8lc4yferyf9ca6.png" alt="Parameters for the Vault Unwrap SecretID step" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using the Unwrap SecretID step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Unwrap SecretID&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9u2a1uwkl3kcsr2kwg7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9u2a1uwkl3kcsr2kwg7.png" alt="Vault Unwrap SecretID step used in a process" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note the use of &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variables&lt;/a&gt; in the step parameters. In this example, the values are created using the Get Wrapped SecretID step template named &lt;code&gt;HashiCorp Vault - AppRole Get Wrapped Secret ID&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can then execute the step in a runbook or deployment process. On successful execution, the sensitive output variable names are displayed in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6yn7pf4fhfe4x06ejn0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6yn7pf4fhfe4x06ejn0.png" alt="Vault Unwrap SecretID step task log" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, the output variable &lt;code&gt;#{Octopus.Action[HashiCorp Vault - AppRole Unwrap Secret ID].Output.UnwrappedSecretID}&lt;/code&gt; can be used to authenticate with Vault and receive a token that can then be used to retrieve secrets.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;HashiCorp Vault - AppRole Unwrap Secret ID&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AppRole Unwrap SecretID and Login step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/aa113393-e615-40ed-9c5a-f95f471d728f/actiontemplate-hashicorp-vault-approle-unwrap-secret-id-and-login" rel="noopener noreferrer"&gt;HashiCorp Vault - AppRole Unwrap Secret ID and Login&lt;/a&gt; step template is provided as a convenient way to combine two step templates used with Vault into one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
AppRole Unwrap SecretID: It retrieves and unwraps a SecretID for an &lt;a href="https://www.vaultproject.io/docs/auth/approle" rel="noopener noreferrer"&gt;AppRole&lt;/a&gt; using a &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-tokens" rel="noopener noreferrer"&gt;wrapping token&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
AppRole login: It authenticates with Vault using a supplied RoleID and the unwrapped SecretID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's designed as the second part of a two-step workflow with Vault:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get a wrapped SecretID using the AppRole Get wrapped SecretID step template.&lt;/li&gt;
&lt;li&gt;Provide the wrapped SecretID stored in a sensitive output variable from the first step to this step template to unwrap and authenticate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once authenticated, the &lt;code&gt;client_token&lt;/code&gt; from the Vault response will be made available as a &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; named &lt;code&gt;AppRoleAuthToken&lt;/code&gt; for use in other steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  AppRole Unwrap SecretID and Login parameters &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The step template uses the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Vault Server URL&lt;/code&gt;: The URL of the Vault instance you are connecting to, including the port (The default is &lt;code&gt;8200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API version&lt;/code&gt;: Choose the API version to use from a drop-down list. Currently, there is only one option: &lt;code&gt;v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;App Role Path&lt;/code&gt;: The path where the &lt;a href="https://www.vaultproject.io/api-docs/auth/approle" rel="noopener noreferrer"&gt;AppRole auth method is mounted&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Role ID&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/auth/approle#roleid" rel="noopener noreferrer"&gt;RoleID&lt;/a&gt; of the AppRole.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Wrapped Token&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-tokens" rel="noopener noreferrer"&gt;wrapping token&lt;/a&gt; used to retrieve the actual Secret ID from Vault.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Token Creation Path&lt;/code&gt;: &lt;em&gt;Optional&lt;/em&gt; The creation path for the wrapped token. If this value is provided, the step template will perform a &lt;a href="https://www.vaultproject.io/api-docs/system/wrapping-lookup" rel="noopener noreferrer"&gt;wrapping lookup&lt;/a&gt; to &lt;a href="https://www.vaultproject.io/docs/concepts/response-wrapping#response-wrapping-token-validation" rel="noopener noreferrer"&gt;validate no malfeasance&lt;/a&gt; has occurred.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5es40zycq7gomgdecev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5es40zycq7gomgdecev.png" alt="Parameters for the Vault Unwrap SecretID and Login step" width="800" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using the Unwrap SecretID and Login step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Unwrap SecretID and Login&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeuc0im6zljk15i4fq2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeuc0im6zljk15i4fq2v.png" alt="Vault Unwrap SecretID and Login step used in a process" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note the use of &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variables&lt;/a&gt; in the step parameters. In this example, the values were created using the Get Wrapped SecretID step template named &lt;code&gt;HashiCorp Vault - AppRole Get Wrapped Secret ID&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can then execute the step in a runbook or deployment process. On successful execution, the sensitive output variable names are displayed in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7o4rgbyun2lm7sgat32f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7o4rgbyun2lm7sgat32f.png" alt="Vault Unwrap SecretID and Login step task log" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, the output variable &lt;code&gt;#{Octopus.Action[HashiCorp Vault - AppRole Unwrap Secret ID and Login].Output.AppRoleAuthToken}&lt;/code&gt; can be used to authenticate, and retrieve secrets.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;HashiCorp Vault - AppRole Unwrap Secret ID and Login&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Retrieving secrets &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;After you've authenticated with Vault, you receive an authentication token that can be used to retrieve secrets. Secrets in Vault are stored in a &lt;a href="https://www.vaultproject.io/docs/secrets" rel="noopener noreferrer"&gt;secrets engine&lt;/a&gt;, of which there are many different types.&lt;/p&gt;

&lt;p&gt;The step templates created to support retrieving secrets focus on the &lt;a href="https://www.vaultproject.io/docs/secrets/kv" rel="noopener noreferrer"&gt;Key-Value (kv)&lt;/a&gt; Secrets Engine as it's a generic Key-Value store used to store arbitrary secrets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieve KV (v1) secrets step&lt;/li&gt;
&lt;li&gt;Retrieve KV (v2) secrets step&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Retrieve KV (v1) secrets step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/9aab9522-25e0-4539-841c-8b726e6b1520/actiontemplate-hashicorp-vault-key-value-(v1)-retrieve-secrets" rel="noopener noreferrer"&gt;HashiCorp Vault - Key Value (v1) retrieve secrets&lt;/a&gt; step template retrieves one or more secrets stored in a &lt;code&gt;v1&lt;/code&gt; Key-Value secrets engine.&lt;/p&gt;

&lt;p&gt;Retrieving a single secret requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The path to the secret.&lt;/li&gt;
&lt;li&gt;An authentication token with permission to access the secret.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Optionally&lt;/em&gt;, a list of field names to retrieve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An advanced feature of the step template offers support for retrieving multiple secrets at once. This requires changing the &lt;strong&gt;Secrets retrieval method&lt;/strong&gt; parameter to &lt;code&gt;Multiple vault keys&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's also possible to recursively retrieve secrets. This is useful when you want to retrieve all secrets for a given path.&lt;/p&gt;

&lt;p&gt;For each secret retrieved, a &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; is created for use in subsequent steps. By default, only a count of the number of variables created will be shown in the task log. To see the names of the variables in the task log, change the &lt;strong&gt;Print output variable names&lt;/strong&gt; parameter to &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Retrieve KV (v1) secrets parameters &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The step template uses the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Vault Server URL&lt;/code&gt;: The URL of the Vault instance you are connecting to, including the port (The default is &lt;code&gt;8200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API version&lt;/code&gt;: Choose the API version to use from a drop-down list. Currently, there is only one option: &lt;code&gt;v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Auth Token&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/auth/token" rel="noopener noreferrer"&gt;token&lt;/a&gt; used to authenticate to retrieve secrets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secrets Path&lt;/code&gt;: The full path to the secret(s) you want to retrieve. The value should include both the path
where the secrets engine is mounted, as well as the path to the secret itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secrets retrieval method&lt;/code&gt;: Choose between retrieving a single secret or multiple secrets. Retrieving a single secret is the equivalent of a &lt;code&gt;vault kv get&lt;/code&gt; command using the &lt;a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret" rel="noopener noreferrer"&gt;Get&lt;/a&gt; method. Retrieving multiple secrets is the equivalent of the combination of both a &lt;code&gt;vault kv list&lt;/code&gt; command using the &lt;a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v2#list-secrets" rel="noopener noreferrer"&gt;List&lt;/a&gt; method and then subsequent &lt;code&gt;vault kv get&lt;/code&gt; commands for each secret.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Recursive retrieval&lt;/code&gt;: If multiple secrets are being retrieved, should any sub-folders also be enumerated and retrieved? The default is: &lt;code&gt;False&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Field names&lt;/code&gt;: Choose specific fields to be retrieved from identified secrets. This is useful when you only want to retrieve specific fields from one or more secrets. You can optionally include a name for the output variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Print output variable names&lt;/code&gt;: Write out the Octopus output variable names to the task log. The default is: &lt;code&gt;False&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1lpoenhe9w29nphwb8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1lpoenhe9w29nphwb8l.png" alt="Parameters for the retrieve KV v1 secrets step" width="800" height="919"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using Retrieve KV (v1) secrets step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Key Value (v1) retrieve secrets&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwcznwnzi3iemadzynnpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwcznwnzi3iemadzynnpc.png" alt="Vault retrieve KV v1 secrets step used in a process" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note the use of the &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; in the &lt;code&gt;Auth Token&lt;/code&gt; parameter. In this example, the value was created using the Unwrap SecretID and Login step template named &lt;code&gt;HashiCorp Vault - AppRole Unwrap Secret ID and Login&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After you've filled in the parameters, you can execute the step in a runbook or deployment process. On successful execution, any matching secrets will be stored as sensitive output variables. If you've configured your step to print the variable names, they'll appear in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnlkhm9mnsnjmhgb52wi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnlkhm9mnsnjmhgb52wi.png" alt="Vault retrieve KV v1 secrets step task log" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, output variables created from matching secrets can be used in your deployment or runbook.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;HashiCorp Vault - Key Value (v1) retrieve secrets&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Retrieve KV (v2) secrets step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://library.octopus.com/step-templates/337f1b67-cdb0-4f33-9e08-6bf804f672d2/actiontemplate-hashicorp-vault-key-value-(v2)-retrieve-secrets" rel="noopener noreferrer"&gt;HashiCorp Vault - Key Value (v2) retrieve secrets&lt;/a&gt; step template retrieves one or more secrets stored in a &lt;code&gt;v2&lt;/code&gt; Key-Value secrets engine.&lt;/p&gt;

&lt;p&gt;One of the key advantages of the &lt;code&gt;v2&lt;/code&gt; Key-Value secrets engine is its support for &lt;a href="https://learn.hashicorp.com/tutorials/vault/versioned-kv" rel="noopener noreferrer"&gt;versioned secrets&lt;/a&gt;. This is useful if you need to roll back secrets in the event of unintentional data loss.&lt;/p&gt;

&lt;p&gt;Retrieving a single secret requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The path to the secret,&lt;/li&gt;
&lt;li&gt;An authentication token with permission to access the secret.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Optionally&lt;/em&gt;, a list of field names to retrieve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step template offers advanced features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Support for retrieving multiple secrets at once. This requires changing the &lt;strong&gt;Secrets retrieval method&lt;/strong&gt; parameter to &lt;code&gt;Multiple vault keys&lt;/code&gt;. It's also possible to recursively retrieve secrets. This is useful to retrieve all secrets for a given path.&lt;/li&gt;
&lt;li&gt;Support for retrieving a specific version of a secret. This is only supported when retrieving a single secret.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For each secret retrieved, a &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; is created for use in subsequent steps. By default, only a count of the number of variables created will be shown in the task log. To see the names of the variables in the Task log, change the &lt;strong&gt;Print output variable names&lt;/strong&gt; parameter to &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Retrieve KV (v2) secrets parameters &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The step template uses the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Vault Server URL&lt;/code&gt;: The URL of the Vault instance you are connecting to, including the port (The default is &lt;code&gt;8200&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;API version&lt;/code&gt;: Choose the API version to use from a drop-down list. Currently, there is only one option: &lt;code&gt;v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Auth Token&lt;/code&gt;: The &lt;a href="https://www.vaultproject.io/docs/auth/token" rel="noopener noreferrer"&gt;token&lt;/a&gt; used to authenticate to retrieve secrets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secrets Path&lt;/code&gt;: The full path to the secret(s) you want to retrieve. The value should include both the path
where the secrets engine is mounted, as well as the path to the secret itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secrets retrieval method&lt;/code&gt;: Choose between retrieving a single secret or multiple secrets. Retrieving a single secret is the equivalent of a &lt;code&gt;vault kv get&lt;/code&gt; command using the &lt;a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret" rel="noopener noreferrer"&gt;Get&lt;/a&gt; method. Retrieving multiple secrets is the equivalent of the combination of both a &lt;code&gt;vault kv list&lt;/code&gt; command using the &lt;a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v2#list-secrets" rel="noopener noreferrer"&gt;List&lt;/a&gt; method and then subsequent &lt;code&gt;vault kv get&lt;/code&gt; commands for each secret.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Recursive retrieval&lt;/code&gt;: If multiple secrets are being retrieved, should any sub-folders also be enumerated and retrieved? The default is: &lt;code&gt;False&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secret Version&lt;/code&gt;: &lt;em&gt;Optional&lt;/em&gt; When retrieving a single secret, choose the version of the secret to retrieve. For example, if you want version 2 of all field values in a secret, enter the value &lt;code&gt;2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Field names&lt;/code&gt;: Choose specific fields to be retrieved from identified secrets. This is useful when you only want to retrieve specific fields from one or more secret(s). You can optionally include a name for the output variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Print output variable names&lt;/code&gt;: Write out the Octopus output variable names to the task log. The default is: &lt;code&gt;False&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fty3ju5sktzoxxa916u2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fty3ju5sktzoxxa916u2o.png" alt="Parameters for the retrieve KV v2 secrets step" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using Retrieve KV (v2) secrets step &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Key Value (v2) retrieve secrets&lt;/strong&gt; step is added to deployment and runbook processes in the &lt;a href="https://octopus.com/docs/projects/steps#adding-steps-to-your-deployment-processes" rel="noopener noreferrer"&gt;same way as other steps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've added the step to your process, fill out the parameters in the step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvc8lnh0qcwc4ymk5nhru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvc8lnh0qcwc4ymk5nhru.png" alt="Vault retrieve KV v2 secrets step used in a process" width="800" height="734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note the use of the &lt;a href="https://octopus.com/docs/projects/variables/output-variables#sensitive-output-variables" rel="noopener noreferrer"&gt;sensitive output variable&lt;/a&gt; in the &lt;code&gt;Auth Token&lt;/code&gt; parameter. In this example, the value was created using the Unwrap SecretID and Login step template named &lt;code&gt;HashiCorp Vault - AppRole Unwrap Secret ID and Login&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After you've filled in the parameters, you can execute the step in a runbook or deployment process. On successful execution, any matching secrets will be stored as sensitive output variables. If you've configured your step to print the variable names, they'll appear in the task log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7aly029aaj0anfp5cxql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7aly029aaj0anfp5cxql.png" alt="Vault retrieve KV v2 secrets step task log" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In subsequent steps, the output variables created from matching secrets can be used in your deployment or runbook.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember to replace &lt;code&gt;HashiCorp Vault - Key Value (v2) retrieve secrets&lt;/code&gt; with the name of your step for any output variable names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The templates covered in this post show how it's possible to extend the functionality of Octopus and retrieve secrets from Vault, or any other secrets manager, and use them in your deployments or runbooks.&lt;/p&gt;

&lt;p&gt;Happy deployments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;For further information, you can read:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://learn.hashicorp.com/tutorials/vault/approle" rel="noopener noreferrer"&gt;AppRole Pull Authentication&lt;/a&gt; tutorial showing how to retrieve SecretIDs securely.&lt;/li&gt;
&lt;li&gt;HashiCorp Vault documentation for the &lt;a href="https://www.vaultproject.io/docs/secrets/kv/kv-v1" rel="noopener noreferrer"&gt;K/V v1 Secrets Engine&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;HashiCorp Vault documentation for the &lt;a href="https://www.vaultproject.io/docs/secrets/kv/kv-v2" rel="noopener noreferrer"&gt;K/V v2 Secrets Engine&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/using-hashicorp-vault-with-octopus-deploy" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>deployment</category>
      <category>security</category>
      <category>vault</category>
    </item>
    <item>
      <title>Octopus Pipe for Bitbucket: octopus-cli-run</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Thu, 06 Aug 2020 10:20:11 +0000</pubDate>
      <link>https://dev.to/octopus/octopus-pipe-for-bitbucket-octopus-cli-run-3pfc</link>
      <guid>https://dev.to/octopus/octopus-pipe-for-bitbucket-octopus-cli-run-3pfc</guid>
      <description>&lt;p&gt;In a previous post, I wrote &lt;a href="https://dev.to/octopus/bitbucket-pipelines-pipes-and-integrating-with-octopus-deploy-580e"&gt;how to create a Bitbucket Pipe and integrate it with Octopus Deploy&lt;/a&gt;. If you’re starting out with Pipes for the first time, it’s worth a read.&lt;/p&gt;

&lt;p&gt;In this post, I’ll give you an overview of the new experimental Bitbucket Pipe for Octopus - &lt;a href="https://bitbucket.org/octopusdeploy/octopus-cli-run/" rel="noopener noreferrer"&gt;octopus-cli-run&lt;/a&gt;.&lt;br&gt;
If you’re interested in trying the experimental pipe, you can use it to run commands from the &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/" rel="noopener noreferrer"&gt;Octopus CLI&lt;/a&gt;, allowing you to further integrate your Atlassian &lt;a href="https://bitbucket.org/product/features/pipelines" rel="noopener noreferrer"&gt;Bitbucket Pipeline&lt;/a&gt; with Octopus to manage your packages, releases, and deployments.&lt;/p&gt;
&lt;h2&gt;
  
  
  In this post
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Pipe YAML Definition

&lt;ul&gt;
&lt;li&gt;Pipe variable definitions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Supported commands

&lt;ul&gt;
&lt;li&gt;Pack&lt;/li&gt;
&lt;li&gt;Push&lt;/li&gt;
&lt;li&gt;Build information&lt;/li&gt;
&lt;li&gt;Create release&lt;/li&gt;
&lt;li&gt;Deploy release&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using the Pipe&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Pipe YAML Definition &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The base definition of the Pipe includes the reference to its repository hosted on &lt;a href="https://bitbucket.org/octopusdeploy/octopus-cli-run/" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt;. It has also been published as &lt;a href="https://hub.docker.com/r/octopipes/octopus-cli-run/" rel="noopener noreferrer"&gt;octopipes/octopus-cli-run&lt;/a&gt; on Docker Hub.&lt;/p&gt;

&lt;p&gt;It has one required &lt;code&gt;CLI_COMMAND&lt;/code&gt; variable. This is the CLI command to run.&lt;/p&gt;

&lt;p&gt;To use the Pipe in your &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file, add the following YAML snippet to the script section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# EXTRA_ARGS: ['&amp;lt;string&amp;gt;','&amp;lt;string&amp;gt;' ..] # Optional&lt;/span&gt;
    &lt;span class="c1"&gt;# DEBUG: "&amp;lt;boolean&amp;gt;" # Optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Pipe also provides an &lt;em&gt;optional&lt;/em&gt; array variable called &lt;code&gt;EXTRA_ARGS&lt;/code&gt; that you can use to include any additional command line arguments for the specified command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pipe variable definitions &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Variables in Bitbucket Pipelines and Pipes are configured as &lt;a href="https://confluence.atlassian.com/bitbucket/variables-in-pipelines-794502608.html" rel="noopener noreferrer"&gt;Environment variables&lt;/a&gt;. As the &lt;code&gt;octopus-cli-run&lt;/code&gt; Pipe contains a number of commands, the specific variables that are required depend on which command you are using. See the &lt;a href="https://bitbucket.org/octopusdeploy/octopus-cli-run/src/master/README.md#markdown-header-variables" rel="noopener noreferrer"&gt;README&lt;/a&gt; for further details of the variables that are required for each command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supported commands &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;octopus-cli-run&lt;/code&gt; Pipe was written with the most commonly used CLI commands in mind, and it’s actually built on top of the &lt;a href="https://hub.docker.com/r/octopusdeploy/octo/" rel="noopener noreferrer"&gt;Octopus CLI Docker image&lt;/a&gt;. This includes the ability to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package your files or build artifacts using &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/pack" rel="noopener noreferrer"&gt;pack&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Send packages to the Octopus built-in repository using &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/pack" rel="noopener noreferrer"&gt;push&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Push build information to Octopus using &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/build-information" rel="noopener noreferrer"&gt;build-information&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Automate creation of releases using &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/create-release" rel="noopener noreferrer"&gt;create-release&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Deploy releases that have already been created using &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/create-release" rel="noopener noreferrer"&gt;deploy-release&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we’ll explore what the Pipeline steps look like for each of the commands using the &lt;strong&gt;PetClinic&lt;/strong&gt; sample application available on &lt;a href="https://bitbucket.org/octopussamples/petclinic/" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt;. To keep it simple, the steps have been reduced to the minimum definition that is needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pack &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;pack&lt;/code&gt; command allows you to create &lt;a href="https://octopus.com/docs/packaging-applications" rel="noopener noreferrer"&gt;packages&lt;/a&gt; (either as zip or nupkg) from files on disk, without the need for a &lt;code&gt;.nuspec&lt;/code&gt; or &lt;code&gt;.csproj&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;To create a package, define a step like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo pack mysql-flyway&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pack'&lt;/span&gt;
          &lt;span class="na"&gt;ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;petclinic.mysql.flyway'&lt;/span&gt;
          &lt;span class="na"&gt;FORMAT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Zip'&lt;/span&gt;
          &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.0.0.0'&lt;/span&gt;
          &lt;span class="na"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;flyway'&lt;/span&gt;
          &lt;span class="na"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;flyway'&lt;/span&gt;
    &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flyway/*.zip"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This packages the &lt;code&gt;flyway&lt;/code&gt; folder and creates a zip file named &lt;code&gt;petclinic.mysql.flyway.1.0.0.0.zip&lt;/code&gt; in the same folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Push &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;push&lt;/code&gt; command enables you to push packages (.zip, .nupkg, .war, etc) to the Octopus &lt;a href="https://octopus.com/docs/packaging-applications/package-repositories/built-in-repository" rel="noopener noreferrer"&gt;built-in repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also supports pushing multiple packages at the same time. To perform a multi-package &lt;code&gt;push&lt;/code&gt;, define a step like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo push&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push'&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
          &lt;span class="na"&gt;PACKAGES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./flyway/petclinic.mysql.flyway.1.0.0.0.zip"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target/petclinic.web.1.0.0.0.war"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pushes both the &lt;code&gt;petclinic.mysql.flyway.1.0.0.0.zip&lt;/code&gt; and the &lt;code&gt;petclinic.web.1.0.0.0.war&lt;/code&gt; packages to Octopus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build information &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;build-information&lt;/code&gt; command helps you to pass information about your build (number, URL, commits) to Octopus. This information can be viewed within Octopus, and can also be used in both &lt;a href="https://octopus.com/docs/managing-releases/release-notes" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; and &lt;a href="https://octopus.com/docs/managing-releases/deployment-notes" rel="noopener noreferrer"&gt;deployment notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have already created a build-information file, you can supply this to the command using the &lt;code&gt;FILE&lt;/code&gt; variable. If the variable isn’t provided, the Pipe will generate its own build information file and send it to Octopus.&lt;/p&gt;

&lt;p&gt;To push an auto-generated build info file, define a step like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo build-information&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-information'&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
          &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.0.0.0'&lt;/span&gt;
          &lt;span class="na"&gt;PACKAGE_IDS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;petclinic.web'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates build information, associates it with version &lt;code&gt;1.0.0.0&lt;/code&gt; of the &lt;code&gt;petclinic.web&lt;/code&gt; package, and pushes it to Octopus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create release &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;create-release&lt;/code&gt; command allows you to create a release in Octopus. You specify the project to create the release for using the &lt;code&gt;PROJECT&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Optionally, you can also deploy the release to one or more environments. To achieve this, you should use the global &lt;code&gt;EXTRA_ARGS&lt;/code&gt; array variable and provide the appropriate options. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EXTRA_ARGS: ['--deployTo', 'Development', '--guidedFailure', 'True']&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To create a release, and let Octopus choose the version to use, create a step like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo create-release&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create-release'&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
          &lt;span class="na"&gt;PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_PROJECT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy release &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;deploy-release&lt;/code&gt; command lets you deploy releases that have already been created. You specify the project and the release number to deploy the release for using the &lt;code&gt;PROJECT&lt;/code&gt; and &lt;code&gt;RELEASE_NUMBER&lt;/code&gt; variables.&lt;/p&gt;

&lt;p&gt;Choose the environment(s) to deploy to by specifying them in the &lt;code&gt;DEPLOY_TO&lt;/code&gt; variable using either the Name or ID, like so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DEPLOY_TO: ['Environments-1', 'Development', 'Staging', 'Test']&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To deploy the &lt;code&gt;latest&lt;/code&gt; release to &lt;code&gt;Development&lt;/code&gt; for a project, create a step like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo deploy-release&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-release'&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
          &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
          &lt;span class="na"&gt;PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_PROJECT&lt;/span&gt;
          &lt;span class="na"&gt;RELEASE_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest'&lt;/span&gt;
          &lt;span class="na"&gt;DEPLOY_TO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Development'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Finally, let’s see the use of the Pipe in multiple steps to make up the complete Bitbucket Pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven:3.6.1&lt;/span&gt;

&lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;master&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build petclinic&lt;/span&gt;
          &lt;span class="na"&gt;caches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;maven&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mvn -B verify -DskipTests -Dproject.versionNumber=1.0.0.0 -DdatabaseUserName=$DatabaseUserName -DdatabaseUserPassword=$DatabaseUserPassword -DdatabaseServerName=$DatabaseServerName -DdatabaseName=$DatabaseName&lt;/span&gt;
          &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target/*.war"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo pack mysql-flyway&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
              &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pack'&lt;/span&gt;
                &lt;span class="na"&gt;ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;petclinic.mysql.flyway'&lt;/span&gt;
                &lt;span class="na"&gt;FORMAT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Zip'&lt;/span&gt;
                &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.0.0.0'&lt;/span&gt;
                &lt;span class="na"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;flyway'&lt;/span&gt;
                &lt;span class="na"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./flyway'&lt;/span&gt;
          &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flyway/*.zip"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo push&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
              &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push'&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
                &lt;span class="na"&gt;PACKAGES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./flyway/petclinic.mysql.flyway.1.0.0.0.zip"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target/petclinic.web.1.0.0.0.war"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo build-information&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
              &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-information'&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
                &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.0.0.0'&lt;/span&gt;
                &lt;span class="na"&gt;PACKAGE_IDS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;petclinic.web'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo create-release&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
              &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create-release'&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
                &lt;span class="na"&gt;PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_PROJECT&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo deploy-release&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octopus-cli-run:0.13.0&lt;/span&gt;
              &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;CLI_COMMAND&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-release'&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SERVER&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_APIKEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_API_KEY&lt;/span&gt;
                &lt;span class="na"&gt;OCTOPUS_SPACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_SPACE&lt;/span&gt;
                &lt;span class="na"&gt;PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$OCTOPUS_PROJECT&lt;/span&gt;
                &lt;span class="na"&gt;RELEASE_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest'&lt;/span&gt;
                &lt;span class="na"&gt;DEPLOY_TO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Development'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it!&lt;/p&gt;

&lt;p&gt;You can view the complete PetClinic &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file on &lt;a href="https://bitbucket.org/octopussamples/petclinic/src/master/bitbucket-pipelines.yml" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sample Octopus project&lt;/strong&gt;&lt;br&gt;
You can see the PetClinic Octopus project in our &lt;a href="https://g.octopushq.com/TargetWildflySamplePetClinic" rel="noopener noreferrer"&gt;samples&lt;/a&gt; instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Using a Bitbucket Pipe really helps to simplify the configuration in your Bitbucket Pipeline, and as an author helps to promote re-use of your actions. Check out &lt;a href="https://bitbucket.org/product/features/pipelines/integrations" rel="noopener noreferrer"&gt;Bitbucket Pipelines&lt;/a&gt; for more information and the &lt;a href="https://bitbucket.org/octopusdeploy/octopus-cli-run/src/master/README.md" rel="noopener noreferrer"&gt;experimental Octopus Pipe&lt;/a&gt; for more details on how you can use Bitbucket and Octopus together.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/octopus-bitbucket-pipe" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>devops</category>
      <category>bitbucket</category>
      <category>pipelines</category>
    </item>
    <item>
      <title>Bitbucket Pipelines: Pipes and integrating with Octopus Deploy</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Tue, 04 Aug 2020 08:32:31 +0000</pubDate>
      <link>https://dev.to/octopus/bitbucket-pipelines-pipes-and-integrating-with-octopus-deploy-580e</link>
      <guid>https://dev.to/octopus/bitbucket-pipelines-pipes-and-integrating-with-octopus-deploy-580e</guid>
      <description>&lt;p&gt;Atlassian’s &lt;a href="https://bitbucket.org/product/features/pipelines" rel="noopener noreferrer"&gt;Bitbucket Pipelines&lt;/a&gt; is a lightweight cloud continuous integration server that uses pre-configured Docker containers, allowing you to define your infrastructure as code. &lt;a href="https://bitbucket.org/product/features/pipelines/integrations" rel="noopener noreferrer"&gt;Pipes&lt;/a&gt; let you add configuration to your Pipelines and are particularly useful for third-party tools.&lt;/p&gt;

&lt;p&gt;In this post, I create an experimental Pipe for an &lt;a href="https://g.octopushq.com/OctopusCLI" rel="noopener noreferrer"&gt;Octopus CLI&lt;/a&gt; command, use it in a Bitbucket Pipeline for our sample node.js application &lt;a href="https://bitbucket.org/octopussamples/randomquotes-js" rel="noopener noreferrer"&gt;RandomQuotes-Js&lt;/a&gt;, and finally, integrate the Pipeline with Octopus.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this post
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What are Bitbucket Pipes?&lt;/li&gt;
&lt;li&gt;
Example Pipe usage

&lt;ul&gt;
&lt;li&gt;Refer to a Pipe in a Pipeline step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Why are Pipes useful?&lt;/li&gt;

&lt;li&gt;Create a Bitbucket Pipe&lt;/li&gt;

&lt;li&gt;

Choose a candidate for a Pipe

&lt;ul&gt;
&lt;li&gt;Create the Pipe repository&lt;/li&gt;
&lt;li&gt;Create the Pipe skeleton&lt;/li&gt;
&lt;li&gt;Create the Pipe’s metadata&lt;/li&gt;
&lt;li&gt;Create the Pipe script&lt;/li&gt;
&lt;li&gt;Mandatory Pipe variables&lt;/li&gt;
&lt;li&gt;Optional Pipe variables&lt;/li&gt;
&lt;li&gt;Run the Pack Pipe&lt;/li&gt;
&lt;li&gt;Complete Pipe script&lt;/li&gt;
&lt;li&gt;Create the Pipe Dockerfile&lt;/li&gt;
&lt;li&gt;Create the Pipe’s own pipeline&lt;/li&gt;
&lt;li&gt;Create the Pipe README&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Run the Pipe&lt;/li&gt;

&lt;li&gt;Test the Pipe&lt;/li&gt;

&lt;li&gt;Publish the Pipe&lt;/li&gt;

&lt;li&gt;

Integrating the Pipe into a Pipeline

&lt;ul&gt;
&lt;li&gt;Use the Pipe&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Integrate the Pipeline with Octopus

&lt;ul&gt;
&lt;li&gt;Push the package to Octopus&lt;/li&gt;
&lt;li&gt;Push build information to Octopus&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Learn more&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are Bitbucket Pipes? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Atlassian &lt;a href="https://confluence.atlassian.com/bitbucket/learn-about-pipes-978200267.html" rel="noopener noreferrer"&gt;says&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pipes provide a simple way to configure a pipeline. They are especially powerful when you want to work with third-party tools. Just paste the pipe into the YAML file, supply a few key pieces of information, and the rest is done for you. You can add as many pipes as you like to your steps, so the possibilities are endless!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pipes build on the core concept of Pipelines, containers. A Pipe makes use of a script that lives inside of a &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; container, and it typically has the commands you’d write in your pipeline YAML file before pipes were available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Pipe usage &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is what the Atlassian &lt;a href="https://bitbucket.org/product/features/pipelines/integrations?p=atlassian/bitbucket-upload-file" rel="noopener noreferrer"&gt;bitbucket-upload-file&lt;/a&gt; Pipe looks like in your Pipeline YAML file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;atlassian/bitbucket-upload-file:0.1.3&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;BITBUCKET_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;'&lt;/span&gt;
    &lt;span class="na"&gt;BITBUCKET_APP_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;'&lt;/span&gt;
    &lt;span class="na"&gt;FILENAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;'&lt;/span&gt;
    &lt;span class="c1"&gt;# ACCOUNT: '&amp;lt;string&amp;gt;' # Optional&lt;/span&gt;
    &lt;span class="c1"&gt;# REPOSITORY: '&amp;lt;string&amp;gt;' # Optional&lt;/span&gt;
    &lt;span class="c1"&gt;# DEBUG: '&amp;lt;boolean&amp;gt;' # Optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;atlassian/bitbucket-upload-file:0.1.3&lt;/code&gt; is the name of the Docker &lt;a href="https://hub.docker.com/r/bitbucketpipelines/bitbucket-upload-file" rel="noopener noreferrer"&gt;image&lt;/a&gt; containing the Pipe to run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BITBUCKET_USERNAME&lt;/code&gt; is an example of a variable you need to provide that has the value for the Pipe to use when executing inside of the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Refer to a Pipe in a Pipeline step &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;There are two ways you can refer to a Pipe in a step within a pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Refer to the Docker image directly:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker://&amp;lt;Docker_Account_Name&amp;gt;/&amp;lt;Image_Name&amp;gt;:&amp;lt;tag&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Refer to a Pipe repository hosted on Bitbucket:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;Bitbucket_account&amp;gt;/&amp;lt;Bitbucket_repo&amp;gt;:&amp;lt;tag&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method looks for the location of the Docker image from the &lt;code&gt;pipe.yml&lt;/code&gt; file within the referenced &lt;code&gt;&amp;lt;Bitbucket_account&amp;gt;/&amp;lt;Bitbucket_repo&amp;gt;&lt;/code&gt; Pipe repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are Pipes useful? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Why go to the trouble of writing a Pipe at all?&lt;/p&gt;

&lt;p&gt;Pipes are all about &lt;em&gt;re-use&lt;/em&gt;. They allow you to repeat the same action in multiple steps of your Pipeline.  By centralizing the core of your action into a Pipe, you end up with a simpler Pipeline configuration. Another key feature of a Pipe versus directly scripting in your Pipeline is the ability to include dependencies that your main Pipeline doesn’t require.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Bitbucket Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A Pipe consists of a bunch of files that make up a Docker image. The Pipe I created has its image based on the pre-existing &lt;a href="https://hub.docker.com/r/octopusdeploy/octo" rel="noopener noreferrer"&gt;octopusdeploy/octo&lt;/a&gt; image. The finished Pipe has been published as &lt;a href="https://hub.docker.com/r/octopipes/pack/" rel="noopener noreferrer"&gt;octopipes/pack&lt;/a&gt; on Docker Hub.&lt;/p&gt;

&lt;p&gt;At first, creating a Pipe might seem quite daunting, but Atlassian provide a helpful step-by-step &lt;a href="https://confluence.atlassian.com/bitbucket/how-to-write-a-pipe-for-bitbucket-pipelines-966051288.html" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I created this Pipe on an Ubuntu machine using a Bash terminal. If you are using a different platform, you may need to tweak the commands you use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Choose a candidate for a Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I’ve often heard people say that naming something is the hardest thing when it comes to software, and the same is true for choosing a command to wrap in a Pipe. However, in most CI/CD pipelines, after you have built and run any tests on your code, you probably want to package your applications. So it felt natural to choose the Octopus CLI &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/pack" rel="noopener noreferrer"&gt;pack&lt;/a&gt; command to create a Pipe for.&lt;/p&gt;

&lt;p&gt;The added bonus was that the &lt;code&gt;pack&lt;/code&gt; command only has a few required parameters, and the optional ones can be tackled with some pipeline magic (more on that later).&lt;/p&gt;

&lt;p&gt;There are two types of Pipe that you can author:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple&lt;/li&gt;
&lt;li&gt;Complete&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I opted for a &lt;strong&gt;Complete&lt;/strong&gt; Pipe so that I can publish it and make use of it in other repositories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Pipe repository &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Next up, I needed to create a new &lt;a href="https://bitbucket.org/octopusdeploy/pack" rel="noopener noreferrer"&gt;octopusdeploy/pack&lt;/a&gt; Git repository in Bitbucket, and clone it locally.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For further information on creating a new Git repository, please see the Atlassian &lt;a href="https://confluence.atlassian.com/bitbucket/create-a-git-repository-759857290.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create the Pipe skeleton &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Atlassian provides a method to generate a skeleton of a Pipe repository using &lt;a href="http://yeoman.io/" rel="noopener noreferrer"&gt;Yeoman&lt;/a&gt;. When you have all the pre-requisites (&lt;code&gt;nodejs&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt;) installed, you can run the generator using the &lt;code&gt;yo&lt;/code&gt; command from a terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yo bitbucket-pipe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompts you to select the Pipe you want to create. I chose &lt;strong&gt;New Advanced Pipe (Bash)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftixvgmglvesyv9ecah6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftixvgmglvesyv9ecah6.png" alt="Bitbucket pipe generator -welcome" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You are prompted with some questions to help fill in the metadata and other useful information for consumers of the Pipe. Once complete, it will generate the required files you need to get started:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp74lwtt5v155acywnuqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp74lwtt5v155acywnuqa.png" alt="Bitbucket pipe generator - complete" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At a minimum, you need to edit the following files to suit your Pipe requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pipe.yml&lt;/li&gt;
&lt;li&gt;pipe/pipe.sh&lt;/li&gt;
&lt;li&gt;Dockerfile&lt;/li&gt;
&lt;li&gt;bitbucket-pipelines.yml&lt;/li&gt;
&lt;li&gt;README.md&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Check other repositories to see how they have written their Pipe!&lt;/p&gt;

&lt;p&gt;One of the great things about every Bitbucket Pipe is that the code is public, so you can browse it. For example, you can view the source code for the &lt;code&gt;bitbucket-upload-file&lt;/code&gt; Pipe on &lt;a href="https://bitbucket.org/atlassian/bitbucket-upload-file/" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a really great way to see how other authors have structured their Pipe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create the Pipe’s metadata &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When you create a &lt;strong&gt;Complete&lt;/strong&gt; Pipe, Atlassian requires you to create a &lt;code&gt;pipe.yml&lt;/code&gt; file. This document contains metadata about your Pipe and includes things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A name for the Pipe.&lt;/li&gt;
&lt;li&gt;The Docker Hub image for your Pipe in the format: &lt;code&gt;account/repo:tag&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A list of Pipe variables where you can specify default values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you chose one of the &lt;em&gt;Advanced&lt;/em&gt; Pipes using the Pipe generator, the &lt;code&gt;pipe.yml&lt;/code&gt; file will be created for you with all of the relevant information already added. Here are the contents of my auto-generated &lt;a href="https://bitbucket.org/octopusdeploy/pack/src/master/pipe.yml" rel="noopener noreferrer"&gt;pipe.yml&lt;/a&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Octo Pack&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopipes/pack:0.0.0&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Creates a package (.nupkg or .zip) from files on disk, without needing a .nuspec or .csproj&lt;/span&gt;
&lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://bitbucket.org/octopusdeploy/pack&lt;/span&gt;
&lt;span class="na"&gt;maintainer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;support@octopus.com&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;octopus&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;package&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deployment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Pipe script &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The main part of your Pipe is the script or binary that will run when it’s executed within a container. It will include all of the logic needed to execute the Pipe task. You can choose any language you are familiar with. When I created the skeleton of our Pipe earlier, I used &lt;a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)" rel="noopener noreferrer"&gt;Bash&lt;/a&gt;, and a sample &lt;code&gt;pipe/pipe.sh&lt;/code&gt; file was created for me to finish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to see the complete Pipe script, skip straight to the end or view the &lt;a href="https://bitbucket.org/octopusdeploy/pack/src/master/pipe/pipe.sh" rel="noopener noreferrer"&gt;source code&lt;/a&gt;. If not, read on!&lt;/p&gt;

&lt;p&gt;The general structure of a Pipe script file follows this convention:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpvuazn57d4ck1kacrjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpvuazn57d4ck1kacrjl.png" alt="pipe script flow" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Mandatory Pipe variables &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;pack&lt;/code&gt; command has five parameters I want the Pipe to handle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;--id&lt;/code&gt; of the package to create.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--format&lt;/code&gt; for the package, e.g. &lt;code&gt;NuPkg&lt;/code&gt; or &lt;code&gt;Zip&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--version&lt;/code&gt; for the package (SemVer).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--basePath&lt;/code&gt; to specify the root folder containing files and folders to pack.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--outFolder&lt;/code&gt; to specify the folder where the generated package will be written.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To support these parameters, I mapped each one to a Bitbucket Pipeline &lt;a href="https://confluence.atlassian.com/bitbucket/variables-in-pipelines-794502608.html" rel="noopener noreferrer"&gt;variable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also followed the validation of variables, as shown in the Atlassian &lt;a href="https://bitbucket.org/atlassian/demo-pipe-bash/src/master/pipe/pipe.sh" rel="noopener noreferrer"&gt;demo-pipe-bash&lt;/a&gt; script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;:?&lt;span class="s1"&gt;'NAME variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks for a &lt;code&gt;$NAME&lt;/code&gt; variable value and errors with a message when the variable isn’t present.&lt;/p&gt;

&lt;p&gt;For the five variables I created, my variable validation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# mandatory parameters&lt;/span&gt;
&lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ID&lt;/span&gt;:?&lt;span class="s1"&gt;'ID variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FORMAT&lt;/span&gt;:?&lt;span class="s1"&gt;'FORMAT variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;:?&lt;span class="s1"&gt;'VERSION variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;:?&lt;span class="s1"&gt;'SOURCE_PATH variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;:?&lt;span class="s1"&gt;'OUTPUT_PATH variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Optional Pipe variables &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Next up were some optional variables consumers of the Pipe could choose to supply if they wish.&lt;/p&gt;

&lt;p&gt;I included an &lt;code&gt;EXTRA_ARGS&lt;/code&gt; array variable to include multiple additional arguments for the &lt;code&gt;pack&lt;/code&gt; command. You can specify this variable by using a special Bitbucket array type in your pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EXTRA_ARGS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--description'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;containing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;spaces'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--verbose'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The array type is really useful, as it provides an easy way to supply any other arguments to the Pipe. In my case, this allows consumers of the &lt;code&gt;pack&lt;/code&gt; Pipe the ability to provide any of the additional argument options that I haven’t explicitly handled.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Advanced Pipe writing techniques:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To learn more about the array type and how its values are passed to the Docker container, please see Atlassian’s &lt;a href="https://confluence.atlassian.com/bitbucket/advanced-techniques-for-writing-pipes-969511009.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lastly, I included a boolean &lt;code&gt;DEBUG&lt;/code&gt; variable to include additional debugging information. You specify its value in your Pipe like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Run the Pack Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;pack&lt;/code&gt; Pipe’s sole purpose is to package a set of files, to achieve that, we make use of our reference to the &lt;a href="https://hub.docker.com/r/octopusdeploy/octo" rel="noopener noreferrer"&gt;octopusdeploy/octo&lt;/a&gt; Docker image I used as a base for our Pipe Docker image. This gives us access to the full &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/" rel="noopener noreferrer"&gt;Octopus CLI&lt;/a&gt; in our Pipe.&lt;/p&gt;

&lt;p&gt;To package the files in the Bitbucket Pipeline, our script needs to run the &lt;code&gt;pack&lt;/code&gt; command and pass in our variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;run octo pack &lt;span class="nt"&gt;--id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FORMAT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--basePath&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--outFolder&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXTRA_ARGS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we check if the command was successful. If it was, we display a success message and set a variable with the filename of the package that was created. If not, we display an error message and halt execution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; command is a helper function specified in a separate &lt;code&gt;common.sh&lt;/code&gt; file. It looks like this:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;run&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nv"&gt;output_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/tmp/pipe-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$RANDOM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

 &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
 &lt;span class="nb"&gt;set&lt;/span&gt; +e
 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
 &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
 &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The function wraps the call to the supplied command, in this case &lt;code&gt;octo pack&lt;/code&gt;, logs the output to a temporary file, and captures the exit status.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Complete Pipe script &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;And that’s all there is to our script. Here is the finished &lt;code&gt;pipe.sh&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# Creates a package (.nupkg or .zip) from files on disk, without needing a .nuspec or .csproj&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Required globals:&lt;/span&gt;
&lt;span class="c"&gt;#   ID&lt;/span&gt;
&lt;span class="c"&gt;#   FORMAT&lt;/span&gt;
&lt;span class="c"&gt;#   VERSION&lt;/span&gt;
&lt;span class="c"&gt;#   SOURCE_PATH&lt;/span&gt;
&lt;span class="c"&gt;#   OUTPUT_PATH&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Optional globals:&lt;/span&gt;
&lt;span class="c"&gt;#   EXTRA_ARGS&lt;/span&gt;
&lt;span class="c"&gt;#   DEBUG&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/common.sh"&lt;/span&gt;

&lt;span class="c"&gt;# mandatory parameters&lt;/span&gt;
&lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ID&lt;/span&gt;:?&lt;span class="s1"&gt;'ID variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FORMAT&lt;/span&gt;:?&lt;span class="s1"&gt;'FORMAT variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;:?&lt;span class="s1"&gt;'VERSION variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;:?&lt;span class="s1"&gt;'SOURCE_PATH variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;:?&lt;span class="s1"&gt;'OUTPUT_PATH variable missing.'&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FORMAT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Default parameters&lt;/span&gt;
&lt;span class="nv"&gt;EXTRA_ARGS_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXTRA_ARGS_COUNT&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEBUG&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

enable_debug

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXTRA_ARGS_COUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="c"&gt;# Flatten array of extra args&lt;/span&gt;
  debug &lt;span class="s2"&gt;"Setting EXTRA_ARGS to empty array"&lt;/span&gt;
  &lt;span class="nv"&gt;EXTRA_ARGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;debug &lt;span class="s2"&gt;"Flattening EXTRA_ARGS"&lt;/span&gt;
init_array_var &lt;span class="s1"&gt;'EXTRA_ARGS'&lt;/span&gt;

debug ID: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
debug FORMAT: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
debug VERSION: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
debug SOURCE_PATH: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
debug OUTPUT_PATH: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
debug EXTRA_ARGS_COUNT: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXTRA_ARGS_COUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
debug EXTRA_ARGS: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXTRA_ARGS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

run octo pack &lt;span class="nt"&gt;--id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FORMAT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--basePath&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--outFolder&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXTRA_ARGS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;OCTO_PACK_FILENAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ID&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$FORMAT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  success &lt;span class="s2"&gt;"Packaging successful. Created package &lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_PATH&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$OCTO_PACK_FILENAME&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;

&lt;span class="k"&gt;else
  &lt;/span&gt;fail &lt;span class="s2"&gt;"Packaging failed."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Pipe Dockerfile &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now that we have our main script to run, we need to create our image using a Dockerfile. If you ran the Pipe generator, you already have the Dockerfile ready to edit to suit your Pipe.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;pack&lt;/code&gt; Pipe, the Dockerfile looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; octopusdeploy/octo:7.3.2&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--update&lt;/span&gt; &lt;span class="nt"&gt;--no-cache&lt;/span&gt; bash

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pipe /&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;a+x /&lt;span class="k"&gt;*&lt;/span&gt;.sh

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/pipe.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dockerfile takes the &lt;code&gt;octopusdeploy/octo&lt;/code&gt; as its base, and then adds &lt;code&gt;bash&lt;/code&gt; to the image. It then copies the contents of the &lt;code&gt;pipe&lt;/code&gt; folder and grants permissions for all users to execute the &lt;code&gt;.sh&lt;/code&gt; files present. Lastly, it sets the &lt;code&gt;ENTRYPOINT&lt;/code&gt; for the container to the pipe.sh file we created earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Pipe’s own pipeline &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When you’ve completed your Pipe, you could deploy your Docker image manually to Docker Hub. However, it’s also possible to get Bitbucket Pipelines to do the heavy lifting for you automatically when you push changes to your Bitbucket repository with your own &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;pack&lt;/code&gt; Pipe, in the auto-generated file, I only modified the push step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;push&lt;/span&gt;
  &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push and Tag&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:3.7&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install semversioner==0.7.0&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chmod a+x ./ci-scripts/*.sh&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./ci-scripts/bump-version.sh&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./ci-scripts/docker-release.sh octopipes/$BITBUCKET_REPO_SLUG&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./ci-scripts/git-push.sh&lt;/span&gt;
    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The step installs &lt;a href="https://pypi.org/project/semversioner/" rel="noopener noreferrer"&gt;semversioner&lt;/a&gt;, which is a python tool to help automatically generate release notes and version your Pipe according to &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;SemVer&lt;/a&gt;. After this, it increments the version of the Pipe, creates a new Docker image, and pushes it to Docker Hub. Finally, it tags the new version and pushes that back to the Bitbucket repository.&lt;/p&gt;

&lt;p&gt;You can view the complete &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file for the &lt;code&gt;pack&lt;/code&gt; Pipe on &lt;a href="https://bitbucket.org/octopusdeploy/pack/src/master/bitbucket-pipelines.yml" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No double Docker push:&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;push&lt;/code&gt; step doesn’t trigger two Docker images to be pushed to Docker Hub when the step is doing its own commit and push back to the Bitbucket repository. The reason for this is that Bitbucket Pipelines support the option to skip a Pipeline run if &lt;code&gt;[skip ci]&lt;/code&gt; or &lt;code&gt;[ci skip]&lt;/code&gt; is included anywhere in the commit message.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create the Pipe README &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;You might be thinking, "Why spend time creating a readme file?" Well, Atlassian themselves recommend it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your readme is how your users know how to use your pipe. We can display this in Bitbucket, so it needs to be written with markdown, in a specific format.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Including an informative &lt;code&gt;README&lt;/code&gt; increases the chances of your users being successful with your Pipe.&lt;/p&gt;

&lt;p&gt;One of the more important parts of the &lt;code&gt;README&lt;/code&gt; is the &lt;strong&gt;YAML Definition&lt;/strong&gt;. This tells users what to add to their &lt;code&gt;bitbucket-pipeline.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Here’s what the &lt;code&gt;pack&lt;/code&gt; one looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopipes/pack:0.6.0&lt;/span&gt;
    &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;"&lt;/span&gt;
      &lt;span class="na"&gt;FORMAT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;"&lt;/span&gt;
      &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;"&lt;/span&gt;
      &lt;span class="na"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;"&lt;/span&gt;
      &lt;span class="na"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;string&amp;gt;"&lt;/span&gt;
      &lt;span class="c1"&gt;# EXTRA_ARGS: "['&amp;lt;string&amp;gt;','&amp;lt;string&amp;gt;' ..]" # Optional&lt;/span&gt;
      &lt;span class="c1"&gt;# DEBUG: "&amp;lt;boolean&amp;gt;" # Optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can view the &lt;code&gt;README&lt;/code&gt; for the &lt;code&gt;octopipes/pack&lt;/code&gt; in full &lt;a href="https://bitbucket.org/octopusdeploy/pack/src/master/README.md" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Since the Pipe is just a Docker image, after you have built the image, you can execute the Pipe using &lt;code&gt;docker run&lt;/code&gt;, passing in any required parameters as environment variables.&lt;/p&gt;

&lt;p&gt;Here’s what the command looks like to run the &lt;code&gt;pack&lt;/code&gt; Pipe to package the root directory from our &lt;code&gt;RandomQuotes-JS&lt;/code&gt; application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"randomquotes-js"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Zip"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.0.0.0"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./out"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 octopipes/pack:0.6.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output shows the successful packaging of the &lt;code&gt;randomquotes-js.1.0.0.0.zip&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcgkwae0l1pnl1cettwqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcgkwae0l1pnl1cettwqk.png" alt="docker run octopipes pack" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To make sure your Pipe does what you expect it to, it’s a good idea to write tests for it. Following Atlassian’s lead, I opted to use &lt;a href="https://www.systutorials.com/docs/linux/man/1-bats/" rel="noopener noreferrer"&gt;BATS&lt;/a&gt; (Bash Automated Testing System).&lt;/p&gt;

&lt;p&gt;Just like a lot of testing frameworks, a test file (which typically ends in &lt;code&gt;.bats&lt;/code&gt;) contains the following constructs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;setup&lt;/code&gt; method to set up any required variables or shared resources for your tests.&lt;/li&gt;
&lt;li&gt;A number of individual &lt;code&gt;@test&lt;/code&gt; declarations; these are your test cases.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;teardown&lt;/code&gt; method to remove any resources you used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is my &lt;a href="https://bitbucket.org/octopusdeploy/pack/src/master/test/test.bats" rel="noopener noreferrer"&gt;test.bats&lt;/a&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bats&lt;/span&gt;

setup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;DOCKER_IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_IMAGE&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"test/pack"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building image..."&lt;/span&gt;
  run docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_IMAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:test &lt;span class="nb"&gt;.&lt;/span&gt;

  &lt;span class="c"&gt;# generated&lt;/span&gt;
  &lt;span class="nv"&gt;RANDOM_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$RANDOM&lt;/span&gt;

  &lt;span class="c"&gt;# locals&lt;/span&gt;
  &lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"MyCompany.MyApp"&lt;/span&gt;
  &lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"zip"&lt;/span&gt;
  &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.0.0.0"&lt;/span&gt;
  &lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"test/code"&lt;/span&gt;
  &lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"test/out"&lt;/span&gt;

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FORMAT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nv"&gt;EXPECTED_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ID&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$FORMAT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Create test output dir&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/out
  &lt;span class="nb"&gt;mkdir test&lt;/span&gt;/out/extract &lt;span class="nt"&gt;-p&lt;/span&gt;

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Create file with random content"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$RANDOM_NUMBER&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/code/test-content.txt
&lt;span class="o"&gt;}&lt;/span&gt;

teardown&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning up files"&lt;/span&gt;
  &lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; a+rwx &lt;span class="nb"&gt;test&lt;/span&gt;/out/
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/out
&lt;span class="o"&gt;}&lt;/span&gt;

@test &lt;span class="s2"&gt;"Create Zip package using Octo pack command"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Run test"&lt;/span&gt;
    run docker run &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FORMAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_IMAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:test

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Status: &lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Output: &lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;

    &lt;span class="c"&gt;# Verify&lt;/span&gt;
    unzip &lt;span class="s2"&gt;"test/out/&lt;/span&gt;&lt;span class="nv"&gt;$EXPECTED_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/out/extract
    run &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"test/out/extract/test-content.txt"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Output: &lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RANDOM_NUMBER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my &lt;code&gt;test.bats&lt;/code&gt; file, the &lt;code&gt;setup&lt;/code&gt; builds a local Docker image of the Pipe called &lt;code&gt;test/pack&lt;/code&gt;. Next, it creates a file with a random number for its contents.&lt;/p&gt;

&lt;p&gt;My &lt;code&gt;test&lt;/code&gt; then executes &lt;code&gt;docker run&lt;/code&gt; (just as I did in my local test) and verifies the container ran to completion, and finally extracts the files from the package and checks the content matches the random number I generated in the &lt;code&gt;setup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tests run as part of my automated CI/CD pipeline configured in my &lt;code&gt;bitbucket-pipeline.yml&lt;/code&gt;. You can also run the tests locally if you have &lt;code&gt;bats&lt;/code&gt; installed.&lt;/p&gt;

&lt;p&gt;Here is the output from a test run of my &lt;code&gt;test.bats&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq36fk91568bhbdrtd2jz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq36fk91568bhbdrtd2jz.png" alt="bats test run output" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s all there is to testing your Pipe!&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish the Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;When you are happy your Pipe is working, you can publish your Docker image directly by running &lt;code&gt;docker push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have set up an automated pipeline like we did earlier, then you can make use of &lt;code&gt;semversioner&lt;/code&gt; to create a changeset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;semversioner add-change &lt;span class="nt"&gt;--type&lt;/span&gt; patch &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Initial Docker release"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you have committed your changeset, push them to Bitbucket and your pipeline should handle the rest. It will also automatically update the version number in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;CHANGELOG.md&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;README.md&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The metadata &lt;code&gt;pipe.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see an example of the &lt;code&gt;pack&lt;/code&gt; Bitbucket pipeline creating the &lt;code&gt;0.5.1&lt;/code&gt; release here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbmr7tgcc783bmovex82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbmr7tgcc783bmovex82.png" alt="Bitbucket pipe pipeline result" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating the Pipe into a Pipeline &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If you’ve got this far, you have your Pipe published to Docker Hub, but how do you integrate that Pipe in another repository’s Pipeline?&lt;/p&gt;

&lt;p&gt;To find out, I imported one of our existing node.js samples &lt;code&gt;RandomQuotes-JS&lt;/code&gt;, which is hosted on &lt;a href="https://github.com/OctopusSamples/RandomQuotes-js" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, into a new Bitbucket repository with the same &lt;a href="https://bitbucket.org/octopussamples/randomquotes-js/" rel="noopener noreferrer"&gt;name&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the Pipe &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Next, I created a &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file and set up my Pipeline. After the build and testing step, I inserted a package step like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pack for Octopus&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export VERSION=1.0.0.$BITBUCKET_BUILD_NUMBER&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/pack:0.6.0&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${BITBUCKET_REPO_SLUG}&lt;/span&gt;
          &lt;span class="na"&gt;FORMAT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Zip'&lt;/span&gt;
          &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${VERSION}&lt;/span&gt;
          &lt;span class="na"&gt;SOURCE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.'&lt;/span&gt;
          &lt;span class="na"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./out'&lt;/span&gt;
          &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;false'&lt;/span&gt;
    &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;out/*.zip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The step looks similar to the examples in the Pipe’s &lt;code&gt;README&lt;/code&gt; file. I’ve added a line just before the &lt;code&gt;pipe&lt;/code&gt; instruction to set a variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export VERSION=1.0.0.$BITBUCKET_BUILD_NUMBER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;export&lt;/code&gt; command tells Bitbucket to create the &lt;code&gt;VERSION&lt;/code&gt; variable and use it in the Pipe.&lt;/p&gt;

&lt;p&gt;I also make use of Bitbucket &lt;a href="https://confluence.atlassian.com/bitbucket/using-artifacts-in-steps-935389074.html" rel="noopener noreferrer"&gt;artifacts&lt;/a&gt;, where I specify a glob pattern to choose any zip files present in the &lt;code&gt;out&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;out/*.zip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the package created by the Pipe to be used by future steps in the Pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate the Pipeline with Octopus &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;After I have created a package, I want to complete the Bitbucket Pipeline by integrating Bitbucket with Octopus; specifically, I want to push the package I created and the related commit information to Octopus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Push the package to Octopus &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;After the packaging step I’d created earlier, I added another step to push the package to the Octopus &lt;a href="https://octopus.com/docs/packaging-applications/package-repositories/built-in-repository" rel="noopener noreferrer"&gt;built-in repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This step makes use of a feature in Bitbucket that allows you to specify a container image, which can be different to the default image used elsewhere in the Pipeline. In this case, I chose the &lt;code&gt;octopusdeploy/octo:7.3.2&lt;/code&gt; Docker image.&lt;/p&gt;

&lt;p&gt;This means I can run the &lt;code&gt;octo push&lt;/code&gt; command and specify the package I created in the previous &lt;code&gt;Pack for Octopus&lt;/code&gt; step like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;octo push --package ./out/$BITBUCKET_REPO_SLUG.$VERSION.zip  --server $OCTOPUS_SERVER --space $OCTOPUS_SPACE --apiKey $OCTOPUS_APIKEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the minimum YAML required to achieve the push to Octopus below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push to Octopus&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octo:7.3.2&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export VERSION=1.0.0.$BITBUCKET_BUILD_NUMBER&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;octo push --package ./out/$BITBUCKET_REPO_SLUG.$VERSION.zip  --server $OCTOPUS_SERVER --space $OCTOPUS_SPACE --apiKey $OCTOPUS_APIKEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Push build information to Octopus &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;To round off the integration, I want to have the &lt;a href="https://octopus.com/docs/packaging-applications/build-servers#build-information" rel="noopener noreferrer"&gt;build information&lt;/a&gt; available within Octopus.&lt;/p&gt;

&lt;p&gt;For me, one of the best things about Octopus is that it’s built &lt;a href="https://octopus.com/docs/octopus-concepts/rest-api" rel="noopener noreferrer"&gt;API-first&lt;/a&gt;. This allows us to build a first-class CLI on top of it. Pushing build information turned out to be pretty easy. Once I knew the format of the JSON payload, I created a &lt;a href="https://bitbucket.org/octopussamples/randomquotes-js/src/master/create-build-info.sh" rel="noopener noreferrer"&gt;Bash script&lt;/a&gt; to do just that using the &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/build-information" rel="noopener noreferrer"&gt;build-information&lt;/a&gt; command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip: Build information payload&lt;/strong&gt;&lt;br&gt;
To help demystify some of the complexities of the build information payload, I followed my colleague Shawn’s excellent &lt;a href="https://octopus.com/blog/manually-push-build-information-to-octopus" rel="noopener noreferrer"&gt;piece&lt;/a&gt; on its structure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To add to our previous &lt;code&gt;Push to Octopus&lt;/code&gt; step, we can include that script to push build information as well. The complete step looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push to Octopus&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octopusdeploy/octo:7.3.2&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apk update &amp;amp;&amp;amp; apk upgrade &amp;amp;&amp;amp; apk add --no-cache git curl jq&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export VERSION=1.0.0.$BITBUCKET_BUILD_NUMBER&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;octo push --package ./out/$BITBUCKET_REPO_SLUG.$VERSION.zip  --server $OCTOPUS_SERVER --space $OCTOPUS_SPACE --apiKey $OCTOPUS_APIKEY&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/bin/sh create-build-info.sh $BITBUCKET_REPO_OWNER $BITBUCKET_REPO_SLUG $BITBUCKET_BUILD_NUMBER $BITBUCKET_COMMIT $BITBUCKET_BRANCH $BITBUCKET_GIT_HTTP_ORIGIN&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;octo build-information --package-id $BITBUCKET_REPO_SLUG --version $VERSION --file=octopus.buildinfo --server $OCTOPUS_SERVER --space $OCTOPUS_SPACE --apiKey $OCTOPUS_APIKEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;create-build-info.sh&lt;/code&gt; script creates a JSON payload output file called &lt;code&gt;octopus.buildinfo&lt;/code&gt;, and then we use that in the &lt;code&gt;build-information&lt;/code&gt; command to push commit information.&lt;/p&gt;

&lt;p&gt;After the commits have been pushed to Octopus, you can see them in the Packages section of the Library:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n1ictd086x45vnfqz5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n1ictd086x45vnfqz5g.png" alt="randomquotes-js buildinfor" width="754" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it!&lt;/p&gt;

&lt;p&gt;You can view the complete &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; file on &lt;a href="https://bitbucket.org/octopussamples/randomquotes-js/src/master/bitbucket-pipelines.yml" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sample Octopus project&lt;/strong&gt;&lt;br&gt;
You can view the RandomQuotes-JS Octopus project setup in our &lt;a href="https://samples.octopus.app/app#/Spaces-104/projects/randomquotes-js/" rel="noopener noreferrer"&gt;samples&lt;/a&gt; instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Creating my first Bitbucket Pipe was pretty straightforward. I can definitely see the advantages of creating a Pipe in Bitbucket. That said, I found that when making changes to the pipe, the feedback cycle took a while to get right. My advice is to write only the minimum functionality you require, and then build on that over time. Integrating a Bitbucket Pipeline with Octopus is a breeze with the Octopus CLI, and for anything more complex, you always have the API at your disposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Take a peek at the &lt;em&gt;experimental&lt;/em&gt; Pipe - &lt;a href="https://bitbucket.org/octopusdeploy/pack/src/master/README.md" rel="noopener noreferrer"&gt;pack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guides: &lt;a href="https://octopus.com/docs/guides" rel="noopener noreferrer"&gt;Octopus CI/CD pipeline guides&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/bitbucket-pipes-and-octopus-deploy" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>devops</category>
      <category>bitbucket</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Farmer: Simpler ARM deployments with Octopus Deploy</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Tue, 21 Jul 2020 08:32:51 +0000</pubDate>
      <link>https://dev.to/octopus/farmer-simpler-arm-deployments-with-octopus-deploy-2ea1</link>
      <guid>https://dev.to/octopus/farmer-simpler-arm-deployments-with-octopus-deploy-2ea1</guid>
      <description>&lt;p&gt;Having worked with Azure for around a year, it’s clear to see why &lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/overview" rel="noopener noreferrer"&gt;ARM templates&lt;/a&gt; are popular. They provide a declarative model to generate entire environments at the touch of a button. &lt;/p&gt;

&lt;p&gt;However, if like me, you’ve ever tried to author an ARM template file, you might have come across one of my biggest gripes with them; they rely on strings and are prone to human error. There’s no compiler to help me out when I have a typo in a template (and there have been plenty of those!).&lt;/p&gt;

&lt;p&gt;I’ve used C# as my primary development language since 2012, however, since then, its functional counterpart, F# has become increasingly popular. As I found out recently, it has some useful features that can help out with my ARM template dilemma. One area in particular where F# excels is its built-in &lt;a href="https://fsharpforfunandprofit.com/posts/correctness-type-checking/" rel="noopener noreferrer"&gt;type-safety&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I’ll demonstrate the type-safety in F# in action by using &lt;a href="https://compositionalit.github.io/farmer/" rel="noopener noreferrer"&gt;Farmer&lt;/a&gt; to generate a simple Azure WebApp ARM template, and then I’ll walk through how you can use its deployment capabilities through Octopus to deploy different WebApps to Azure directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this post
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is Farmer?&lt;/li&gt;
&lt;li&gt;Why is Farmer needed?&lt;/li&gt;
&lt;li&gt;Create the Farmer template&lt;/li&gt;
&lt;li&gt;Package the Farmer template&lt;/li&gt;
&lt;li&gt;Deploy the Farmer template&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Farmer? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The authors of Farmer &lt;a href="https://compositionalit.github.io/farmer/about/" rel="noopener noreferrer"&gt;says&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Farmer is an open source, free to use .NET domain-specific-language (DSL) for rapidly generating non-complex Azure Resource Manager (ARM) templates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To use Farmer, you create a &lt;a href="https://compositionalit.github.io/farmer/quickstarts/template/" rel="noopener noreferrer"&gt;Farmer template&lt;/a&gt;. These are .NET Core applications that reference Farmer via a &lt;a href="https://www.nuget.org/packages/Farmer/" rel="noopener noreferrer"&gt;NuGet package&lt;/a&gt;, and they define your Azure resources that you wish to create.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Farmer needed? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Rather than repeating what is already there, I encourage you to read the &lt;a href="https://compositionalit.github.io/farmer/about/" rel="noopener noreferrer"&gt;About section&lt;/a&gt; of the Farmer documentation for more details about the motivations to create a DSL for ARM templates.&lt;/p&gt;

&lt;p&gt;For me, the highlights are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It provides a set of types that you can use to create Azure resources, and it eliminates the chances of creating an invalid template as they are strongly-typed.&lt;/li&gt;
&lt;li&gt;It can generate simple ARM templates in a very concise manner and optionally, deploy them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create the Farmer template &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To create a Farmer template, we first need to create a .NET Core application. You can do this in your IDE of choice, or if you prefer the command-line, you can use the &lt;code&gt;dotnet new&lt;/code&gt; command, passing the template of the type of application you require. &lt;/p&gt;

&lt;p&gt;It’s typical to use a console application for a Farmer template, and you can create one with the &lt;code&gt;dotnet new console&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-lang&lt;/span&gt; &lt;span class="s2"&gt;"F#"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"netcoreapp3.1"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"SimpleAzureWebApp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new F# .NET Core 3.1 application with the name &lt;strong&gt;SimpleAzureWebApp&lt;/strong&gt;, using the &lt;code&gt;-n&lt;/code&gt; parameter we supplied.&lt;/p&gt;

&lt;p&gt;Next, we need to add Farmer to the project by running the &lt;code&gt;add package&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Farmer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have our dependencies, we can go ahead and edit the &lt;code&gt;Program.fs&lt;/code&gt; file which was auto-generated for us when we created the new console application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to see the complete program, skip straight to the end or view the &lt;a href="https://github.com/OctopusSamples/farmertemplates/blob/main/src/SimpleAzureWebApp/Program.fs" rel="noopener noreferrer"&gt;source code&lt;/a&gt;. If you’d like more details, read on!&lt;/p&gt;

&lt;h3&gt;
  
  
  Template parameters
&lt;/h3&gt;

&lt;p&gt;To make the Farmer template flexible, we’ll add some parameters to the application. This will allow us to supply different values and Farmer will create our resources in Azure based on those values.&lt;/p&gt;

&lt;p&gt;The first three we need are related to authenticating with Azure. These values can be obtained by creating an &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals" rel="noopener noreferrer"&gt;Azure Service Principal&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AppID&lt;/strong&gt;: The application identifier used for the Service Principal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret&lt;/strong&gt;: The password used for the Service Principal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TenantID&lt;/strong&gt;: The ClientID used for the Service Principal.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Securing credentials:&lt;/strong&gt;&lt;br&gt;
Store the credentials you use to log in to Azure in a secure location, such as a Password Manager, or your Octopus Deploy instance using, preferably, an &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets/azure#azure-service-principal" rel="noopener noreferrer"&gt;Azure account&lt;/a&gt; or &lt;a href="https://octopus.com/docs/projects/variables/sensitive-variables" rel="noopener noreferrer"&gt;sensitive variables&lt;/a&gt;. You should also avoid committing them into source control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To run the application, we’ll also supply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource Group Name&lt;/strong&gt;: Which resource group to add the Azure WebApp to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebApp Name&lt;/strong&gt;: The name to give to the Azure WebApp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebApp SKU&lt;/strong&gt;: What type of &lt;a href="https://azure.microsoft.com/en-us/pricing/details/app-service/" rel="noopener noreferrer"&gt;App Service plan&lt;/a&gt; to use for the WebApp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebApp Location&lt;/strong&gt;: The data center location where you’d like to host the Azure WebApp.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To add our required parameters, the code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azAppId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azSecret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azTenantId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azResourceGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azWebAppName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azWebAppSku&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azWebAppLocation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assigns the parameters from the argument collection supplied to the program when it runs, based on their position from the command-line.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Parameter validation:&lt;/strong&gt;&lt;br&gt;
I don’t show parameter validation in this example, but you may want to consider adding it to your Farmer template to ensure they have acceptable values.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Define Azure Resources
&lt;/h3&gt;

&lt;p&gt;After we have our parameter values, we can define our Azure WebApp in F#:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;webAppSku&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;WebApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Sku&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;azWebAppSku&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;webApp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;azWebAppName&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="n"&gt;webAppSku&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we assign the WebApp SKU to a variable named &lt;code&gt;webAppSku&lt;/code&gt;. This is done by a helper function to return a strongly typed &lt;code&gt;Sku&lt;/code&gt;. Then we create our &lt;code&gt;webApp&lt;/code&gt; variable using the Farmer &lt;a href="https://compositionalit.github.io/farmer/api-overview/resources/web-app/" rel="noopener noreferrer"&gt;Web App builder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, we create our ARM deployment using the Farmer &lt;a href="https://compositionalit.github.io/farmer/api-overview/resources/arm/" rel="noopener noreferrer"&gt;ARM deployment builder&lt;/a&gt;, which in this example, consists of the location to deploy to, and the Azure WebApp as previously defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;deployLocation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;azWebAppLocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;deployment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="n"&gt;deployLocation&lt;/span&gt;
    &lt;span class="n"&gt;add_resource&lt;/span&gt; &lt;span class="n"&gt;webApp&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Built-in type safety
&lt;/h4&gt;

&lt;p&gt;In both of the previous code examples, the power of the F# type-system comes into its own. It’s not possible to create a value that is invalid according to its type.&lt;/p&gt;

&lt;p&gt;Let’s see an example. Suppose I wanted to create our Azure WebApp with a &lt;code&gt;Sku&lt;/code&gt; which had a value of &lt;code&gt;VeryFree&lt;/code&gt;. If I try to create that in our application, the compiler will give me a warning, and it won’t build:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4h7l0saykuhdmx9s965.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4h7l0saykuhdmx9s965.png" alt="Farmer Compiler warning" width="525" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because the compiler knows the string value &lt;code&gt;VeryFree&lt;/code&gt; is the wrong type, and instead should be a &lt;code&gt;Sku&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;This is where Farmer really excels over crafting your own ARM template by hand. Its use of F# provides you with type safety to ensure you have valid templates from the outset.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Farmer and ARM:&lt;/strong&gt;&lt;br&gt;
There is a more detailed comparison between Farmer and ARM templates &lt;a href="https://compositionalit.github.io/farmer/arm-vs-farmer/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Generate ARM template
&lt;/h3&gt;

&lt;p&gt;When you have your Azure resources modeled, Farmer supports different ways to &lt;a href="https://compositionalit.github.io/farmer/api-overview/template-generation/" rel="noopener noreferrer"&gt;generate the ARM template&lt;/a&gt;. One way is to write it out to a file directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="n"&gt;deployment&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quickWrite&lt;/span&gt; &lt;span class="s2"&gt;"output"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then take this file and deploy to Azure using your preferred method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment to Azure
&lt;/h3&gt;

&lt;p&gt;In addition to generating the ARM template, you can also, optionally, have Farmer execute the deployment to Azure when the application runs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Azure CLI required&lt;/strong&gt;&lt;br&gt;
If you use the Integrated deployment to Azure feature, you will need the Azure CLI installed on the machine where you run your application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our example &lt;strong&gt;SimpleAzureWebApp&lt;/strong&gt; application, we’ll take advantage of this feature. &lt;/p&gt;

&lt;p&gt;Before we can execute the deployment, we need to authenticate with Azure. Farmer comes with a &lt;code&gt;Deploy.authenticate&lt;/code&gt; command, and you call it by passing in the credentials you supplied to the application previously, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="nn"&gt;Deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt; &lt;span class="n"&gt;azAppId&lt;/span&gt; &lt;span class="n"&gt;azSecret&lt;/span&gt; &lt;span class="n"&gt;azTenantId&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the authenticate call finishes, it returns a list of the Azure subscriptions associated with the Service Principal. In this example, these results are piped to the &lt;code&gt;ignore&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;If there are any errors authenticating with Azure, an error will be raised. If the login succeeds, we then need to get Farmer to execute our deployment, using the &lt;code&gt;Deploy.execute&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;azResourceGroupName&lt;/span&gt; &lt;span class="nn"&gt;Deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NoParameters&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can query the results of the ARM deployment, but as with the authenticate call, we ignore them. Similarly, any errors on deployment will be surfaced as an exception. &lt;/p&gt;

&lt;h3&gt;
  
  
  Complete Farmer template
&lt;/h3&gt;

&lt;p&gt;And that’s all there is to our application. Here is the finished &lt;code&gt;Program.fs&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;Farmer&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Farmer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builders&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;SimpleAzureWebApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SkuExtension&lt;/span&gt;

&lt;span class="p"&gt;[&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EntryPoint&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azAppId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azSecret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azTenantId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azResourceGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azWebAppName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azWebAppSku&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;azWebAppLocation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;webAppSku&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;WebApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Sku&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;azWebAppSku&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;webApp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;azWebAppName&lt;/span&gt;
        &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="n"&gt;webAppSku&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;deployLocation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;azWebAppLocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;deployment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="n"&gt;deployLocation&lt;/span&gt;
        &lt;span class="n"&gt;add_resource&lt;/span&gt; &lt;span class="n"&gt;webApp&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Authenticating with Azure&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nn"&gt;Deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt; &lt;span class="n"&gt;azAppId&lt;/span&gt; &lt;span class="n"&gt;azSecret&lt;/span&gt; &lt;span class="n"&gt;azTenantId&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Deploying Azure WebApp %s (%s) into %s using Farmer&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="n"&gt;azWebAppName&lt;/span&gt; &lt;span class="n"&gt;azResourceGroupName&lt;/span&gt; &lt;span class="n"&gt;azWebAppLocation&lt;/span&gt;

    &lt;span class="n"&gt;deployment&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;azResourceGroupName&lt;/span&gt; &lt;span class="nn"&gt;Deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NoParameters&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;

    &lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Deployment of Azure WebApp %s (%s) complete!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="n"&gt;azWebAppName&lt;/span&gt; &lt;span class="n"&gt;azResourceGroupName&lt;/span&gt;

    &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;// return an integer exit code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Package the Farmer template &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now we have the application written, the next step is to package it for use with Octopus. For sake of simplicity, I build and package the application using command-line tools, but I recommended automating this as part of a full CI/CD pipeline.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are new to building .NET Core applications, we have a number of &lt;a href="https://octopus.com/docs/guides?application=ASP.NET%20Core" rel="noopener noreferrer"&gt;guides&lt;/a&gt; that include step-by-step instructions to setup a CI/CD pipeline using various tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To build the &lt;strong&gt;SimpleAzureWebApp&lt;/strong&gt; application, we run a &lt;code&gt;dotnet publish&lt;/code&gt; command in the application directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet publish &lt;span class="nt"&gt;-o&lt;/span&gt; output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This builds and publishes the console application and places the binaries in the &lt;code&gt;output&lt;/code&gt; folder, as specified by the use of the &lt;code&gt;-o&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Next, we need to package the application, and this time we use the Octopus CLI &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/pack" rel="noopener noreferrer"&gt;pack&lt;/a&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;octo pack &lt;span class="nt"&gt;--id&lt;/span&gt; SimpleAzureWebApp &lt;span class="nt"&gt;--format&lt;/span&gt; Zip &lt;span class="nt"&gt;--version&lt;/span&gt; 1.0.0.0 &lt;span class="nt"&gt;--basePath&lt;/span&gt; output 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a file named &lt;code&gt;SimpleAzureWebApp.1.0.0.0.zip&lt;/code&gt; which can either be uploaded to the Octopus &lt;a href="https://octopus.com/docs/packaging-applications/package-repositories/built-in-repository" rel="noopener noreferrer"&gt;built-in repository&lt;/a&gt; or an external &lt;a href="https://octopus.com/docs/packaging-applications/package-repositories" rel="noopener noreferrer"&gt;package repository&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can push to the Octopus built-in repository using the Octopus CLI command, &lt;a href="https://octopus.com/docs/octopus-rest-api/octopus-cli/push" rel="noopener noreferrer"&gt;push&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;octo push &lt;span class="nt"&gt;--package&lt;/span&gt; SimpleAzureWebApp.1.0.0.0.zip &lt;span class="nt"&gt;--server&lt;/span&gt; https://my.octopus.url &lt;span class="nt"&gt;--apiKey&lt;/span&gt; API-XXXXXXXXXXXXXXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the package has been uploaded, we can set-up Octopus to run our application to deploy to Azure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy the Farmer template &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;One of the cool things about Octopus is that you get to choose how to deploy. With the introduction of &lt;a href="https://octopus.com/docs/operations-runbooks" rel="noopener noreferrer"&gt;Operations Runbooks&lt;/a&gt; last year, that flexibility has been extended even further to operations tasks, for example managing your infrastructure. &lt;/p&gt;

&lt;h3&gt;
  
  
  Create the runbook
&lt;/h3&gt;

&lt;p&gt;To execute our Farmer template, we’ll create a runbook that deploys it to Azure. To do that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new project in Octopus.&lt;/li&gt;
&lt;li&gt;Go to the runbook process from the Operations ➜ Runbooks section. &lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;ADD RUNBOOK&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;From the Overview, Click &lt;strong&gt;DEFINE YOUR RUNBOOK PROCESS&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;ADD STEP&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the step selection, choose the &lt;a href="https://octopus.com/docs/deployment-examples/custom-scripts/run-a-script-step" rel="noopener noreferrer"&gt;Run a script&lt;/a&gt; step and give it a name. By using the script step, we can use the &lt;a href="https://octopus.com/docs/deployment-examples/custom-scripts/run-a-script-step#referencing-packages" rel="noopener noreferrer"&gt;reference package&lt;/a&gt; feature to include our package as part of the script execution. &lt;/p&gt;

&lt;p&gt;To include our package, in the &lt;strong&gt;Referenced Packages&lt;/strong&gt; section, click &lt;strong&gt;ADD&lt;/strong&gt; and add the SimpleAzureWebApp package we uploaded earlier:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqy02wpmcs40yqv2o7al.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqy02wpmcs40yqv2o7al.png" alt="Add SimpleAzureWebApp Package Reference" width="604" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep all of the defaults and click &lt;strong&gt;OK&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add the runbook script
&lt;/h4&gt;

&lt;p&gt;Next, we need to add the inline script which will execute our Farmer template. We start by adding the required Azure credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$appId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Project.Azure.Account.Client"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Project.Azure.Account.Password"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Project.Azure.Account.TenantId"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script references a number of expanded &lt;a href="https://octopus.com/docs/projects/variables/azure-account-variables#azure-account-variable-properties" rel="noopener noreferrer"&gt;Azure account variable properties&lt;/a&gt; such as &lt;code&gt;Client&lt;/code&gt; and &lt;code&gt;TenantId&lt;/code&gt; from the project variable named &lt;code&gt;Project.Azure.Account&lt;/code&gt;. This is handy as we don’t need to specify separate variables for each property.&lt;/p&gt;

&lt;p&gt;After we have the credentials, we want to specify the Azure WebApp parameters, including the Resource Group and WebApp name that will be passed to our SimpleAzureWebApp .NET Core application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$resourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Project.Azure.ResourceGroupName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$webAppName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Project.Azure.WebAppName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$webAppSku&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Project.Azure.WebAppSku"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$webAppLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Project.Azure.WebAppLocation"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we get the path of the extracted Farmer template package using a &lt;a href="https://octopus.com/docs/deployment-examples/custom-scripts/run-a-script-step#accessing-package-references-from-a-custom-script" rel="noopener noreferrer"&gt;package variable&lt;/a&gt; called &lt;code&gt;Octopus.Action.Package[SimpleAzureWebApp].ExtractedPath&lt;/code&gt; and then set the working directory to that path and call the &lt;code&gt;dotnet run&lt;/code&gt; command passing in all of our parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$farmerPackagePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Octopus.Action.Package[SimpleAzureWebApp].ExtractedPath"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$farmerPackagePath&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SimpleAzureWebApp.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$appId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$resourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$webAppName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$webAppSku&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$webAppLocation&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;.NET Core runtime pre-requisite&lt;/strong&gt;&lt;br&gt;
In order for this script step to execute, it requires the .NET Core runtime to be installed on the deployment target or worker where the step is configured to execute.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Add the variables
&lt;/h4&gt;

&lt;p&gt;We also need to add the variables referenced in the script above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39cian15nmkmj9hguivg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39cian15nmkmj9hguivg.png" alt="Project variables" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Project.Azure.Account&lt;/code&gt; variable is an &lt;a href="https://octopus.com/docs/projects/variables/azure-account-variables" rel="noopener noreferrer"&gt;Azure account variable&lt;/a&gt;, and the rest are text variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the runbook
&lt;/h3&gt;

&lt;p&gt;If you’ve got this far, the last part is to bring it all together and run our runbook in Octopus and deploy the Farmer template to Azure.&lt;/p&gt;

&lt;p&gt;You can see an example runbook run to development creating the Azure WebApp called &lt;code&gt;farmer-webapp-dev&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2204drty43n6ci8pezjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2204drty43n6ci8pezjy.png" alt="Farmer Azure Runbook run" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the runbook has run to completion, you can check your WebApp has been created using the &lt;a href="https://portal.azure.com" rel="noopener noreferrer"&gt;Azure portal&lt;/a&gt;. Here is the corresponding WebApp that was created in Azure as a result of the runbook run to development:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9f5g1e3c0l98x09j9ez2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9f5g1e3c0l98x09j9ez2.png" alt="Azure portal Farmer webapp" width="722" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The great thing about this technique of using Farmer to generate and deploy your resources to Azure is that you can version control your templates. The code that defines your infrastructure can live alongside the code that runs on it. Plus, no more hassle manually editing JSON files, and who doesn’t want that! &lt;/p&gt;

&lt;p&gt;Until next time, Happy Deployments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://octopus.com/docs/deployment-examples/azure-deployments" rel="noopener noreferrer"&gt;Octopus Azure deployment examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://octopus.com/docs/guides" rel="noopener noreferrer"&gt;Octopus CI/CD guides&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/farmer-and-octopus-deploy" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>devops</category>
      <category>deployment</category>
      <category>azure</category>
    </item>
    <item>
      <title>Convert an existing application to use rolling deployments</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Mon, 20 Jul 2020 08:29:56 +0000</pubDate>
      <link>https://dev.to/octopus/convert-an-existing-application-to-use-rolling-deployments-1438</link>
      <guid>https://dev.to/octopus/convert-an-existing-application-to-use-rolling-deployments-1438</guid>
      <description>&lt;p&gt;In a previous post, I wrote about the &lt;a href="https://octopus.com/blog/ultimate-guide-to-rolling-deployments" rel="noopener noreferrer"&gt;benefits of the rolling deployments pattern&lt;/a&gt; as a way to reduce application downtime at deployment time. Designing an application to fit this deployment pattern is arguably much easier when you’re first creating it, but where do you start with an existing application, and how do you take the application and convert it to use the rolling deployments pattern?&lt;/p&gt;

&lt;p&gt;In this post, I show you how to convert an existing application to use the rolling deployments pattern in Octopus with the help of &lt;a href="https://octopus.com/docs/deployment-patterns/rolling-deployments#Rollingdeployments-Childsteps" rel="noopener noreferrer"&gt;child steps&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this post
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The application&lt;/li&gt;
&lt;li&gt;Sequential deployment process&lt;/li&gt;
&lt;li&gt;
Convert to a rolling deployment process

&lt;ul&gt;
&lt;li&gt;Scale up the servers&lt;/li&gt;
&lt;li&gt;Choosing a load balancer&lt;/li&gt;
&lt;li&gt;Load balancer target pools&lt;/li&gt;
&lt;li&gt;Create a new project&lt;/li&gt;
&lt;li&gt;Convert the PetClinic deployment process&lt;/li&gt;
&lt;li&gt;Switch over to new infrastructure&lt;/li&gt;
&lt;li&gt;Clean-up&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The application &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I am going to use &lt;a href="https://github.com/spring-projects/spring-petclinic" rel="noopener noreferrer"&gt;PetClinic&lt;/a&gt; as an example and convert the deployment process for the application from one that runs deployment steps sequentially in Octopus to a rolling deployment process. PetClinic is a sample Spring Boot application written in Java that has two main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A web front-end.&lt;/li&gt;
&lt;li&gt;A database.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;I don’t explain how to build the PetClinic application in this post. If you are new to building Java applications, we have a number of &lt;a href="https://octopus.com/docs/guides?application=java" rel="noopener noreferrer"&gt;guides&lt;/a&gt; that include step-by-step instructions to setup CI/CD pipelines for various tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For both the sequential and rolling deployment processes, the PetClinic application and &lt;a href="https://www.mysql.com/" rel="noopener noreferrer"&gt;MySQL&lt;/a&gt; database are hosted in &lt;a href="https://cloud.google.com/gcp" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;. All of the infrastructure used in these examples, including the servers, load balancer, and databases are re-created regularly using &lt;a href="https://octopus.com/docs/operations-runbooks" rel="noopener noreferrer"&gt;Operations Runbooks&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some caveats
&lt;/h3&gt;

&lt;p&gt;It’s important to highlight that this post won’t cover every element required for a zero-downtime deployment. It makes some assumptions about the application set-up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The database is already deployed in a highly available configuration. For more information on MySQL high availability, refer to the &lt;a href="https://dev.mysql.com/doc/mysql-ha-scalability/en/ha-overview.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Changes to the database are made in a backward and forward compatible way using &lt;a href="https://flywaydb.org/" rel="noopener noreferrer"&gt;Flyway&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Any required session state is persisted for when an individual server is being deployed to.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sequential deployment process &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;For deployments where you aren’t concerned about application downtime, Octopus caters for this by running steps sequentially one after the other, by default.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Start trigger&lt;/strong&gt;&lt;br&gt;
It’s also possible to configure your deployment process to run steps in &lt;a href="https://octopus.com/docs/deployment-process/conditions#start-trigger" rel="noopener noreferrer"&gt;parallel&lt;/a&gt;. However, care should be taken to avoid situations where steps running in parallel depend on one another.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The existing PetClinic application is modeled using this sequential deployment technique. The deployment process consists of a number of key steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://octopus.com/docs/deployment-process/steps/manual-intervention-and-approvals" rel="noopener noreferrer"&gt;manual intervention&lt;/a&gt; approval step for the &lt;strong&gt;Production&lt;/strong&gt; environment only.&lt;/li&gt;
&lt;li&gt;Flyway DB migration &lt;a href="https://library.octopus.com/listing/flyway" rel="noopener noreferrer"&gt;community step template&lt;/a&gt; steps to display the migration state and apply any new database changes.&lt;/li&gt;
&lt;li&gt;A deploy to &lt;a href="https://wildfly.org/" rel="noopener noreferrer"&gt;WildFly&lt;/a&gt; step for the PetClinic web front-end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition, the deployment process includes steps that will post messages to a &lt;a href="https://slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; channel with deployment progression updates.&lt;/p&gt;

&lt;p&gt;The complete deployment process can be seen here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsa7uoh76564o9lppl8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsa7uoh76564o9lppl8l.png" alt="Project sequential deployment process" width="800" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After &lt;a href="https://octopus.com/docs/managing-releases#creating-a-release" rel="noopener noreferrer"&gt;creating a release&lt;/a&gt; in Octopus, you can see an example of the sequential deployment in action to the &lt;strong&gt;Development&lt;/strong&gt; environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvooop6sngf7r4l1a12py.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvooop6sngf7r4l1a12py.png" alt="Project sequential deployment run" width="725" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The steps are run one at a time until the deployment is complete. When the &lt;strong&gt;Deploy PetClinic web app&lt;/strong&gt; step is run, the application becomes unavailable to serve requests to users. &lt;/p&gt;

&lt;p&gt;Perhaps unsurprisingly, the infrastructure used in these deployments (per environment) looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl21gaf4foaryzeud23qp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl21gaf4foaryzeud23qp.png" alt="Project sequential infrastructure" width="734" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single &lt;a href="https://ubuntu.com/" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt; virtual machine hosting the Wildfly application server.&lt;/li&gt;
&lt;li&gt;A MySQL database hosted in a Google &lt;a href="https://cloud.google.com/sql/" rel="noopener noreferrer"&gt;Cloud SQL&lt;/a&gt; service.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sample Octopus project&lt;/strong&gt;&lt;br&gt;
You can see the PetClinic sequential deployment process &lt;em&gt;before&lt;/em&gt; the conversion to a rolling deployment process in our &lt;a href="https://g.octopushq.com/PatternRollingSamplePetClinicNoRollingDeploy" rel="noopener noreferrer"&gt;samples instance&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Convert to a rolling deployment process &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we have seen the deployment process for the existing application, we first need to decide on what our infrastructure will look like for the rolling deployment process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scale up the servers &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In order to reduce downtime and still serve requests for users, we need to scale up the number of servers we use. We also need a load-balancer that we can use to control which servers are available.&lt;/p&gt;

&lt;p&gt;In the previous sequential deployment example, we had a single virtual machine per environment. To keep things simple we’ll keep the infrastructure for the &lt;strong&gt;Development&lt;/strong&gt; environment the same as before. For the &lt;strong&gt;Test&lt;/strong&gt; and &lt;strong&gt;Production&lt;/strong&gt; environments, however, the infrastructure will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3kwnavu7uo0snvadpw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3kwnavu7uo0snvadpw0.png" alt="Project rolling infrastructure" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This includes a &lt;em&gt;shared&lt;/em&gt; load balancer, and this time, two application servers in each environment, connecting to the MySQL database as before.&lt;/p&gt;

&lt;p&gt;After you have created your new servers, you also need to add them in Octopus as new &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets#adding-deployment-targets" rel="noopener noreferrer"&gt;deployment targets&lt;/a&gt; and tag them with any appropriate &lt;a href="https://octopus.com/docs/octopus-concepts/target-roles" rel="noopener noreferrer"&gt;target roles&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing a load balancer &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;There are many different types of load balancer, but a key requirement for this rolling deployments example is the ability to control which servers are available to serve traffic. For this reason, and as this example is running from Google Cloud, we’ll use a &lt;a href="https://cloud.google.com/load-balancing/docs/network/" rel="noopener noreferrer"&gt;network load balancer&lt;/a&gt;. This provides a way to add and remove our servers from the load balancer as part of the deployment process, which we’ll see a little later on. For more information about setting up a network load balancer, please refer to the &lt;a href="https://cloud.google.com/load-balancing/docs/network/setting-up-network" rel="noopener noreferrer"&gt;Google documentation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this example, the load balancer is shared between the &lt;strong&gt;Test&lt;/strong&gt; and &lt;strong&gt;Production&lt;/strong&gt; environments. To route traffic to the correct place, a different TCP port is used at the load balancer to identify the intended environment. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port &lt;code&gt;8080&lt;/code&gt; is used for traffic destined for the &lt;strong&gt;Test&lt;/strong&gt; environment.&lt;/li&gt;
&lt;li&gt;Port &lt;code&gt;80&lt;/code&gt; is used for traffic destined for the &lt;strong&gt;Production&lt;/strong&gt; environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Load balancer target pools &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Previously, users accessed the PetClinic web front-end directly on a single virtual machine. Here, we use dedicated target pools for the &lt;strong&gt;Test&lt;/strong&gt; and &lt;strong&gt;Production&lt;/strong&gt; environments. A &lt;a href="https://cloud.google.com/load-balancing/docs/target-pools" rel="noopener noreferrer"&gt;target pool&lt;/a&gt; is the name given to a group of virtual machine instances hosted in Google Cloud. &lt;/p&gt;

&lt;h3&gt;
  
  
  Create a new project &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In order to change our deployment process, and keep the ability to deploy PetClinic sequentially, we need to create a new project. One way to achieve this is by &lt;a href="https://octopus.com/docs/projects#clone-a-project" rel="noopener noreferrer"&gt;cloning&lt;/a&gt; the existing project.&lt;/p&gt;

&lt;p&gt;In the existing project, under &lt;strong&gt;Settings&lt;/strong&gt;, use the overflow menu (...) and select &lt;strong&gt;Clone&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8jnqzgukmn0h4s1sc0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8jnqzgukmn0h4s1sc0i.png" alt="Project clone menu option" width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give the new project you are cloning from the original project a name, review the settings, and when you are satisfied, click &lt;strong&gt;SAVE&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96li6hwc2lj19rrjoxq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96li6hwc2lj19rrjoxq7.png" alt="Project clone menu option" width="596" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert the PetClinic deployment process &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Next, we’ll convert the deployment process for the project itself. Not all of the PetClinic deployment process lends itself naturally to the rolling deployments pattern, and for this reason, we are focusing on the web front-end of PetClinic. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Choosing what to convert&lt;/strong&gt;&lt;br&gt;
It’s important to decide for yourself what elements of your project’s deployment process should be converted to use a rolling deployments pattern. In certain situations, it could make things &lt;em&gt;harder to deploy&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Configure a rolling deployment
&lt;/h4&gt;

&lt;p&gt;To convert the &lt;strong&gt;Deploy PetCinic web app&lt;/strong&gt; step to a rolling deployment we perform the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the step in the deployment process editor, expand the section &lt;strong&gt;On Targets in Roles&lt;/strong&gt;, and click &lt;strong&gt;CONFIGURE A ROLLING DEPLOYMENT&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4mtsq50leuabuxinnml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4mtsq50leuabuxinnml.png" alt="PetClinic step configure rolling deployment" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;Rolling Deployment&lt;/strong&gt; option that appears, choose a &lt;strong&gt;Window size&lt;/strong&gt;. I’ve chosen a Window Size of &lt;code&gt;1&lt;/code&gt; since we will only deploy to a maximum of two servers per environment:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwgzqgubzneip8aakzce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwgzqgubzneip8aakzce.png" alt="PetClinic step configure rolling deployment window size" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;SAVE&lt;/strong&gt; to update the deployment step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Add the child steps
&lt;/h4&gt;

&lt;p&gt;We’ve configured a rolling deployment, but it’s not very intelligent yet. Currently, the process deploys PetClinic to each server one at a time, taking each instance of the application offline as it deploys.&lt;/p&gt;

&lt;p&gt;We need to add new steps to our rolling deployment to safely deploy new versions of the PetClinic application to each virtual machine and continue serving traffic to users on the other server.&lt;/p&gt;

&lt;p&gt;These steps will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve the virtual machine name.&lt;/li&gt;
&lt;li&gt;Remove the virtual machine from the load balancer &lt;em&gt;before&lt;/em&gt; the PetClinic deployment.&lt;/li&gt;
&lt;li&gt;Add the virtual machine into the load balancer &lt;em&gt;after&lt;/em&gt; the PetClinic deployment.&lt;/li&gt;
&lt;li&gt;Test the PetClinic web front-end is available.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In Octopus, adding multiple steps to a rolling deployment process is done with &lt;a href="https://octopus.com/docs/deployment-patterns/rolling-deployments#Rollingdeployments-Childsteps" rel="noopener noreferrer"&gt;child steps&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;gcloud CLI and authorization&lt;/strong&gt;&lt;br&gt;
Most of the commands used for interacting with Google in this next section make use of the &lt;a href="https://cloud.google.com/sdk/gcloud" rel="noopener noreferrer"&gt;Google Cloud CLI&lt;/a&gt;. To use the &lt;strong&gt;gcloud&lt;/strong&gt; CLI you usually need to authorize it. For further information on gcloud authorization, please refer to the &lt;a href="https://cloud.google.com/sdk/docs/authorizing" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  Add a new child step
&lt;/h5&gt;

&lt;p&gt;To add a child step, we open the overflow menu (...) for the existing &lt;strong&gt;Deploy PetClinic web app&lt;/strong&gt; step and select &lt;strong&gt;Add child step&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2jpzpw3kazrfiq6rswo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2jpzpw3kazrfiq6rswo.png" alt="Project rolling deployment add new child step" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are presented with the &lt;strong&gt;Choose Step Template&lt;/strong&gt; selector where we choose the required step type.&lt;/p&gt;

&lt;p&gt;Next, I’ll walk through the new child steps needed to complete the rolling deployment process. Some of the script examples have been reduced to the minimum needed to highlight the key parts.&lt;/p&gt;

&lt;h5&gt;
  
  
  Retrieve the instance name
&lt;/h5&gt;

&lt;p&gt;This &lt;a href="https://octopus.com/docs/deployment-examples/custom-scripts/run-a-script-step" rel="noopener noreferrer"&gt;script step&lt;/a&gt; is required so that we can identify the name of the virtual machine hosted in Google Cloud for use when removing and adding to the load balancer. We do this by querying Google with the gcloud &lt;code&gt;instances describe&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$machineName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Octopus.Machine.Name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$instanceName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;instances&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$machineName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$projectName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$zone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"get(name)"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--quiet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a match is found using the machine identified by the Octopus system variable &lt;code&gt;Octopus.Machine.Name&lt;/code&gt;, the script sets an &lt;a href="https://octopus.com/docs/projects/variables/output-variables" rel="noopener noreferrer"&gt;output variable&lt;/a&gt; with the name as recorded in Google Cloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Set-OctopusVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"InstanceName"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$instanceName&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Remove the machine from the load balancer
&lt;/h5&gt;

&lt;p&gt;When we have the name of the machine we are deploying to, we need to remove it from the load balancer target pool. However, to prevent an attempt to remove the virtual machine from the target pool when it’s not present to start with, we can run the gcloud &lt;code&gt;target-pools describe&lt;/code&gt; command to check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target-pools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$targetPoolName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"flattened(instances[])"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$region&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$projectName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--quiet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the instance is found in the target pool, we run the gcloud &lt;code&gt;target-pools remove-instances&lt;/code&gt; command supplying the instance name with the &lt;code&gt;--instances&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$instanceName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Octopus.Action[Retrieve machine instance name].Output.InstanceName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target-pools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remove-instances&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$targetPoolName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$instanceName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--instances-zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$zone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$projectName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--quiet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the virtual machine has been removed from the load balancer, we can proceed to deploy the PetClinic application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We don’t need to add a new child step to deploy the PetClinic application, as it already exists. Instead, we will place that step in the right place after we have added the necessary child steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  Testing the PetClinic application
&lt;/h5&gt;

&lt;p&gt;After the PetClinic front-end has been deployed, we can test if it’s responding to requests by adding a community &lt;a href="https://g.octopushq.comCommunityContributedStepTemplates" rel="noopener noreferrer"&gt;step template&lt;/a&gt; called &lt;strong&gt;HTTP - Test URL&lt;/strong&gt; as a child step.&lt;/p&gt;

&lt;p&gt;We use a variable of &lt;code&gt;#{Project.Wildfly.Url}&lt;/code&gt; to test that it returns an HTTP 200 OK response. This will tell us if the application is running. Any other HTTP response will result in a failure.&lt;/p&gt;

&lt;h5&gt;
  
  
  Add the machine to the load balancer
&lt;/h5&gt;

&lt;p&gt;Lastly, when the PetClinic application has been verified as online, we can add it back to the load balancer target pool. We do this by running the gcloud &lt;code&gt;target-pools add-instances&lt;/code&gt; command and supplying the instance name (as before) with the &lt;code&gt;--instances&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$instanceName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OctopusParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Octopus.Action[Retrieve machine instance name].Output.InstanceName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target-pools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add-instances&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$targetPoolName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$instanceName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--instances-zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$zone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$projectName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--quiet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Re-order the child steps
&lt;/h5&gt;

&lt;p&gt;After all of the child steps have been added, you can reorder them if necessary. In our case, we need to move the original &lt;strong&gt;Deploy PetClinic web app&lt;/strong&gt; step to the middle so that we don’t deploy the application to the virtual machine &lt;em&gt;until&lt;/em&gt; it has been removed from the load balancer.&lt;/p&gt;

&lt;p&gt;To reorder the child steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the overflow menu (...) and select &lt;strong&gt;Reorder child steps&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Rearrange the steps in the desired order.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;SAVE&lt;/strong&gt; when done.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The rolling deployment process
&lt;/h4&gt;

&lt;p&gt;Some of the steps in the rolling deployment process aren’t required for the &lt;strong&gt;Development&lt;/strong&gt; environment. This is because we’re not using a load balancer in that environment. To skip the steps that don’t need to run, we use an environment &lt;a href="https://octopus.com/docs/deployment-process/conditions#environments" rel="noopener noreferrer"&gt;run condition&lt;/a&gt;. This will skip the steps applicable to the load-balanced environments when deploying to &lt;strong&gt;Development&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The complete rolling deployment process is shown here: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lkibuw0m4csk18mhhmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lkibuw0m4csk18mhhmp.png" alt="Project rolling deployment run" width="621" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see an example deployment to &lt;strong&gt;Production&lt;/strong&gt; using the new rolling deployment process:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3nk8c24yxhhlumbx209.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3nk8c24yxhhlumbx209.png" alt="Project rolling deployment run" width="726" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it! We’ve successfully converted our deployment process from a sequential one to a rolling deployment process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sample Octopus project&lt;/strong&gt;&lt;br&gt;
You can see the complete PetClinic deployment process &lt;em&gt;after&lt;/em&gt; the conversion to a rolling deployment process, in our &lt;a href="https://g.octopushq.com/PatternRollingSamplePetClinicRollingDeploy" rel="noopener noreferrer"&gt;samples instance&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Switch over to new infrastructure &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In order to use our new infrastructure, we need to direct our users to our application via the load balancer. The simplest way to do that is to adjust your DNS records. In the case of PetClinic, I just need to adjust the DNS &lt;a href="https://en.wikipedia.org/wiki/List_of_DNS_record_types" rel="noopener noreferrer"&gt;A record&lt;/a&gt; to point to the load balancer and wait for the DNS changes to update.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Changing any DNS records may result in a period of time where users are still connecting directly to the virtual machines. This is usually no more than 24 hours, but this will depend on your DNS provider and how long the DNS changes take to propagate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Clean-up &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;At this point, you no longer need your old Octopus project. You can clean it up by either disabling it (and effectively archiving it) or delete it if you no longer need it for any auditing requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As you can hopefully see from this post, with a few steps, you can switch from a sequential deployment process in Octopus to one using the rolling deployments feature. This allows you to benefit from reduced downtime, safe in the knowledge that your application can remain online to serve requests to your users.&lt;/p&gt;

&lt;p&gt;Until next time, Happy Deployments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://octopus.com/docs/deployment-patterns/rolling-deployments" rel="noopener noreferrer"&gt;Octopus rolling deployments docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://octopus.com/docs/guides" rel="noopener noreferrer"&gt;Octopus CI/CD guides&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/convert-to-rolling-deployments" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>deployment</category>
    </item>
    <item>
      <title>The ultimate guide to rolling deployments</title>
      <dc:creator>Mark Harrison</dc:creator>
      <pubDate>Mon, 13 Jul 2020 16:46:19 +0000</pubDate>
      <link>https://dev.to/octopus/the-ultimate-guide-to-rolling-deployments-ld1</link>
      <guid>https://dev.to/octopus/the-ultimate-guide-to-rolling-deployments-ld1</guid>
      <description>&lt;p&gt;When deploying new versions of modern software, like web applications and services, it’s still common for some teams to take their entire website down while they &lt;em&gt;do deployments&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If the majority of your customers only use your application during business hours, then this big-bang approach is probably acceptable, but what happens if your customers are using your applications 24-7?&lt;/p&gt;

&lt;p&gt;Today, users expect applications to be available all the time, and there are a few deployment patterns you can use to achieve zero-downtime. In this post, I’ll discuss one of these patterns in more depth; rolling deployments. I’ll also provide you with some practical examples of how to implement rolling deployments with different tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this post
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What are rolling deployments?&lt;/li&gt;
&lt;li&gt;Why are they useful?&lt;/li&gt;
&lt;li&gt;Rolling deployment patterns in practice&lt;/li&gt;
&lt;li&gt;A word on databases&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are rolling deployments? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A rolling deployment is a deployment pattern (also known as an incremental deployments, batched deployments, or ramped deployment) where new software is delivered, usually to one or more deployment targets at a time, until all of the targets have the updated version of the software rolled out.&lt;/p&gt;

&lt;p&gt;A typical process looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;With two nodes running &lt;code&gt;v1.0&lt;/code&gt; of your application, drain the first node to be updated, take it out of the load-balancer pool, and leave the remaining node online to serve traffic:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63e9v5dcyyp00207yjl2.png" alt="Rolling Deployment: Draining nodes" width="412" height="325"&gt;
&lt;/li&gt;
&lt;li&gt;Stop the &lt;code&gt;v1.0&lt;/code&gt; application from running on the drained node, then deploy the new &lt;code&gt;v1.1&lt;/code&gt; version. &lt;em&gt;Optionally&lt;/em&gt;, verify the deployment was successful by running tests on your newly deployed application. All the while, maintaining at least one node running &lt;code&gt;v1.0&lt;/code&gt; of your application:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtutqfvk57qke2cdznwn.png" alt="Rolling Deployment: Update nodes with new versions" width="410" height="323"&gt;
&lt;/li&gt;
&lt;li&gt;After the first node has updated successfully, proceed with draining the remaining node still running &lt;code&gt;v1.0&lt;/code&gt; of your application, while your new &lt;code&gt;v1.1&lt;/code&gt; version is now online serving traffic:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fsdbnzvqpqifxxboj32.png" alt="Rolling Deployment: Drain remaining nodes in pool" width="410" height="323"&gt;
&lt;/li&gt;
&lt;li&gt;Stop the &lt;code&gt;v1.0&lt;/code&gt; application on the remaining node from running, deploy the new &lt;code&gt;v1.1&lt;/code&gt; version. Again, optionally verify the deployment was successful:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0ahgo52xhx3l1rqu3ip.png" alt="Rolling Deployment: Update remaining nodes in pool" width="410" height="323"&gt;
&lt;/li&gt;
&lt;li&gt;Finally, after &lt;code&gt;v1.1&lt;/code&gt; of your application has been deployed successfully to all of your nodes, your rolling deployment is complete!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2cfias0l8jo37i0k0kl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2cfias0l8jo37i0k0kl2.png" alt="Rolling Deployment: Update remaining nodes in pool" width="410" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to ramp up your rolling deployment and deliver a new version to more than one node simultaneously, say two for example, then it would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7t7wmgterhaf2xf8h687.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7t7wmgterhaf2xf8h687.png" alt="Rolling Deployment: Update multiple nodes in pool" width="615" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This incremental approach is often favored in web applications which sit behind a load balancer, as most load balancers support a concept known as &lt;strong&gt;Connection draining&lt;/strong&gt;. This allows connections to a service to finish naturally and prevents new connections from being established.&lt;/p&gt;

&lt;p&gt;By performing this action, instances which are selected to be updated, can be removed from the available pool after they have finished their work, while others remain online serving traffic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Although the scenario above describes a web application rolling deployment, it’s possible to achieve rolling deployments for other types of applications, providing they are built in a way that supports ending their process safely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, Octopus Deploy’s &lt;a href="https://octopus.com/docs/administration/high-availability" rel="noopener noreferrer"&gt;High Availability&lt;/a&gt; configuration also has a &lt;a href="https://octopus.com/docs/administration/high-availability/managing-high-availability-nodes#ManagingHighAvailabilityNodes-Drain" rel="noopener noreferrer"&gt;drain option&lt;/a&gt;, which prevents any new tasks from executing, and finishes up any tasks it’s currently executing until idle. Features like draining, allow for the safe termination of a process, which can then be updated and brought back online.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are they useful? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So why use rolling deployments over other patterns such as canary or blue/green? Well, rolling deployments offer the following benefits:&lt;/p&gt;

&lt;h3&gt;
  
  
  Incremental update
&lt;/h3&gt;

&lt;p&gt;New versions of your application are rolled-out incrementally. This allows you to verify it’s working, for example, by running health checks or tests before moving on to the next batch of updates.&lt;/p&gt;

&lt;p&gt;In the event that you need to initiate a rollback, you can also do this in a safe and controlled manner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping the lights on
&lt;/h3&gt;

&lt;p&gt;While you go about updating a small number of your application instances, the rest continue to serve requests. This means there is no downtime for your application, and it’s available for your users throughout the deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallelism
&lt;/h3&gt;

&lt;p&gt;You can usually control the number of concurrent instances that are deployed to at any one time. Further deployments won’t start until a previous deployment has finished.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can use the &lt;strong&gt;Window size&lt;/strong&gt; option within an Octopus rolling deployment to control how many deployment targets can be deployed to at once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Rolling deployment patterns in practice &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To demonstrate the different approaches to rolling deployments, we have a very simple .NET Core 3.1 application which will display a web page.&lt;/p&gt;

&lt;p&gt;The HTML for the section I’m interested in is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"display-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Welcome&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If you are seeing this, then &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Congratulations!&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt; You've got the example application running. &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

    @if(Settings.Value.AppVersion == "0.0.2") {
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;v0.0.2 of the application comes with this text &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    }
    @if(Settings.Value.AppVersion == "0.0.3") {
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;But don't miss out on v0.0.3 of the application which comes with this text! &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    }
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code for the application is available on &lt;a href="https://github.com/OctopusSamples/rolling-deploy-web-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and has a &lt;a href="https://github.com/OctopusSamples/rolling-deploy-web-example/releases" rel="noopener noreferrer"&gt;Tag&lt;/a&gt; corresponding to the three different &lt;code&gt;AppVersion&lt;/code&gt; values. A Docker image has also been published as &lt;a href="https://hub.docker.com/r/octopusdeploy/rolling-deploy-web-example" rel="noopener noreferrer"&gt;octopusdeploy/rolling-deploy-web-example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to see just how easy it would be to perform a rolling deploy of this application using some popular technologies and tools, so I’ll demonstrate rolling deployments with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Octopus&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Docker rolling application updates &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Docker has become the de facto container technology to use in the last few years. It will come as no surprise therefore, that it natively supports rolling deployments with its concept of a Docker &lt;a href="https://docs.docker.com/engine/swarm/how-swarm-mode-works/services" rel="noopener noreferrer"&gt;service&lt;/a&gt;. Typically, a service is a small piece of a much larger architectural picture and is popular with microservices.&lt;/p&gt;

&lt;p&gt;Services support a number of different options, including a rolling update policy as well as the ability to rollback.&lt;/p&gt;

&lt;h4&gt;
  
  
  Docker containerized application
&lt;/h4&gt;

&lt;p&gt;I’m running Docker on an &lt;a href="https://ubuntu.com/download/server" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt; server and using our pre-built container image. There are a couple of ways to install Docker on Ubuntu:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the Ubuntu repository by running: &lt;code&gt;sudo apt-get install docker.io&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the Official &lt;a href="https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-docker-engine---community" rel="noopener noreferrer"&gt;Docker guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I opted for the Ubuntu repository as it seemed quicker and easier, but your mileage may vary. Whichever method you choose, it’s worth ensuring you meet the installation &lt;a href="https://docs.docker.com/install/linux/docker-ce/ubuntu/#prerequisites" rel="noopener noreferrer"&gt;prerequisites&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the sake of simplicity, I’ll be interacting with Docker in an SSH terminal session to my Linux box. There are production-ready setups to automate this, which feature the definition of your services in a &lt;a href="https://docs.docker.com/compose/compose-file/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; file, including sections to control automatic updates and rollback settings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Permissions requirement:&lt;/strong&gt;&lt;br&gt;
Most of the commands in this demonstration make use of &lt;a href="https://www.linux.com/tutorials/linux-101-introduction-sudo/" rel="noopener noreferrer"&gt;sudo&lt;/a&gt;. By default, the Docker daemon runs as the root user and requires elevated permissions to execute commands. If you prefer not to use &lt;code&gt;sudo&lt;/code&gt; when executing your commands, be sure to follow the Docker &lt;a href="https://docs.docker.com/install/linux/linux-postinstall/" rel="noopener noreferrer"&gt;post-install&lt;/a&gt; instructions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Firstly, to see the Docker image of this running standalone, we’ll run it with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5001:5001 octopusdeploy/rolling-deploy-web-example:0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unsurprisingly, running this Docker image displays the web page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fdocker-run.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fdocker-run.png" title="width=500" width="572" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don’t explain how to build a container image in this post. If you are new to Docker, my colleague Shawn has written an excellent series on how to containerize a &lt;a href="https://octopus.com/blog/containerize-a-real-world-web-app" rel="noopener noreferrer"&gt;real world application&lt;/a&gt;, instead of another “Hello World” example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Container clean-up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A quick-tidy up is needed next. To delete the container we created using the &lt;code&gt;run&lt;/code&gt; command above, we need to stop it, and then remove it using the &lt;code&gt;rm&lt;/code&gt; command. We can do this in a condensed one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker stop &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;ancestor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;octopusdeploy/rolling-deploy-web-example:0.0.1 &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{{.ID}}"&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This locates our container by the image name &lt;code&gt;octopusdeploy/rolling-deploy-web-example:0.0.1&lt;/code&gt; and passes that to the &lt;code&gt;stop&lt;/code&gt; command, and finally passes that to the &lt;code&gt;rm&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;To deploy more than one instance of our container, we need to create our Docker service. This uses &lt;a href="https://docs.docker.com/engine/swarm" rel="noopener noreferrer"&gt;Docker Swarm&lt;/a&gt; as its orchestrator under the hood.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Docker Kubernetes orchestrator&lt;/strong&gt;&lt;br&gt;
Docker also supports Kubernetes as an orchestrator when deploying containers using the Docker &lt;a href="https://docs.docker.com/engine/reference/commandline/stack" rel="noopener noreferrer"&gt;stack&lt;/a&gt; command, but it’s not possible to specify the orchestrator when using &lt;code&gt;service create&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So let’s see what our command to create a service looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker service create &lt;span class="nt"&gt;--name&lt;/span&gt; rolling-deploy-svc &lt;span class="nt"&gt;--replicas&lt;/span&gt; 3 &lt;span class="nt"&gt;--publish&lt;/span&gt; &lt;span class="nv"&gt;published&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5001,target&lt;span class="o"&gt;=&lt;/span&gt;5001 &lt;span class="nt"&gt;--update-delay&lt;/span&gt; 10s &lt;span class="nt"&gt;--update-parallelism&lt;/span&gt; 1 octopusdeploy/rolling-deploy-web-example:0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s quite a lot going on in that command, so let’s unpick what we are asking of Docker here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;--name&lt;/code&gt; is self explanatory.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--replicas&lt;/code&gt; flag controls the number of containers we want (3).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--publish published=5001,target=5001&lt;/code&gt; specifies the service to be accessed on port 5001, using Swarm’s &lt;a href="https://docs.docker.com/engine/swarm/ingress/#publish-a-port-for-a-service" rel="noopener noreferrer"&gt;routing mesh&lt;/a&gt; which acts essentially like a software load-balancer.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--update-delay&lt;/code&gt; configures the time delay (10s) between updates to a service task.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--update-parallelism&lt;/code&gt; controls the maximum number of tasks that Docker will schedule simultaneously (1).&lt;/li&gt;
&lt;li&gt;Lastly, we specify the image to use: &lt;code&gt;octopusdeploy/rolling-deploy-web-example:0.0.1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hint:&lt;/strong&gt;&lt;br&gt;
When running &lt;code&gt;service create&lt;/code&gt; for the first time, you may receive a warning, just as I did: &lt;code&gt;This node is not a swarm manager&lt;/code&gt;. To fix this, run either of the following commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sudo docker swarm init&lt;/code&gt;: This will initialize your current node as a swarm manager.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sudo docker swarm join&lt;/code&gt;: This will connect your local node to swarm.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Executing this results in our service being deployed to Docker Swarm with three instances:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wxi1w4m7crknaz1f800kr9ztt
overall progress: 3 out of 3 tasks
1/3: running   [==================================================&amp;gt;]
2/3: running   [==================================================&amp;gt;]
3/3: running   [==================================================&amp;gt;]
verify: Service converged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also check our service has the correct update configuration by running the &lt;code&gt;service inspect&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker service inspect rolling-deploy-svc &lt;span class="nt"&gt;--pretty&lt;/span&gt;

ID:             bh03s0yjzkevzkkwvu8q2h0jj
Name:           rolling-deploy-svc
Service Mode:   Replicated
 Replicas:      3
Placement:
UpdateConfig:
 Parallelism:   1
 Delay:         10s
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Update order:      stop-first
RollbackConfig:
 Parallelism:   1
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Rollback order:    stop-first
ContainerSpec:
 Image:         octopusdeploy/rolling-deploy-web-example:0.0.1@sha256:4da10d630025bf268b855b0b4afafa7334769ab6d0b3e75e11a3f11949708552
 Init:          &lt;span class="nb"&gt;false
&lt;/span&gt;Resources:
Endpoint Mode:  vip
Ports:
 PublishedPort &lt;span class="o"&gt;=&lt;/span&gt; 5001
  Protocol &lt;span class="o"&gt;=&lt;/span&gt; tcp
  TargetPort &lt;span class="o"&gt;=&lt;/span&gt; 5001
  PublishMode &lt;span class="o"&gt;=&lt;/span&gt; ingress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result of this shows we have our desired &lt;code&gt;UpdateConfig&lt;/code&gt; which will update one task at a time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Docker service update
&lt;/h4&gt;

&lt;p&gt;Now we can update the container image for &lt;code&gt;octopusdeploy/rolling-deploy-web-example&lt;/code&gt; to &lt;code&gt;v0.0.2&lt;/code&gt; by running the &lt;code&gt;service update&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker service update rolling-deploy-svc &lt;span class="nt"&gt;--image&lt;/span&gt; octopusdeploy/rolling-deploy-web-example:0.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker runs the update to each container, one task at a time just as we have configured it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;overall progress: 0 out of 3 tasks
1/3: running   [=============================================&amp;gt;     ]
2/3:
3/3:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the first task is complete, it moves onto task two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;overall progress: 1 out of 3 tasks
1/3: starting  [==================================================&amp;gt;]
2/3: ready     [=====================================&amp;gt;             ]
3/3:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Until all the tasks to update the containers to &lt;code&gt;v0.0.2&lt;/code&gt; are complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;overall progress: 3 out of 3 tasks
1/3: running   [==================================================&amp;gt;]
2/3: running   [==================================================&amp;gt;]
3/3: running   [==================================================&amp;gt;]
verify: Service converged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsing to the website now shows the text which applies for &lt;code&gt;v0.0.2&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fdocker-service-v0.0.2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fdocker-service-v0.0.2.png" title="width=500" width="572" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Docker service rollback
&lt;/h4&gt;

&lt;p&gt;Just as it’s straight-forward to roll-out, it’s also possible to manually rollback with a simple command in Docker.&lt;/p&gt;

&lt;p&gt;Firstly, we will update to our final version &lt;code&gt;v0.0.3&lt;/code&gt; of the application by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker service update rolling-deploy-svc &lt;span class="nt"&gt;--image&lt;/span&gt; octopusdeploy/rolling-deploy-web-example:0.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We verify the new &lt;code&gt;v0.0.3&lt;/code&gt; version by running the &lt;code&gt;service inspect&lt;/code&gt; command, passing in a &lt;code&gt;--format&lt;/code&gt; parameter for only the output we want to see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker service inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.Spec.TaskTemplate.ContainerSpec.Image}}'&lt;/span&gt; rolling-deploy-svc

octopusdeploy/rolling-deploy-web-example:0.0.3@sha256:151a8f2aaed0192bf9f22eaeff487d546e6ff8fec4d0691e6697dede743b187c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Docker Swarm knows the versions we deployed, we can revert to the previous one (&lt;code&gt;v0.0.2&lt;/code&gt;) using the &lt;code&gt;rollback&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker service rollback rolling-deploy-svc

rolling-deploy-svc
rollback: manually requested rollback
overall progress: rolling back update: 3 out of 3 tasks
1/3: running   &lt;span class="o"&gt;[&amp;gt;&lt;/span&gt;                                                  &lt;span class="o"&gt;]&lt;/span&gt;
2/3: running   &lt;span class="o"&gt;[&amp;gt;&lt;/span&gt;                                                  &lt;span class="o"&gt;]&lt;/span&gt;
3/3: running   &lt;span class="o"&gt;[&amp;gt;&lt;/span&gt;                                                  &lt;span class="o"&gt;]&lt;/span&gt;
verify: Service converged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once successfully rolled back, it has confirmed the service is running.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hint:&lt;/strong&gt;&lt;br&gt;
 As I didn’t specify any parameters to the &lt;code&gt;rollback&lt;/code&gt; command, Docker will by default, rollback one task at a time with no delays between each one. You can specify different values by passing the following to the command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--rollback-parallelism&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--rollback-delay&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Docker &lt;a href="https://docs.docker.com/engine/reference/commandline/create/#options" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; has a full list of parameters you can use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can verify the rollback was successful using the same command to inspect the service as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker service inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.Spec.TaskTemplate.ContainerSpec.Image}}'&lt;/span&gt; rolling-deploy-svc

octopusdeploy/rolling-deploy-web-example:0.0.2@sha256:4843a91ba84ace97cb11a6e3f68827a8c28a628d509159910c868f9ad02c3053
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This results in the expected &lt;code&gt;v0.0.2&lt;/code&gt; version in the output.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Database rollbacks&lt;/strong&gt;&lt;br&gt;
When a service in Docker utilizes a database for storage, it’s important to have a strategy in place to deal with a service rollback, particularly if the database is within a container. Docker won’t rollback database changes for you automatically, and that could leave your database and application in an incompatible state. The Docker &lt;a href="https://docs.docker.com/storage/" rel="noopener noreferrer"&gt;storage&lt;/a&gt; documentation provides some guidance on the different options for storage in a container.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Docker service clean-up
&lt;/h4&gt;

&lt;p&gt;Finally, to remove our Docker service we just run the &lt;code&gt;rm&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;markh@ubuntu01:~$ sudo docker service rm rolling-deploy-svc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Docker summary
&lt;/h4&gt;

&lt;p&gt;As you can see, it doesn’t take much setup to get rolling deployments working in Docker. Coupled with its support for rollbacks makes it an attractive option to consider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kubernetes rolling updates &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Rolling deployments in Kubernetes is called &lt;a href="https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#rolling-update" rel="noopener noreferrer"&gt;rolling updates&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A pod’s instances will be updated incrementally with new ones. It supports both a max number or percentage of pods to be unavailable during an update, as well as a max number of new pods that can be created. In addition to this, Kubernetes has a handy built-in feature to allow updates to be reverted to a previous version.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To find out more about Kubernetes, my colleague Shawn continued his container series focussing on &lt;a href="https://octopus.com/blog/kubernetes-for-the-uninitiated" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Kubernetes &lt;a href="https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; on updates includes a nice diagram showing how it works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmx41on6w1jkskdq2es1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmx41on6w1jkskdq2es1m.png" width="514" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Kubernetes cluster setup
&lt;/h4&gt;

&lt;p&gt;Just as before, I’ll re-use our pre-built container image for this demonstration, this time using &lt;a href="https://microk8s.io/" rel="noopener noreferrer"&gt;MicroK8s&lt;/a&gt;. I’ll also interact with it primarily in an SSH terminal session.&lt;/p&gt;

&lt;p&gt;Canonical, the authors, describe MicroK8s as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a single package of k8s for 42 flavours of Linux. Made for developers, and great for edge, IoT and appliances.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s useful for people like me who want to try out Kubernetes or do some development with it.&lt;/p&gt;

&lt;p&gt;One of the benefits of MicroK8s is that it doesn’t require any type of Virtual Machine as with other Kubernetes choices (such as &lt;a href="https://minikube.sigs.k8s.io/" rel="noopener noreferrer"&gt;Minikube&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Before you install MicroK8s, it’s worth noting that there are some prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 18.04 LTS or 16.04 LTS (or other OS that supports &lt;code&gt;snapd&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;At least 20G free disk space.&lt;/li&gt;
&lt;li&gt;4GB of RAM.&lt;/li&gt;
&lt;li&gt;An internet connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To install MicroK8s, we run the &lt;a href="https://docs.snapcraft.io/installing-snapd" rel="noopener noreferrer"&gt;snap&lt;/a&gt; &lt;code&gt;install&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;snap &lt;span class="nb"&gt;install &lt;/span&gt;microk8s &lt;span class="nt"&gt;--classic&lt;/span&gt;

microk8s v1.17.0 from Canonical✓ installed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Full install output&lt;/strong&gt;&lt;br&gt;
If you want to see the full install output from &lt;code&gt;snap&lt;/code&gt;, run the &lt;code&gt;snap changes&lt;/code&gt; command:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;snap changes

ID   Status  Spawn               Ready               Summary
1    Done    today at 10:17 UTC  today at 10:17 UTC 
Initialize system state
2    Done    today at 10:17 UTC  today at 10:17 UTC  Initialize device
3    Done    today at 14:38 UTC  today at 14:38 UTC  Install &lt;span class="s2"&gt;"microk8s"&lt;/span&gt; snap
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;From this, you can then run the command &lt;code&gt;snap change 3&lt;/code&gt;, where &lt;code&gt;3&lt;/code&gt; is the value from the &lt;code&gt;ID&lt;/code&gt; column above for the installation of MicroK8s. This will give you a line breakdown of the install steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes deployments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we have MicroK8s installed and running, let’s go ahead and create a Kubernetes deployment using our existing image &lt;code&gt;rolling-deploy-web-example&lt;/code&gt; and set it to listen on port 5001.&lt;/p&gt;

&lt;p&gt;Google describes Kubernetes &lt;a href="https://cloud.google.com/kubernetes-engine/docs/concepts/deployment" rel="noopener noreferrer"&gt;deployments&lt;/a&gt; as items which:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;represent a set of multiple, identical Pods with no unique identities. A Deployment runs multiple replicas of your application and automatically replaces any instances that fail or become unresponsive. In this way, Deployments help ensure that one or more instances of your application are available to serve user requests. Deployments are managed by the Kubernetes Deployment controller.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This sounds perfect for a rolling deployment.&lt;/p&gt;

&lt;h4&gt;
  
  
  Kubernetes containerized application setup
&lt;/h4&gt;

&lt;p&gt;To set up our deployment for our application, we will use the &lt;a href="https://kubernetes.io/docs/reference/kubectl/kubectl/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt; binary which is packaged with MicroK8s. This is the command-line interface (CLI) for managing Kubernetes. It’s specially prefixed with &lt;code&gt;microk8s.&lt;/code&gt; to avoid any naming conflicts with any other instances of Kubernetes you may have running. If we run the &lt;code&gt;create deployment&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl create deployment rollingdeploy-microk8s &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;octopusdeploy/rolling-deploy-web-example:0.0.1

deployment.apps/rollingdeploy-microk8s created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MicroK8s will create our deployment and confirm it has been successfully created.&lt;/p&gt;

&lt;p&gt;Next, we’ll set the application pods to listen on port &lt;code&gt;5001&lt;/code&gt;. To do that, we run the &lt;a href="https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#expose" rel="noopener noreferrer"&gt;expose&lt;/a&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl expose deployment rollingdeploy-microk8s &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NodePort &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5001

service/rollingdeploy-microk8s exposed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Kubernetes dashboard
&lt;/h5&gt;

&lt;p&gt;Although the &lt;code&gt;rollingdeploy-microk8s&lt;/code&gt; pod has been created, it might not be available immediately. We can check its status by looking at our service using the Kubernetes &lt;a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;, which is included as an &lt;a href="https://microk8s.io/docs/addon-dashboard" rel="noopener noreferrer"&gt;add-on&lt;/a&gt; in MicroK8s.&lt;/p&gt;

&lt;p&gt;Attempting to access the dashboard remotely requires you to jump through a few hoops. After the add-on was enabled, I found the simplest way was to create a proxy from my machine to the server by running the &lt;code&gt;kubectl proxy&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl proxy &lt;span class="nt"&gt;--accept-hosts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;--address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0

Starting to serve on &lt;span class="o"&gt;[&lt;/span&gt;::]:8001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there you can access the dashboard on port &lt;code&gt;8001&lt;/code&gt;, but you’ll need either a &lt;code&gt;Kubeconfig&lt;/code&gt; file or &lt;code&gt;Token&lt;/code&gt; to login. See the MicroK8s &lt;a href="https://microk8s.io/docs/addon-dashboard" rel="noopener noreferrer"&gt;Dashboard add-on&lt;/a&gt; for further details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You can skip the login by setting the &lt;code&gt;--enable-skip-login&lt;/code&gt; argument for the dashboard container, but this isn’t advised as it goes against security best practices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once open, you can use the dashboard to deploy containerized applications, manage and interact with your cluster resources.&lt;/p&gt;

&lt;p&gt;In order to perform a rolling update, first we need more than one replica of our application. We can scale our deployment directly from the dashboard by clicking on the three ellipsis on the right hand side of the &lt;strong&gt;Deployments&lt;/strong&gt; section:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-dashboard-scale-ellipsis.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-dashboard-scale-ellipsis.png" title="width=500" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For our Kubernetes deployment, I updated the &lt;strong&gt;Desired Replicas&lt;/strong&gt; to 3 so I can perform a rolling update and then hit &lt;strong&gt;Scale&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-dashboard-scale.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-dashboard-scale.png" title="width=500" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Equivalent kubectl commands&lt;/strong&gt;&lt;br&gt;
You may have noticed the dashboard provides the equivalent command to run for our action. For scaling our resource, that is:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl scale &lt;span class="nt"&gt;-n&lt;/span&gt; default deployment rollingdeploy-microk8s &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;After the pods have been provisioned, we can confirm this by querying the pod’s status directly by running the &lt;code&gt;get pod&lt;/code&gt; command (names may vary):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl get pod

NAME                                      READY   STATUS    RESTARTS   AGE
rollingdeploy-microk8s-794bdc64c4-fv7zt   1/1     Running   0          76s
rollingdeploy-microk8s-794bdc64c4-t6mh5   1/1     Running   0          76s
rollingdeploy-microk8s-794bdc64c4-trr6f   1/1     Running   0          76s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify our application is working, we need to find the port that has been exposed by Kubernetes to the deployment we created at the start by running &lt;code&gt;get service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl get service rollingdeploy-microk8s

NAME                     TYPE       CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;          AGE
rollingdeploy-microk8s   NodePort   10.152.183.39   &amp;lt;none&amp;gt;        5001:32334/TCP   1m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, the port is &lt;code&gt;32334&lt;/code&gt; so my URL to access my service is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://ubuntu01.octopusdemos.com:32334
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
The port may be different when running this on your own machine. A random port, in the range 30000-32767 (by default) will  be assigned by Kubernetes as we chose a &lt;code&gt;NodePort&lt;/code&gt; type when we ran the &lt;code&gt;expose&lt;/code&gt; command earlier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open the URL in a browser, and we can see that we have &lt;code&gt;v0.0.1&lt;/code&gt; for our application running in Microk8s:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-v0.0.1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-v0.0.1.png" title="width=500" width="569" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Kubernetes rolling update
&lt;/h4&gt;

&lt;p&gt;Let’s go ahead and instruct Kubernetes to update our three pods with &lt;code&gt;v0.0.2&lt;/code&gt; of our image &lt;code&gt;octopusdeploy/rolling-deploy-web-example&lt;/code&gt; by running the &lt;code&gt;set image&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deployment/rollingdeploy-microk8s rolling-deploy-web-example&lt;span class="o"&gt;=&lt;/span&gt;octopusdeploy/rolling-deploy-web-example:0.0.2 &lt;span class="nt"&gt;--record&lt;/span&gt;

deployment.apps/rollingdeploy-microk8s image updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we can watch the live progress of our rollout until its complete by running the &lt;code&gt;rollout status&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl rollout status deployment.v1.apps/rollingdeploy-microk8s

Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 1 out of 3 new replicas have been updated...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 1 out of 3 new replicas have been updated...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 1 out of 3 new replicas have been updated...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 2 out of 3 new replicas have been updated...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 2 out of 3 new replicas have been updated...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 2 out of 3 new replicas have been updated...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 1 old replicas are pending termination...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; rollout to finish: 1 old replicas are pending termination...
deployment &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; successfully rolled out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see it indicated it was updating one Pod at a time.&lt;/p&gt;

&lt;p&gt;Kubernetes deployments ensure that only a certain number of pods are down while they are being updated. It does this by creating a new pod and destroying the old ones after it has completed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Default pod update control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, Kubernetes ensures there are at least 75% of the desired number of pods available.&lt;br&gt;
In addition, another default is to create no more than 25% of the overall desired number of pods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open the URL in a browser, and we can see we have &lt;code&gt;v0.0.2&lt;/code&gt; of our application running in Microk8s:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-v0.0.2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fmicrok8s-v0.0.2.png" title="width=500" width="569" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The deployment’s rollout was triggered here, as &lt;code&gt;set image&lt;/code&gt; caused an update to the underlying deployment pod’s &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates" rel="noopener noreferrer"&gt;template&lt;/a&gt;. A template is a specification document that describes the way a &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/" rel="noopener noreferrer"&gt;replication controller&lt;/a&gt; should create an actual pod.&lt;/p&gt;

&lt;p&gt;We can see what the template looks like for our application by running &lt;code&gt;edit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl edit deployment.v1.apps/rollingdeploy-microk8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens the template file in a text editor. For me, that’s in the terminal itself. You can edit this file interactively. Changing the deployment pod’s template (the section within &lt;code&gt;.spec.template&lt;/code&gt;) will result in triggering the deployment’s rollout:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh80tq6tgg1xkt2gtvmnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh80tq6tgg1xkt2gtvmnf.png" width="800" height="826"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hint:&lt;/strong&gt;&lt;br&gt;
Other updates to a deployment, like the scaling we did earlier, don’t result in a rollout being triggered.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Kubernetes deployment rollback
&lt;/h4&gt;

&lt;p&gt;A successful rolling deployment is obviously what we all hope for, but it’s inevitable that at some point, you’ll need to initiate a rollback, either part of the way through a rollout itself or some time after.&lt;/p&gt;

&lt;p&gt;With Kubernetes, all of a deployment’s rollout history is kept in the system by default. That means, you can rollback anytime you want.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
It’s possible to change the amount of history that’s stored for a deployment’s rollout (by modifying the revision history limit), but it’s not generally recommended as this limits your ability to rollback a deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To see the rollout history for our deployment we run the &lt;code&gt;rollout history&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl rollout &lt;span class="nb"&gt;history &lt;/span&gt;deployment.v1.apps/rollingdeploy-microk8s

deployment.apps/rollingdeploy-microk8s
REVISION  CHANGE-CAUSE
1         &amp;lt;none&amp;gt;
2         kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deployment/rollingdeploy-microk8s rolling-deploy-web-example&lt;span class="o"&gt;=&lt;/span&gt;octopusdeploy/rolling-deploy-web-example:0.0.2 &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/snap/microk8s/1107/credentials/client.config &lt;span class="nt"&gt;--record&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can choose to revert back to the previously deployed &lt;code&gt;v0.0.1&lt;/code&gt; version by running &lt;code&gt;rollout undo&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl rollout undo deployment.v1.apps/rollingdeploy-microk8s

deployment.apps/rollingdeploy-microk8s rolled back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hint:&lt;/strong&gt;&lt;br&gt;
You can also choose to revert to a specific revision of your application by running:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;markh@ubuntu01:~$ sudo microk8s.kubectl rollout undo deployment.v1.apps/rollingdeploy-microk8s --to-revision=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;Where the &lt;code&gt;--to-revision&lt;/code&gt; parameter has the revision you wish to go back to.&lt;/p&gt;

&lt;p&gt;The Kubernetes &lt;a href="https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-undo-em-" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; has a full list of parameters you can use.&lt;/p&gt;

&lt;p&gt;We can confirm we have rolled back, either by looking back in the dashboard, viewing the application in a browser, or by running the &lt;code&gt;describe&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl describe deployment

Name:                   rollingdeploy-microk8s
Namespace:              default
CreationTimestamp:      Wed, 22 Jan 2020 13:19:30 +0000
Labels:                 &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rollingdeploy-microk8s
Annotations:            deployment.kubernetes.io/revision: 3
Selector:               &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rollingdeploy-microk8s
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rollingdeploy-microk8s
  Containers:
   rolling-deploy-web-example:
    Image:        octopusdeploy/rolling-deploy-web-example:0.0.1
    Port:         &amp;lt;none&amp;gt;
    Host Port:    &amp;lt;none&amp;gt;
    Environment:  &amp;lt;none&amp;gt;
    Mounts:       &amp;lt;none&amp;gt;
  Volumes:        &amp;lt;none&amp;gt;
Conditions:
  Type           Status  Reason
  &lt;span class="nt"&gt;----&lt;/span&gt;           &lt;span class="nt"&gt;------&lt;/span&gt;  &lt;span class="nt"&gt;------&lt;/span&gt;
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  &amp;lt;none&amp;gt;
NewReplicaSet:   rollingdeploy-microk8s-794bdc64c4 &lt;span class="o"&gt;(&lt;/span&gt;3/3 replicas created&lt;span class="o"&gt;)&lt;/span&gt;
Events:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows us the &lt;code&gt;Image&lt;/code&gt; is set to &lt;code&gt;octopusdeploy/rolling-deploy-web-example:0.0.1&lt;/code&gt; as we expected.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Database rollbacks&lt;/strong&gt;&lt;br&gt;
As with Docker, it’s important to understand what will happen with any databases you have when you initiate a Kubernetes rollback. Google has a great &lt;a href="https://cloud.google.com/blog/products/databases/to-run-or-not-to-run-a-database-on-kubernetes-what-to-consider" rel="noopener noreferrer"&gt;article&lt;/a&gt; discussing running databases on Kubernetes in more depth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Kubernetes deployment clean-up
&lt;/h4&gt;

&lt;p&gt;To remove all of the resources associated with our Kubernetes deployment, we use the &lt;code&gt;delete&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;markh@ubuntu01:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;microk8s.kubectl delete services,deployment rollingdeploy-microk8s &lt;span class="nt"&gt;-n&lt;/span&gt; default

service &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; deleted
deployment.apps &lt;span class="s2"&gt;"rollingdeploy-microk8s"&lt;/span&gt; deleted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Kubernetes summary
&lt;/h4&gt;

&lt;p&gt;It felt like there was a little more to the setup for a rolling deployment with Kubernetes than with Docker, particularly to gain access to the dashboard, but after it was all configured, it worked excellently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rolling deployments with Octopus &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Octopus has supported the concept of rolling deployments since &lt;a href="https://octopus.com/blog/new-in-2.0/rolling-deployments" rel="noopener noreferrer"&gt;Octopus 2.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the use of child steps, we can set-up our deployment process for the &lt;code&gt;rolling-deploy-web-example&lt;/code&gt; application in Octopus.&lt;/p&gt;

&lt;p&gt;After creating a new project, we configure a rolling deployment with three steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A script to remove the node from the load balancer.&lt;/li&gt;
&lt;li&gt;Deployment of the web application.&lt;/li&gt;
&lt;li&gt;A script to add the node back into the load balancer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To achieve an incremental release in Octopus, we need to make our &lt;strong&gt;Window size&lt;/strong&gt; lower than the total number of deployment targets. In my example, I set this to &lt;code&gt;1&lt;/code&gt;, as you can see below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fod-rolling-win-size.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.octopus.com%2Fblog%2F2020-01%2Fultimate-guide-to-rolling-deployments%2Fod-rolling-win-size.png" title="width=500" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have two deployment targets configured with the &lt;a href="https://octopus.com/docs/octopus-concepts/target-roles" rel="noopener noreferrer"&gt;target role&lt;/a&gt;: &lt;code&gt;rolling-deploy-webapp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When I deploy the release to the &lt;code&gt;Test&lt;/code&gt; environment, Octopus deploys to one deployment target at a time, as I configured in my deployment process earlier:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finj4zn4alzzpd0whuche.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finj4zn4alzzpd0whuche.png" width="643" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s all there is to it! Check out our &lt;a href="https://octopus.com/docs/deployment-patterns/rolling-deployments" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for a complete reference on rolling deployments in Octopus.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sample Octopus project&lt;/strong&gt;&lt;br&gt;
You can view this Octopus project setup in our &lt;a href="https://g.octopushq.com/PatternRollingSampleAzureWebApp" rel="noopener noreferrer"&gt;samples&lt;/a&gt; instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A word on databases &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Usually one of the main sticking points with rolling deployments is the database. Performing rolling deployments which involve some kind of persistent storage can be tricky, though not impossible. The devil is always in the detail.&lt;/p&gt;

&lt;p&gt;If you want to perform rolling deployments which includes database changes, I recommend deploying the database first. You also want to ensure any changes you make to your database are backward compatible with previous versions of code you have deployed.&lt;/p&gt;

&lt;p&gt;Lastly, it’s definitely a good idea to test your rollback strategy &lt;em&gt;frequently&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have an excellent series of posts on &lt;a href="https://octopus.com/database-deployments" rel="noopener noreferrer"&gt;database deployments&lt;/a&gt; that discuss this topic and more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;No matter which tooling you use, rolling deployments is just one pattern available to optimize deployment of your software. But with an incremental approach, it allows you to keep your applications online while rolling out newer versions of your software in a controlled manner, often with native support for rollbacks, making it a firm favorite of mine for minimal disruption.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CI/CD pipeline guides&lt;/strong&gt;&lt;br&gt;
If you need any additional help configuring your CI/CD pipeline, we have created the &lt;a href="https://octopus.com/docs/guides" rel="noopener noreferrer"&gt;Octopus Guides&lt;/a&gt; which include step-by-step instructions to setup CI/CD pipelines for various technology stacks, including Docker and Kubernetes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;This post was originally published at &lt;a href="https://octopus.com/blog/ultimate-guide-to-rolling-deployments" rel="noopener noreferrer"&gt;octopus.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>octopus</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
