<?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>The Ops Community ⚙️: Mel Kaulfuß 👩🏻‍🦰💻✌️</title>
    <description>The latest articles on The Ops Community ⚙️ by Mel Kaulfuß 👩🏻‍🦰💻✌️ (@melissakaulfuss).</description>
    <link>https://community.ops.io/melissakaulfuss</link>
    <image>
      <url>https://community.ops.io/images/6nr95hvNza52Xgj5Ak_7-GvBZXOTchgbRT9H_B6SrcQ/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL3Vz/ZXIvcHJvZmlsZV9p/bWFnZS8xMzc4Lzg4/NjcwZDI1LWUwN2Qt/NGZkZS05YzZkLTRj/MDNkNzdjYjFmMS5q/cGc</url>
      <title>The Ops Community ⚙️: Mel Kaulfuß 👩🏻‍🦰💻✌️</title>
      <link>https://community.ops.io/melissakaulfuss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://community.ops.io/feed/melissakaulfuss"/>
    <language>en</language>
    <item>
      <title>Best Practices for Terraform workflows in CI/CD</title>
      <dc:creator>Mel Kaulfuß 👩🏻‍🦰💻✌️</dc:creator>
      <pubDate>Mon, 11 Sep 2023 04:27:45 +0000</pubDate>
      <link>https://community.ops.io/melissakaulfuss/best-practices-for-terraform-workflows-in-cicd-26g0</link>
      <guid>https://community.ops.io/melissakaulfuss/best-practices-for-terraform-workflows-in-cicd-26g0</guid>
      <description>&lt;p&gt;Most engineers get started with Terraform using the command-line interface (CLI) to deploy and manage infrastructure directly from their local machine. However, as projects evolve, so does the need for collaborative, scalable, secure, and resilient workflows. Transitioning from a local Terraform workflow to a shared CI/CD pipeline can seem like a daunting task but if you take the leap, you won’t look back.&lt;/p&gt;

&lt;p&gt;In this guide, I'll cover some best practices and the things you'll need to consider for your Terraform CI/CD workflows.&lt;/p&gt;

&lt;p&gt;Table of Contents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version control&lt;/li&gt;
&lt;li&gt;Project structure&lt;/li&gt;
&lt;li&gt;Managing Terraform configuration for different environments&lt;/li&gt;
&lt;li&gt;Managing secrets&lt;/li&gt;
&lt;li&gt;Terraform state and state locking&lt;/li&gt;
&lt;li&gt;Handling concurrency&lt;/li&gt;
&lt;li&gt;Version constraints&lt;/li&gt;
&lt;li&gt;Enforcing code quality&lt;/li&gt;
&lt;li&gt;Using policies for governance and compliance&lt;/li&gt;
&lt;li&gt;Pre-preparing the build environment&lt;/li&gt;
&lt;li&gt;Using a shared plugin cache&lt;/li&gt;
&lt;li&gt;Approval gates&lt;/li&gt;
&lt;li&gt;Managing CI/CD with Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Version Control
&lt;/h2&gt;

&lt;p&gt;Like any other code, Infrastructure code, including Terraform, should be version controlled too. Using a Source Code Management (SCM) system like git with platforms such as GitHub, GitLab, or Bitbucket ensures that all infrastructure changes are auditable, collaborative, and reversible. Being able to revert to a previous configuration to see what changes were made, when they were made, and who made them is critical when managing your infrastructure using code. This also means everyone on the team can view and contribute to the projects consistently, and means you can utilize fully-automated CI/CD by setting up triggers for your pipeline on repository actions like commits and pull requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# Local .terraform directories&lt;/span&gt;
&lt;span class="err"&gt;**/.&lt;/span&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt;&lt;span class="cm"&gt;/*

# .tfstate files
*.tfstate*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version 
# control as they are data points which are potentially sensitive and subject 
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;🙋‍♀️ When storing your Terraform code in version control, make sure you use a terraform .gitignore file like this one](&lt;a href="https://github.com/github/gitignore/blob/main/Terraform.gitignore"&gt;https://github.com/github/gitignore/blob/main/Terraform.gitignore&lt;/a&gt;) to ensure files with sensitive data aren't accidentally committed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;

&lt;p&gt;There are many different approaches for how to structure and store Terraform code, and there is no one-size-fits-all approach. It all comes down to how you want to manage your code, any contributions, and how it will end up being used in projects and teams.&lt;/p&gt;

&lt;p&gt;Terraform files can be stored in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a project repository (alongside application code)&lt;/li&gt;
&lt;li&gt;a separate repository (for the Terraform files)&lt;/li&gt;
&lt;li&gt;multiple repositories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best way to get started is to store your Terraform code in the same repo as the application code. This is also a common approach for smaller, less-distributed teams. It's always best to start simple! Understand how you like to work, &lt;em&gt;really&lt;/em&gt; feel any pain, so you can better understand the most suitable abstractions that work for your team moving forward. Abstracting too early on leads to ill-informed decisions.&lt;/p&gt;

&lt;p&gt;Separating Terraform code into reusable and composable modules that live in separate repositories is also a great solution, especially for teams with a lot of projects, and complex infrastructure requirements. Besides keeping your Terraform DRY, a benefit of this approach is that you can have a mix of private (restricted) repositories for managing more secure things such as roles and permissions, and engineers are still able to compose their own Terraform configurations by including these pre-defined (and locked down) modules.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.hashicorp.com/terraform/language/modules/develop/structure"&gt;standard module structure&lt;/a&gt; is a great guide that provides a recommended file and directory layout structure for reusable modules distributed in separate repositories. Or if you’re interested in further assessing the differences between two more common approaches (Mono repo vs. Multi repo), &lt;a href="https://www.hashicorp.com/blog/terraform-mono-repo-vs-multi-repo-the-great-debate"&gt;this blog post from HashiCorp&lt;/a&gt; dives into more detail and explains the pros and cons of each approach.&lt;/p&gt;
&lt;h2&gt;
  
  
  Managing Terraform configuration for different environments
&lt;/h2&gt;

&lt;p&gt;There are a variety of conventions for how to handle different environments (e.g. development, staging and production) in Terraform.&lt;/p&gt;

&lt;p&gt;You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create variables prefixed by the environment i.e. &lt;code&gt;dev-s3-bucket&lt;/code&gt;, &lt;code&gt;staging-s3-bucket&lt;/code&gt;, &lt;code&gt;prod-s3-bucket&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use a git branch naming convention and long-lived environment branches.&lt;/li&gt;
&lt;li&gt;Use Terraform &lt;a href="https://developer.hashicorp.com/terraform/language/state/workspaces"&gt;workspaces&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Separate environment specific configurations in into their own folders i.e. development, staging, production.&lt;/li&gt;
&lt;li&gt;And/or use supplementary tools like &lt;a href="https://terragrunt.gruntwork.io/"&gt;Terragrunt&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common approach is to separate environment configurations into individual folders within the same project.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-application/
└── terraform/
  ├── main.tf
  ├── variables.tf
  ├── outputs.tf
  └── environments/
  ├── development/
  │ └── main.tf  
  ├── staging/        
  │ └── main.tf        
  └── production/            
    └── main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When working in a specific environment, scripts automatically locate and execute the Terraform commands in that environment’s directory. This is a great way to ensure each environment’s configuration and state remains isolated.&lt;/p&gt;

&lt;p&gt;If you’re just getting started with Terraform, it's best to start simple with something that works for your immediate and short-term needs. As you learn what works and what doesn't work you can always adapt your approach.&lt;/p&gt;
&lt;h2&gt;
  
  
  Managing secrets
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What not to do
&lt;/h3&gt;

&lt;p&gt;When we're working locally we’ll often handle secrets in pretty "simple" ways. We'll commonly use environment variables on our local machines, and sometimes we might even hard-code them during the initial stages of development 😓. These methods might be quick, but they don't scale and they pose significant security risks, configurations can be accidentally pushed to version control with sensitive data included and &lt;a href="https://travis-ci.community/t/security-bulletin/12081"&gt;it happens more often than you think&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt; &lt;span class="s2"&gt;"my_db_instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;
  &lt;span class="c1"&gt;# DO NOT DO THIS!!!  &lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myS3cretp@ssw0rd"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Use a secrets manager
&lt;/h3&gt;

&lt;p&gt;Secrets management is serious business, especially in shared, collaborative CI/CD workflows. You should use a dedicated centralized secrets manager such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/secretsmanager/"&gt;AWS Secrets Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-au/products/key-vault"&gt;Azure Key Vault&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/secret-manager"&gt;Google Cloud Secret Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.vaultproject.io/"&gt;HashiCorp Vault&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools store, manage, log, and control access to tokens, passwords, certificates, and other secrets and offer greater control and visibility. When managing access with tools like these, it's important to apply the principle of least privilege – that is to restrict access to the users and systems that absolutely need it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tips for working with secrets
&lt;/h3&gt;

&lt;p&gt;Once the required secrets are made available to your build system or build agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use environment variables and Terraform &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/secretsmanager_secret_version"&gt;data sources&lt;/a&gt; to reference your secrets in your Terraform code.&lt;/li&gt;
&lt;li&gt;Make sure state files are not tracked in version control, and that they’re included in your &lt;code&gt;Terraform.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use a &lt;a href="https://developer.hashicorp.com/terraform/language/state/remote"&gt;remote state&lt;/a&gt; file along with advanced linting and validation techniques to prevent exposing secret values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most CI/CD tools allow you to choose and integrate with any secrets management platform in various ways. For example, in Buildkite you can use &lt;a href="https://buildkite.com/docs/pipelines/secrets#exporting-secrets-with-environment-hooks"&gt;environment hooks&lt;/a&gt; to conditionally allow access to secrets based on the requesting repository, team, pipeline, commit author, or any other logic you choose.&lt;/p&gt;
&lt;h2&gt;
  
  
  State and state locking
&lt;/h2&gt;

&lt;p&gt;Terraform maintains the "state" of your infrastructure in a state file (it's a snapshot of the current infrastructure resources). It's usually  named &lt;code&gt;terraform.tfstate&lt;/code&gt;. State files should not be tracked in version control because they can potentially contain sensitive information.&lt;/p&gt;

&lt;p&gt;In a local Terraform workflow, this state file is stored on a developer's machine. Using local state in Terraform projects that have multiple collaborators can cause some pretty frustrating problems. Changes can confusingly get mixed up, and that usually results in more errors (that can be very difficult to untangle). Also, to make things worse, without a clear, consistent record of changes to state rolling back to a previous version cleanly can be problematic.&lt;/p&gt;

&lt;p&gt;Fortunately Terraform provides an option for storing state remotely by configuring a &lt;a href="https://developer.hashicorp.com/terraform/language/settings/backends/configuration"&gt;backend&lt;/a&gt; in your Terraform code, usually in the root module.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-tfstate-bucket"&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"path/to/my/key"&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
    &lt;span class="nx"&gt;dynamodb_table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-lock-table"&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This example configures a backend using Amazon S3 combined with DynamoDB for state locking, so that when Terraform detects a supported backend is in use, it will automatically lock the state.&lt;/p&gt;

&lt;p&gt;State locking is especially important in team environments or CI/CD pipelines where there can be multiple attempts to modify infrastructure simultaneously. When Terraform state is locked, it prevents multiple concurrent operations (e.g. terraform apply or terraform plan) from being performed on the same state. This mechanism ensures that state changes are atomic and mitigates the risk of state corruption or inconsistencies due to overlapping operations. If another user or process tries to perform an operation while the state is locked, Terraform will return an error and prevent that action from proceeding until the lock is released.&lt;/p&gt;

&lt;p&gt;Restrict access to the remote state file, to highly privileged administrators, and build systems in certain build contexts i.e. main branch builds, and even a set of specific users.&lt;/p&gt;
&lt;h2&gt;
  
  
  Handling concurrency
&lt;/h2&gt;

&lt;p&gt;We can’t talk about state locking without also looking at how to handle concurrency, a major concern for anyone making changes in a shared environment, be it infrastructure, or application. Most CI/CD platforms will have a way of controlling concurrency to ensure jobs and tasks don't collide with each other, for example, only one job can ever deploy to an environment at a time.&lt;/p&gt;

&lt;p&gt;In Buildkite, you can use &lt;a href="https://buildkite.com/docs/pipelines/controlling-concurrency#concurrency-groups"&gt;concurrency groups&lt;/a&gt; and/or &lt;a href="https://buildkite.com/docs/pipelines/controlling-concurrency#concurrency-and-parallelism"&gt;concurrency gates&lt;/a&gt; to control concurrency and still benefit from running CI jobs in parallel.&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;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;deploy.sh"&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:rocket:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Deploy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;production"&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;
  &lt;span class="s"&gt;agents:    &lt;/span&gt;
    &lt;span class="s"&gt;deploy&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;
    &lt;span class="s"&gt;concurrency&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1  &lt;/span&gt;
    &lt;span class="s"&gt;concurrency_group&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-payment-gateway/deploy"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the Buildkite pipeline step definition above, only one job in the &lt;code&gt;'my-payment-gateway/deploy'&lt;/code&gt; concurrency group will be executed at a time, across an entire Buildkite organization’s pipelines and builds.&lt;/p&gt;

&lt;p&gt;Creating strict CI/CD concurrency rules combined with Terraform state locking ensures that infrastructure deployments won’t conflict with each other, and that updates will always happen sequentially.&lt;/p&gt;
&lt;h2&gt;
  
  
  Version constraints
&lt;/h2&gt;

&lt;p&gt;Making sure that the installed versions of components remain consistent between developer workstations and build environments reduces inconsistencies, errors and confusion. Using strict versioning is one way to ensure consistency, which becomes especially important when working at scale, on a shared codebase, or in a distributed team.&lt;/p&gt;

&lt;p&gt;There are three components in Terraform which use of versioning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform CLI&lt;/li&gt;
&lt;li&gt;Terraform providers&lt;/li&gt;
&lt;li&gt;Terraform modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terraform uses &lt;a href="https://semver.org/"&gt;Semantic Versioning&lt;/a&gt;'s major.minor.patch format, ie. &lt;code&gt;2.3.11&lt;/code&gt; to specify versions and requirements for these components.&lt;/p&gt;
&lt;h3&gt;
  
  
  Understanding the trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Adopting and maintaining strict versioning takes effort, but provides consistency and increased reliability.&lt;/li&gt;
&lt;li&gt;A more lenient, low-effort approach makes taking advantage of new features and bug fixes slightly easier without having to update your terraform configuration, but this trade off can result in errors and confusion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before you decide to adopt strict versioning, consider the size and experience of your team, along with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The amount of time you can dedicate to monitoring, reviewing, updating, and testing new versions.&lt;/li&gt;
&lt;li&gt;Whether you can live with the technical debt generated if you fall behind new versions.&lt;/li&gt;
&lt;li&gt;Whether you can guarantee changes are thoroughly tested and simulated.&lt;/li&gt;
&lt;li&gt;How well your development, test, and staging environments reflect your production environment.
How critical the infrastructure or service is.&lt;/li&gt;
&lt;li&gt;Whether you can rollback if an issue is identified at a later stage, outside of testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might decide to accept the relatively low risk of using a more lenient, less strict versioning standard, or decide the risk is not acceptable, and enforce strict versioning constraints.&lt;/p&gt;

&lt;p&gt;HashiCorp recommends using the &lt;code&gt;~&amp;gt;&lt;/code&gt; version constraint to pin only major and minor versions, meaning patch versions are used without needing to update the Terraform configuration.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;      &lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 2.13.0"&lt;/span&gt;&lt;span class="err"&gt;    &lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;  &lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.12.29"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;~&amp;gt;&lt;/code&gt; symbol in the above &lt;code&gt;required_version = "~&amp;gt; 0.12.29"&lt;/code&gt; allows the patch version to be greater than 29 but requires the major and minor versions (0.12) to match the version that the configuration specifies.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enforcing code quality with formatting, linting and validation
&lt;/h2&gt;

&lt;p&gt;Linting Terraform code is incredibly valuable, it enforces a consistent style across Terraform projects, making code easier to read and understand. When integrated into CI/CD, it catches syntactical errors early, minimizing things going wrong with infrastructure deployments. Automated quality checks significantly speed up the code review process by ensuring only high-standard code is ready to review and deploy. Linting can improve code reliability, reduce cognitive load and help developers learn by setting clear coding styles and standards.&lt;br&gt;
There are some great features and tools to help improve Terraform code quality:&lt;/p&gt;

&lt;p&gt;Terraform CLI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/terraform/cli/commands/fmt"&gt;terraform fmt&lt;/a&gt; and &lt;a href="https://developer.hashicorp.com/terraform/cli/commands/validate"&gt;terraform validate&lt;/a&gt; are built in methods to help with formatting and validating&lt;/li&gt;
&lt;li&gt;Extending Terraform's native linting and validation:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/terraform-linters/tflint"&gt;tflint&lt;/a&gt; offers more comprehensive linting, catching resource-specific errors and allowing custom rule checks.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aquasecurity/tfsec"&gt;tfsec&lt;/a&gt; scans Terraform code for potential security vulnerabilities, providing in-depth security insights.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.checkov.io/"&gt;checkov&lt;/a&gt; evaluates Terraform configurations against hundreds of security and compliance benchmarks, ensuring robustness beyond basic validations. Together, these tools fortify the Terraform workflow, ensuring code quality, security, and compliance.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checking code formatting and validation should be automated and happen in CI/CD pipelines. If the checks don't pass – conditionally block or fail builds so that the issues need to be fixed before being merged. To avoid the inevitable “&lt;em&gt;it worked on my machine&lt;/em&gt;” moments, consider using a &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks"&gt;git pre-commit hook&lt;/a&gt; to enforce checks being run locally before code is committed and pushed. Incorporating container images into your project development workflow and documenting a process around how they are built and used can also help address other consistency issues.&lt;/p&gt;

&lt;p&gt;When it comes to consistency and code quality, what automation can’t solve, open communication, collaboration, good documentation (such as engineering manuals) and README files can.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using policies for governance and compliance
&lt;/h2&gt;

&lt;p&gt;Limiting and restricting the resources that can be created, and the configuration of those resources is essential. This kind of governance can usually be set in the platform in which the resources are being created, using features like &lt;a href="https://aws.amazon.com/controltower"&gt;AWS Control Tower&lt;/a&gt;. But tools like &lt;a href="https://www.openpolicyagent.org/docs/latest/terraform/"&gt;Open Policy Agent (OPA)&lt;/a&gt; offer an even more powerful layer of governance and compliance for Terraform projects.&lt;/p&gt;

&lt;p&gt;OPA provides fine-grained, programmable control over Terraform configurations. Being able to codify policies means best practices and any organizational and regulatory standards set are automatically enforced.&lt;/p&gt;

&lt;p&gt;Automating governance and compliance policies fosters a secure and compliant infrastructure-as-code practice by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preemptively catching violations, reducing the risk of deploying non-compliant resources, and strengthening security posture and operational reliability.&lt;/li&gt;
&lt;li&gt;Streamlining the review process, ensuring consistent adherence to policies without manual oversight.&lt;/li&gt;
&lt;li&gt;Providing contributors with a guardrail to:

&lt;ul&gt;
&lt;li&gt;Ensure their infrastructure changes remain within defined boundaries&lt;/li&gt;
&lt;li&gt;Get feedback early in the development lifecycle before deploying to a target environment and getting a permission error
The challenge with having multiple layers of governance is that it can often lead to conflicting policies between those defined within your project, and those enforced by maintainers of the target platform, so it’s important for these teams to collaborate closely and define a clear process for how governance can remain aligned across teams.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Pre-preparing the build environment
&lt;/h2&gt;

&lt;p&gt;For Terraform to run consistently and efficiently in CI/CD, the build environment should be prepared in advance. Ahead of any jobs being run, install any build dependencies such as the Terraform CLI, and other required command-line tools. Additionally, build any machine images or container images used in CI/CD pipelines on a regular basis. Dependencies will be kept up to date, and considerable time saved by not having to do this set up every time a job runs or a container/machine is started. You can also fetch an up-to-date clone of any git repositories used as part of these image builds. And to really speed things up, run a &lt;code&gt;terraform init&lt;/code&gt; to pre-fetch the required Terraform providers and plugins, so that the job in CI/CD is already ready to go.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using a shared plugin cache
&lt;/h2&gt;

&lt;p&gt;To speed things up even more, consider a shared plugin cache. By default, Terraform looks for plugins in a project’s local &lt;code&gt;.terraform directory&lt;/code&gt;, which is generally unique to each job. You can override this by leveraging the &lt;a href="https://developer.hashicorp.com/terraform/cli/config/config-file#provider-plugin-cache"&gt;TF_PLUGIN_CACHE_DIR&lt;/a&gt; environment variable to specify a different local directory as a shared plugin cache. This means each distinct plugin binary is only downloaded once, and shared by all terraform workloads on the same machine.&lt;/p&gt;

&lt;p&gt;As a bonus, if you’re using container images for this process, these same pre-built container images can be re-used for local development on developer machines, for a consistent and fast development and testing environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  Approval gates
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches"&gt;Branch protection rules&lt;/a&gt; are commonly used to disable forced pushes, ensure that pull requests (PR’s) are reviewed, and ensure that required CI tests have passed before allowing a change to a protected branch.&lt;/p&gt;

&lt;p&gt;In most scenarios, when a PR is raised, an associated CI/CD pipeline is triggered. On successful execution of the pipeline, and when the required tests and validations have passed, this is often sufficient evidence that the PR can be merged, either automatically or with additional reviews and approvals. This can often be enough, but in some cases you might want to conditionally block a build from proceeding, and require a privileged user to review the status and results of steps within the build, and optionally unblock it.&lt;/p&gt;

&lt;p&gt;Requiring approval in a CI/CD pipeline instead of relying solely on branch protection rules can be more adaptable and situationally responsive. There are scenarios where it might be preferable to block a CI/CD pipeline and require manual approval. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sensitive infrastructure changes&lt;/strong&gt;: Certain modifications, such as those affecting security groups, databases, or critical services, can have broad implications. For these high-stakes changes, an additional layer of manual oversight in the CI/CD pipeline, regardless of the branch being used, can prevent potential mishaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource cost implications&lt;/strong&gt;: If a change might spin up numerous resources or higher-tiered services that could increase costs substantially, having an approval step can help teams manage and be aware of budgetary impacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production deployments&lt;/strong&gt;: When pushing changes to production environments, extra caution is often warranted. Even if code has passed all branch-related checks, a final approval before production deployment ensures that changes are validated in the context of the production environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateful operations&lt;/strong&gt;: Operations that can cause data loss, like database migrations or schema changes, may benefit from a pipeline approval process. This ensures a deliberate confirmation step before proceeding with potentially irreversible actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex multi-service deployments&lt;/strong&gt;: For changes spanning multiple services or microservices, coordinating deployments can be challenging. An approval step can allow teams to ensure that interdependencies are correctly managed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regulatory and compliance concerns&lt;/strong&gt;: For organizations subject to regulatory requirements, having a manual approval step can assist in maintaining compliance, ensuring that changes align with legal and industry standards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incident response and outages&lt;/strong&gt;: During active incidents or outages, it might be necessary to halt all deployments temporarily. Even if the code change isn't directly related to the ongoing issue, introducing new changes could complicate mitigation efforts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance windows&lt;/strong&gt;: Some organizations have predetermined maintenance windows where changes are allowed. Outside of these windows, deployments might be blocked, requiring manual approval to proceed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation of external resources&lt;/strong&gt;: If a change relies on external APIs, datasets, or services, you might want to have an approval gate to ensure these external resources are available, updated, and working as expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Branch protection rules are valuable for enforcing coding standards and collaborative practices. However approvals that are embedded as part of the CI/CD workflow provide a more flexible way to address real-time operational, financial, and security concerns. &lt;/p&gt;

&lt;p&gt;Buildkite has &lt;a href="https://buildkite.com/docs/pipelines/block-step"&gt;block steps&lt;/a&gt;, these can be used at any point in the middle of a build to conditionally block a build from proceeding using custom logic. For example, a build provisioning new infrastructure can be blocked (or paused) following the &lt;code&gt;terraform plan&lt;/code&gt; or &lt;code&gt;terraform apply&lt;/code&gt; in Staging, and only be progressed to &lt;code&gt;terraform apply&lt;/code&gt; in Production following a review and approval by a particular user. Block steps, when used together with &lt;a href="https://buildkite.com/docs/pipelines/defining-steps#dynamic-pipelines"&gt;dynamic pipelines&lt;/a&gt;, can provide a framework for deciding the next course of action to take, based on user input.&lt;/p&gt;
&lt;h2&gt;
  
  
  Managing CI/CD with Terraform
&lt;/h2&gt;

&lt;p&gt;Terraform isn't just for orchestrating application resources and infrastructure; it can also be used for managing CI/CD pipelines, and other resources in your CI/CD platform. While managing the CI/CD platform is often out of scope for an individual project, utilizing Terraform in this capacity allows for a unified, version-controlled approach to both infrastructure and CI/CD configuration.&lt;br&gt;
Buildkite has an &lt;a href="https://registry.terraform.io/providers/buildkite/buildkite/latest/docs"&gt;official provider in the Terraform Registry&lt;/a&gt; that can be used to configure and manage pipelines, teams, clusters, and more.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?si=_8hjgZdX7wSJJ_QZ&amp;amp;v=s0GoxgRjy80&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://community.ops.io/images/3i1d6aqij0TLo4Ay5xlRs6JBWmoqUDUTFhcT1_-Srdo/rt:fit/w:800/g:sm/mb:500000/ar:1/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS9z/MEdveGdSank4MC9t/YXhyZXNkZWZhdWx0/LmpwZw" height="450" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?si=_8hjgZdX7wSJJ_QZ&amp;amp;v=s0GoxgRjy80&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          Managing CI/CD resources with Terraform - Demo - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Discover how to use Terraform to manage a build and deploy stack. This demo show you how to:– Integrate and manage Github and Buildkite– Create and manage Bu...
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://community.ops.io/images/DZZa3_Uh_L5DI3dIVM5YQEjP1YIMy0F9cDzBk1LLBMg/rt:fit/w:800/g:sm/mb:500000/ar:1/aHR0cHM6Ly93d3cu/eW91dHViZS5jb20v/cy9kZXNrdG9wL2Zj/ODE1OWU4L2ltZy9m/YXZpY29uLmljbw" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



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

&lt;p&gt;There are heaps of best practices, tools, and strategies available for working with Terraform, but it's important to remember that it's not always necessary —or feasible— to adopt them all at once.&lt;/p&gt;

&lt;p&gt;I've explored a number of practices and configurations that I hope will help enhance your Terraform workflows. Remember, starting simple is perfectly acceptable and honestly, advisable too! Don't abstract or over-engineer too early, let your needs, and your project's requirements guide the evolution of your Terraform CI/CD workflows. Every team and project is unique, so it's vital to choose practices that work for you! Have fun!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://buildkite.com/blog/"&gt;Buildkite blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>cicd</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Using GitHub's pull request merge queue in your CI pipelines</title>
      <dc:creator>Mel Kaulfuß 👩🏻‍🦰💻✌️</dc:creator>
      <pubDate>Tue, 28 Feb 2023 01:32:47 +0000</pubDate>
      <link>https://community.ops.io/melissakaulfuss/using-githubs-pull-request-merge-queue-in-your-ci-pipelines-58ng</link>
      <guid>https://community.ops.io/melissakaulfuss/using-githubs-pull-request-merge-queue-in-your-ci-pipelines-58ng</guid>
      <description>&lt;p&gt;There's been quite an audible buzz of excitement about GitHub's &lt;a href="https://github.blog/changelog/2023-02-08-pull-request-merge-queue-public-beta/"&gt;pull request merge queues&lt;/a&gt; from our users recently. The merge queue pattern increases the efficiency and stability of software delivery workflows by automatically handling pull request merges into your busiest target branches. Merge queues ensure that pull requests get merged in the correct order, and significantly reduce CI workloads, and potential delivery blockers.&lt;/p&gt;

&lt;p&gt;Prior to merge queue, merging pull requests into main involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merging main into the pull request branch, so changes wouldn’t break main when merged&lt;/li&gt;
&lt;li&gt;Kicking off a build in CI (for these newly pulled and merged changes)&lt;/li&gt;
&lt;li&gt;Discovering another PR was merged before the build finished&lt;/li&gt;
&lt;li&gt;Rinse and repeat 🌀😵‍💫&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Finally&lt;/em&gt; merging the PR into main&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/ouEovr75Bnypcg4mV0lpNvRnTR4uOX9ZXZ5SSHCYUiY/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL201dXRp/dHRnNTczazg1OXdn/YWh1LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/ouEovr75Bnypcg4mV0lpNvRnTR4uOX9ZXZ5SSHCYUiY/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL201dXRp/dHRnNTczazg1OXdn/YWh1LnBuZw" alt="The endless cycle that is main branch diverging from the version in your pull request branch :(" width="880" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d often be rushing to get my PR merged before someone else snuck theirs through, or I’d forget to pull the latest changes from main into my feature branch before pushing to main. Often, my PR merge would fail, because it wasn’t in sync with the main branch. I saw red–quite literally.&lt;/p&gt;

&lt;p&gt;Merge queues automatically build pull request branches with the most recent change (or PR) to be merged ahead of it in the queue. 🙌&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/oyOs546KICkVVlDMrOpZ0ZGoeK1OA25wAXWtLNXFrzU/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL3ozMTU5/cnlvNTNsYmhlYWVn/dTBkLnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/oyOs546KICkVVlDMrOpZ0ZGoeK1OA25wAXWtLNXFrzU/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL3ozMTU5/cnlvNTNsYmhlYWVn/dTBkLnBuZw" alt="GitHub's pull request merge queue automatically builds PRs with the previous commit in the queue." width="880" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The merge queue isn’t new – &lt;a href="https://www.uber.com/blog/research/keeping-master-green-at-scale"&gt;Uber&lt;/a&gt; and &lt;a href="https://shopify.engineering/introducing-the-merge-queue"&gt;Shopify&lt;/a&gt; have been using them for close to five years in their pipelines in order maintain high throughput and low commit turnaround time on their busy main branches (think thousands of merges to main per day!). If you have lots of contributors, and high volume of pull requests going to main and if you have frequent flaky merges blocking subsequent commits and creating a backlog of builds, you might find pull request merge queue to be a useful tool,as it minimizes merges tripping over each other on the way to main. There’s some good news if you’re a GitHub Enterprise or open source organization, because you can easily play with it now too: &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/merging-a-pull-request-with-a-merge-queue"&gt;merge queue is now available in public beta&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using merge queue in your Buildkite pipelines
&lt;/h2&gt;

&lt;p&gt;Include the &lt;code&gt;gh-readonly-queue/{base_branch}/*&lt;/code&gt; filter pattern in &lt;strong&gt;Pipeline Settings → GitHub → Branch Limiting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/rs_owyhIfavwBoQjGdig8nGGQyKbz4frtyVNwZknaMU/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL3BkbnB2/Y2FtNWN2eGsxbHFj/dDd6LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/rs_owyhIfavwBoQjGdig8nGGQyKbz4frtyVNwZknaMU/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL3BkbnB2/Y2FtNWN2eGsxbHFj/dDd6LnBuZw" alt="Displays the branch filter pattern that's found in the branch limiting setting" width="880" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the newly-released “Skip builds with existing commits” checkbox in your &lt;strong&gt;Pipeline Settings → GitHub → GitHub Settings&lt;/strong&gt;. This will ensure that you will no longer kick off any unnecessary builds (because there's a green build on the commit ahead of your PR in the merge queue). 🤯&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/0vyVMdgZE4hFFWkOtJY4yy0xc-i880qb13NTvZ-ghp0/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL20zM3N2/Z2tieXdwMnowOXJ0/eDRjLnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/0vyVMdgZE4hFFWkOtJY4yy0xc-i880qb13NTvZ-ghp0/w:880/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL2Fy/dGljbGVzL20zM3N2/Z2tieXdwMnowOXJ0/eDRjLnBuZw" alt='Check the new "skip builds with existing commits" checkbox in your pipeline GitHub settings.' width="880" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The efficiency merge queue offers is exciting, though you should keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is an experimental feature and GitHub has flagged that it’s likely to change&lt;/li&gt;
&lt;li&gt;Configuring branch protection and required checks don’t currently work, and are on our radar in the next couple of months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's more about how to use it in the &lt;a href="https://buildkite.com/docs/tutorials/github-merge-queue"&gt;Buildkite docs&lt;/a&gt;, you can obviously also use merge queue in GitHub Actions, and in some other CI/CD providers, so go try merge queues out – I'd love to hear what you think!&lt;/p&gt;

</description>
      <category>buildkite</category>
      <category>cicd</category>
      <category>github</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Managing Buildkite CI/CD resources with Terraform</title>
      <dc:creator>Mel Kaulfuß 👩🏻‍🦰💻✌️</dc:creator>
      <pubDate>Mon, 21 Nov 2022 23:20:57 +0000</pubDate>
      <link>https://community.ops.io/melissakaulfuss/managing-buildkite-cicd-resources-with-terraform-2702</link>
      <guid>https://community.ops.io/melissakaulfuss/managing-buildkite-cicd-resources-with-terraform-2702</guid>
      <description>&lt;p&gt;Those responsible for managing infrastructure and associated user accounts &amp;amp; permissions across a large team understand the importance of having a central place to manage these things. They’ll also understand that the ease with which they’re able to manage these things has a direct correlation with potential security risks and scalability. Eliminating manual tasks is a core principle of SRE, especially when it reduces cost, and automation improves how quickly we can adapt to change; in technologies, environments, and integrations with third-party tools, and reduces the chance of human error.&lt;/p&gt;

&lt;p&gt;Infrastructure as Code exists as a way to improve efficiency, remain nimble and adaptable to change. It can also be the key to fostering DevOps practices in engineering teams – allowing developers to better understand and be involved in defining configuration as part of their software development processes.&lt;/p&gt;

&lt;p&gt;I probably don't need to tell you that defining configuration as code; provides consistency and clearly documents conventions, reduces the chance of introducing errors and allows infrastructure changes to be tested just like application code.&lt;/p&gt;

&lt;p&gt;It also offers all the benefits that using a source control manager provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralised read/write and team permissions&lt;/li&gt;
&lt;li&gt;Rollbacks and versioning&lt;/li&gt;
&lt;li&gt;A robust commit and review process on PRs&lt;/li&gt;
&lt;li&gt;Surfaces configuration details for others to learn from and contribute to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s no surprise that our users also want to manage their CI/CD resources with Infrastructure as Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Buildkite Terraform provider
&lt;/h2&gt;

&lt;p&gt;I recently caught up with Chloe Hutchison from Elastic to talk about how they use Terraform internally to manage CI/CD.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“&lt;em&gt;The less you have to think about as a software engineer the better. We try to make magic like that happen everywhere as much as possible.” And that’s why we use the Terraform provider…It helps us reinforce best practices and standards. And by defining pipelines via Terraform, it helps the CI team make sure that everybody's using pipelines defined in the repository, and away from the Buildkite UI in terms of making steps and pipelines – which we think is the best pattern because it's next to their code and keeps everything consistent between teams. We tell them: Don't think about that; just use this.&lt;/em&gt;” –– &lt;strong&gt;Chloe Hutchison, CI Team Lead &amp;amp; Principal SRE, Elastic&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many of our users rely on Terraform to manage their infrastructure and over the years have built and used their own custom Buildkite providers. We now have an official Buildkite provider in the Terraform Registry that is built and fully supported by us. Our users are now better able to manage their Buildkite CI/CD resources as infrastructure as code.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://registry.terraform.io/providers/buildkite/buildkite/latest/docs"&gt;official Buildkite provider&lt;/a&gt; allows users to configure and manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent tokens&lt;/li&gt;
&lt;li&gt;Pipelines&lt;/li&gt;
&lt;li&gt;Pipeline schedules&lt;/li&gt;
&lt;li&gt;Teams&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Chloe says that the team at Elastic “&lt;em&gt;doesn't like to use 'click-ops' at all if we can avoid it…we had things that were provisioned by 'click-ops' before there were good Terraform providers available and it's a nightmare to manage; changes get lost, people get confused, and nothing is consistent.&lt;/em&gt;”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Elastic’s CI team uses the Buildkite Terraform provider via the &lt;a href="https://github.com/hashicorp/terraform-cdk"&gt;Cloud Development Kit for Terraform&lt;/a&gt; (CDKTF). CDKTF allows users to use familiar programming languages like TypeScript, Go, .NET, Python, and Java to define and provision infrastructure, giving users access to the Terraform ecosystem while also leveraging their existing toolchains.&lt;/p&gt;

&lt;p&gt;Chloe describes the benefits of CDKTF as being, “a programming layer on top of Terraform which allows us to use multiple providers, and do a bit of fancy code around it…I like doing it that way because it means that if there are any limitations with one of the providers that we're using, we can just code around them.”&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“&lt;em&gt;Our teams are using it to set up repos, CI pipelines, team memberships, and to specify Access Control Lists (ACLs). We have a data structure that teams can use to say “Give me a whole new project”. It goes and creates the GitHub repository, the Vault secrets (to be used in CI), Buildkite pipelines, and does a bunch of stuff in the background so that teams get a secure environment for each Buildkite agent they run. It can even set up scheduled builds&lt;/em&gt;”, says Chloe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check out this demo to learn how to use Terraform to configure remote state, include official providers, create a Buildkite pipeline, trigger builds automatically once a commit is pushed to GitHub, and configure an Elastic Stack of autoscaling Buildkite agents in AWS.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/s0GoxgRjy80"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;p&gt;For more information on using Terraform with Buildkite, check out these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/buildkite/buildkite/latest/docs"&gt;Terraform Provider Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jradtilbrook/hashitalks-demo-2022"&gt;Managing CI/CD Resources with Terraform talk demo repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/buildkite/terraform-provider-buildkite"&gt;Official Buildkite Provider GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://buildkite.com/blog/terraform-techniques-with-buildkite"&gt;Terraform Techniques with Buildkite&lt;/a&gt;, Samuel Cochran, 2016&lt;/li&gt;
&lt;li&gt;Join us at &lt;a href="https://buildkite.com/unblock"&gt;Unblock&lt;/a&gt; (our annual user conference) to see more Buildkite and CI/CD related content!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>cicd</category>
      <category>buildkite</category>
    </item>
  </channel>
</rss>
