YAML based Azure Pipeline approach for CI/CD of Terraform workspace

This note is a short addition to the last post –CI/CD of Terraform workspace with YAML-based Azure Pipelines. At the end of that article, I mentioned a few alternate routes regarding YAML-based Azure Pipelines, which I will be covering here.

So that we are on the same page, I broke down the build pipeline into four stages: Validate, Dev, Test, and Prod. Under stage: validate, I added a job that included terraform init and terraform validate for all workspaces. Then under stage: Dev, I added three jobs – plan, approve, and apply. Finally, I followed the same approach for stage: Test and stage: Prod.

After following the build workflow in the azure-pipelines.yaml file, you must have made at least two observations.
Observation 1: under both the jobs job: plan and job: apply (inside stages -Dev, Test, and Prod), I have included a terraform init task despite having initialized under the stage: validate.

Observation 2: The terraform plan task (inside job: plan) does not create a tfplan file for the terraform apply task (inside job: apply) to consume. That means, if there is a state change in the underlying infrastructure from the time the last terraform plan was run, review and approved, to the time the terraform apply was run, the plan applied may not be the same plan that was reviewed.
Let me explain that via an example. Say, at a terraform plan task step, based on the existing underlying infrastructure state, terraform created a list of resources and their desired states. This list of resources and their desired states is known as a plan. The following job is job: approve where someone reviews the list of resources and their desired states and based on what is required -resumes or rejects the job. While that is being reviewed, say someone else makes a change to the underlying infrastructure state. This state change is not captured in the review process of the previous step. Then, when terraform apply is run, it won’t make the same state changes that were captured in the terraform plan task. But instead will take into consideration the state change that was made after the terraform plan was run. This renders the review process meaningless. The solution is to create the desired state file (aka tfplan file) at the terraform plan task. And then use the tfplan file in the terraform apply step. This approach ensures that plan file that is review is the same plan file that is applied to the underlying infrastructure. However, given that the time in the review stage is only 15 mins in the use case, the chances of an infrastructure state change are relatively low -but it is possible.
I came up with a few options that may address these two observations, and I have listed below the pros and cons of these approaches.

Option 1: No build artifact is created after stage: validate
This is the current state of the azure-pipelines.yaml file.
Pros: (i) the approach is secure since no artifacts are accessible to anyone who may have read permission to the build pipeline.
Cons: (i) terraform init is run multiple times on the same code base, and (ii) no plan file is created, and hence the observation 2 becomes a concern.

Option 2: Create a build artifact after stage: validate
Pros: (i) Run terraform init only once.
Cons :(i) The build artifact is heavy since it includes the .terraform folder that contains the provider package, and (ii) the build artifact also contains a state file that exposes the secret access keys that terraform uses to provision resources. As a result, anyone who has read permission to the build pipeline may access the keys. This is a security flaw.

Option 3: Create a build artifact after stage: validate and create a tfplan file in terraform plan step and use that same tfpan file in the terraform apply step.
Pros: (i) Run terraform init only once, and (ii) address the concern raised in observation 2 above.
Cons: Same as the previous option.

All the options mentioned above will work, and we have to choose the one that makes the most sense to us. There can also be variations to the above options to address the “cons” that I have raised. Like, instead of creating a build pipeline artifact, the artifact may be stored in a shared folder with restricted access. Whichever option is chosen, my advice would be to take security concerns very seriously.

I hope you found this note useful. Would you mind commenting if you believe I have missed or misstated something?

3 thoughts on “YAML based Azure Pipeline approach for CI/CD of Terraform workspace

  1. A very well article. Thanks to your article I have implemented the complete CI/CD process. I would request if the implementation of the secuirty flaws of publishing artifacts done would be really great.

    Liked by 1 person

    1. Hi Nabeel,
      Sorry for responding late to your question. You see, the artifact will also contain the .terraform folder. Depending on whether you provisioned to store the state file in a remote location (s3 for AWS) or local (no backend), there must be a terraform.tfstate file inside the .terraform folder. That state file has the access-key and secret-key information. If you configured a backend, that tfstate file would have access information to the s3 bucket -access and secret key. If you have not configured the backend, the tfstate file will have information on the entire infrastructure that Terraform provisioned -access-key and secret-key. It would help if you never shared these secret and access keys, and hence I discourage, creating an artifact with the `.terraform` folder. Let me know if that explanation helped.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s