# 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.

Follow those steps to apply all changes described in this step

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

Our Terraform now have 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 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

You can notice how tags are usually used

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

Notice how usually customer use our defined variables as tag special tags env, project, customer and cycloid.io which are used by some of our features like billing to filter components.

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

note

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 into the Terraform plan and apply job, we will introduce a standard YAML tip, allowing us to reuse YAML code as a template YAML Alias indicators.

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


 














 
 




 

 


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

This code above defines a pipeline task that we will use in our terraform plan an terraform apply jobs. The task expect 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 define now 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 reads the output of the plan and if it's ok, manually triggers the terraform-apply job by clicking on it and press the + button plus button

We also added our special task defined earlier *task-get-commit in both jobs. As saw above this task create a variable file extracted-vars/terraform.tfvars that we specify during the call of the Terraform resource using 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, click on the Refresh pipeline button Refresh pipeline.

It's time to create our instance, run the terraform-plan job, the output should inform 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.