Saturday, February 3, 2018

An Introduction to Terraform

An Introduction to Terraform



Learn the basics of Terraform in this tutorial, step-by-step tutorial of how to deploy a EC2 with Running web server on AWS.

In this post, we’re going to introduce the basics of how to use Terraform to define and manage your infrastructure.




What is Terraform?


Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.


Infrastructure as Code


Infrastructure is described using a high-level configuration syntax. This allows a blueprint of your data center to be versioned and treated as you would any other code. Additionally, infrastructure can be shared and re-used.



The Official Terraform Getting Started documentation Having a good introducing the individual elements of Terraform (i.e. resources, input variables, output variables, etc), so in this guide, we’re going to focus on how to put those elements together to use a real-time example of web server deployment in EC2.

This guide is targeted at AWS and Terraform newbies, so don’t worry if you haven’t used either one before. We’ll walk you through the entire process, step-by-step:
1.  Set up your AWS account
2. Install terraform
3. Configure the terraform script
4. Test the server accesibility
5. Clean up


Set Up Your AWS Account
Terraform can provision infrastructure across many different types of cloud providers, including AWS, Azure, Google Cloud and many others. For this tutorial, we picked Amazon Web Services (AWS) because:
  It provides a huge range of reliable and scalable cloud hosting services, including Elastic Compute Cloud (EC2)Auto Scaling Groups (ASGs), and Elastic Load Balancing (ELB).
● AWS is the most popular cloud infrastructure provider, so far. AWS offers a generous Free Tier which should allow you to run all of these examples for free.
When you first register for AWS, you initially sign in as the root user. This user account has access permissions to everything, so from a security perspective, we recommend only using it to create other user accounts with more limited permissions (see IAM Best Practices).
To create a more limited user account, Move over to the Identity and Access Management (IAM) console, click “Users”, and click the  “Add Users” button. Enter a name for the user and make sure “Programmatic access” is checked in Access Type:



Once you’ve done with credentials, click to “Next:Permissions” Button,On Next Screen will ask for Set Permission for User and you will get 3 Options:
1) Add User in specific Group
2) Copy Permission from Existing User
3) Attached the Policy
Here we selected Options  3) Attached the Policy. By default, a new IAM user does not have permissions to do anything in the AWS account. To be able to use Terraform for the examples in this tutorial, add the AmazonEC2FullAccess permission (learn more about Managed IAM Policies here):


Click the “Create” button and you’ll be able to see security credentials for that user, which consist of Access Key ID and a Secret Access Key. You MUST save these immediately, as they will never be shown again. We recommend storing them somewhere securely  ,so you can use them a little later in this tutorial.You can also download the csv file with these created user credentials information.




Install Terraform
Follow the instructions here to install Terraform. When you’re done, you should be able to run the terraform command:


 # terraform
usageterraform [--version] [--help] <command> [args]
In order for Terraform to be able to make changes in your AWS account, you will need to set the AWS credentials for the user you created earlier as environment variables:

export AWS_ACCESS_KEY_ID=(your access key id)
export AWS_SECRET_ACCESS_KEY=(your secret access key)

Deploy a Web server in EC2


Terraform code is written in a language called HCL in files with the extension “.tf”. It is a declarative language, so your goal is to describe the infrastructure you want, and Terraform will figure out how to create it. Terraform can create infrastructure across a wide variety of platforms, or what it calls providersincluding AWS, Azure, Google Cloud and many others. The first step to using Terraform is typically to configure the provider(s) you want to use. Create a file called “main.tf” and put the following code in it:

 provider "aws" {
 
region = "eu-west-1"
}

This tells Terraform that you are going to be using the AWS provider and that you wish to deploy your infrastructure in the “eu-west-1” region (AWS has data centers all over the world, grouped into regions and availability zones, and eu-west-1 is the name for data centers in Ireland). You can configure other settings for the AWS provider, but for this example, since you’ve already configured your credentials as environment variables, you only need to specify the region.
For each provider, there are many different kinds of “resources” you can create, such as servers, databases, and load balancers. Before we deploy a whole cluster of servers, let’s  first figure out how to deploy a single server that will run simple “Hello, World” web server. In Amazon Web services a server is called an “EC2 Instance.” To deploy an EC2 Instance, add the following code to main.tf:


 resource "aws_instance" "myfirstec2" {
 
ami = "ami-d834aba1"
 instance_type = 
"t2.micro"
}
Each resource specifies a type (in this case, “aws_instance”), a name (in this case “example”) to use as an identifier within the Terraform code, and a set of configuration parameters specific to the resource. The aws_instance resource documentation lists all the parameters it supports. Initially, you only need to set the 
following ones:
    amiThe Amazon Machine Image to run on the EC2 Instance. Amazon Linux AMI 2017.09.1 (HVM), SSD Volume Type in eu-west-1.
  instance_typeThe type of EC2 Instance to run. Each EC2 Instance Type has different amount CPU, memory, disk space, and networking specs. The example above uses “t2.micro”, which has 1 virtual CPU, 1GB of memory, and is part of the AWS free tier.
In a terminal, go into the folder where you created main.tf, and run the “terraform plan” command:


# terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

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.myfirstec2
      id:                           <
computed>
      ami:                          "ami-d834aba1"
      associate_public_ip_address:  <
computed>
      availability_zone:            <
computed>
      ebs_block_device.#:           <
computed>
      ephemeral_block_device.#:     <
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>
      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.

------------------------------------------------------------------------

The plan command lets you see what Terraform will do before actually doing it. This is a great way to sanity check your changes before unleashing them onto the world. The output of the plan command is a little like the output of the diff command: resources with a plus sign (+) are going to be created, resources with a minus sign (-) are going to be deleted, and resources with a tilde sign (~) are going to be modified. In the output above, you can see that Terraform is planning on creating a single EC2 Instance and nothing else, which is exactly what we want.  

To actually create the instance, run the “terraform apply” command:
    
     # terraform apply

Terraform will perform the following actions:
  + aws_instance.myfirstec2
      id:                           
<computed>
      ami:                          "ami-d834aba1"
      associate_public_ip_address: 
<computed>
      availability_zone:           
<computed>
      ebs_block_device.#:          
<computed>
      ephemeral_block_device.#:     
<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>
      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.

You have deployed a Server with Terraform! To verify this, you can login to the EC2 console, and you will see your EC2 Instance running in EC2 Console as per below view:




It’s working, but it’s not the most exciting example. For one thing, the Instance doesn’t have a name. To add one, you can add a tag to the EC2 instance:

resource "aws_instance" "myfirstec2" {
          
ami = "ami-d834aba1"
           instance_type =
"t2.micro"
           tags {
                
Name = "terraform-myfirstec2"
           }
}

Run the plan command again to see what this would do:

# terraform plan
Refreshing Terraform state
in-memory prior to plan...
The refreshed state will be used to calculate
this plan, but will not be
persisted to local
or remote state storage.

aws_instance.myfirstec2: Refreshing state... (ID: i
-00c8bbb1c554945e7)

------------------------------------------------------------------------

An execution plan has been generated
and is shown below.
Resource actions are indicated with the following symbols:
  ~ update
in-place

Terraform will perform the following actions:

  ~ aws_instance.myfirstec2
      tags.%:   
"0" => "1"
      tags.Name:
"" => "terraform-myfirstec2"


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

------------------------------------------------------------------------


Terraform keeps track of all the resources it already created for this set of templates, so it knows your EC2 Instance already exists (note how Terraform says “Refreshing state…” when you run the plan command), and it can show you a diff between what’s currently deployed and what’s in your Terraform code . The difference above shows that Terraform wants to create a single tag called “Name”, which is exactly what we want, so you should run the “apply” command again. When you refresh your EC2 console, you will see the given changes as below:




      Deploy  HTTP web server in EC2


The next step is to run a web server on this Instance. In a real-world use case, we’re going to run a simple web server that always returns the text “Welcome to My First EC2 Instance Web Server” using a index.html file present in web server:


#!/bin/bash
Yum install httpd
echo "Welcome to My First EC2 Instance Web Server" > /var/www/html/index.html
Service httpd start

This is a bash script that writes the text “Welcome to My First EC2 Instance Web Server” into index.html to serve that file at the URL “/”. With install httpd will install apache server in EC2 and will create the directory /var/www/html and we will create index.html file in that location to server web page and then we starting the http service using command service httpd start.




We’re going to run the script above as part of the EC2 Instance’s User Data, which AWS will execute when the instance is booting time so in case of autoscale new instance will have predefined configuration installed.




resource "aws_instance" "myfirstec2" {
           
ami = "ami-d834aba1"
            instance_type =
"t2.micro

            user_data = <<-EOF
                        #!/bin/bash
                        yum install httpd
                        echo "
Welcome to My First EC2 Instance Web   
                        Server" >  /var/www/html/index.html
                        service httpd start
                        EOF
            tags {
                  Name = "
terraform-myfirstec2"
             }
}


The “<<-EOF” and “EOF” are Terraform’s heredoc syntax, which allows you to create multiline strings without having to put “\n” all over the place (learn more about Terraform syntax here).

You need to do one more thing before this web server works. By default, AWS does not allow any incoming or outgoing traffic from an EC2 Instance. To allow the EC2 Instance to receive traffic on port 80, you need to create a security group:





resource "aws_security_group" "instance" {
 
name = "terraform-example-instance"
 ingress {
  
from_port = 80
   to_port =
80
   protocol =
"tcp"
   cidr_blocks = [
"0.0.0.0/0"]
 }
}
The code above creates a new resource called aws_security_group (notice how all resources for the AWS provider start with “aws_”) and specifies that this group allows incoming TCP requests on port 80 from the CIDR block 0.0.0.0/0. CIDR blocks are a concise way to specify IP address ranges. For example, a CIDR block of 10.0.0.0/24 represents all IP addresses between 10.0.0.0 and 10.0.0.255. The CIDR block 0.0.0.0/0 is an IP address range that includes all possible IP addresses, so the security group above allows incoming requests on port 80 from any IP.



Note that in the security group above, we copied & pasted port 8080. To keep your code DRY and to make it easy to configure the code, Terraform allows you to define input variables:


variable "web_server_port" {
 
description = "The port the server will use for HTTP requests"
}
You can use this variable in your security group via Terraform interpolation syntax:
from_port = "${var.web_server_port}"
to_port =
"${var.web_server_port}"
If you now run the plan or apply command, Terraform will prompt you to enter a value for the from_port and to_port variable:
# terraform plan
var.web_server_port
 The port the server will use for HTTP requests

  Enter a value: 80
Another way to provide a value for the variable is to use the “-var” command line option:
# terraform plan -var web_server_port="80"
If you don’t want to enter the port manually every time, you can specify a default value as part of the variable declaration (note that this default can still be overridden via the “-var” command line option):
variable "web_server_port" {
 
description = "The port the server will use for HTTP requests"
 
default = 80
}

One last thing to do: you need to tell the EC2 Instance to actually use the new security group. To do that, you need to pass the ID of the security group into the vpc_security_group_ids parameter of the aws_instance resource. How do you get this ID?
In Terraform, every resource has attributes that you can reference using the same syntax as interpolation. You can find the list of attributes in the documentation for each resource. For example, the aws_security_group attributes include the ID of the security group, which you can reference in the EC2 Instance as follows:
vpc_security_group_ids = ["${aws_security_group.instance.id}"]
The syntax is “${TYPE.NAME.ATTRIBUTE}”. When one resource references another resource, you create an implicit dependency. Terraform parses these dependencies, builds a dependency graph from them, and uses that to automatically figure out in what order it should create resources (e.g. Terraform knows it needs to create the security group before using it with the EC2 Instance). In fact, Terraform will create as many resources in parallel as it can, which means it is very fast at applying your changes. That’s the beauty of a declarative language: you just specify what you want and Terraform figures out the most efficient way to make it happen.
If you run the plan command, you’ll see that Terraform wants to replace the original EC2 Instance with a new one that has the new user data (the “-/+” means “replace”) and to add a security group:
# terraform apply
var.web_server_port
  Port the server will use
for HTTP requests serve

  Enter a value:
80

aws_instance.myfirstec2: Refreshing state... (ID: i
-00c8bbb1c554945e7)

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.myfirstec2
     
id:                                    <computed>
      ami:                                  
"ami-d834aba1"
      associate_public_ip_address:           <computed>
      availability_zone:                     <computed>
      ebs_block_device.
#:                    <computed>
      ephemeral_block_device.
#:              <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>
      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>
      tags.%:                                
"1"
      tags.Name:                            
"terraform-myfirstec2"
      tenancy:                               <computed>
      user_data:                            
"e24dcf69cef29301fdb38a432284c77f963990bb"
      volume_tags.%:                         <computed>
      vpc_security_group_ids.
#:              <computed>

  + aws_security_group.instance
     
id:                                    <computed>
      description:                          
"Managed by Terraform"
      egress.
#:                              <computed>
      ingress.
#:                             "1"
      ingress
.2214680975.cidr_blocks.#:      "1"
      ingress
.2214680975.cidr_blocks.0:      "0.0.0.0/0"
      ingress
.2214680975.description:        ""
      ingress
.2214680975.from_port:          "80"
      ingress
.2214680975.ipv6_cidr_blocks.#: "0"
      ingress
.2214680975.protocol:           "tcp"
      ingress
.2214680975.security_groups.#:  "0"
      ingress
.2214680975.self:               "false"
      ingress
.2214680975.to_port:            "80"
      name:                                 
"terraform-security-group"
      owner_id:                              <computed>
      revoke_rules_on_delete:               
"false"
      vpc_id:                                <computed>


Plan:
2 to add, 0 to change, 0 to destroy.
This is exactly what we want, so run the apply command again and you’ll see your new EC2 Instance deploying:
In the description panel at the bottom of the screen, you’ll also see the public IP address of this EC2 Instance. Give it a minute or two to boot up and then try to wget or Browser access with Instance Public IP Address at port 80 with index.html file (you can also do the same with curl utility) 

# wget http://34.217.9.203/index.html
--
2018-01-31 11:08:27--  http://34.217.9.203/index.html
Connecting to
34.217.9.203:80... connected.
HTTP request sent, awaiting response...
200 OKLength: 44 [text/html]
Saving to:
'index.html'

index.html          
100%[===================>]      44  --.-KB/s    in 0s      2018-01-31 11:08:27 (9.65 MB/s) - 'index.html' saved [44/44]




Terraform also giving the better way to find the Pubic Ip as output, However, having to manually play around the EC2 console to find this IP address is no suggestable. Should use terraform output variable:

output "public_ip" {
 
value = "${aws_instance.myfirstec2.public_ip}"
}

We’re using the interpolation syntax again to reference the public_ip attribute of the aws_instance resource. If you run the apply command again, Terraform will not apply any changes (since you haven’t changed any resources), but it’ll show you the new output:



# terraform applyaws_security_group.instance: Refreshing state... (ID: sg-db91dba1)
aws_instance.example: Refreshing state... (ID: i-61744350)
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
public_ip = 34.217.9.203



9 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. I am happy to find this post very useful for me, as it contains a lot of information. I always prefer to read the quality content I found in you post. Thanks for sharing.
    Cloud Computing Courses
    Cloud computing course in Chennai

    ReplyDelete
  3. Very nice post here and thanks for it .I always like and such a super contents of these post.Excellent and very cool idea and great content of different kinds of the valuable information's.Data Science Training In Chennai

    Data Science Online Training In Chennai

    Data Science Training In Bangalore

    Data Science Training In Hyderabad

    Data Science Training In Coimbatore

    Data Science Training

    Data Science Online Training

    ReplyDelete
  4. Thank you for sharing detailed information on Terraform starting from basics. I really liked the blog. Keep posting more. Thanks.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Chennai's No.1 software training institute, Infycle Technologies, provides the best Big Data Hadoop Training in Chennai for students, freshers, and tech professionals along with other corporate courses such as Data Science, Cloud computing, DevOps, Digital Marketing, Python, Big Data, Selenium, Java, Hadoop, iOS, and Android development with 100% hands-on training. After the completion of training, the students will be sent for placement interviews in the core MNC's. Call 7502633633 to get more info and a free demo.Big Data Hadoop Training in Chennai | Infycle Technologies

    ReplyDelete

Wireless Security Configuration: Protect Your Network Now!

Introduction: In today’s connected world, wireless networks are as common as smartphones, and they’re often the gateway to our personal, pr...