Learn AWS Services With Terraform

Photo by Sam Moqadam on Unsplash

Before I made my transition to IT years ago, my understanding of “The Cloud” was mainly based on the services I interacted with. My understanding of its capabilities was limited to massive storage, because my interaction with the cloud was to download a song I had purchased, that I previously didn’t want stored on my device at the time, or I thought of file sharing with DropBox. Fast forward a few years later at my first IT job, and delving into the world of Amazon Web Services. I was completely overwhelmed with the multitude of services. Signing into the console made me scared to touch anything. Fast forward a few more years and I have a couple of certifications under my belt and three plus years experience working with AWS in production. Some of my biggest leaps in understanding how the services work was not in the console, but using AWS CLI or coding infrastructure builds with Boto (AWS SDK for Python). I also learned to write IaC (infrastructure as code) using CloudFormation and Terraform. If I were to develop a curriculum to learn AWS services in a meaningful way, I would start by just having a basic, high-level understanding of the services. I then would start with Terraform builds and then compare those builds to its manifestation in the console (GUI). In addition to being a very useful tool for deploying infrastructure, coding in Terraform is a great way of learning AWS resources.

If you would like to see the full Terraform configuration in addition to the snippets I will place in the article, you can check out a couple of my Terraform configurations here. The first is a “web server” (minus a database) and the second is an SQS triggered Lambda function.

The most fundamental block type in Terraform is the resource block. This block allows you to declare what type of service (resource) you would like to create. Lets look at how to create an ec2 instance with the resource block. An ec2 instance is basically a virtual server on a bigger server. In a Terraform configuration it would look like this:

resource "aws_instance" "web_server" {ami                         = var.amivpc_security_group_ids      = ["${aws_security_group.webserver-   sg.id}"]instance_type               = var.instance-typesubnet_id                   = aws_subnet.public.idcount                       = var.ec2countassociate_public_ip_address = trueuser_data        = var.user_datauser_data_base64 = var.user_data_base_64key_name         = var.key_name}

You can clearly see the ami (Amazon Machine Image), vpc security group id (virtual private cloud), instance type (referring to size and/or IO of the virtual server), subnet id to place your instance in, and a public ip so that your server is reachable outside of the VPC. The remaining declarations are optional. The bare minimum for your ec2 instance to be created is ami and instance type. On a more advanced level you would need to know how the different instance types would effect the performance of an application or if an AMI you chose is suited for your purposes. In addition, you can see that this service calls other services in the configuration. Terraform has helpful documentation for most services. In the documentation it will show you what is required and what is optional. It also shows code examples. I see these as cliff notes to understanding the service on a more technical and practical level.

Understanding how resource blocks call one another will help one learn what each configuration is used for. Earlier, I called the aws_security_group resource to assign to the vpc_security_group_ids variable in my ec2 instance. The aws_security_group resource configuration is as follows:

resource "aws_security_group" "webserver-sg" {depends_on = [aws_vpc.my-vpc,aws_subnet.public,aws_subnet.private]description = "HTTP, PING, SSH"# Name of the security Groupname = "webserver-sg"# VPC ID in which Security group has to be createdvpc_id = aws_vpc.my-vpc.id# Created an inbound rule for webserver accessingress {description = "HTTP for webserver"from_port   = 80to_port     = 80protocol    = "tcp"cidr_blocks = [""]}ingress {description = "HTTPS for webserver"from_port   = 443to_port     = 443protocol    = "tcp"cidr_blocks = [""]}# Created an inbound rule for pingingress {description = "Ping"from_port   = 0to_port     = 0protocol    = "ICMP"cidr_blocks = [""]}# Outward Network Traffic for the webserveregress {description = "output from webserver"from_port   = 0to_port     = 0protocol    = "-1"cidr_blocks = [""]}}

One of the first things you can see is the “depends_on” section of this resource. As you can see, this resource, as configured, depends on the vpc resource as well as two separate subnet resources. Drilling down into the configuration with the resources we allow you to understand the bigger picture of how everything is connected in this configuration. This understanding helps the student get past understanding the services in an abstract manner and makes one think of the services in a more functional way. In other words, it demystifies the typical technical jargon and, in my view, makes the “cloud” more simple.

One of the best learning methods, to learn something efficiently, is the feedback loop method. It is a reality check that informs you that your knowledge is limited. One of the things that was very frustrating to me when using CloudFormation was writing a configuration and applying, only to find a slew of errors. Perhaps there is a better way to check the configuration before applying it in CloudFormation, but I never got to that point. I found CloudFormation unnecessarily complex . When using Terraform, one of the best tools I use for a feedback loop is the terraform validate command. This command will not only check your syntax, but it will also check to see if there is any internal inconsistency. For example, if you attempted to create a resource without the necessary configurations and ran the validate command, you will receive an exact reason as to why you cannot create that resource and what needs to be done in order for you to successfully create said resource. With this information and with constant practice you can learn these services in more depth and fortify a more practical understanding of the services and how they fit into an overall infrastructure plan.

In my opinion, the best asset of Terraform is its depth and quality documentation as well as the Terraform Registry. The Terraform Registry is stock full of single resource and multi-resource modules. By viewing the source code of these modules you can have a solid understanding of best practices. I often reference modules by Anton Babenko. Most of his modules are the go-to modules for AWS services. I would suggest studying his modules and try to build your own module as close to his examples as possible.

The amount of services in the different cloud platforms are too much for one person to retain detailed knowledge to long term memory. Attempting to learn a cloud service merely by just knowing superfluous details is no way to become efficient in DevOps tooling. I would suggest using Terraform to build actual infrastructures and test those infrastructures to maintain a deep but practical knowledge and understating of services. If I knew what I know now when I first started learning AWS, I would have saved countless hours reading up on unnecessary information.