Step by Step procedure for deploying to AWS

Deploying to AWS with Ansible and Terraform

Terraform allows you to define infrastructure as code (IaC) and deploy it repeatably with the same end result. The application infrastructure is defined in code by defining needed components like compute instances, storage buckets, networks, load-balancers, firewalls etc. Terraform will then take this blueprint and plan how to reach the desired state defined in the code. This also allows TerraForm to do incremental changes by comparing the defined (changed) state with the deployed (current) state and execute only the needed changes. We simply create a file (or multiple files) with the .tf extension and defining all the components we need.

1. Setting up the environment
For this course I used Ubuntu server with RAM size is 1 GB 1 core CPU

a. Install python and verify whether python is installed or not

$sudo apt-get update

$sudo apt-get install python3.6

$python --version

b. Install Python pip

$apt install python-pip

$pip install --upgrade pip

c. Download Terraform

$curl-o (link from website),
it will generate .zip file

d. Create a folder(/bin/terraform) and unzip the terraform.zip in that folder

$mkdir /bin/terraform

$unzip terraform.zip -d /bin/terraform

Set the terraform path

$export PATH= $PATH:/bin/terraform

e. Verfify whether terraform is installed or not

$terrafoem --version

f. Install AWS CLI

$pip install aws cli --upgrade

$apt-get update

g. Install software properties

$apt-get install software-properties-common

h. Add repository which will contain ansible

$apt-add-repository ppa:ansible/ansible

i. Update the packages

$apt-get update

j. Install ansible

$apt-get install ansible

h. Verify whether ansible is installed or not

$ansible --version

i. We need to generate the key to access the server for that we execute

$ssh-keygen

j. Save the key in /root/.ssh/filename

k. To access the ssh agent

$ssh-agent bash

$ssh-add ~/.ssh/filename

l. To make sure the key is present

$ssh-add -l

m. Modify ansible configuration file

$vim /etc/ansible/ansible.conf

Disable host_key_checking i.e setting host_key_checking= False

n. Create a working directory

$mkdir terransible

2. IAM and DNS setup

a. Go to IAM console
Select IAM,
Select Users,
Add user say terransible

b. Attach administer policy to the user

Make sure that we download the credentials

d. Configure Route 53

e. Configure aws

$aws configure-profile superhero

f. Register domains

Select domain add or edit name servers.

take name servers which are in the code

Files needed to create to set up environment

1.main.tf 2. variables.tf 3. terraform.tfvars 4.userdata 5.aws_hosts 6. wordpress.yml 7.s3update,yml

main.tf pulls the variables from variables.tf which in turn the pulls the values from terraform.tfvars. To make this reusable we create two scripts for variables. variables.tf has variables. terraform.tfvars will populate the variables. Terraform is going to create environment based on main.tf . terraform will create userdata and AWS access userdata which will populate aws_hosts with ip address of dev instance and name of s3 bucket. Ansible going to use to deploy wordpress.yml and s3update.yml which will allow the dev instance to send the code to s3 bucket.

We specify multiple resources in a single file and configure these resources to work together.

Steps involved in creating the files

a. Go to terransible directory

$cd terransible

$touch main.tf variables.tf terraform.tfvars

$touch userdata aws_hosts wordpress.yml s3update.yml

After that lets create variables so that terraform can access the environment

$vim main.tf

provider "aws" {
  region  = "${var.aws_region}"
  profile = "${var.aws_profile}"
}

To reference these variables create empty variables in variables.tf

$vim variables.tf
Add the empty variables

variable "aws_region" {}
variable "aws_profile" {}

Define these variables in terraform.tfvars file

$vim terraform.tfvars
Define the variables as follows:

aws_profile		= "superhero"
aws_region		= "us-east-1"

Initialization is done using the command terraform init

$terraform init

When we are creating the resources simultaneously we have to add empty variables in variables.tf

3. Adding IAM resources in infrastructure. In this we create access profile, access policy, access role.

In the following piece of code, we tell in which region to deploy the resources, which credentials to use and which profile to use (which is defined by a section in the shared credentials file) and which role TerraForm should assume to deploy the defined infrastructure.

Go to main.tf

$vim main.tf

Add the code in main.tf as follows:

#------------IAM---------------- 

#S3_access

resource "aws_iam_instance_profile" "s3_access_profile" {
  name = "s3_access"
  role = "${aws_iam_role.s3_access_role.name}"
}

resource "aws_iam_role_policy" "s3_access_policy" {
  name = "s3_access_policy"
  role = "${aws_iam_role.s3_access_role.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role" "s3_access_role" {
  name = "s3_access_role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
  {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
  },
      "Effect": "Allow",
      "Sid": ""
      }
    ]
}
EOF
}

4. Create VPC

Go to main.tf add the code as follows and save

#-------------VPC-----------

resource "aws_vpc" "wp_vpc" {
  cidr_block           = "${var.vpc_cidr}"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags {
    Name = "wp_vpc"
  }
}

5. Create Internet Gateway

To be able to access the instances (which have a mapped public IP) from the internet and allows access to the internet we will need an internet gateway, so let’s define one!:

Go to main.tf add the code as follows and save it

#internet gateway

resource "aws_internet_gateway" "wp_internet_gateway" {
  vpc_id = "${aws_vpc.wp_vpc.id}"

  tags {
    Name = "wp_igw"
  }
}

6. Create Route tables

We also will need to set-up a route-table to attach to the subnets which define the default route and allows the subnets to talk to each other

Go to main.tf add the code as follows and save
# Route tables

resource "aws_route_table" "wp_public_rt" {
  vpc_id = "${aws_vpc.wp_vpc.id}"

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.wp_internet_gateway.id}"
  }

  tags {
    Name = "wp_public"
  }
}

resource "aws_default_route_table" "wp_private_rt" {
  default_route_table_id = "${aws_vpc.wp_vpc.default_route_table_id}"

  tags {
    Name = "wp_private"
  }
}

resource "aws_subnet" "wp_public1_subnet" {
  vpc_id                  = "${aws_vpc.wp_vpc.id}"
  cidr_block              = "${var.cidrs["public1"]}"
  map_public_ip_on_launch = true
  availability_zone       = "${data.aws_availability_zones.available.names[0]}"

  tags {
    Name = "wp_public1"
  }
}

resource "aws_subnet" "wp_public2_subnet" {
  vpc_id                  = "${aws_vpc.wp_vpc.id}"
  cidr_block              = "${var.cidrs["public2"]}"
  map_public_ip_on_launch = true
  availability_zone       = "${data.aws_availability_zones.available.names[1]}"

  tags {
    Name = "wp_public2"
  }
}

resource "aws_subnet" "wp_private1_subnet" {
  vpc_id                  = "${aws_vpc.wp_vpc.id}"
  cidr_block              = "${var.cidrs["private1"]}"
  map_public_ip_on_launch = false
  availability_zone       = "${data.aws_availability_zones.available.names[0]}"

  tags {
    Name = "wp_private1"
  }
}

resource "aws_subnet" "wp_private2_subnet" {
  vpc_id                  = "${aws_vpc.wp_vpc.id}"
  cidr_block              = "${var.cidrs["private2"]}"
  map_public_ip_on_launch = false
  availability_zone       = "${data.aws_availability_zones.available.names[1]}"

  tags {
    Name = "wp_private2"
  }
}

7. Creating s3 VPC endpoint

Go to main.tf and add the code as follows

#create S3 VPC endpoint
resource "aws_vpc_endpoint" "wp_private-s3_endpoint" {
  vpc_id       = "${aws_vpc.wp_vpc.id}"
  service_name = "com.amazonaws.${var.aws_region}.s3"

  route_table_ids = ["${aws_vpc.wp_vpc.main_route_table_id}",
    "${aws_route_table.wp_public_rt.id}",
  ]

  policy = <<POLICY
{
    "Statement": [
        {
            "Action": "*",
            "Effect": "Allow",
            "Resource": "*",
            "Principal": "*"
        }
    ]
}
POLICY
}

resource "aws_subnet" "wp_rds1_subnet" {
  vpc_id                  = "${aws_vpc.wp_vpc.id}"
  cidr_block              = "${var.cidrs["rds1"]}"
  map_public_ip_on_launch = false
  availability_zone       = "${data.aws_availability_zones.available.names[0]}"

  tags {
    Name = "wp_rds1"
  }
}

resource "aws_subnet" "wp_rds2_subnet" {
  vpc_id                  = "${aws_vpc.wp_vpc.id}"
  cidr_block              = "${var.cidrs["rds2"]}"
  map_public_ip_on_launch = false
  availability_zone       = "${data.aws_availability_zones.available.names[1]}"

  tags {
    Name = "wp_rds2"
  }
}

resource "aws_subnet" "wp_rds3_subnet" {
  vpc_id                  = "${aws_vpc.wp_vpc.id}"
  cidr_block              = "${var.cidrs["rds3"]}"
  map_public_ip_on_launch = false
  availability_zone       = "${data.aws_availability_zones.available.names[2]}"

  tags {
    Name = "wp_rds3"
  }
}

8. Subnet Associations

Go to main.tf add the code and save

# Subnet Associations

resource "aws_route_table_association" "wp_public_assoc" {
  subnet_id      = "${aws_subnet.wp_public1_subnet.id}"
  route_table_id = "${aws_route_table.wp_public_rt.id}"
}

resource "aws_route_table_association" "wp_public2_assoc" {
  subnet_id      = "${aws_subnet.wp_public2_subnet.id}"
  route_table_id = "${aws_route_table.wp_public_rt.id}"
}

resource "aws_route_table_association" "wp_private1_assoc" {
  subnet_id      = "${aws_subnet.wp_private1_subnet.id}"
  route_table_id = "${aws_default_route_table.wp_private_rt.id}"
}

resource "aws_route_table_association" "wp_private2_assoc" {
  subnet_id      = "${aws_subnet.wp_private2_subnet.id}"
  route_table_id = "${aws_default_route_table.wp_private_rt.id}"
}

resource "aws_db_subnet_group" "wp_rds_subnetgroup" {
  name = "wp_rds_subnetgroup"

  subnet_ids = ["${aws_subnet.wp_rds1_subnet.id}",
    "${aws_subnet.wp_rds2_subnet.id}",
    "${aws_subnet.wp_rds3_subnet.id}",
  ]

  tags {
    Name = "wp_rds_sng"
  }
}

9. Creating Public, Private and RDS Security Groups

We will be attaching security groups to the defined compute instances and also the defined load balancer to only allow the specified incoming (ingress) and specified outgoing (egress) traffic. Besides using CIDR blocks subnets, we can also define other security groups as allowed traffic.

Go to main.tf and add the code
#Security groups

resource "aws_security_group" "wp_dev_sg" {
  name        = "wp_dev_sg"
  description = "Used for access to the dev instance"
  vpc_id      = "${aws_vpc.wp_vpc.id}"

  #SSH

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["${var.localip}"]
  }

  #HTTP

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["${var.localip}"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

#Public Security group

resource "aws_security_group" "wp_public_sg" {
  name        = "wp_public_sg"
  description = "Used for public and private instances for load balancer access"
  vpc_id      = "${aws_vpc.wp_vpc.id}"

  #HTTP 

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

  #Outbound internet access

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

#Private Security Group

resource "aws_security_group" "wp_private_sg" {
  name        = "wp_private_sg"
  description = "Used for private instances"
  vpc_id      = "${aws_vpc.wp_vpc.id}"

  # Access from other security groups

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["${var.vpc_cidr}"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

#RDS Security Group
resource "aws_security_group" "wp_rds_sg" {
  name        = "wp_rds_sg"
  description = "Used for DB instances"
  vpc_id      = "${aws_vpc.wp_vpc.id}"

  # SQL access from public/private security group

  ingress {
    from_port = 3306
    to_port   = 3306
    protocol  = "tcp"

    security_groups = ["${aws_security_group.wp_dev_sg.id}",
      "${aws_security_group.wp_public_sg.id}",
      "${aws_security_group.wp_private_sg.id}",
    ]
  }
}

10. Creating S3 code bucket

Go to main.tf and add the code as follows

#S3 code bucket

resource "random_id" "wp_code_bucket" {
  byte_length = 2
}

resource "aws_s3_bucket" "code" {
  bucket        = "${var.domain_name}-${random_id.wp_code_bucket.dec}"
  acl           = "private"
  force_destroy = true

  tags {
    Name = "code bucket"
  }
}

11. Creating DB instance and dev instance

Go to main.tf and the code

#---------compute-----------

resource "aws_db_instance" "wp_db" {
  allocated_storage      = 10
  engine                 = "mysql"
  engine_version         = "5.6.27"
  instance_class         = "${var.db_instance_class}"
  name                   = "${var.dbname}"
  username               = "${var.dbuser}"
  password               = "${var.dbpassword}"
  db_subnet_group_name   = "${aws_db_subnet_group.wp_rds_subnetgroup.name}"
  vpc_security_group_ids = ["${aws_security_group.wp_rds_sg.id}"]
  skip_final_snapshot    = true
}

#key pair

resource "aws_key_pair" "wp_auth" {
  key_name   = "${var.key_name}"
  public_key = "${file(var.public_key_path)}"
}

#dev server

resource "aws_instance" "wp_dev" {
  instance_type = "${var.dev_instance_type}"
  ami           = "${var.dev_ami}"

  tags {
    Name = "wp_dev"
  }

  key_name               = "${aws_key_pair.wp_auth.id}"
  vpc_security_group_ids = ["${aws_security_group.wp_dev_sg.id}"]
  iam_instance_profile   = "${aws_iam_instance_profile.s3_access_profile.id}"
  subnet_id              = "${aws_subnet.wp_public1_subnet.id}"

  provisioner "local-exec" {
    command = <<EOD
cat <<EOF > aws_hosts 
[dev] 
${aws_instance.wp_dev.public_ip} 
[dev:vars] 
s3code=${aws_s3_bucket.code.bucket}
domain=${var.domain_name} 
EOF
EOD
  }

  provisioner "local-exec" {
    command = "aws ec2 wait instance-status-ok --instance-ids ${aws_instance.wp_dev.id} --profile superhero && ansible-playbook -i aws_hosts wordpress.yml"
  }
}

12. Creating Load Balancer

In front of the application, we will be placing a classic load balancer, which will load-balance incoming web traffic ( port 80 ).

Go to main.tf and add the code as follows

#load balancer

resource "aws_elb" "wp_elb" {
  name = "${var.domain_name}-elb"

  subnets = ["${aws_subnet.wp_public1_subnet.id}",
    "${aws_subnet.wp_public2_subnet.id}",
  ]

  security_groups = ["${aws_security_group.wp_public_sg.id}"]

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  health_check {
    healthy_threshold   = "${var.elb_healthy_threshold}"
    unhealthy_threshold = "${var.elb_unhealthy_threshold}"
    timeout             = "${var.elb_timeout}"
    target              = "TCP:80"
    interval            = "${var.elb_interval}"
  }

  cross_zone_load_balancing   = true
  idle_timeout                = 400
  connection_draining         = true
  connection_draining_timeout = 400

  tags {
    Name = "wp_${var.domain_name}-elb"
  }
}

13. Creating AMI and launch configuration

Go to main.tf and add the code as follows

#AMI 

resource "random_id" "golden_ami" {
  byte_length = 8
}

resource "aws_ami_from_instance" "wp_golden" {
  name               = "wp_ami-${random_id.golden_ami.b64}"
  source_instance_id = "${aws_instance.wp_dev.id}"

  provisioner "local-exec" {
    command = <<EOT
cat <<EOF > userdata
#!/bin/bash
/usr/bin/aws s3 sync s3://${aws_s3_bucket.code.bucket} /var/www/html/
/bin/touch /var/spool/cron/root
sudo /bin/echo '*/5 * * * * aws s3 sync s3://${aws_s3_bucket.code.bucket} /var/www/html/' >> /var/spool/cron/root
EOF
EOT
  }
}

#launch configuration

resource "aws_launch_configuration" "wp_lc" {
  name_prefix          = "wp_lc-"
  image_id             = "${aws_ami_from_instance.wp_golden.id}"
  instance_type        = "${var.lc_instance_type}"
  security_groups      = ["${aws_security_group.wp_private_sg.id}"]
  iam_instance_profile = "${aws_iam_instance_profile.s3_access_profile.id}"
  key_name             = "${aws_key_pair.wp_auth.id}"
  user_data            = "${file("userdata")}"

  lifecycle {
    create_before_destroy = true
  }
}

14. Creating and configuring Auto Scaling Group

Go to main.tf and add the code as follows

#ASG 

#resource "random_id" "rand_asg" {
# byte_length = 8
#}

resource "aws_autoscaling_group" "wp_asg" {
  name                      = "asg-${aws_launch_configuration.wp_lc.id}"
  max_size                  = "${var.asg_max}"
  min_size                  = "${var.asg_min}"
  health_check_grace_period = "${var.asg_grace}"
  health_check_type         = "${var.asg_hct}"
  desired_capacity          = "${var.asg_cap}"
  force_delete              = true
  load_balancers            = ["${aws_elb.wp_elb.id}"]

  vpc_zone_identifier = ["${aws_subnet.wp_private1_subnet.id}",
    "${aws_subnet.wp_private2_subnet.id}",
  ]

  launch_configuration = "${aws_launch_configuration.wp_lc.name}"

  tag {
    key                 = "Name"
    value               = "wp_asg-instance"
    propagate_at_launch = true
  }

  lifecycle {
    create_before_destroy = true
  }
}

15. Creating Route53 records

Go to main.tf and add the code

#---------Route53-------------

#primary zone

resource "aws_route53_zone" "primary" {
  name              = "${var.domain_name}.com"
  delegation_set_id = "${var.delegation_set}"
}

#www 

resource "aws_route53_record" "www" {
  zone_id = "${aws_route53_zone.primary.zone_id}"
  name    = "www.${var.domain_name}.com"
  type    = "A"

  alias {
    name                   = "${aws_elb.wp_elb.dns_name}"
    zone_id                = "${aws_elb.wp_elb.zone_id}"
    evaluate_target_health = false
  }
}

#dev 

resource "aws_route53_record" "dev" {
  zone_id = "${aws_route53_zone.primary.zone_id}"
  name    = "dev.${var.domain_name}.com"
  type    = "A"
  ttl     = "300"
  records = ["${aws_instance.wp_dev.public_ip}"]
}

#secondary zone

resource "aws_route53_zone" "secondary" {
  name   = "${var.domain_name}.com"
  vpc_id = "${aws_vpc.wp_vpc.id}"
}

#db 

resource "aws_route53_record" "db" {
  zone_id = "${aws_route53_zone.secondary.zone_id}"
  name    = "db.${var.domain_name}.com"
  type    = "CNAME"
  ttl     = "300"
  records = ["${aws_db_instance.wp_db.address}"]
}

16. Creating Ansible Playbooks

We have two files created in terraform 1.wordpress.yml 2. S3update.yml.

In wordpress.yml we write playbook to install apache and to download wordpress application

$vim wordpress.yml

---
-hosts: dev

become: yes

remote_user: ec2-user

tasks:

- name: Install Apache.

yum: name={{ item }} state=present

with_items:

- httpd

- php

- php-mysql

- name: Download WordPress

get_url: url=http://wordpress.org/wordpress-latest.tar.gz dest=/var/www/html/wordpress.tar.gz force=yes

- name: Extract WordPress

command: &quot;tar xzf /var/www/html/wordpress.tar.gz -C /var/www/html --strip-components 1&quot;

- name: Make my directory tree readable

file:

path: /var/www/html/

mode: u=rwX,g=rX,o=rX

recurse: yes

owner: apache

group: apache

- name: Make sure Apache is started now and at boot.

service: name=httpd state=started enabled=yes

...

Run ansible playbook wordpress.yml. Apache is installed and wordpress is downloaded

$vim s3update.yml

- hosts: dev
  become: yes
  remote_user: ec2-user
  tasks:
  - name: Update S3 code bucket
command: aws s3 sync /var/www/html/ s3://{{ s3code }}/ --delete
  - shell: echo "define('WP_SITEURL','http://dev."{{ domain }}".com');"  >> wp-config.php
args:
  chdir: /var/www/html/
  - shell: echo "define('WP_HOME','http://dev."{{ domain }}".com');"  >> wp-config.php
args:
  chdir: /var/www/html/

17. Simultaneously we add the empty variables in variables.tf

Go to variables.tf and add the variables

variable "aws_region" {}
variable "aws_profile" {}
data "aws_availability_zones" "available" {}
variable "localip" {}
variable "vpc_cidr" {}

variable "cidrs" {
  type = "map"
}

variable "db_instance_class" {}
variable "dbname" {}
variable "dbuser" {}
variable "dbpassword" {}
variable "key_name" {}
variable "public_key_path" {}
variable "domain_name" {}
variable "dev_instance_type" {}
variable "dev_ami" {}
variable "elb_healthy_threshold" {}
variable "elb_unhealthy_threshold" {}
variable "elb_timeout" {}
variable "elb_interval" {}
variable "asg_max" {}
variable "asg_min" {}
variable "asg_grace" {}
variable "asg_hct" {}
variable "asg_cap" {}
variable "lc_instance_type" {}
variable "delegation_set" {}

In the Same we have to add values to terraform.tfvars file

$vim terraform.tfvars

localip = "104.173.212.11/32";

aws_profile ="superhero";

aws_region = "us-east-1";

vpc_cidr = "10.0.0.0/16";

cidrs = {

public1 = "10.0.1.0/24";

public2 = "10.0.2.0/24";

private1 ="10.0.3.0/24";

private2 = "10.0.4.0/24";

rds1 = "10.0.5.0/24";

rds2 = "10.0.6.0/24";

rds3 ="10.0.7.0/24";

}

db_instance_class = &quot;db.t2.micro;

dbname = &quot;superherodb;

dbuser = &quot;superhero;

dbpassword = &quot;superheropass;

key_name = &quot;kryptonite;

public_key_path = &quot;/root/.ssh/kryptonite.pub;

domain_name = &quot;bravethecloud;

dev_instance_type = &quot;t2.micro;

dev_ami = &quot;ami-b73b63a0;

elb_healthy_threshold = "2";

elb_unhealthy_threshold = "2";

elb_timeout = "3";

elb_interval = "30";

asg_max = "2";

asg_min = "1";

asg_grace = ";300";

asg_hct = "EC2";

asg_cap = "2";

lc_instance_type = "t2.micro";

delegation_set = "N1HDAZB52OQ3IV";

test = {}

Run ansible playbooks wordpress.yml and s3update.yml
$ansible-playbook -v wordpress.yml
Type yes at the prompt to confirm that you’d like to accept the SSH connection to this host.
$ansible-playbook -v s3update.yml
Type yes at the prompt to confirm that you’d like to accept the SSH connection to this host.
Ansible can automatically query your AWS inventory to get server IPs and tags, which can make building the inventory files aws_hosts simpler.

Finally we do last minute check list

  1. ansible – version

  2. check whether host_key_checking is false

  3. Make sure key is added ssh sgent

  4. terraform --version

  5. aws --version

After defining everything we need to deploy the application-infrastructure we simply run terraform init in the same folder as where you created your terraForm.tfvars file to initialize the TerraForm environment

Then it is time to run terraform plan which tells terraform to see what has to be done to deploy the infrastructure we defined before. Terraform will ask you to input any variables you didn’t define on the command line.

If the output of the command looks ok, we can then deploy the application to AWS by typing: terraform apply