Deploying Sitecore with Terraform Part 1

During last month’s Sitecore symposium I had the pleasure to present with my colleague Paula Simontacchi on deploying Sitecore through Terraform. This is the first post in a series of 2 and will discuss Terraform features which are beneficial when deploying Sitecore.

Part 2 goes into deploying a Sitecore solution in Azure based on a sample Github repo.

Terraform introduction

Terraform is an Infrastructure as Code (IaC) tool developed by Hashicorp. A Terraform solution is written in the HCL language, which is a proprietary language from Hashicorp. Below small code sample shows how to create a Resource Group and Virtual Network in Azure:

resource "azurerm_resource_group" "tfsimple" {
  name     = "tf-resources"
  location = "${var.location}"
}

resource "azurerm_virtual_network" "tfsimple" {
  name                = "tf-network"
  address_space       = ["10.0.0.0/16"]
  location            = "${azurerm_resource_group.tfsimple.location}"
  resource_group_name = "${azurerm_resource_group.tfsimple.name}"
}

There are many good resources to learn Terraform in more detail, for example the Terraform docs site or this PluralSight training. A few key concepts will be covered here. It is encouraged to have a solid understanding of Terraform before using it in production Sitecore deployments.

Multi-provider based model

Terraform uses a provider based model and has providers for almost everything you would want to deploy to. For example it supports all major cloud providers but also has providers for solutions like Cloudflare, Docker or F5. A more elaborate list can be found in Terraform’s site here

Recently Jamstack based architectures are getting more popular in Sitecore. The Terraform provider based model is a great option here as well as Terraform has providers for common resources in this stack including their Netlify and Akamai providers.

Terraform workflow

Below diagram shows the typical Terraform workflow:

  1. Init: Initializes working directory and downloads providers
  2. Plan: Creates and displays the execution plan
  3. Apply: Makes the changes to the underlying platform
  4. Destroy: Deletes the changes made in step 3

Step 1 is not necessary when you have already initialized the working directory and have downloaded all providers.

Step 4 is optional as well. However it is recommended to always destroy and reprovision infrastructure at least when changes are made to it, to ensure Terraform stays up-to-date and can continue to be used reliably to stand up infrastructure.

Plan/Dry run

The plan phase, also referred to as dry-run sometimes, is the most interesting phase. During this phase the Terraform code is compared to the underlying deployment and displays the difference i.e. the updates it will make during the apply phase. This is useful for following reasons:

  1. The result of the deployment can be validated without actually running it. This can save a lot of time and money in most Sitecore deployments as deploying all infrastructure is a time consuming process and doing this many times can result in significant cost.
  2. The result of the plan phase can be saved for execution later, for example by a different team or during a maintenance window. Running the saved plan will avoid any surprises and will provision the infrastructure exactly as per the plan
  3. In an enterprise scenario infrastructure will be provisioned from CI/CD and not from a local developers machine. Performing a dry-run is a good validation before pushing changes to source control.

Modularity

Terraform natively supports modules. Modules can be used to create reusable infrastructure. Modules can reference other modules as well. Common resources to create through modules are Subnets, Vnets, Security Groups or Vms.

A module typically creates resources based on some values passed in through variables. Outputs can be used to pass information about the created resources back to the calling code. Below is a sample module which will create a windows VM. It will use some of the variables to determine the correct settings and it will return the public IP as an output.

module "windowsservers" {
    source                        = "Azure/compute/azurerm"    
    version                       = "1.2.0"
    location                      = "${var.location}"
    remote_port                   = "3389"
    vm_size                       = "${lookup(var.vm_size, var.environment)}"
    vnet_subnet_id                = "${module.network.vnet_subnets[0]}"
  }

  output "windows_vm_public_ip"{
    value = "${module.windowsservers.public_ip_address}"
  }

Dependency Tracking

Providers in Terraform are aware of dependencies between resources. This provides some key benefits:

  • Create resources in parallel: any independent resources are created in parallel. In a Sitecore scenario this means that all the databases can be stood up at the same as the VMs
  • Visualize architectural dependencies: Terraform can generate a dependency graph which will show dependencies between all the resources in the deployment
  • IDE support: popular IDE’s have plugins for Terraform which show where each resource or variable is used, similar to “find references” in Visual Studio. This is helpful when understanding the impact of changes made to the Terraform solution.

Terraform vs. ARM

ARM is a popular solution for Sitecore deployments in Azure, however there are some benefits to using Terraform even in Azure. Below comparison lists some key differences:

TerraformARM
plan/dry-run: Validates with the deployment in place and calculates delta. This delta can be saved for later use Validation: Will validate syntax, but does not compare to underlying deployment
HCL language: supports features like interpolation, attributes, and comments JSON language: Powerful, but missing features like interpolation and does not support comments
Modules: Modules are first-class citizen Modules: Modules can be created through nested templates, but not supported natively
Usage: Terraform has a provider for almost any resource. Supports hybrid cloud or Azure in combination with other infra, e.g. Azure with Cloudflare CDNUsage: specific to Azure