Octopus Deploy – Variables & Variable Sets

DevOps is a relatively new space in the software engineering world. There are a smattering of tools to aid in the automation of application deployments, but precious little guidance with respect to patterns and practices for using the new tools. As a guy who loves leaning on principles this lack of attention to best practices leaves me feeling a bit uncomfortable. Since I’m leading a migration to Octopus Deploy, I thought I would share some of the decisions we’ve made.

This series of posts is an attempt to start a conversation about best practices. I want to be clear: We have not been applying these ideas long enough to know what all of the ramifications are. Your mileage may vary.

Posts in this series
1. Environments
2. Roles
3. Variables & Variable Sets

Variables & Variable Sets

Octopus Deploy allows you to modify your application’s configuration through the use of variables. You can define variables at the project level, or share variable values between projects through variable sets. If you have relatively little sharing of variables between projects you will likely prefer to create variables at the project level. My team manages over 50 different applications. Many of them are web services designed to support SOA. The net impact is that we have a lot of shared variables and for this reason we define variables exclusively through variable sets. This saves us time hunting for where a given variable is defined.

We use 2 kinds of variable sets
1. Global
2. Role based

Global variable sets define values that might be required across the company irrespective of any particular application, or that are more easily managed together. For example, we wish to capture metadata about environments. Octopus itself does not have a facility for tagging environments with arbitrary metadata. To satisfy this goal we created a variable set called “environment” in which we create variables to indicate values such as “owner” and “abbr”. We use these values to compose the values of other variables such as dns addresses or email addresses.

We also have some environments for which we do not create dns addresses for the sites. In these environments we need to install web applications with alternate ports. We keep a variable sets to define the port number we use for web applications in these environments since they must be unique across the web server.

The number of global variable sets should be as small as possible.

Role based variable sets are variables defined for the specific roles they target. If we have a role called heroes-iis we will also have a variable set called heroes-iis. Since we create roles on a per-deployed-application basis, this helps us keep roles, projects, and variable sets linked. If heroes-iis as web service end points, this variable set may be included in some other project that depends on those end points.

Naming Conventions

It is important to have naming conventions for your variables. I highly recommend prefixing all variables in a variable set with the name of the variable set to avoid potential naming collisions. For example, If I have a variable set called heroes-iis it will have variables with names like:

  • heroes-iis.application-pool.name
  • heroes-iis.application-pool.password

Define a Standard Structure for Similar Variable Sets

Once you get the rhythm of installing applications with Octopus, you will discover that similar kinds of applications have similar variable definition needs. You can save yourself a lot of time and Chrome tabs by establishing a variable set template that you use when creating a variable set for each kind of application you deploy. Here is our variable set template for web applications being deployed to iis:

Variable Set Name Segment Field Variable Name Notes
name-iis application-pool name name-iis.application-pool.name The name of the application pool
username name-iis.application-pool.username the username the application pool runs under
password name-iis.application-pool.password the password the application pool runs under
host name.host This corresponds to the site name as registered in IIS. It does not include the protocol (http://, https://). It should be blank if the site is being deployed into an environment without a dns entry.
site-name name.site-name This will be just the name of the web application in environments that do not have a dns entry. If the environment has a dns entry, it should resolve the host property.
site-root name.site-root This is the url root for the site. It should include the protocol (http://, https://) as well as the port, and any additional routing information.
endpoints endpoint-name name.endpoints.endpoint-name A web service may expose one or more endpoints. These should have unique names. Their values should be defined with reference to the host and port variables.
connection-strings cs-name name.connection-strings.cs-name The name of the connection string in the config file.

Scope

Octopus Deploy allows you to scope variables by environment, role, or channel (as of 3.3). The scoping rules are as follows:

(environment1 OR environment2 OR ...) AND (role1 OR role2 OR ...) AND (channel1 OR channel2 OR ...)

I recommend that you scope variable values as broadly as possible. Use composed variable values where you can to minimize the number of variable values you have to maintain. For example:


heroes-iis.connection-strings.heroes-db => "Server=#{environment.sql-server.url}; Database=#{heroes-db.database-name};" heroes-db.database-name => #{HEROES_}#{environment.name} environment.sql-server.url => http://sql-server.#{environment.name}.com

By using a composed variable value I don’t need to scope the connection string variable itself. Instead, I can confine scoping to environment.name and satisfy the resolution of all of the descendant variables. This minimizes the number of variables I have to actively maintain as new environments are created.

Leave a Reply