Building a Custom EC2 Module with an Amazon Machine Image Using Terraform

In Terraform, modules are blocks containing multiple resources being used together. Modules allow you to feed information and other “child” modules into a “parent” module, though “parent” modules cannot be fed into “child” modules. In this exercise, we will make a custom module so that a standardized EC2 instance with an Amazon Linux 2 AMI can be easily reproduced and adjusted in the future.

Using modules in Terraform is great, but one should be cautious of over-using modules because it can be challenging to understand and maintain an unorganized or redundant family of modules.

  • My Github
  • Terraform
  • AWS CLI
  • IDE of choice (I used Atom)

First I forked a “create ec2” repository from my Level Up In Tech course’s Github account (Image 1).

Image 1: Forking “create ec2” repo (circled in Yellow)

Then in Terminal I confirmed that I was in my home directory, and I used the command git clone https://github.com/kczarzasty/terraformec2.git to clone the forked repo (Image 2).

Image 2: Cloning the forked repo

Then I confirmed that I had a new working directory named “terraformec2” by changing into that directory (Image 3).

Image 3: Changing into new directory “terraformec2”

Now that the repository was set, I needed to set up my files for the module. Within the “terraformec2” directory, I made a new directory called “instance,” and therein I created my main.tf, outputs.tf, providers.tf, and variables.tf files (Image 4).

Image 4: Setting up the environment

We then needed to transfer the resource and provider section of ec2.tf to our new providers.tf of our instance module, and so I did so in Atom as seen in Image 5 & 6.

Image 5: Copying resource and provider from “ec2.tf”
Image 6: Pasting resource and provider into “providers.tf”

I then copied the instance resource from ec2.tf and pasted it in main.tf to be the provider (Image 7).

Image 7: Pasting the instance resource into “main.tf” from “ec2.tf”

I then cleared the information in ec2.tf, and replaced it with , as it will be our module file intaking code from the other files.

Image 8: Re-writing “ec2.tf” to source from the other files

I then changed directories back one by using the command cd ../. Once in the terraformec2 directory, I ran the following respectively to ensure my EC2 Instance was being created: terraform init, terraform fmt, terraform validate, terraform plan, terraform apply. Our successful result after typing “yes” to confirm terraform apply is shown in Image 9.

Image 9: The successful result of terraform apply

I confirmed in the my AWS Console that the EC2 instance had been created in region us-west-2 (Image 10).

Image 10: Confirming in the AWS Console the creation of the EC2 instance

We no longer needed this instance (it was just a test), so I then ran the command terraform destroy and typed “yes” to confirm the action (Image 11).

Image 11: Terraform destroy

At this point we had successfully set up the module, but as it stands at this point in the article, it is just a simple EC2 module offering no variability.

So now added variables to the module so that we can adjust the EC2 Instance in the future as needed.

First I took the three original resources & made appropriate variables for each in variables.tf (Image 12).

Image 12: Making appropriate variables in “variables.tf” for our original three resources

I then referenced these variables in main.tf, (Image 13) where main.tf formly had the fixed values set.

Image 13: Referencing appropriate variables

I then added a variable to variables.tf so that in the future we can specify how many instances we want to create (Image 14).

Image 14: Adding Instance Count to “variables.tf”

Then in ec2.tf I added a line for Instance Count and made it equal to 2 (Image 15).

Image 15: Adding Instance Count to “ec2.tf”

Then I made an adjustment. I was concerned that, when launching multiple EC2 instances, it would be difficult to differentiate between instances. Therefore in main.tf I wrote Line 6 so that the Instance Count would be indicated in the name. Note I also wrote +1 because the instance count starts at zero (Image 16).

Image 16: Clarifying Instance Count in the name in “main.tf”

Now that our module was increasingly customizable through the use of variables, I wrote my outputs.tf file which govern what information is printed in Terminal after successful terraform apply.

I elected to clarify Public IPs and EC2 Tags by writing in outputs.tf as seen in Image 17.

Image 17: Writing “outputs.tf”

I then wrote the desired outputs in a new rootoutputs.tf in /terraformec2 to go along with the ec2.tf module (Image 18).

Image 18: Writing “rootoutputs.tf”

I now wanted to pause and ensure the module was working. When I ran terraform validate, I discovered that my count index was invalid (Image 19).

Image 19: Error due to Line 6 of main.tf having no context

I then resolved this issue by adding Line 4 to main.tf as seen in Image 20.

Image 20

I then re-ran terraform validate, and this time everything was valid (Image 21).

Image 21: Valid configuration

I then ran terraform apply and confirmed “yes.” Image 22 shows that the resources were created and our outputs from rootoutputs.tf were printed.

Image 22: Successful apply with expected output

I then ran terraform destroy again, so as to avoid running these resources, accruing costs, etc.

Now we’ve made our custom module. This would be a great tool to reuse in the future, so we will push it to Github.

To do so I first made a .gitignore file in the root directory so as to ignore files that I didn’t want pushed (Image 23).

Image 23: Writing .gitignore

To ensure all files were set, I ran the command git status (Image 24).

Image 24: git status

To stage my files I ran the following command as seen in Image 25 to include all files that weren’t tracked or staged were ready.

git add ec2.tf .DS_Store .gitignore .terraform.lock.hcl instance/ rootoutputs.tf

Image 25: git add

I then ran the command git commit -m “initial commit” to commit my files (Image 26).

Image 26: git commit

I then used the command git push origin to push the files to my Github (Image 27).

Image 27: git push

I then confirmed in my Github that the custom EC2 module and all relevant files were there, and indeed they were (Image 28).

Image 28: Custom EC2 Module successfully pushed to my Github

Thanks for reading.

Credit: Level Up In Tech as was credited in the body of the article.

Cloud & DevOps. Also Blockchain.