DEV Community

Cover image for Comprehensive Guide to Infrastructure Continuous Integration and Continuous Deployment with Terraform and GitLab CI/CD
Azeez Lukman
Azeez Lukman

Posted on

Comprehensive Guide to Infrastructure Continuous Integration and Continuous Deployment with Terraform and GitLab CI/CD

The practice of Infrastructure as Code (IAC) has witnessed an exponential surge in popularity in recent years. DevOps engineers, in particular, have found IAC to be a transformative approach, enabling them to automate infrastructure provisioning and management, resulting in more efficient and scalable processes.

In this comprehensive guide, we will delve into the world of Infrastructure Continuous Integration (CI) and Continuous Deployment (CD) with GitLab, tailored for advanced DevOps professionals aiming to optimize their workflows.

Getting to Know Terraform

Before we embark on our journey into CI/CD with GitLab, it's crucial to have a solid understanding of Terraform. Terraform, developed by HashiCorp, is a powerful open-source Infrastructure as a Code tool. It empowers users to define their infrastructure as code, offering a clear and repeatable method for provisioning and managing resources across diverse cloud providers and on-premises environments. Some key advantages of Terraform include:

  • Declarative Configuration: Terraform employs a declarative syntax, allowing you to describe the desired end state of your infrastructure, while Terraform determines the steps to achieve it.
  • Multi-Cloud Support: Terraform boasts compatibility with a multitude of cloud providers, facilitating the implementation of a multi-cloud strategy.
  • Version Control: Terraform configurations are stored in version control repositories, fostering collaboration and change tracking.

Overview of GitLab CI/CD

GitLab encompasses a comprehensive DevOps platform with robust CI/CD capabilities. It empowers DevOps engineers to automate processes such as code testing, building, and deployment, including Terraform configurations. The fundamental components of GitLab CI/CD include:

  • Runners: GitLab Runners serve as agents responsible for executing CI/CD jobs. They are adaptable and can be installed on various platforms, ensuring that your pipelines can run tasks across diverse environments.
  • Pipelines: Pipelines define the complete process of code building, testing, and deployment.
  • Jobs: Each job within a pipeline represents an individual task, such as executing tests or applying Terraform configurations. We will also explore how to use GitLab's artifact feature to store files and data generated by a job, which is invaluable for transferring data between jobs or stages within a pipeline.

Setting Up GitLab for Terraform

To kickstart your GitLab CI/CD journey for Terraform, the initial step is to create a GitLab project or repository to host your Terraform code. Properly structuring your Terraform project within a Git repository is important for effective version control and collaboration. Consider organizing your repository with directories dedicated to different environments (e.g., development, staging, production) and adopt Git branching strategies.

Creating CI/CD Pipelines for Terraform

GitLab CI/CD pipelines are defined using a .gitlab-ci.yml file situated within your repository. This YAML file outlines the stages, jobs, and dependencies for your CI/CD process. For Terraform, your CI/CD pipeline may encompass stages such as:

  • Linting: The process of examining Terraform code for syntax and style-related issues.
  • Validation: Ensuring that Terraform configurations are both valid and safe to apply.
  • Planning and Applying: Creating an execution plan and implementing changes to the infrastructure.
  • Testing: Running infrastructure tests to validate that the changes meet the specified requirements.

*Create the Pipeline File*

Start by creating a file named .gitlab-ci.yml in your GitHub project. This YAML file will define your CI/CD pipeline configuration. The file should be created in the root directory to ensure that GitLab can automatically detect and use it to set up and run your CI/CD pipeline for that project. If you place it in a subdirectory, GitLab will not recognize it as the configuration file for the project's pipeline.

touch ****.gitlab-ci.yml****
Enter fullscreen mode Exit fullscreen mode

Define Stages and Jobs

Inside your .gitlab-ci.yml file, define the stages and jobs for your Terraform CI/CD pipeline. Here's an example configuration:

stages:
  - plan
  - apply

variables:
  TF_VAR_AWS_ACCESS_KEY_ID: "<AWS_ACCESS_KEY_ID>"
  TF_VAR_AWS_SECRET_ACCESS_KEY: "<AWS_SECRET_ACCESS_KEY>"

plan:
  stage: plan
  script:
    - terraform init
    - terraform plan -out=terraform.tfplan

apply:
  stage: apply
  script:
    - terraform apply -auto-approve terraform.tfplan
  only:
    - master # You can adjust the branch where you want to apply changes
Enter fullscreen mode Exit fullscreen mode

In this example, we have two stages: plan and apply. The plan stage initializes Terraform and creates a plan file, while the apply stage applies the changes using the generated plan file. We also set two variables, TF_VAR_AWS_ACCESS_KEY_ID and TF_VAR_AWS_SECRET_ACCESS_KEY, which allow you to specify your AWS credentials, remember to replace them with your actual values. More information on securing the use of these credentials will be provided later in this article.

ok… let’s break things down a little more

In GitLab CI/CD, a pipeline is divided into stages, and each stage contains one or more jobs. Stages represent different phases of your CI/CD process, and jobs within each stage define specific tasks to be executed. In the example above, we've defined two stages: plan and apply. Here's what each stage represents:

  • Plan Stage: This is the first stage in our CI/CD pipeline. In this stage, we typically perform tasks related to Terraform planning. Planning involves checking the current state of your infrastructure, comparing it to your Terraform code, and generating an execution plan that describes what changes will be made when you apply your Terraform configuration.

    Within this stage, a job named plan is defined. This job belongs to the plan stage and contains the terraform commands to init and plan the terraform configuration.

  • Apply Stage: The second stage in our pipeline is the apply stage. This is where we actually apply the changes to our infrastructure based on the execution plan generated in the previous stage. Applying Terraform changes may involve creating, updating, or deleting resources in your cloud infrastructure.

    The job within this stage apply stage specifies the script that defines the commands to be executed as part of the job. Here's what the command does:

    • terraform apply -auto-approve terraform.tfplan: Applies the Terraform changes using the execution plan generated in the previous stage. The auto-approve flag automatically approves and applies the changes without requiring manual confirmation.
    • only: This section specifies the branch where this job should be executed. In this example, the apply job will only run when changes are pushed to the main branch. You can adjust this branch name to match your specific deployment strategy.

Trigger the pipeline run

The most common and fundamental way to trigger a GitLab CI/CD pipeline is by pushing code changes to your GitLab repository. GitLab is designed to automatically detect changes in your codebase and initiate the CI/CD pipeline accordingly. This seamless integration ensures that your pipeline runs whenever there are new commits or merged branches in your repository. You can also manually trigger a pipeline via the GitLab UI.

Save your .gitlab-ci.yml file and commit it to your GitLab repository. Push the changes to trigger your first CI/CD pipeline.

Once you've pushed your changes, visit your GitLab repository's CI/CD section to monitor the progress of your pipeline. GitLab will automatically detect the .gitlab-ci.yml file and start executing the defined stages and jobs.

Review Pipeline Output

As the pipeline runs, you can review the output of each job and check for any errors or issues. If everything is successful, your Terraform changes will be applied to the specified environment.

Coffee break…

You made it this far Congratulations on your first successful pipeline deployment.

Grab some coffee before we continue to reviewing some more concepts you can put in place to ensure you are securely deploying these changes into your environment.

Manual Approvals

In the previous example, you may have noticed that the pipeline run deployed changes directly into the environment. However, this is usually not desired in production environments.

Manual approvals in GitLab CI/CD are a critical feature that allows you to introduce a human validation step into your automated pipeline. This validation ensures that certain stages or jobs in your CI/CD process won't proceed without manual confirmation, typically by authorized team members. This concept helps maintain control, security, and accuracy in your deployment process, particularly when deploying to production environments or making significant changes to your infrastructure.

To make a job dependent on manual approval, you include the when: manual attribute in the job definition. This attribute tells GitLab that the job should not automatically start but should wait for manual intervention.

Update the apply stage in your configuration to reflect the attribute:

apply:
  stage: apply
  script:
    - terraform apply -auto-approve terraform.tfplan
  only:
    - master # You can adjust the branch where you want to apply changes
  when: manual 
Enter fullscreen mode Exit fullscreen mode

How does the manual approval work?

  1. Manual Trigger: When the pipeline reaches a job that has when: manual, GitLab will display a "Play" button next to that job in the pipeline view. Only users with appropriate permissions can click this button to manually trigger the job.
  2. Authorization: GitLab ensures that only authorized users, such as team leads or administrators, can approve and trigger manual jobs. Access control and permissions are managed through GitLab's role-based access control system.
  3. Manual Intervention: When a team member clicks the "Play" button to trigger a manual job, they may be prompted to provide additional information or confirm the action. This step adds a layer of accountability and documentation to the process.
  4. Job Execution: Once the manual job is triggered, it proceeds as usual, executing the defined script or commands.
  5. Subsequent Jobs: After the manual job has been executed and approved, the pipeline continues with any subsequent jobs that are not set to when: manual.

Securely Storing Secrets

To manage sensitive information like API keys and credentials, utilize GitLab's built-in secret management. Store these secrets as CI/CD environment variables to keep them secure.

Securing secrets in GitLab CI/CD is a critical practice for several important reasons. It is not just a best practice; it's a fundamental aspect of maintaining the security, compliance, and reliability of your automation processes. By implementing robust secret management practices, you reduce the risk of data breaches, protect sensitive information, and enhance the overall security posture of your projects.

GitLab provides a robust mechanism for managing and securely accessing secrets. Let's explore this concept step by step:

Access Project Settings

  1. Log in to your GitLab account.
  2. Navigate to the project where you want to store secrets.
  3. Click on "Settings" in the project's sidebar.

Configure CI/CD Variables

  1. In the project's "Settings" page, click on "CI/CD" in the left sidebar.
  2. Scroll down to the "Secret variables" section and click "Expand."
  3. Here, you can add and manage secret variables specific to your project. These variables are securely encrypted and can be accessed by CI/CD jobs.

Add Secret Variables

  1. Click on the "Add variable" button.
  2. Fill in the variable details:
    • Key: This is the name of the secret variable, e.g., API_KEY.
    • Value: Enter the actual secret value, such as an API key or password.

Add secret variables for both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY these would be available in the environment variables of your pipeline run and are automatically passed to the terraform.

Protect Secret Variables

GitLab provides an option to protect secret variables. When a variable is protected, it cannot be accessed by unprotected branches or tags. This is useful for ensuring that sensitive secrets are only available in authorized environments.

Accessing Secret Variables in CI/CD Jobs

Now you can securely utilize the secret variables in your pipeline. Update the variables section of your YAML file as shown below:

variables:
  TF_VAR_AWS_ACCESS_KEY_ID: "$AWS_ACCESS_KEY_ID"
  TF_VAR_AWS_SECRET_ACCESS_KEY: "$AWS_SECRET_ACCESS_KEY"
Enter fullscreen mode Exit fullscreen mode

These variables are used to pass the AWS access key and secret access key to the Terraform commands. They are denoted by the $ symbol.

Conclusion

The implementation of Infrastructure Continuous Integration and Continuous Deployment with GitLab for Terraform equips DevOps engineers with the means to streamline their workflows, ensure infrastructure reliability, and expedite the delivery of new features and updates. By embracing IAC principles and leveraging GitLab's CI/CD capabilities, you are well-prepared to meet the demands of modern infrastructure management.

Begin by establishing your GitLab project, defining CI/CD pipelines, and integrating Terraform. As you gain experience, delve into advanced concepts to further enhance your journey in infrastructure automation. The synergy between GitLab and Terraform provides a formidable toolkit for achieving excellence in Infrastructure as Code.

Additional Resources

Top comments (0)