GitLab - terraform plan and apply

How do you apply changes in terraform? In most cases you run terraform plan and then terraform apply and type yes. This approach works great on your local machine, but how to apply changes (and only the changes you want!) in GitLab job where you do not have access to shell? How to do that, when you cannot approve the output of apply command?

You can use terraform apply -auto-approve, but it might be risky... No one likes to destroy something on production without a priori knowledge. So, can we run terraform plan, check the output and then run terraform apply in another step? We can, but still it might be risky operation. Why? Because plan and apply are separated operations! They know nothing about each other. So, apply can change something which was not showed in plan.

But... according to Terraform Documentation:

The optional -out argument can be used to save the generated plan to a file for later execution with terraform apply, which can be useful when running Terraform in automation.

This way we can run terraform plan with -out parameter and then use this file in terraform apply command. Therefore, we know that only changes from plan command will be applied (if there were any, of course).

So, let's say that we have two environments: dev and prod. We want to run terraform plan command, and if everything is alright, we want to run terraform apply and it should apply changes from the plan only. We need to save a file from plan command and use it later in apply. For this we can use artifacts keyword. We will also use some other keywords. Here is the first part of the .gitlab-ci.yml file (link to the repository you will find at the end of the post):

We keep our terraform logic in dev and prod catalogs. We have two stages: plan and apply.

    .common job: This is a hidden job which will be used later by extends keyword. We provide terraform image (obviously) and entrypoint for this image (by default, entrypoint is set to terraform command, so without changing entrypoint we couldn't run some commands like cd for example). The job will be manual, and we also have before_script section. We want to go to dev or prod ($ENV_NAME environment variable) catalogue and run terraform init command.

    .plan job: Another hidden job which will be used later. Here we inherit from .common job using extends keyword, we set stage to plan, and run terraform plan command with param -out. This way we tell terraform to save the plan to a specific file: $ENV_NAME.plan. This environment variable $ENV_NAME we will provide in another jobs. Additionally, we set artifacts section. With this we can easily save whatever we want and use it later in other jobs. So, here we want to keep the generated plan.

    .apply job: Here we also inherit from .common job, but stage is set to apply, and the command in script section is terraform apply -input=false $ENV_NAME.plan. We will use this job and .plan soon...

So we have a backbone; now, we are going to add the rest of stuff. We have two catalogs: dev and prod. I am going to provide a code only for dev jobs, because the logic for prod is the same. The only difference is the name of jobs and the value of $ENV_NAME environment variable.

We have three jobs here, but one of them is hidden so in a pipeline you will see only two jobs: dev plan and dev apply.

    .dev job: we only set a variable here.

    dev plan job: in this job we only inherit from the previous ones: .plan and .dev. We get the whole logic from .plan job, and from .dev we get info about $ENV_NAME environment variable.

    dev apply job: it is similar to dev plan job, but we inherit from .apply job. Additionally, we use dependencies keyword, telling GitLab that we want artifacts only from dev plan job.

The same thing has been done for prod environment. So, the pipeline looks that:

The pipeline

In dev plan job you will find such output:

dev plan

As you can see, terraform saved the plan to dev.plan file (in dev catalog) and the artifact has been uploaded. In the next job dev apply you will find this guy:

dev apply

And here, we used dev.plan file which has been uploaded by dev plan job! That's it!

Here is the link to the repository terraform-plan-apply; you will find the rest of code there.

And here is a video:


 

Comments

Popular posts from this blog

GitLab - extends keyword

Managing Secrets in GitLab / Git

GitLab - trigger keyword