Adam Burnett
Terraform is a tool from HashiCorp (makers of Vagrant) that allows you to codify your infrastructure into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
Use it for:
Traditional infrastructure work is done by a team other than the feature development team. At best this work follows a different process from the rest of the application. At worst it is entirely manual.
Using an IaC tool makes creating an entirely self contained team possible. Infrastructure provisioning can now follow the same release process as the rest of the software. This is the heart and sole of DevOps!
Configuration syntax: HCL
variable "aws_region" { default = "us-east-1" }
resource "aws_instance" "web" {
count = "${var.enabled ? 1 : 0}"
instance_type = "m1.small"
ami = "${lookup(var.aws_amis, var.aws_region)}"
vpc_security_group_ids = ["sg1234", "sg5678"]
tags {
"MyTag" = "foobar"
}
}
A Resource represents a component of your infrastructure. This could be a VM instance, firewall rule, user permission, networking configuration, etc.
A resource consists of a name, type and configuration and is specific to a provider.
resource "aws_instance" "web" {
# ^^^type^^^ ^name
# configuration
instance_type = "m1.small"
...
}
Resources have inputs and outputs. Outputs can be ref
Resources have inputs and outputs. Outputs can be referenced to create
implicit dependencies between resources. Terraform will
ensure correct resource creation / destruction order.
resource "aws_security_group" "global-egress" {
name = "global-egress"
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web" {
vpc_security_group_ids = ["${aws_security_group.global-egress.id}"]
...
}
Multiples of a resource can be created via a "count". The index
of the instance can be used control values on a per-instance basis where
needed.
variable "instance_ips" {
default = {
"0" = "10.11.12.100"
"1" = "10.11.12.101"
"2" = "10.11.12.102"
}
}
resource "aws_instance" "app" {
count = "3"
private_ip = "${lookup(var.instance_ips, count.index)}"
# ...
}
Resources with a count are referenced by index.
${aws_instance.app.0.id} # the first one
${aws_instance.app.*.id} # all of them
Referencing all instances with *
resource "aws_elb" "web" {
instances = ["${aws_instance.app.*.id}"]
}
Provisioners are used to execute scripts on a local or remote
machine as part of resource creation or destruction. Provisioners
can be used to bootstrap a resource, cleanup before destroy, run
configuration management, etc.
resource "aws_instance" "app" {
...
provisioner "remote-exec" {
inline = [
"apt-get update",
"consul join ${aws_instance.web.private_ip}",
]
}
}
Connections via SSH and WinRM supported.
A provider is what manages the resources you define. The provider name will match the prefix of the resource name.
Providers are defined in the terraform configuration.
provider "aws" {
access_key = "foo"
secret_key = "bar"
region = "us-east-1"
}
provider "digitalocean" {
token = "${var.do_token}"
}
Terraform must store state containing its view of your managed infrastructure. This state is used to create the plan for applying changes.
There is an in-depth explination on why this state is needed on the Terraform site at https://www.terraform.io/docs/state/purpose.html
State can be stored in a local file or in a remote store. How you plan on using Terraform and integrating it into your organization will determine your best course of action.
Remote Data Stores
State can be inspected with terraform show
and terraform console
.
Secrets will be in plain text!
For one developer using local state committed to source control is good enough. But what if you have a team? Multiple people applying changes at the same time can corrupt the state.
As of Terraform 0.9 there is native support for locking the state in a backend. This will allow only one change at a time to be applied.
3rd party tools like Terragrunt have sprung up to help manage organizational issues that arise with integrating Terraform into the enterprise.
TBD: talk about state as a data source
Modules are used to create reusable components in Terraform as well as for basic code organization.
Modules are most useful when defined with input and output parameters. This allows you to create generic components that can be imported into any configuration.
Ok, let's just look at one...
Modules can be referenced locally or imported from source control.
Collection of terraform community modules can be found at https://github.com/terraform-community-modules