makecommands for your terraform needs
This contains a series of make targets to aid in some basic terraform
management and bootstrapping.
Note, this is still a work in progress.
Copy .tf.makefile into your project in your own way. Note, it
should not be renamed.
Then include this file in your own Makefile as such (prefferably at the
bottom):
include .tf.makefile
#eof
Note, the #eof line.
Else, if you are starting fresh you can start off a new project with:
$ make -f .tf.makefile .tf.makefile:init
This will create a Makefile for you with .tf.makefile included.
NOTE, for Mac OS users,
gnu-makeandgnu-sedwill be required for full compatibility. These can be installed easily via homebrew.
$ make [{service-name}/]tf:new
.tf.makefile assumes a particular directory structure for your Terraform
projects.
This is the basic structure that would need to be employed.
my-project/
.templates/
provider.tfvars
variables.tfvars
._tf.makefile
.tf.makefile
Makefile
provider.tf
variables.tf
This struture can be automatically generated with
$ make tf:new
Note, the layout above is used for a flat (or single) service structure, where you are working with one service or you want to keep everything in a single directory.
Note, if you want to introduce .tf.makefile into your project, you can still
run tf:new, but please back up your Makefile if you already have one in
use.
For a multiple service project, the layout varies just slightly from the above.
my-project/
.templates/
provider.tfvars
variables.tfvars
.tf.makefile
Makefile
provider.tf
serivce-a/
.templates/
variables.tfvars
._tf.makefile
Makefile
variables.tf
serivce-b/
.templates/
variables.tfvars
._tf.makefile
Makefile
variables.tf
variables.tf
To generate the base structure for a multiple service project,
$ make tf:new MULTI=true
Note, the main difference between a single and multiple service project is the
multiple project does not include a generated ._tf.makefile in the root
directory, as well as not including any Terraform generated targets against the
root directory, this will be explained shortly.
Each service (service-a, service-b) can then be generated using
$ make {service-name}/tf:new
Example
$ make service-a/tf:new
Note, you will notice that each service does not include it's own
provider.tf or a provider.tfvars (in the templates directory). It is
assumed that multiple service projects all share the same Provider.
.templates/
Many of my projects are written for and deployed by third parties. Because of
this .templates provide a way to configure for a specific use case or
configuration while leaving your variables and terraform code unchanged.
A sample .templates/variables.tfvars may come packaged likes this
# domain is the domain name configured to our nginx instance
#
#domain="production.domain.com"
And this would correspond to the variable you define in your variables.tf
variable "domain" {
default = "production.domain.com"
}
Given this, if you are setting up a "staging" deployment, you can then configure this value for your that particular use case
# domain is the domain name configured to our nginx instance
#
domain="staging.domain.com"
Note, .templates/ files are copied over during the tf:make process
described below.
$ make [{service-name}/]tf:make
Once you are ready to plan or deploy or just check your work, tf:make will
setup your directory for you by,
-
Copying over the
.tfvarsin your.templatesdirectory out to it's parent foldermy-project/ .templates/ provider.tfvars variables.tfvarsbecomes
my-project/ .templates/ provider.tfvars variables.tfvars .provider.tfvars .variables.tfvarsThese copied
.tfvarscan now be configured for your particular use case and will be used when callingterraformcommands via the-var-file=flag.Note,
tf:makemay be run a number of times, subsequent to the first run, copied.tfvarswill not be overwritten. To overwrite existing.tfvarsyou can calltf:makewith a!(bang).$ make tf:make!Along with copying over
.tfvarsit also redefines the{service-name}_VAR_FILES=variable to ensure all are accounted for if you happen to add additional.tfvarsfiles into your.templatesdirectory.Note, this variable can be found in the generated
._tf.makefileand will looks something likeSERVICE-A_VAR_FILES= \ -var-file=./.provider.tfvars \ -var-file=./.variables.tfvars
- It runs
terraform initto bring the terraform dependencies you defined into your project.
-
If you are running
tf:makeon a service folder it will link your provider into that service as asymlink.my-project/ ... provider.tf service-a/ ...Given
service-a, running$ make service-a/tf:makewill produce
my-project/ ... provider.tf service-a/ ... provider.tf -> /path/to/my-project/prvider.tf
Note, you need to run tf:make on all services as well as the root directory.
This can be done with the convenience target TODO tf:make:all.
$ make [{service-name}/]tf:plan
This is your standard target call to terraform plan, this will -out=plan.
And use the {SERVICE-NAME}_VAR_FILES= variables to include your .tfvars
files.
$ make [{service-name}/]tf:apply
This calls terraform apply plan, using the a plan you may have planned out
in an earlier tf:plan.
Note, if there is no plan file, it will automatically run tf:plan to
create a plan output before running terraform apply ....
tf:apply will always use an existing plan. If you want enfore a "re-plan"
before applying, run tf:apply appended with a ! (bang).
$ make tf:apply!
$ make [{service-name}/]tf:destroy
tf:destroy calls terraform destroy and will run through terraform's
destroy process.
Because this is "undoable" tf:destroy adds an extra confirmation of it's own,
where it will ask you to submit a generated key. If the input key matches it
will proceed with the terraform destroy at which point you will be asked by
terraform itself to confirm once again.
Note, there is no built-in tf:destroy target that requires no confirmation,
eg. tf:destroy appended with a ! (bang). But you can use the "private"
targets tf:__destroy__ and build your own. But
you are on your own on this...
There are a basic set of shortcuts that come generated as part of the tf:new
process.
$ make {service-name}
This allows you to run tf:apply! for any "root" service or sub-service by
it's "name" (directory name).
Single service example:
my-project/
...
._tf.makefile
.tf.makefile
You can call tf:apply! with
$ make my-project
In a multiple service example:
my-project/
...
.tf.makefile
service-A/
...
service-B/
...
You can call any one of your services' tf:apply! ({service-name}/tf:apply!)
with
$ make service-A
// equivalent to
$ make service-A/tf:apply!
Note, all of these commands are called from the "root" directory, you do not
have to cd into the service's directory to run these commands. You can run
ALL your tf commands for ANY service from the "root" directory.
If you happen to want to cd and run commands in a service, the plan,
apply and destroy targets are available without the tf: prefix.
$ $(pwd)
=> /path/to/my-project
$ cd service-A
$ make plan
You can, of course, continue to use the tf: prefix as well, if you wish.
$ cd service-A
$ make tf:plan
.tf.makefile comes with a few helpers to make SSHing into your boxes a bit
easier.
To setup your project (or service) for SSH, a few steps need to be taken.
Create a .ssh directory
$ make [{service-name}/]mkdir:.ssh
This will create a .ssh directory for you, where you keys will ultimately be
stored in.
Note, we advise you source control the directory itsef, but ignore what's in it. It can be done in git with
.gitignore
**/.ssh/**/*
!**/.ssh/.gitkeep
Generate your key
$ make [{service-name}/]ssh-keygen
This runs your standard ssh-keygen and stores the the keys in .ssh/ as
id_rsa.
Note, this key should be included into your deployments. I will not go into details here as this will vary based on your terraforms.
Expose an ipv4_address output
Note, this may not be possible if your terraforms are in charge of deploying multiple instances of something, ie. say a box in an AWS Auto Scaling Group. You would have to manually enter your IP address, which you have the option to do.
But, if you do have a single box you want to access and don't want to look up
the IP address in your provider's console. Providing a terraform output
variable with the name ipv4_address will help with that.
An example might look like
output "ipv4_address" {
value = "${digitalocean_droplet.default.ipv4_address}"
}
https://github.com/nowk/terraform-example/blob/master/droplet.tf
$ make ssh[:{service-name}]
When you are ready to SSH, you can just go
$ make ssh
Or for one of your services
$ make ssh:service-A
If you have provided an ipv4_address output it will use that, if not you will
be prompted to provide the IP address. As well as provide the login for the
current SSH session you are about to begin.
- Add a
tf:make:alltarget to runtf:makeon all. - Multiple service example
- Clean up some
definevs shell function definitions - Did I cover everything I should have in the README?