· · ·

Azure DevOps – Part 1: What is CI/CD and Terraform?

blog_img_cloud_DevOps

Part 1 of a multiple-part series on Azure DevOps Pipelines

/introduction

If you have started using Terraform for your infrastructure management in Azure, you’re already thinking the right way and ahead of a lot of folks. Being able to define your infrastructure as code (IaC) is a huge plus.
It is repeatable, you have version-controlling and it is far more reliable than clicking your infrastructure together through the Azure Portal. Try keeping track of your resources that way, you’ll notice its a pain ITA.

Now, controlling your infrastructure with Terraform is one thing, but who runs the code?

If the answer to that question is “me, of course, from my laptop!”, you’re still ahead of many, but there’s room to level up towards, what you’ll hear a lot of times, the best practices.
That’s where CI/CD and Pipelines come in. In the first part of this series I want to lay out the foundation so we can talk about it on the same level. We’ll cover what CI/CD means in the context of azure/terraform, why it matters and how Azure DevOps Pipelines fits into our picture.

In the next blog we will build our first pipeline step by step using the classic editor. From there on, we will start adjusting some things and look at alternative to the classic editor where yaml files come into play.
I don’t know how many parts there will be at the end because this topic is a never ending and always improvable topic.

/quick word on terraform

Before we start with this topic – if you’re brand new to IaC and terraform, here’s the short version.

Terraform is an open source tool by HashiCorp that lets you define your infrastructure in configuration files using a human readable Language called HashiCorp Configuration Language (HCL). It is a declarative language, meaning you describe what you want in the end – a virtual network, a cirtual machine in that network, a storage account – and Terraform figures out how to create or update it.

The key idea of HCL is IaC: your infrastructure is described in files that live in a repository, just like application code before. That then means that it can be reviewed, versioned and tracked over time.

Terraform uses Providers to manage resources on the cloud platforms like Azure. Every supported infrastructure platform has a Provider that defines which resources are available to deploy and manage and then performs API calls to handle those resources.

/continuous Integration (CI)

The CI is an automated process that lets engineers and developers merge code changes into a shared branch more frequently. Each merge triggers tests to verify the code is working as expected.

It formerly was used only for developers where the goal was to have multiple developers working simultaneously on different features of the same application. Companies usually had whats called a merge-day where all the changes of the application are merged together – which often resulted in a tedious, manual and time intensive work afterwards.

With terraform, CI means automatically validating your IaC every time a change is pushed to your repository. This typically includes:

terraform fmt       #check formatting
terraform validate  #catch syntax errors
terraform plan      #preview what changes will be made to the azure environment

Instead finding out later that the Terraform config is broken when you try to apply the changes manually, CI catches it immediately before it ever reaches your actual infra.

/continuous Delivery (CD)

CD takes it one step further. Once the changes have been validated and reviewed in the previous step, CD automates the actual applying and deploying of those changes. In Terraform language we run the terraform apply* command against our Azure environment in a controlled, consistent and auditable way.

terraform apply     #creates, updates or deletes resources

The Goal of CD in short: your infra changes flow through a reliable, automated process rather than depending on someone remembering to run the right commands in the right order at the right time.

/the “Old way” and its problems

Without the mentioned process, a typical Terraform workflow looks something like this; a Team member of yours writes the Terraform code locally on their machine, runs terraform plan to check the written code, then runs terraform apply directly against a live azure environment from their laptop. It feels fine at first, then the team grows, the infra gets more complex and things start to go wrong and unmanageable.

Following are some Problems that could develop:

Someone destory a production resource by accident. Terraform apply shows you a plan before it runs, lets say you’re in a hurry or correcting an error and you’re deploying it for the 10th time today, its easy to skim past the line that said “azurerm_sql_server.main will be destroyed“. By the time you notice it’s already done and deleted the resource. Restoring it, if even possible, takes hours.

It worked on my machine – a classic IT phrase. One developer used Terraform 1.3, another one used Terraform version 1.6. One is authenticated to the wrong Subscription with more rights and the other one is authenticated to the right one with limited rights. One has an environment variable set the other doesnt and so on and on. Figuring out why it works on one but not the other takes time to troubleshoot.

Credentials are everywhere. To run Terraform locally against our Azure Infra, every developer needs credentials. Sensitive data or key is stored in the .env files, shell profiles. One compromised laptop via a hacking attack and your entire azure environment is at stake.

Nobody actually knows what’s deployed. Was the change applied? Did it finish? Did the deploy error halfway through the process and leave things in a broken state? Without centralized logs of every plan and apply, you’re left wondering and digging for more messages in hopes of piecing together the history of your infra.

There is no review process. When only one person is able to apply without any second pair of eyes on what’s about to change, disasters are bound to happen that otherwise could be avoided. A misconfigured firewall rule, an accidentally deleted resource, a storage account set to public – these things go straight into production with no checkpoint.

“These points will never happen to us, we are professionals!”. The scary part is that none of this requires carelessness. These are normal, predictable failures that happen to careful, experienced teams. All because manual processes have no “safety net”.

CI/CD replaces this chaos with a process that is repeatable, visible and last but not least controlled.

/where Azure DevOps comes in

First of all what is Azure DevOps? It’s Microsofts platform for planning, building and delivering software and in our case Infrastructure. The Pipeline feature gives you a CI/CD engine that natively integrates to Azure.
The platform does not only give you access to pipelines, but it also gives you tools like Repository where you can store your code online and centralized and other things that are out of scope for this blog.

These are some Terraform workflow that mitigate some of the old-ways problems:

Automated Validation on every change pushed. Everytime a developer pushes a change to our repo, the pipeline automatically runs terraform fmt, terraform validate and terraform plan

Controlled and auditable. Instead of someone running terraform apply locally on their machine, the pipeline does it for them – using a consistent environment, the right credentials and a full log of everything that happened during the run

Plan review before apply. You configure the pipeline to pause, show the output for human to review after terraform plan and only proceed if everything checks out with terraform apply, only with explicit approval of course – no surprises in this workflow.

No scattered credentials. Your Service Principal or App Registration etc. are all managed in Azure DevOps. No credentials have to be saved on the computer itself, besides maybe the SSH key used to push into AzureDevops – but that not a Pipeline thing, thats a SSH thing.

Environment Separation. You build pipelines that deploy to a dev environment automatically, but require manual approval before touching production – all within the same pipeline workflow.

/the Big Picture of Terraform pipeline

At a high level, how a CI/CD workflow could look like with Azure DevOps pipelines (keep in mind that this is not set into stone, this can vary a lot, depending on your needs):

Code pushed to repo (terraform change)
->
Pipeline is triggered automatically
->
First agent runs changes
-> 
terraform fmt - check formatting
-> 
terraform validate - check for syntax errors
->
terraform plan - preview changes against Azure
->
Makes plan output a file called "artifact"
->
[Optional] Human reviews the plan and approves
->
Second agent runs, grabbing the given "artifact" from before
->
terraform apply - changes are applied to Azure
->
Team gets notified of success or failures

Each step is defined once in the pipeline and runs automatically from that point on every single time. The infra changes become structured and auditable. There is no guessing around anymore.

/ways to build a pipeline

There are mainly two ways to build a pipeline in Azure DevOps. You could use the Classic Editor or a YAML Pipeline.

Classic Editor: Is a visual GUI in Azure DevOps where you configure your pipeline by clicking together the tasks from a library and filling in settings through menus. It is a beginner friendly way of starting to build a pipeline because it does not need coding skills.
If you ever want to transition, there is a way of exporting the configuration from Editor to a YAML file.

YAML Pipelines: This is a code based approach where the whole pipeline is defined in a .yml file stored in your repository, alongside your terraform code. It is generally more powerful and flexible but it comes with a steeper learning curve since you have to understand code and be able to write it.

As mentioned the Classic Editor is a good starting point because it is more for beginners given that you can pick and choose via GUI what you need. It gives you a solid mental model of how pipelines work and everything you learn directly translates to YAML, when you’re ready to move.

/whats next?

Now that we understand what CI/CD is and how it benefits us in the context of IaC and Terraform, why it matters and what Azure DevOps Pipelines offers for us, we’re ready to build one.

In Part 2, we’ll walk through creating our first Terraform pipeline step by step using the Classic Editor – from connecting our repo to running our first successful terraform plan against our Azure infra. No prior DevOps experience needed.