Building my AWS Home Lab (Part 1)


I recently sold my existing home lab environment and now run all of my work off a MacBook. The day I handed over my home lab server was a good day, but a sad day. My home lab saw me through the RHCSA exam, helped me learn Chef, and provided the backdrop for many of the articles I have posted here. As useful as my home lab has been, there was always a part of me which was dissatisfied. My home lab ran Proxmox, which is an absolutely fantastic and open source Hypervisor project. But to really get the most use out of my hypervisor, I would have needed a VMware ESXi license, and those aren’t exactly cheap. Furthermore, I kept running into issues created by my monolithic home lab design. All of my services were running off of one single hardware asset. I wanted to be able to maintain a production work environment as well as a test environment for side projects, without risking the stability of the production environment. Running everything off of one server chassis was not going to cut it. I considered trying to convince my wife of the merits of buying another 1200 dollar computer for my home lab, but I honestly wasn’t convinced in the value of that proposition myself. Enter AWS.

Rather than building up a secondary home lab server in my apartment, I could push all my work off onto an AWS environment. These days I don’t really have any large scale projects running on my home lab. I don’t host a Plex server or stream games from Steam. I also don’t keep any important records in Owncloud and now much of my data is stored in Apple’s iCloud. My main focus has been Terraform, Ansible, and Automation with Ruby. None of those tools require large hard drives, multi-threading, or lots of ram. Rather, my new focus requires flexibility. AWS provides a fantastic environment for spinning up and tearing down simple test environments within a prototyped private network. My production environment (Which hosts this website) is safe and secure, and I can tear down and rebuild my test environment without worrying about harming the uptime of this website. Additionally, with the way I have configured my AWS environment, I pay about $20 per month to use their services. My home lab cost me about $1200 to build over the course of two years and about $10 dollars per month in electricity. Conversely, the AWS $20 per month bill over the course of 2 years comes out to $480 dollars. Less than half the cost required to build my server. So for me and my use case, migrating to AWS was a simple decision.

Transitioning to AWS VPC

Once I had made the choice to migrate to AWS, I began to configure my AWS environment. To start, I created a new VPC (virtual private cloud) environment within my AWS development account partition (I maintain distinct AWS accounts for my production and development environmets). AWS VPCs are a framework which allows you to generate a virtualized network environment, similar to a homelab intranet, with optional internet access. This VPC will then provide the networking context for all of the virtual machines associated with my homelab. Below are the steps I took to set up my AWS VPC.

To create a new VPC, begin by navigating to the VPC Management Console frame within the AWS Console. Then select the Launch VPC Wizard button.

Once you have launched the VPC creation wizard, select the VPC with a “Single Public Subnet option” and then choose the select button in the lower right had corner. You are welcome to configure a private subnet VPC (this option will offer you better security). That said, for our use case, I don’t care about the security of these endpoints (They will be torn down and rebuilt frequently and do not house any data), so an easily accessible public subnet is sufficient for my needs.

After configuring the VPC Subnet context, we will need to provision the VPC name and subnet context.

First, name your new VPC, and then give the subnet a non-generic name. These options will make searching for your network easier within the AWS console and AWS CLI tools. Once you have finished with the VPC configuration, hit the Create VPC button in the lower right. Once that is done, you will be returned to the VPC Management Console, and your new VPC will show up below. Make sure to take note of the VPC ID, as we will be using it later.

Creating AWS EC2 Resources

After I built my VPC, I then added some EC2 (Elastic Cloud Compute) endpoints to my VPC. Elastic Cloud Compute is Amazon’s virtualization platform. For my homelab, I have opted to use a mixture of Amazon Linux and RedHat Linux endpoints. Long term, I will be automating the full deployment process of new endpoints with Terraform, Ansible, and the AWS CLI, but for now I will show the GUI based deployment method.

From the AWS Console, navigate to the EC2 Management Console. Then, select the Launch Instance button from the central panel (located halfway down the console window).

After selecting the Launch instance, we will need to select which EC2 image we will be deploying. For our use case, we will be choosing the standard x86 64-bit Amazon Linux distribution, available through the Quick Start list.

After choosing the Amazon Linux distribution, we will need to specify what level of compute, memory, and network capacity we need for the virtual machine. Because my homelab is purpose built to test out IT Automation toolsets and does not host any data or services, I will not need to create an endpoint with substantial resources. To that end, I have opted to use the t2.micro EC2 instance type. This instance is Free Tier eligible, which will also drive down costs. Simply select the t2.micro instance checkbox, and then “Next” in the bottom right.

From the next screen, we will be configuring the device specific networking and security requirements of the new EC2 instance. First, select the network dropdown and choose the new VPC which we created earlier. Then, tell the VPC to auto-assign this endpoint with a Publicly available IP address (Again, not something we would do in production and not a safe practice in general, but acceptable in this very transient environment where speed and utility is more important than security). Finally, select the Review and Launch button from the bottom right.

The EC2 wizard will next prompt you with an overview of the device which you have configured, and then prompt you to launch the endpoint. During the launch sequence, the console will prompt you to use an existing authentication key or create a new key for accessing the new EC2 instance. If you do not already have a key pair created, select “Create a new key pair”. Then, name the new key and then download the Key Pair. After the key has been downloaded, you can finish generating the EC2 instance by selecting “Launch Instance” from the bottom right.

After a few moments, the new endpoint will now be available from within the EC2 Instances window, as shown below:

To connect to the EC2 instance from your terminal, open up the EC2 instance from within the AWS console. Then, find the Public IPv4 Address of the endpoint. Using the endpoint IP, you will be able to connect to the endpoint with the following ssh command:

 ssh -I "/path/to/key.pem" ec2-user@PublicIPv4

Reporting on AWS EC2 Resources with AWS CLI

Being able to spin up new AWS resources is awesome, and super helpful. But, turning these assets off and on can be a bit of a pain. Especially once we have built out our full asset list with multiple endpoints running concurrently. To that end, we can use the AWS CLI tool to craft a few complex commands which will automatically turn on/off the devices within our VPC environment, all from a local command prompt. First though, we have to configure the AWS CLI. Amazon provides excellent documentation for this process, so I won’t duplicate work by presenting it here.

Once your AWS CLI environment has been configured, you can test that it is working by running the following command:

aws ec2 describe-vpcs

This command should output a list of VPCs currently associated with the AWS account configured for the AWS CLI. Below is the output of that command in my environment. The ID of the VPC(s) will show up in the last field within the output, labeled “VpcId”.

 - CidrBlock:
   - AssociationId: REDACTED
       State: associated
   DhcpOptionsId: REDACTED
   InstanceTenancy: default
   IsDefault: false
   OwnerId: 'REDACTED'
   State: available
   - Key: Name
     Value: Devbox

Next, using queries against the EC2 instances associated with this AWS account, we can sort for instances which are connected to this VPC:

aws ec2 describe-instances --filters "Name=vpc-id,Values=REDACTED"  --query 'Reservations[].Instances[].[InstanceId,Tags[*].Value,PublicIpAddress,PrivateIpAddress,State.Name] | map(&[], @)' --output text

That command is rather long and complex, but when ran, it returns the following:

i-REDACTED     Ansible Host      stopped
i-REDACTED     Ansible Node B       stopped
i-REDACTED     Ansible Node A      stopped

This command first starts by finding all EC2 instances which are associated with our VPC. Then, it prints out a report, starting with the EC2 Instance ID. The next column shows the Name tag which we have ascribed to each instance. The third column reports the internal IP of the endpoint, and then the last column shows the current status of the endpoint. If the devices were turned on, the report would also display the public IP addresses of the endpoints.

To get further clarity on how this particular command is generating that report, let’s break it down into chunks, starting at the front:

aws ec2 describe-instances

From the left broken down based on whitespace, this command states that we are using the AWS CLI command line tool. Then, we are telling the AWS CLI to perform an action within the EC2 service. Then, we are telling the tool to provide a report about our EC2 instances.

--filters "Name=vpc-id,Values=REDACTED"

Next, we are specifying that we want to filter the list of reported devices down to just devices contained within the “REDACTED” VPC.

--query 'Reservations[].Instances[].[InstanceId,Tags[*].Value,PublicIpAddress,PrivateIpAddress,State.Name] | map(&[], @)' --output text

Finally, we are telling the report what information we want to be included in the report. The reported elements are included within the final layer of brackets, right after the “Reservations[].Instances[].” text block. After the report elements, we include a mapping section. The mapping section and “–output text ” section simply allows us to choose how the report is formatted.

Automating AWS EC2 Resources with AWS CLI

Now, for the real meat and potatoes, we can combine the query we just created with another aws ec2 command to start and stop our EC2 instances associated with the VPC. Those two commands are listed below:

aws ec2 stop-instances --instance-ids $(aws ec2 describe-instances --filters "Name=vpc-id,Values=REDACTED"  --output text --query 'Reservations[].Instances[].[InstanceId]' | tr '\n' ' ' | sed 's/.$//')
aws ec2 start-instances --instance-ids $(aws ec2 describe-instances --filters "Name=vpc-id,Values=REDACTED"  --output text --query 'Reservations[].Instances[].[InstanceId]' | tr '\n' ' ' | sed 's/.$//')

As with the previous command, there are multiple elements to this command structure. In fact, both of these commands are actually compound commands which integrate portions of the previous command.

aws ec2 stop-instances --instance-ids

First, we are telling the terminal that we are using the AWS CLI command line tool. Then, as with the previous command, we are telling the CLI tool that we are going to interface with the EC2 service. Then, we are going to tell the CLI to stop some running instances. We then need to provide the instance ID’s for those instances. We are going to achieve this by proving the output of our original VPC query command as the input for this new stop-instances command.

$(aws ec2 describe-instances --filters "Name=vpc-id,Values=REDACTED"  --output text --query 'Reservations[].Instances[].[InstanceId]' | tr '\n' ' ' | sed 's/.$//')

The $() surrounding this command delineates the results of its constituents as the input of the –instance-ids command. The contents of the brackets contain another describe-instances command. The only difference between this command and the previous describe-instances is the formatting. This new version outputs just the Instance ID, and displays each ID in one line with a space separating each ID. This formatting variation is processed by piping the output of the describe-instances command through the last two commands:

| tr '\n' ' ' | sed 's/.$//'

the tr command trims the output, and in this case forces all of the Instance IDs into one line. Then, the sed command adds a space between each of the Instance IDs. This process is required, as that is the formatting which the stop-instances command expects.

Now, using the start and stop instances commands, I can spin up and spin down my AWS homelab environment without any effort at all.

For an extra level of simplicity, I created aliases in my terminal environment for all three longform commands (start-instances, stop-instances, and describe-instances). Now, instead of typing out:

aws ec2 start-instances --instance-ids $(aws ec2 describe-instances --filters "Name=vpc-id,Values=REDACTED"  --output text --query 'Reservations[].Instances[].[InstanceId]' | tr '\n' ' ' | sed 's/.$//')

I simply type in:



AWS is awesome, and can be configured to be a pretty dang cheap homelab alternative. You can also automate everything in AWS.

Leave a Comment