Conf42 DevOps 2024 - Online

Multiple Terraform Projects in a Mono-Repo. How to Survive a Mess?

Video size:


Do you have a set of projects sitting in a mono-repo and having various workspaces, structures, and Terraform versions? The pain of switching the versions and remembering all combinations? I’d share my experience in managing such projects. How is it related to Docker Compose? I’ll tell you…


  • Today we will try to answer a question, how to survive that mess. I am a DevOps engineer and I used to be a software engineer. I try to automate things and I fan of clean code and open source. Our agenda for today is first I will explain prerequisites that we have. Then we will have a problem deep dive.
  • Docker projects have various workspaces, various structures and terraform versions. With terracompose, use cases are actually here actions that we can run. It's a very clean approach with config as a code and it has high visibility and traceability.
  • First demo project is a common so as I mentioned, it will create a vpcs for all our projects. Second demo project contains for speed and for readability projects. We have two environments, nonprod and production. It's very useful for development, but not out of development.
  • In the future plans I want to make actions customizable and also allow users to create a custom action or override default action. It's an open source and it's a simple yaml that you can create for 1 hour. I encourage you to contribute to this project.


This transcript was autogenerated. To make changes, submit a PR.
Hello everyone and welcome to my talk multiple terraform projects in a Monorepo and today we will try to answer a question, how to survive that mess. A couple words about me. I am a DevOps engineer and I used to be a software engineer. I try to automate things and I fan of clean code and open source and infrastructure as a code. Here you can find QR code and link to my profile on LinkedIn. So if you have questions about this terrap compose tool, or if you want to just talk with me about some DevOps topics or something else, you can reach me out there. So let's start when someone tell you that it has everything as an infrastructure, as a code, it might be something similar to that picture. So it probably not something that you are expecting out of these words, right? So you can see there as a code, but it may be copy pasted, or maybe not clean, or maybe not updated and so on. So it might be different expectations out of it. I was in this situation several times and today I will try to guide you through my experience out of managing such projects. And our agenda for today is first I will explain prerequisites that we have. Then we will have a problem deep dive. Then I will try to explore existing solutions. What we have there, I will introduce terracompose then and a short demo and a couple final words in the end. So let's start. We have as a prerequisite a set of projects in a monorepo. They have various workspaces, which is mostly equal to environments, various structures and terraform versions. And if we look into our projects, you might have similar situation that you have some projects in your folder, whatever name I select projects. And you might also have some common stuff like creating vpcs or something like that which is shared across these projects. And it might have a little bit different location here. In this example it's a common, you can see it on the left picture. Also you can have environments based approach of structures. Your projects, as you can see on the right picture with modules. When we talk about various workspaces, mostly we talk about different number of workspaces per project and different naming combinations. So some of the projects might have staging and production only, some might have life stage and so on. Also we have different structures here because some of the projects might have components which add additional level of the file structure. And in the end we might have different terraform versions across the projects and also different versions inside the project between different workspaces, because you might be in the process of upgrading terraform and you have on managing, for example, one version of terraform. And in production still you can have different version. Okay, so let's summarize our complaints. We have following pain points, different terraform versions, and it's a pain of switching terraform versions on local between different projects. Also you have there a risk of incompatibility of the snippets. And also here we have a problem that our workspaces not visible and not trackable by versions control system. And that's why you need to memorize everything. And it creates a problem when you involve someone for troubleshooting or onboard newcomers. Aren't there any solutions on the market? Let's see. So we have terraform cloud, which is famous SaaS solution. It's a native solution for terraform and it's really good, but it's a pricey solution and it's not solve everything that we need. So it still for us has a low visibility for workspaces. And you still need to memorize your structure and workspaces and the price for sure. If we take a look into other tools, terraground, terramate, terraspace, they are also really good and they cover some pain points, but not all of them. Instead they create also some pain points, because with introducing them, you need to maintain some files, they are required, or some file structures or so on. So they create some amount of overhead. Sometimes it does make sense to use it, sometimes not. But we are trying to talk about the lightweight solution. So we do not want to have overhead, almost nothing. And then three years ago I thought that. Okay, wait, I already saw it somewhere and I remember that we have Docker compose and docker and Docker compose have a configuration for the projects in yaml. It's a very clean approach with config as a code and it has high visibility and traceability. And the idea behind terracompose, exactly that. Why cannot we do something like that with the terraform projects? Okay, and let me show you this quick wrapper. So terracompose, use cases, use cases are actually here actions that we can run in terracompose, we can run plan, apply workspaces, run shell and help more often. For sure we using plan and apply. And under the hood these actions are having terraform commands and providing output of them. We also can run command inside the container with run action. We can list workspaces and we can run a shell inside the container and join it. If we take a look on collaboration diagram here, we can see that when we trigger terracompose action under the hood, Terracompose will read configuration which is written into the config file and based on that run terraform command inside the docker. Also it will run this action, wrap it into some different commands like change directory, terraform, init, select workspaces and so on. With this setup we solve our problem with terraform versions because we use Docker which allow us to have different terraform versions on the same machine. Also we solve problem with different structure and workspaces and memorizing them because we can customize our alias with different path, different workspace and we don't need to memorize them. Only one that we need to do is to actually create this config file. We also improve our validation approach because we chain terraform commands and we automatically run validation. We will not forget about it and it will also automate some cleanup things after running action. So it will also help us in this way when we will talk about capabilities. So here I would stress that we don't need to memorize it and it's a part of code base so it has same visibility. You can have merge requests and you can have a documentation just at your code base about workspaces. Also having terraform commands provide us a guaranteed validated plan and it will just speed up maintenance and development because it require less time for double check, for printing, repeated comments and in the end it means less validation failures on CI when you will push your code because it shift left validations and it require from you to do all checks locally. First in the configuration file you can see two main blocks. This is default which contains shared properties across the aliases and aliases which is actually our projects and environments. And here you can see that this terraform version that we define in default might be overwritten in alias. The required property is only one path only. This is distinguish aliases between each other and we have here also example of the hooks usage. Hooks will help us to customize our projects even more so we are able to run scripts or commands before some particular activities and or after. And this way we will be able to do some workarounds for some h cases. We will talk about it later. Here you can see the output of the help action. So it lists available actions and also it lists available aliases. And now it's time for demo. First of all I want to emphasize few points about demo. So first we will have it in AWS. Second demo project I prepare it and it contains for speed and for readability projects that creates only vpcs and security groups. And also we will keep terraform state locally for simplicity, but please never do it in production. Here I want to show how I connect to AWS. So it's just a simple AWS profile and I export also profile name with environment variable. I also created these commands, this aliases which is just help us to not print that long commands during the demo. So let's check that we have only default VPC and default security group. Okay, everything is prepared. Let's download the tool. So I'm downloading the terracompose tool and copied into the local bin. I also will clone the terracompose demo project which is available in GitHub. And let's start with the first demo project is a common so as I mentioned, it will create a vpcs for all our projects and we can use, and we can create resources in these vpcs. So here we have folder with terraform state. We have two environments, nonprod and production. And we have here tf varst files, one managing with workspace and another one does not match with his name with workspace name. So for this use case we for staging just define workspace property. And for production we define beside workspace property, we define also tfvars property. So this is a very simple example when we can omit some break in naming combinations and we can use a random name for tfwars. Just put this name into the alias config. Okay, let's build a plan. So this is just a simple command. As you can see this is plan and then alias name. Now you see the output that we are running, terraform init. We see the normal Tf init output. Also we can see that workspace Nonprod has been found in config and we will select it and we see the information about the main factors for our plan. This is workspace and this is tfwars name. Then after checking plan, you can see that this plan is actually saved in this file. So we can even manually check it if you want. Okay, then we can run workspaces alias. This is also just a short link to list the available alias workspaces. Sorry. And we see that we currently are in nonprod workspace. So let's apply. When we run apply, it's also easy. It's just apply and name workspace. You don't need to define all details about files and so on. So it's done already. Under the hood we see in which directory we are, we are running terraform in need standard output of it, selecting workspace. And now we see that, okay, we want to apply file with this name, are you sure about that? And also we can see how long time ago it was modified. So now you can decide, is that exactly file that you want to apply or not? So if we print, yes, we can proceed. And our sources were created. Now let's build a plan for production. And you remember that there we had an TfVars named differently from workspace, but terraform plan is there and file also pick it up correctly. Now let's apply. And now you see that, okay, I ran the command, but now it contains a suffix. So with the suffix debug you can enable debug mode which will run it without init, without format check and without validation. So it's very useful for development process and debugging process, because you don't need to wait until everything will be validated and in its process will finished and so on. So it's very useful for development, but I would not recommend doing that out of development. So you need to have a clear understanding for what you are doing that. Okay, so we want to apply our changes, they are applied. And also let's check how we can run other commands. So we can just trigger action, run alias and then we can put any commands that we want to run, they will be run in the container. So here you can see that we list our state and we see that our VPC is there. And also I want to show you that our vpcs were really created and they are in place. Okay, now let's check more complex example. Here we see project that have two compose, let's go with the app component. And here we see that we have beside our Tf state folder with two workspaces integration and live. We have also here some tf state for experiment. So for example you are doing some experiment in integration workspace and you have this TF state folder just for integration workspace and you want to apply that. So how can you do it for that you can use backend config property and define their workspace deer with another location for the terraform state. Or for example, if you are using remote backend, you can put here some key and use that. So that just allow you to run terraform in it with some custom backend config. And hooks. Help us here because when we will jump from one environment to another environment we will see error because we ran terraform init with different backend configs. So for work around that issue we can just run hooks which removing terraform folder on both of these environments and it will solve our problem. This is just an example, you might have different use case and let's see how it works. So I going to run a plan on integration. We see here that we trigger before df init hook and we run our terraform init using backend config. We see config sorry, init output and selecting workspace. And we can check that in the end we are in workspace integration with TFwars integration tFwars. Okay, we see the plan here, no errors. So I'm going to apply it now. After resource were created, we see that changes are actually were recorded into experiment TF state and TF state with origin was not modified. So you can see that this option really works. And our security group also in place, we can see it here. Okay, now let's try to run interactive shell into container. And for that we will use shell action. We also need to approve it because for the projects purposes and we are inside the container. Now let's check our workspace list. Okay, so now we just check in that we are in the backend configuration experimental one, and for live we can just run terra compose action plan. It will run before Tf init hook again, run normal Tfe init without experimental backend config. And you can see that here we have plan that has been built incorrect workspace with correct tfwars file. Let's apply. Okay, so the resource was created and here we also can see them. Good. I think it's enough for this demo. I hope that you saw that with this setup, terracompose tool is really helpful when we we can just run action name and alias and it will run it with the properties with the arguments that we defined in the config file and we don't have to remember them and double check validity and so on. Let's back now to our presentation. So with that said, all done now, what do you think? Let's check our points. So here we have almost everything solved switch and terraform versions. It's now only attribute in YaML thanks to the running it into Docker snippets and compatibility visualization. Workspace traceability of the workspace automate routines needs to remember all these were solved chain in commons still. Well, it's a solved, but it's hard coded. That's why I market it as partially solved, because in the future plans I want to make them customizable and also allow users to create a custom action or override default action to solve different problems, or maybe just because to fix something. And if we talk about complexity and price. It's an open source and it's a simple yaml that you can create for 1 hour. So I tried to implement it into different projects and I spent about 1 hour and the job was done. Now let's talk about the future. So since that was just a simple wrapper, a quick wrapper written three years ago. So I didn't thought about the proper interface design and this is something that I see needs to be improved. Also, I want to make actions customizable as I just mentioned. And who knows, maybe rewriting go how do you think? So I actually want to encourage you to contribute to this project. You can find links to this project in QR code and in my GitHub account. Please join. And with that I want to say thank you for your attention.

Dmytro Prekrasnyi

Principal DevOps Engineer @ Westwing

Dmytro Prekrasnyi's LinkedIn account

Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways