# Create a Terraform module and add Terraform apply job in the pipeline

Pipeline step4

Terraform plan job is now configured and working. It's time to push some real Terraform codes to describe a cloud instance using Terraform module format (opens new window).

Follow those steps to apply all changes described in this step

The previous lines create a Terraform module that contains the description of a cloud instance, and an init bash script to run at startup. To know more about Terraform module, feel free to read the Terraform module documentation (opens new window)

Our Terraform now has the following files:

  • A module under the terraform/module-<provider_name> directory
  • A main Terraform file to simply call the module terraform/main.tf
  • An output (opens new window) file terraform/outputs.tf to forward module outputs to the pipeline. We will use it later.
  • Several files inside the module
    • Module output file terraform/module-<provider_name>/outputs.tf to specify which information the module will return.
    • Variables file terraform/module-<provider_name>/variables.tf to define what are the parameters we can pass to the module.
    • A terraform/module-<provider_name>/vm.tf file containing the definition of the cloud instance.
    • The template of the startup script terraform/module-<provider_name>/userdata.sh.tpl run in the instance

TIPS

Notice how customers usually use our defined variables with those specific tags env, project, customer, and cycloid.io, used by some of our features like billing to filter components.

  tags {
    "cycloid.io" = "true"
    env          = var.env
    project      = var.project
    customer     = var.customer
  }
}
1
2
3
4
5
6
7

The userdata.sh.tpl startup script will use our two previously defined variables to know the URL and the version (commit id) we want to deploy on it.

stack-sample/terraform/provider.tf

variable "git_code_commit" {
  default = "origin/code"
}
variable "git_code_repo" {
  default = "https://github.com/cycloid-community-catalog/docs-step-by-step-stack.git"
}
1
2
3
4
5
6

NOTES

We use a startup bash script to deploy the website source code on our instance to make this guide easier. On production, you might want to go using dedicated tools like Ansible

Our new Terraform code now requires 2 variables to be run git_code_commit and git_code_repo. To get the commit id we will need a little bash script. As we will need it in the Terraform plan and apply job, we will introduce a standard YAML tip, allowing us to reuse YAML code as a template with YAML Alias indicators (opens new window).

To summarize it will allow us to define a YAML code that we will put under shared section, and the name &task-get-commit. That we will be able to reuse using *task-get-commit.

stack-sample/pipeline/pipeline.yml


 














 
 




 

 


shared:
  - &task-get-commit
    task: get-commit
    config:
      platform: linux
      image_resource:
        type: docker-image
        source:
          repository: cycloid/cycloid-toolkit
          tag: latest
      run:
        path: /bin/bash
        args:
        - -c
        - |
          DIR=${PWD}
          echo "git_code_commit = \"$(cat git_code/.git/ref)\"" | tee -a extracted-vars/terraform.tfvars
          echo "git_code_repo = \"${GIT_CODE_REPO}\"" | tee -a extracted-vars/terraform.tfvars
      params:
        ENV: ((env))
        GIT_CODE_REPO: ((code_git_public_repository))
      inputs:
        - name: git_code
      outputs:
        - name: extracted-vars
          path: "extracted-vars"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

The code above defines a pipeline task (opens new window) used in our terraform plan and terraform apply jobs. The task expects our website source code (git_code) as input. Then run a bash script to generate a terraform variables file terraform.tfvars in the defined output extracted-vars.

The Terraform code is now ready to be tested and applied. To follow best practices, we will implement a "manual validation" in the pipeline to run terraform apply after a terraform plan.

Let's now define the Terraform apply job using our previously defined task.

stack-sample/pipeline/pipeline.yml






 










 




 
 






 



 





 




 
 

groups:
- name: overview
  jobs:
  - unittest
  - terraform-plan
  - terraform-apply

jobs:
  - name: terraform-plan
...
    plan:
      - do:
        - get: git_code
          trigger: true
          passed:
            - unittest
        - *task-get-commit
        - put: tfstate
          params:
            plan_only: true
            terraform_source: git_stack/stack-sample/terraform
            var_files:
              - extracted-vars/terraform.tfvars

  - name: terraform-apply
    build_logs_to_retain: 10
    plan:
      - do:
        - get: git_stack
          trigger: false
          passed:
            - terraform-plan
        - get: tfstate
          trigger: false
          passed:
            - terraform-plan
        - get: git_code
          passed:
            - terraform-plan
        - *task-get-commit
        - put: tfstate
          params:
            plan_run: true
            terraform_source: git_stack/stack-sample/terraform
            var_files:
              - extracted-vars/terraform.tfvars
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

This sample implements a manual approval by adding the terraform-apply without any trigger: true configured on the get resources. That means the terraform-apply will never be triggered automatically. The usual workflow is to trigger terraform-plan, let a user read the plan's output, and if it's ok, manually trigger the terraform-apply job by clicking on it and pressing the + button plus button

In both jobs, we added our special task defined earlier, *task-get-commit. As seen above, this task creates a variable file extracted-vars/terraform.tfvars that we specify during the call of the Terraform resource using the var_files option.

Add and commit those changes in Git:

git add .
git commit -m "Step 4"
git push origin stacks
1
2
3

Get back to Cycloid's dashboard, and click on the Refresh pipeline button Refresh pipeline.

It's time to create our instance, run the terraform-plan job, the output should inform us that Terraform is about to create our new cloud instance:

Terraform plan

If everything is ok for you, as described earlier, go on the terraform-apply job and trigger it:

Terraform apply

After the apply, a new cloud instance should be present on your cloud provider console.


# Key points to remember