Welcome to the very first blog post of this year. This post is an introduction to Terraform which is a tool to manage various cloud infrastructure services in the form of code. You essentially codify your infrastructure and thus also known as Infrastructure as Code (IaC).
Talking about Digital Transformations that organizations go through, the cloud has proved to be of immense importance and often stands at the epicenter of their transformation journey. Over the years and decades, it has become very evident that cloud platforms not only help reduce time and cost but rather – let the customers focus on their core business.
Due to the increasing demand and flexibility of cloud providers, it becomes more important to manage cloud infrastructure resources. Terraform works on the concept of Infrastructure as Code (IaC). In simple terms, the ability to represent your infrastructure in the form code is attributed to IaC.
Let us take for example any compute resource on a given cloud – EC2 on AWS. Requesting an EC2 instance from AWS is a matter of signing up with AWS, providing a bunch of values, and click on a “Launch” button. Your resource will be ready in a few minutes. As far as you are able to provide those values to AWS, nothing stops you from having your own resources on the given cloud provider. Of course, this is a traditional way to do the same.
Terraform provides a way to take these credentials and inputs in the form of configurations and process these configurations to create a resource in the target cloud. These configurations are a way to describe the desired resource in a language that is understood by Terraform. Internally, Terraform makes use of cloud provider APIs to carry out the creation of the resource.
Terraform is a product by Hashicorp, and uses Hashicorp Configuration Language (HCL) syntax to represent the configurations. In the given example, EC2 instance can be represented in HCL as below in its simplest form.
provider “aws” {
region = “us-west-1”
}
resource “aws_instance” “myec2” {
ami = “ami-12345qwert”
instance_type = “t2.micro”
}
This small example is enough to understand the capabilities of Terraform. The code contains 2 blocks-
provider
and resource
. provider block lets the Terraform know, that we are going to use aws
provider in the region “us-west-1
”. resource block let’s the Terraform know that out of all the infrastructure resources offered by AWS, we want to create a resource of type “instance
” (EC2). This is represented by 1st parameter to resource block “aws_instance
”. The 2nd parameter is the name of our choice to be given to the resource – in this case, “myec2”. Resource block has a couple of arguments that indicate the AWS machine image and the type of instance to be used to create this resource.Thus we have successfully managed to express our infrastructure in the form of code. We will not go into the details of the syntax in this post as it would be covered in another post. However, let us go through certain advantages of IaC.
Since infrastructure creation is now condensed in config/code files, it is easier to maintain the same since we can now leverage version control systems like Git to collaborate and maintain the same.The time required for the planning phase of infrastructure is drastically reduced as the configurations can be written in a short amount of time and are readily consumed by Terraform to create cloud resources in the matter for few minutes.Changes to the infrastructure are easier and can be compared to code changes. Advantages for application management lifecycle in case of software development are also applicable for infrastructure. Thus, making it more efficient.
Orchestration: Terraform acts as the core of orchestration when it comes to creating cloud resources to deploy various end-to-end services.
Cloud agnostic: Since Terraform supports most of the clouds including AWS, MS Azure, and GCP, it becomes less of a worry when it comes to vendor lock-in issues. Terraform registry provides the documentation for all the supported cloud providers. Syntax patterns used to code infrastructure on various clouds are the same, so the learning curve related to provider-specific APIs can be put on a back burner.
Declarative syntax: Infrastructure expressed in Terraform files is declarative – thus as a developer, you don’t need to worry about making terraform understand the steps required to create a resource. Rather, all you need to do is let Terraform know about the desired state and Terraform takes care of steps internally.
Modules: Terraform provides modules functionality which helps in reusing the Terraform code. A complex infrastructure can be divided into multiple modules and each module can be reused in different projects. It is very easy to convert a given terraform configuration into modules and Terraform has its eco-system for pre-built modules.
State: When the infrastructure is created by Terraform, a state is maintained, which can be shared with other team members for collaboration purposes. Terraform offers the ability of remote state management, which helps prevent confusion amongst team members in case if they attempt to recreate the infrastructure.
Provisioning: Terraform is not a full-blown provisioning tool, but it helps in day 1 provisioning activities. Terraform’s local-exec and remote-exec blocks provide the ability to run inline scripts which can be used to install software components once the resource is created successfully. This is especially useful to assist configuration management tools like Chef, Ansible, and Salt Stack to install their respective agents, and have them send “UP” signal once they are installed successfully.
Open Source: Terraform is available for use as open source software as well as Enterprise version.
There are simple steps involved in successfully executing the Terraform code. These steps are closely related to the lifecycle of resources being created on cloud platforms. Again, these steps are cloud-agnostic, meaning the same steps/commands are used to create, update, and destroy resources on any given cloud provider.
Note: This blog post doesn’t cover installation steps for Terraform. It is assumed Terraform CLI is already installed on the system.
init
– Once we have configuration files ready, the very first command to run is terraform init
. The Terraform binary installation does not include support for all the cloud providers at once. Instead, based on the provider to be used, appropriate plugins are downloaded before the Terraform code is executed. In our example, running terraform init
would download the “aws provider” plugin. This command helps initialize the backend for a given Terraform directory.plan
– terraform plan
is used to generate an execution plan. Based on the configuration provided, Terraform generates an execution plan. In this phase, Terraform performs feasibility checks in terms of syntax errors, API authentication, state verification, etc. Running terraform plan
highlights if anything needs to be fixed before actual execution. If successful, it outputs a summary of potential changes in the infrastructure. It is ideal to run this before apply, as it makes you aware of any risks before modifying the infrastructure.apply
– terraform apply
is used to execute any changes to the infrastructure. It is recommended to run terraform plan
before running terraform apply
, as planning creates a plan file which is referred to during apply phase. However, if in case terraform apply
is executed directly, a new plan file will be created automatically.destroy
– terraform destroy
is used to destroy any resources which are part of the current configuration/state.Let us wrap this post by executing the code we wrote in the previous section in the similar sequence.
Note: please use the correct AMI based on your preferred region.
Navigate to the directory where above mentioned code is saved in a .tf file.
terraform init
Output:
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v3.22.0...
- Installed hashicorp/aws v3.22.0 (signed by HashiCorp)
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
terraform plan
Output:
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.example will be created
+ resource "aws_instance" "myec2" {
+ ami = "ami-12345qwert"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
. . .
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
terraform apply
Output:
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.myec2: Creating...
aws_instance.myec2: Still creating... [10s elapsed]
aws_instance.myec2: Still creating... [20s elapsed]
aws_instance.myec2: Still creating... [30s elapsed]
aws_instance.myec2: Still creating... [40s elapsed]
aws_instance.myec2: Still creating... [50s elapsed]
aws_instance.myec2: Creation complete after 51s [id=i-04ef3120a0006a153]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
terraform destroy
Output:
Terraform destroy
Plan: 0 to add, 0 to change, 1 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_instance.myec2: Destroying... [id=i-04ef3120a0006a153]
aws_instance.myec2: Still destroying... [id=i-04ef3120a0006a153, 10s elapsed]
aws_instance.myec2: Still destroying... [id=i-04ef3120a0006a153, 20s elapsed]
aws_instance.myec2: Still destroying... [id=i-04ef3120a0006a153, 30s elapsed]
aws_instance.myec2: Still destroying... [id=i-04ef3120a0006a153, 40s elapsed]
aws_instance.myec2: Still destroying... [id=i-04ef3120a0006a153, 50s elapsed]
aws_instance.myec2: Still destroying... [id=i-04ef3120a0006a153, 1m0s elapsed]
aws_instance.myec2: Destruction complete after 1m5s
Destroy complete! Resources: 1 destroyed.
In the next post, we would go through Terraform Syntax.