Terraforming with a whale (docker)
Updated: 19 Nov 2020
tl;dr
examples at the bottom: https://blog.elreydetoda.site/docker-terraform/#example-cmds
Intro and Credits
So, I have to thank one of my friends Mr. Jamie Bowman, because he did an initial blog post that helped me get my head around the concept of doing this. Also, he included helpful debugging environment variables TF_LOG=TRACE
right there in his blog. So I didn't have to go and look them up in the terraform docs (which I feel like I live in all of the HashiCorp product's docs... 😅️).
Hackery...
Docker first...
Extra info
So, in the normal context of a long running container I would definitely recommend against doing what is called a bind mount -v
(especially of the docker socket) without taking the proper precautions...checkout why here: https://blog.elreydetoda.site/resources/#dockerexploits.
Also, apparently you can't specify a bind mount on the root of the docker container (i.e. -v "${PWD}":/
)...this make sense after I thought about it, because you replace the directory of whatever you bind to, but just thought I would throw that info out there 🙃️.
Environment variables
This section will kind of include some terraform knowledge, but you will need to use the -e
flag to pass in environment variables to terraform through docker's cli flags. (i.e. -e TF_VAR_auth_token
or -e TF_VAR_auth_token='<your_token>
' the latter is if you haven't exported the environment variable)
Terraform
Dumb stuff...
So, to begin this section...there are some dumb things about terraform's docker implementation of it's different actions. They make sense, but are some of the pitfalls when starting to use terraform in a container.
- You can't pass in a
.tf
file to specify you only want a specific file to get applied, I am sure there is a legit reason for that but I think that is silly... - When you do the
init
command and pass in the folder as to where it should init off of (i.e. the location of your terraform file(s)), it won't use that as the folder to place your.terraform
folder which holds all the plugin information (and more, but that is the only thing that affected me in this situation)
Together now...
So, as you can probably see from the video above I did eventually get everything to run properly and output things as I wanted them to. So, the trick to get them to work together was to specify a bind mount for both the location of your terraform files (i.e. -v "${PWD}":/terraform
) and the .terraform
folder that is located in /bin
(i.e. -v "${PWD}/.terrform":/.terraform/
. This allows you to continually run the official HashiCorp docker container without having to already pre-stage the plugins from the host operating system.
Background
So, the reason why I was doing this was because I am migrating my packer automation project to using terraform, and I didn't want to use my script that I created as a hack around downloading and installing terraform to the OS. I wanted to use a more clean way of handling terraform and thought why not docker. So, now I can install docker though snap and then run terraform and packer with docker.
Also, this allows me to run containers asynchronously to perform multiple terraform tasks (i.e. linting ) at the same time, which only will gain me a few second but it is the thought that counts 😁️
Example cmds
Project specific
Here is a logical chain of commands that you could probably run to do everything I discussed about in this blog for my project (I will put general command below this section):
# first do an init to pull down plugins
docker run -it --rm -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light init /terraform/
# then do a plan out to a file
docker run -it --rm -e TF_VAR_project_id -e TF_VAR_auth_token -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light plan -out /terraform/main.plan /terraform/
# apply the plan
docker run -it --rm -e TF_VAR_project_id -e TF_VAR_auth_token -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light apply -state /terraform/main.tfstate /terraform/main.plan
# lastly destroy everything
docker run -it --rm -e TF_VAR_project_id -e TF_VAR_auth_token -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light destroy -auto-approve -state /terraform/main.tfstate /terraform/
# add this variable to debugging any commands from above
-e TF_LOG=TRACE
## for example
docker run -it --rm -e TF_LOG=TRACE -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light init /terraform/
General commands
# first do an init to pull down plugins
docker run -it --rm -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light init /terraform/
# then do a plan out to a file
docker run -it --rm -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light plan -out /terraform/main.plan /terraform/
# apply the plan
docker run -it --rm -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light apply -state /terraform/main.tfstate /terraform/main.plan
# lastly destroy everything
docker run -it --rm -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light destroy -auto-approve -state /terraform/main.tfstate /terraform/
# add this variable to debugging any commands from above
-e TF_LOG=TRACE
## for example
docker run -it --rm -e TF_LOG=TRACE -v "${PWD}/.terrform":/.terraform/ -v "${PWD}":/terraform hashicorp/terraform:light init /terraform/
Video (asciinema)
More DevOps goodness
Check out more DevOps like posts by checking out the devops tagged posts for this blog: https://blog.elreydetoda.site/tag/devops/
Or you can checkout my project where I implemented these terraform commands in a script on my GitHub: https://github.com/elreydetoda/packer-kali_linux/