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

Kevin Czarzasty
6 min readJul 12, 2021

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.

For this exercise, I needed:

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

STEP 1: Fork & Clone Repo

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”

STEP 2: Module Setup

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.

STEP 3: Variables

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”

STEP 4: Outputs

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.

STEP 5: Github

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.

--

--