jyotir bhandari
7 min readJan 18, 2018

Overview of Terraform in 10 mins

* Installation
* Environment and Modules
* Commands
* Structure
* Developing modules
* Integrating modules in environments
* Planning deployment in the environment
* Deploying the environment
* Destroying the environment

Installation

Installation for terraform is quite simple, you can download a zip file for the binary in linux distribution and as for Mac OS, brew command can be used to install it.

Let’s see how it is done in Mac…..

$ brew install terraform

Environments and Modules

We have environments and modules. Environments prod, staging, dev and testing are architecture for 4 tier development. Modules are re-usable resources which are expected to be used again and again in any environment.

Environments

A finely-divided list of tiers for environments :

prod:

The production infrastructure mostly uses cluster of different application, different services of AWS cloud, monitoring and many more integrated with each other.

This environments is critical and is to be used only by DevOps. Automation has given System Administrator lot of power, but one need to understand that with “great power comes great responsibility”. So, it is advised to use this environment with care.

staging:

This is a pre-prod environment, which will be created when something needs to tested thoroughly before pushing to prod. This environment would be destroyed immediately after testing is completed.

dev:

This environment is for developers. They can install, configure, checkout repos with different branches as per their need or requirement. The instance will be terminated once the instance in shutdown. For developer’s convenience image with basic setup are booted. Developer can use their private keys to access the instance.

testing:

This environment is for DevOps, one can test automation with prod like environment and apply the same on any of the environments.

Modules

Modules as discussed above are re-usable resources. Best practices involves having 3 standard files main.tf, variables.tf and output.tf. The main.tf is the a file which have combination of the resources. What are resources ??? Resources are the aws api services which are triggered as per the requirement. For example — aws_instance will create instance based on different parameters we provide.

resource "aws_instance" "main" {
ami = "ami_id"
instance_type = "t2.nano"
......
......
}

In the above aws_instance is the resource, whereas ami and instance_type are arguments.

Let’s move to variables.tf file, as the name suggest this file contains the name of the variables. Variables type can be a list, string and map.

variable "instance_id" {
description = "....."
type = "list"
default = []
}

Another example can be…

variable "instance_id" {
description = "....."
type = "string"
default = ""
}

In the above examples you see that default string type is in “”whereas list type is in[] .

This brings us to our last file output.tf, this file contains the output value of the resources deployed.

For example: To fetch the id of the instance created from the main.tf file, we need create file with following content.

output "instance_id" {
value = "aws_instance.main.id"
}

Commands

The mentioned command is for all the purposes.

$ terraform command <arguments>

The mentioned command is executed before applying or deploying any changes. The command identifies error and also updates any addition, modification or destruction to the resources or module. This command should be made as a habit to run before applying or deploying.

$ terraform plan

The mentioned command will apply/deploy any addition, modification or removal of resources or module.

$ terraform apply

The mentioned command will destroy all the resource or modules in any environment.

$ terraform destroy

We can use all the above commands with arguments too. Suppose I just want to check changes or deploy or destroy specific resource or module.

$ terraform plan -target=aws_instance.main
$ terraform apply -target=aws_instance.main
$ terraform destroy -target=aws_instance.main

Besides, the other command which are rarely used are but are important. The following will taint or untaint any resource or module. Once, any resource or module is tainted, the changes will apply only to that very resource.

$ terraform taint <arguments>
$ terraform untaint <arguments>

Structure

The structure of the terraform can be seen as visible as below.

└── aws
├── env
│ ├── dev
│ ├── prod
│ │ ├── instance.tf
│ │ └── security_group.tf
│ ├── staging
│ └── testing
└── modules
└── module_name
├── main.tf
├── output.tf
└── variables.tf

As you can see from the above tree, there is aws which is a cloud provider and inside it are env and modules folders. Environment having 4 tier architecture with dev, prod, staging and testing. Similarly, there is modules folder and inside it there are standard files.

Developing modules

main.tf

resource "aws_elasticache_parameter_group" "main" {
name = "${var.env}-${var.name}-ecache-param"
family = "${var.elasticache_family}"
lifecycle {
create_before_destroy = true
}
}
resource "aws_elasticache_cluster" "main" {
cluster_id = "${var.env}-${var.name}-ecache"
engine = "${var.elasticache_engine}"
node_type = "${var.elasticache_node_type}"
num_cache_nodes = "${var.elasticache_nodes_num}"
port = "${var.elasticache_port}"
subnet_group_name = "${var.elasticache_subnet_group}"
security_group_ids = ["${var.elasticache_security_groups}"]
parameter_group_name = "${aws_elasticache_parameter_group.main.id}"
lifecycle {
create_before_destroy = true
}
}

variables.tf

variable "env" {
description = "Name of the respective environment"
default = ""
}
variable "name" {
description = "Name of the elasticache"
default = ""
}
variable "elasticache_family" {
description = "Family name to be used for parameter group"
default = ""
}
variable "elasticache_engine" {
description = "engine to be used redis/memcache"
default = ""
}
variable "elasticache_node_type" {
description = "instance type to be used"
default = ""
}
variable "elasticache_nodes_num" {
description = "number of nodes in the cluster"
default = ""
}
variable "elasticache_port" {
description = "port to be used for elasticache memcache/redis"
default = ""
}
variable "elasticache_subnet_group" {
description = "subnet group to be used for elasticache memcache/redis"
default = ""
}
variable "elasticache_security_groups" {
description = "secrity groups to be used for elasticache memcache/redis"
default = ""
}

output.tf

output "elasticache_addr" {
value = "${aws_elasticache_cluster.main.cache_nodes.0.address}"
}

To brief about the above code, two resources are getting created with one as parameter group and another as elasticache. Similarly, you have expected variables that you are going to use in environments. Mostly variables are interdependent with output of other modules or resources being variable of the other module or resource.

Integrating modules in environments

redis.tf

module "redis" {
source = "github.com/jyotirbhandari/terraform/aws/modules/elasticache"
env = "testing"
name = "redis"
elasticache_family = "redis3.2"
elasticache_engine = "redis"
elasticache_node_type = "cache.t2.micro"
elasticache_nodes_num = 1
elasticache_port = "6379"
elasticache_subnet_group = "${module.networking.elasticache_subnet_group}"
elasticache_security_groups = "${module.sg.sg_id}"
}

You can see from the above that a module named redis is created which is using source as elasticache. The variables defined in modules are applied with actual values.

Planning deployment in the environment

$ terraform plan+ module.redis.aws_elasticache_cluster.main
apply_immediately: "<computed>"
availability_zone: "<computed>"
az_mode: "<computed>"
cache_nodes.#: "<computed>"
cluster_address: "<computed>"
cluster_id: "testing-redis-ecache"
configuration_endpoint: "<computed>"
engine: "redis"
engine_version: "<computed>"
maintenance_window: "<computed>"
node_type: "cache.t2.micro"
num_cache_nodes: "1"
parameter_group_name: "${aws_elasticache_parameter_group.main.id}"
port: "6379"
replication_group_id: "<computed>"
security_group_ids.#: "1"
security_group_ids.2556801745: "sg-388e1348"
security_group_names.#: "<computed>"
snapshot_window: "<computed>"
subnet_group_name: "testing-elasticache-subnet-group"
+ module.redis.aws_elasticache_parameter_group.main
description: "Managed by Terraform"
family: "redis3.2"
name: "testing-redis-ecache-param"

As one can see the above plan is from the redis.tf file in above section. Including modules enables to write less code with accuracy.

Deploying the environment

$ terraform apply -target=module.redismodule.redis.aws_elasticache_parameter_group.main: Creating...
description: "" => "Managed by Terraform"
family: "" => "redis3.2"
name: "" => "testing-redis-ecache-param"
module.redis.aws_elasticache_parameter_group.main: Creation complete (ID: testing-redis-ecache-param)
module.redis.aws_elasticache_cluster.main: Creating...
apply_immediately: "" => "<computed>"
availability_zone: "" => "<computed>"
az_mode: "" => "<computed>"
cache_nodes.#: "" => "<computed>"
cluster_address: "" => "<computed>"
cluster_id: "" => "testing-redis-ecache"
configuration_endpoint: "" => "<computed>"
engine: "" => "redis"
engine_version: "" => "<computed>"
maintenance_window: "" => "<computed>"
node_type: "" => "cache.t2.micro"
num_cache_nodes: "" => "1"
parameter_group_name: "" => "testing-redis-ecache-param"
port: "" => "6379"
replication_group_id: "" => "<computed>"
security_group_ids.#: "" => "1"
security_group_ids.2556801745: "" => "sg-388e1348"
security_group_names.#: "" => "<computed>"
snapshot_window: "" => "<computed>"
subnet_group_name: "" => "testing-elasticache-subnet-group"
module.redis.aws_elasticache_cluster.main: Still creating... (10s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (20s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (30s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (40s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (50s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (1m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (1m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (1m20s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (1m30s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (1m40s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (1m50s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (2m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (2m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (2m20s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (2m30s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (2m40s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (2m50s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (3m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (3m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (3m20s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (3m30s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (3m40s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (3m50s elapsed)
module.redis.aws_elasticache_cluster.main: Still creating... (4m0s elapsed)
module.redis.aws_elasticache_cluster.main: Creation complete (ID: testing-redis-ecache)
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

As you can see in the above output redis cache is implemented with respective instance_type, subnet_id, engine etc. Similarly, you can deploy other resources and modules.

Destroying the environment

Similar, to above I would be using specific target to destroy instead of complete destruction.

$ terraform destroy -target=module.redisaws_elasticache_cluster.main: Refreshing state... (ID: testing-redis-ecache)
module.redis.aws_elasticache_cluster.main: Destroying... (ID: testing-redis-ecache)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 10s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 20s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 30s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 40s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 50s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 1m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 1m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 1m20s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 1m30s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 1m40s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 1m50s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 2m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 2m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 2m20s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 2m30s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 2m40s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 2m50s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 3m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 3m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 3m20s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 3m30s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 3m40s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 3m50s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 4m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 4m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 4m20s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 4m30s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 4m40s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 4m50s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 5m0s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 5m10s elapsed)
module.redis.aws_elasticache_cluster.main: Still destroying... (ID: testing-redis-ecache, 5m20s elapsed)
module.redis.aws_elasticache_cluster.main: Destruction complete
module.redis.aws_elasticache_parameter_group.main: Destroying... (ID: testing-redis-ecache-param)
module.redis.aws_elasticache_parameter_group.main: Destruction complete
Destroy complete! Resources: 2 destroyed.

Whoopsie daisy !!!! That’s a quick overview of Terraform. You can integrate module from below repository.

https://github.com/jyotirbhandari/terraform