VERY Quick Start: Terraform

by Jannik Arndt

This post contains the absolut essence from the Terraform Getting Started Guide: https://www.terraform.io/intro/getting-started/install.html

Preparation

brew install terraform

Create aws.tf:

provider "aws" {
  region     = "us-east-1" 
}

resource "aws_instance" "example" {
  ami           = "ami-2757f631" // https://cloud-images.ubuntu.com/locator/ec2/
  instance_type = "t2.micro"
}

AMI = Amazon Machine Images

AWS credentials are stored in environment vars:

export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...

Init

$ terraform init
...
Terraform has been successfully initialized!
...
$ terraform plan -out planfile
  + aws_instance.example
      ami:                          "ami-2757f631"
      associate_public_ip_address:  "<computed>"
...
$ terraform apply planfile
aws_instance.example: Creating...
...
aws_instance.example: Still creating... (10s elapsed)
aws_instance.example: Still creating... (20s elapsed)
aws_instance.example: Still creating... (30s elapsed)
aws_instance.example: Still creating... (40s elapsed)
aws_instance.example: Creation complete (ID: i-00b2e1a29daee4371)
$ terraform show
aws_instance.example:
  id = i-00b2e1a29daee4371
  ami = ami-2757f631
  associate_public_ip_address = true

Change

ami-2757f631 (Ubuntu 16.04 LTS AMI) => ami-b374d5a5 (Ubuntu 16.10 AMI)

In aws.tf:

provider "aws" {
  region     = "us-east-1"
}

resource "aws_instance" "example" {
  ami           = "ami-b374d5a5"
  instance_type = "t2.micro"
}
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
aws_instance.example: Refreshing state... (ID: i-00b2e1a29daee4371)
...
-/+ aws_instance.example (new resource required)
      ami:                          "ami-2757f631" => "ami-b374d5a5" (forces new resource)
      associate_public_ip_address:  "true" => "<computed>"
...
$ terraform apply
aws_instance.example: Refreshing state... (ID: i-00b2e1a29daee4371)
aws_instance.example: Destroying... (ID: i-00b2e1a29daee4371)
...
aws_instance.example: Destruction complete
aws_instance.example: Creating...
  ami:                          "" => "ami-b374d5a5"
  associate_public_ip_address:  "" => "<computed>"
...
aws_instance.example: Creation complete (ID: i-05a8f2e88ae0faace)

Destroy

$ terraform plan -destroy
Refreshing Terraform state in-memory prior to plan...
...
  - aws_instance.example
...
$ terraform destroy
aws_instance.example: Refreshing state... (ID: i-05a8f2e88ae0faace)
...

  - aws_instance.example

Terraform will delete all your managed infrastructure, as shown above. 
There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.example: Destroying... (ID: i-05a8f2e88ae0faace)
aws_instance.example: Destruction complete

Destroy complete! Resources: 1 destroyed.

Assigning an IP to the instance

In aws.tf:

provider "aws" {
  region     = "us-east-1"
}

resource "aws_instance" "example" {
  ami           = "ami-b374d5a5"
  instance_type = "t2.micro"
}

resource "aws_eip" "ip" {
  instance = "${aws_instance.example.id}"
}
$ terraform apply planfile 
aws_instance.example: Creating...
...
aws_instance.example: Creation complete (ID: i-0478b3c5b6a085287)
aws_eip.ip: Creating...
...
aws_eip.ip: Creation complete (ID: eipalloc-6be79a58)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
$ terraform refresh
aws_instance.example: Refreshing state... (ID: i-0478b3c5b6a085287)
aws_eip.ip: Refreshing state... (ID: eipalloc-6be79a58)
$ terraform show
aws_eip.ip:
  id = eipalloc-6be79a58
  association_id = eipassoc-590d9e6c
  domain = vpc
  instance = i-0478b3c5b6a085287
  network_interface = eni-73845aa6
  private_ip = 172.31.17.98
  public_ip = 52.204.255.194
  vpc = true
aws_instance.example:
  id = i-0478b3c5b6a085287
  ami = ami-b374d5a5
  associate_public_ip_address = true

Dependencies

The order in which resources are created is determined by terraforms analysis of (implicit) dependencies. Dependencies can also be defined explicitly, for example:

resource "aws_eip" "ip" {
  instance   = "${aws_instance.example.id}"
  depends_on = ["aws_instance.example"] # <- explicit dependency
}

To view a dependency graph:

$ terraform graph > graph.dot

And open with graphviz: aws_eip.ip aws_instance.example provider.aws [root] meta.count-boundary (count boundary fixup) [root] provider.aws (close) [root] root

Provisioning/Bootstrapping

https://www.terraform.io/docs/provisioners/index.html

Change aws.tf:

resource "aws_instance" "example" {
  ami           = "ami-b374d5a5"
  instance_type = "t2.micro"
  
  # add provisioner
  provisioner "local-exec" {
      command = "echo ${aws_instance.example.public_ip} > ip_address.txt"
  }
}

# add output
output "ip" {
  value = "${aws_eip.ip.public_ip}"
}

Provisioners are only run, when a resource is first created!

$ terraform destroy
...
$ terraform apply
...
Outputs:

  ip = 50.17.232.209

Blog


I recently created a wonderful bug.




This is a basic example how to implement oAuth2 using Akka HTTP and Scala. It provides three endpoints. From the clients point of view:

  • / — publicly accessible, returns “Welcome!”,
  • /auth — provide your username and password, receive an access_token in return,
  • /api — secured by oAuth, send the access_token in a header to gain access.

From the server’s point of view:

  • / — publicly accessible, do nothing,
  • /auth — receive basic auth credentials, verify they’re in the list of known credentials, create an access_token, return it,
  • /api — receive authorization header, check if access_token is in list of valid tokens.

Since oAuth tokens are short lived, the server also has to invalidate expired tokens.




Getting a Akka HTTP-based backend up and running on Heroku for free can be done in less then 30 minutes — if you know the tricks.