Terraform for Beginner - AWS
Tags: devops, aws
Published: 2019-06-01 15:52:30

# Learning journey #RoadToDevOps
 
Last week I pick Terraform as my study and learning topic.

[Terraform](https://www.terraform.io/) are very useful and powerful tools for DevOps development, it allows you writing a plan (Infrastructure as Code) and manage your cloud service.

which mean you can write a cloud infrastructure in coding, such as define how many instanse services, what port number you should active in the firewall. It's interesting to handling so many servers just a few lines of code.

## Introduction
By using Terrafrom to deploy a virtual machine (ubuntu OS) in AWS EC2.

## Objectives

In this article, you should able:
- Setup AWS account 
- Use Basic Terraform-CLI command
- Writing Terraform Language (HCL)
- Deploy Virtual machine (EC2) in AWS cloud.

## AWS Setup

If you are new in AWS please sign up here enjoy [AWS Free Tier
](https://aws.amazon.com/free/)

We need User's `Access Key ID` and `Secret Key ID` from AWS User, and grant promission to `User` with `AmazonEC2FullAccess`.

if you dont know how to create user -> [watch this](https://www.youtube.com/watch?v=XMi5fXL2Hes)


## Terraform 


Ensure your machine are [Install Terraform](https://learn.hashicorp.com/terraform/getting-started/install.html).


We know if you writing in `javascript` programming, you will name the file extension as `*.js`. In Terraform the will define the extension as `*.tf` and using JSON format or HCL (HashiCorp Configuration Language).

You may feel strange what is HCL (HashiCorp Configuration Language). No worry, It's not complicated and easy to learn and understand. [HCL Docs](https://www.terraform.io/docs/configuration/index.html)


Once you have done your `*.tf` script, you have to use [Terrform-CLI](https://www.terraform.io/docs/commands/index.html) to execute `tf` files.


### Hands-on

We will use keywords from `HCL`.

| keywords  | description  | example  | 
|---|---|---|
| provider  | define cloud service provider  | aws, google  |
| resource  | define the resource we will use  | aws_instance, google_compute_instance  |
| variable  | define variable  | -  |
| output  | print output after executed  | -  |

We will use keywords from `Terraform CLI`.

| keywords  | description  | 
|---|---|
| init  | To init terraform project in the folder  |
| plan  | To evaluate current your infrastructure from `*.tf`   |
| apply  | To add all define resources. |
| show  | To list resource infomation and current state  |
| destroy  | To remove all defined resources.   |



### start

1. Create a folder in your machine.

2. create a files `my-first-vm.tf`.

3. Open the files in the editor and start code.

```tf
# my-first-vm.tf

# We are pointing "aws" as our provider.

provider "aws" {
    region = "ap-southeast-1"
    access_key = "<access-key-from-aws-user>"
    secret_key = "<secret-key-from-aws-user>"
}
```

command:

```bash
$ terraform init

Initializing provider plugins...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

* provider.aws: version = "~> 2.12"

Terraform has been successfully initialized!
```


append resource "aws_instance" in `my-first-vm.tf`

```tf
provider "aws" {...}

resource "aws_instance" "ec2" { # I called name "ec2", you can change it
  ami = "ami-0dad20bd1b9c8c004" # Image: Ubuntu Server 18.04 LTS (HVM), SSD Volume Type
  instance_type = "t2.micro" # VM Spec
}
```

command:

```bash
$ terraform plan

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + aws_instance.ec2
      id:                           <computed>
      ami:                          "0dad20bd1b9c8c004"
      arn:                          <computed>
      associate_public_ip_address:  <computed>
      availability_zone:            <computed>
      cpu_core_count:               <computed>
      cpu_threads_per_core:         <computed>
      ebs_block_device.#:           <computed>
      ephemeral_block_device.#:     <computed>
      get_password_data:            "false"
      host_id:                      <computed>
      instance_state:               <computed>
      instance_type:                "t2.micro"
      ipv6_address_count:           <computed>
      ipv6_addresses.#:             <computed>
      key_name:                     <computed>
      network_interface.#:          <computed>
      network_interface_id:         <computed>
      password_data:                <computed>
      placement_group:              <computed>
      primary_network_interface_id: <computed>
      private_dns:                  <computed>
      private_ip:                   <computed>
      public_dns:                   <computed>
      public_ip:                    <computed>
      root_block_device.#:          <computed>
      security_groups.#:            <computed>
      source_dest_check:            "true"
      subnet_id:                    <computed>
      tenancy:                      <computed>
      volume_tags.%:                <computed>
      vpc_security_group_ids.#:     <computed>


Plan: 1 to add, 0 to change, 0 to destroy.
```

it showing infrastructure going to execute, you will see `computed` it's because the terraform will help you assign the value, if you are not defined.

In additional, you see `Plan: 1 to add, 0 to change, 0 to destroy.` it mean 1 resource will be executed.


So in this stage, actually you already completed setup an instance.

command:

```bash
$ terraform apply
Terraform will perform the following actions:

  + aws_instance.ec2

...

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

...

aws_instance.ec2: Still creating... (10s elapsed)
aws_instance.ec2: Still creating... (20s elapsed)
aws_instance.ec2: Creation complete after 22s (ID: i-083d6c6c7b01d1640)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

Congratulations, you are successful setup an instance EC2 in AWS, you may login and check it out in AWS dashboard.

Try on this command to get resource infomation.
```bash
$ terraform show 

aws_instance.ec2:
  id = i-083d6c6c7b01d1640
  ami = ami-0dad20bd1b9c8c004
  arn = arn:aws:ec2:ap-southeast-1:907443295242:instance/i-083d6c6c7b01d1640
  associate_public_ip_address = true
...
...
```

!!!!

But wait, how you able to access without setup `ssh` in the firewall.


let append resource "aws_security_group" in `my-first-vm.tf`

```tf
provider "aws" {...}

resource "aws_instance" "ec2" {...}

# Setup aws_security_group with SSH access

resource "aws_security_group" "allow_ssh" {
  name        = "allow ssh"
  description = "only ssh"

  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"]
  }
}
```

Now we have let our "ec2" instance to apply "aws_security_group" we define, and also assign keypair for the instance.

so we can use the `keypair` to access instance with port `ssh 22`.

if you dont know how to create keypair in AWS, [watch this](https://www.youtube.com/watch?v=DpyLAdMD09w)

```tf
provider "aws" {...}

resource "aws_instance" "ec2" { # I called name "ec2", you can change it
  ami = "ami-0dad20bd1b9c8c004" # Image: Ubuntu Server 18.04 LTS (HVM), SSD Volume Type
  instance_type = "t2.micro" # VM Spec
  security_groups = ["${aws_security_group.allow_ssh.name}"]
  key_name = "aws-anthony"
}

# Setup aws_security_group with SSH access

resource "aws_security_group" "allow_ssh" {...}

```

command:

```bash
$ terraform apply
aws_instance.ec2: Refreshing state... (ID: i-083d6c6c7b01d1640)

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
-/+ destroy and then create replacement

Terraform will perform the following actions:

-/+ aws_instance.ec2 (new resource required)
...
...

+ aws_security_group.allow_ssh
...
...

Plan: 2 to add, 0 to change, 1 to destroy.
...
...
...
aws_instance.ec2: Still creating... (10s elapsed)
aws_instance.ec2: Still creating... (20s elapsed)
aws_instance.ec2: Creation complete after 32s (ID: i-079e9b8c1d98e4af5)

Apply complete! Resources: 2 added, 0 changed, 1 destroyed.

```

You may see it's different from just now, because terraform will keep track all resource status in `*.tfstate`.
So it shows that, it will `destroy` the previous instance and recreate again.

great, you now can check it out in your AWS dashboard. 

finally, you successfully create an instance in Ubuntu OS. It's trouble to login AWS dashboard and checking what is the instance public ip address.

you may use `output` keywords to display the public IP address.


```tf
provider "aws" {...}

resource "aws_instance" "ec2" {...}

resource "aws_security_group" "allow_ssh" {...}

output "showPublicIP" {
  value = "${aws_instance.ec2.*.public_dns}"
}
```

```bash
$ terraform apply 

aws_security_group.allow_ssh: Refreshing state... (ID: sg-0ca7e9133a701f216)
aws_instance.ec2: Refreshing state... (ID: i-079e9b8c1d98e4af5)

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

showPublicIP = [
    ec2-3-0-184-126.ap-southeast-1.compute.amazonaws.com
]

```

```bash
ssh -i aws-anthony.pem ubuntu@ec2-3-0-184-126.ap-southeast-1.compute.amazonaws.com
```

## Summary

Terraform always execute the `*tf` in folder, each infrastructure should create a folder, such as `aws-instance` or `google-instance`.

if you feel put everything in 1 `.tf` file is to mess, you may seperate in different `.tf` such as aws_provider.tf , aws_security.tf



[example code](https://gist.github.com/AnthonyLaw/0738a40d9f7e46111f6d670aa7684fdc)


[store sepearte example](https://github.com/AnthonyLaw/terraform-starter-pack)