Terraform EC2 Instance

Before I jump into the post, let's get a definition:  "Terraform is an open-source infrastructure as code software tool created by HashiCorp. It enables users to define and provision a datacenter infrastructure using a high-level configuration language known as Hashicorp Configuration Language, or optionally JSON."

We use AWS mostly and a little bit of Digital Ocean.  The majority of the instances are created from the GUI but I've been working on a project that requires the spin up and shutdown of similar servers and that's where Infrastructure as Code comes in handy.

In order to get setup, we need to create a user that can spin up infrastructure:



This user only needs Programmatic access but in order to do all the things it needs to do, the user needs EC2FullAccess:


Tags are optional:


We review our user:


When we're finished, we need to retrieve the Access ID and the Secret.  We probably want to store these in a password manager since this is the only time we're going to see the pair ever again:


With our Terraform config, we "could" store the credentials in the configuration file but that's not be a great idea as they could be retrieved by an attacker.  Instead, we can keep them in our password manager and call them when needed:


Continuing on with our setup, we need to create an SSH key for login:


When we're finished, we download the key, store it in a safe place, and hit it with chmod 600 in order to protect it.


Finally, we're at the point where we configure the Terraform config. 

provider "aws" {
  region = "us-west-2"
}

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"]
}

resource "aws_security_group" "test-c2" {
  name = "test-c2"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "test-c2" {
  ami           = "${data.aws_ami.ubuntu.id}"
  instance_type = "t2.micro"
  security_groups = ["test-c2"]
  key_name = "test-primary-key"
  tags = {
    Name = "dropbox-c2"
  }
}



This is a simple config.  EC2 instance, open port 22, Ubuntu 18.

Initialize the current directory:


Create an execution plan:


Apply the changes:


When it's finished:


When we look at our EC2 instances in AWS:


And finally, we SSH into our new box with the key we setup earlier:


A final point -- if we change the configuration file, modifying our server design, when we apply once again, it will destroy the existing instance and recreate another with our new settings.  So obviously, if you have work on this server, it will be wiped.

The real benefit to Terraform is when you want to recreate an exact configuration or roll out a number of near identical configurations.