Custom scripts and tools are a common way for DevOps professionals to manage resources within their infrastructure.
These scripts, however can become challenging to run, share, scale, and maintain, particularly as the complexity of the infrastructure grows and the team size expands.
In this blog post, we'll explore how you can use Retool to build custom easy to use admin panels that can simplify your common DevOps processes.
We will use an example of managing Amazon EC2 virtual servers to demonstrate how Retool can help you build a custom tool that simplifies common tasks, such as launching new instances, starting and stopping instances, and checking instance status.
By the end of this post, you'll understand how you can use Retool’s drag-and-drop UI components and built-in integrations to quickly convert common DevOps processes into flexible software accessible to your entire team.
Let’s get started!
Log in to Retool and create a new Resource for Amazon EC2 API using Retool's built-in REST API integration. We will be using Retool’s built-in AWS v4 authentication, so we wouldn’t need to worry about writing complex authentication code. Retool will handle that for us in a secure way.
- Name ->
Amazon EC2
- Base URL ->
https://ec2.amazonaws.com
- Headers ->
Accept: application/json
- Authentication ->
AWS v4
- AWS Region ->
<YOUR AWS REGION>
- AWS Access Key ID ->
<YOUR AWS Access Key ID>
- AWS Secret Key ID ->
<YOUR AWS Secret Key ID>
💡Note: You would need an AWS Access Key ID and AWS Secret Key ID for the IAM role or user with the AmazonEC2FullAccess
policy attached for this step.
Now that we have connected Retool with Amazon EC2 API, we are ready to build the actual user interface to display it. But before we add UI components to our canvas, let's look at the data we would need to build out the functionality for our EC2 Instance Manager, and how we will get it.
- List of EC2 Instances - Send GET request to EC2 API's DescribeInstances Action.
- Launch a new EC2 Instance - Send POST request to EC2 API's RunInstances Action
- Stop an Instance by Instance ID - Send POST request to EC2 API's StopInstances Action, along with the Instance ID.
- Start an Instance by Instant ID - Send POST request to EC2 API's StartInstances Action, along with the Instance ID.
We will create a new GET Resource Query for the EC2 API’s DescribeInstances action.
Let’s name it getAllEC2Instances
. We will choose the resource we created earlier for Amazon EC2 API as the Resource. Choose the Action Type as GET. The base URL for the Resource Query will automatically be set to ec2.amazonaws.com
. Add this as URL params -
1?Action=DescribeInstances&Filter.1.Name=instance-type&Filter.1.Value.1=t2.micro&Filter.2.Name=architecture&Filter.2.Value.1=x86_64&Version=2016-11-15
In this request, we are using two filters to search for instances with specific attributes -
- instance-type: Instance type refers to an EC2 template that specifies the amount of resources (such as CPUs and memory) that a virtual machine will have. In this case, the value for the instance-type filter is "t2.micro", which are instances that have 1 vCPU and 1GB of memory.
- architecture: Architecture refers to the type of processor used by the instance. In this case, the value for the architecture filter is "x86_64", which refers to a 64-bit processor architecture commonly used in personal computers.
Let’s try out this query by hitting the Preview button. You will see the Response of the query in the panel under the query.
The Amazon EC2 API returns results in XML format. Luckily, Retool parses that XML automatically for us, and presents that in a JSON format. You can view this data by clicking on the State tab in the left sidebar. The instance data we are looking for is nested under data -> parsedXML -> DescribeInstanceResponses -> reservationSet -> 0 -> item
The XML data returned by the EC2 API is deeply nested, making it difficult to extract the relevant information for each instance. So, we will write a bit of custom JavaScript code to “transform” the data into an easily readable array of objects using a JavaScript Transformer.
The fields we want to display in our table are - Architecture, DNS Name, Instant State, Image ID, Reservation ID, IP Address, Instance Name, Launch Time, Instance ID, Group Name, and Region.
So, let’s add a new Transformer with this JavaScript code to simplify the data into an array of objects that contain only the relevant information we need for each instance, making it much easier to read and work with. We will then use the output of this function to power other parts of our app, starting with the Table.
1// Store data from the EC2 API in the variable data
2const data = {{getAllEC2Instances.data.parsedXml.DescribeInstancesResponse.reservationSet['0'].item}};
3
4// Initialize an empty array to hold final data
5let finalData = [];
6
7// Function to capitalize the first letter of a string
8function capitalizeFirstLetter(string) {
9 return string.charAt(0).toUpperCase() + string.slice(1);
10}
11
12// Loop through each key in the data object
13Object.keys(data).forEach(key => {
14
15 // Retrieve specific data points for each instance and assign to variables
16 let architecture = data[key].instancesSet[0].item[0].architecture[0];
17 let dnsName = data[key].instancesSet[0].item[0].dnsName[0];
18 let instanceState = capitalizeFirstLetter(data[key].instancesSet[0].item[0].instanceState[0].name[0]);
19 let imageId = data[key].instancesSet[0].item[0].imageId[0];
20 let reservationId = data[key].reservationId[0];
21 let ipAddress = typeof(data[key].instancesSet[0].item[0].ipAddress) !== 'undefined' ? data[key].instancesSet[0].item[0].ipAddress[0] : 'N/A';
22 let instanceName = typeof(data[key].instancesSet[0].item[0].tagSet) !== 'undefined' ? data[key].instancesSet[0].item[0].tagSet[0].item[0].value[0] : 'N/A';
23 let launchTime = data[key].instancesSet[0].item[0].launchTime[0];
24 let instanceId = data[key].instancesSet[0].item[0].instanceId[0];
25 let groupName = data[key].instancesSet[0].item[0].groupSet[0].item[0].groupName[0];
26 let region = data[key].instancesSet[0].item[0].placement[0].availabilityZone[0];
27
28 // Create a new object containing all relevant data points
29 let newObj = {
30 'architecture': architecture,
31 'dnsName': dnsName,
32 'instanceState': instanceState,
33 'imageId': imageId,
34 'reservationId': reservationId,
35 'ipAddress': ipAddress,
36 'instanceName': instanceName,
37 'launchTime': launchTime,
38 'instanceId': instanceId,
39 'groupName': groupName,
40 'region': region
41 };
42
43 // Add new object to the finalData array
44 finalData.push(newObj);
45});
46
47// Log finalData to the console and return it
48console.log(finalData);
49return finalData;
50
Click on Preview to see the results of this transformer. As you can see, it’s much cleaner and easier to read and navigate.
Drag the Table Component on to the canvas, and change the Data property to reference our formatted EC2 instance data using Retool's double curly brace data binding to {{transformInstanceData.value}}
.
The table will be populated with the data for your EC2 instances. Nice, we are on our way now with the list of EC2 instances being listed. But, it's a bit hard to know the status of these instances at a quick glance. Let's fix that.
To make it easier to see the state of the instances, let’s add a background color to the Instance State
column of our table. Change the Background property of the Instance State column to this conditional -
1{{self.toLowerCase() === "running" ? 'rgba(165, 255, 110, 0.5)':self.toLowerCase() === "stopped" ? 'rgba(255, 141, 150, 0.5)':'rgba(255, 188, 99, 0.5)'}}
If the state is “running”, then the color code rgba(165, 255, 110, 0.5) is returned, representing a light green color with a 50% transparency.
If the state is “stopped”, then the color code rgba(255, 188, 99, 0.5) is returned, representing a light red color with a 50% transparency.
Create a new POST Resource Query for the EC2 API’s RunInstances action.
We will eventually add a Listbox component to select the image type, but for now, let's hardcode the image ID to ami-0dfcb1ef8550277af
, which is a Linux image provided by Amazon in the AWS Free Tier called Amazon Linux 2 AMI (HVM) - Kernel 5.10
.
launchNewEC2Instance
- Action Type ->
POST
- Headers ->
application/x-www-form-urlencoded; charset=utf-8
- Body ->
x-www-form-urlencoded
- Body Params:
- Action ->
RunInstances
- ImageId ->
ami-0dfcb1ef8550277af
- InstanceType ->
t2.micro
- MinCount ->
1
- MaxCount ->
1
- Version ->
2016-11-15
Now that we have the query for launching a new instance, we can drag a button component to the canvas, and connect this query to it by creating an event handler for the button, and setting its Click event to fire off the launchNewEC2Instance
query.
To ensure that the table automatically displays the newly launched instance, we will add getAllEC2Instances query to the Success section of the launchNewEC2Instance
query, so it’s fired off automatically when a new instance is created successfully.
That's it. Let's test it. Clicking the Launch new EC2 Instance button should launch a new T2.Micro
EC2 instance using the image ID - ami-0dfcb1ef8550277af
, and the table should automatically be updated with the status of the new instance set to "Pending" (it can take a few seconds for the new EC2 instance to complete spinning up, at which point the status will automatically change to "Running")
To stop and restart our existing instances, we will add two new Resource Queries, which will use EC2 API's StopInstances and StartInstances actions. We will then connect these queries to two actions buttons in the table.ru
Let's create a query called stopEC2InstanceByInstanceID, using the configuration below:
- Action Type ->
POST
- Headers ->
application/x-www-form-urlencoded; charset=utf-8
- Body ->
x-www-form-urlencoded
- Body Params:
- Action ->
StopInstances
- InstanceId.1 ->
{{ table1.selectedRow.data['Instance ID'] }}
- Version ->
2016-11-15
Next,create a query called startEC2InstanceByInstanceID, using the same configuration as above, except the Action, which should be StartInstances
In the Table’s inspector, navigate to the Actions section, and add two buttons for Start, and Stop.
Then, set the Action query property for these buttons to their respective queries we created earlier - Stop button (stopEC2InstanceByInstanceID
), Start button (startEC2InstanceByInstanceID
).
To ensure the table automatically displays the updated status of the instance after stopping/starting an instance, we will add getAllEC2Instances
query to the “Success” section of the startEC2InstanceByInstanceID
and stopEC2InstanceByInstanceID
queries, so it’s fired off automatically when an instance stops or starts successfully.
Finally, let’s add a Listbox component to the canvas, so we can choose the type of image we would like to launch.
You can use Amazon EC2’s DescribeImages action to get details about the images. But since we only want to list the images available in the free tier, instead of calling an API, we will simply create a JSON object with the list of images we want to display.
Create a new JavaScript query called imageDataJSON and paste this code into it.
1const imageData = [
2 {
3 "id": 1,
4 "image_id": "ami-0c2b0d3fb02824d92",
5 "image_title": "Microsoft Windows Server 2022 Base",
6 "show_flag": true
7 },
8 {
9 "id": 2,
10 "image_id": "ami-0dfcb1ef8550277af",
11 "image_title": "Amazon Linux 2 AMI (HVM) - Kernel 5.10",
12 "show_flag": true
13 },
14 {
15 "id": 3,
16 "image_id": "ami-0c9978668f8d55984",
17 "image_title": "Red Hat Enterprise Linux 9",
18 "show_flag": true
19 },
20 {
21 "id": 4,
22 "image_id": "ami-0557a15b87f6559cf",
23 "image_title": "Ubuntu Server 22.04 LTS",
24 "show_flag": true
25 }
26];
27
28return imageData;
To ensure our Listbox is automatically populated when the app opens, we will check the box Run this query on page load?
in the advanced tab for the imageDataJSON
query.
Now that we have the data ready for the EC2 images we want to be able to choose from, we are ready to build the UI for it.
Drag a Listbox component to the canvas, and change the Data Source
property to reference the data returned by the imageDataJSON
JavaScript query we just created. Also, change the value
and label
properties of the listbox to item.image_id
(value that should be passed when the item is selected) and item.image_title
(label value that should be displayed in the Listbox) respectively.
Finally, we will update our launchNewEC2Instance
to reference the image selected in the Listbox Component by changing the ImageId
URL parameter to {{ listbox1.selectedItem.image_id }}
.
Congratulations! After this long journey, you now have your very own Amazon EC2 Instance Manager. With the power to stop, start, and launch instances, and choose from a variety of EC2 images, you've become a DevOps superhero.
To wrap up this post, we used the example of an EC2 Instance Manager to explore how Retool's drag-and-drop UI components and built-in integrations can help you quickly build custom admin panels that simplify common DevOps tasks.
We went through how to add a Resource for Amazon EC2 API, launch and manage EC2 instances, how to change the background color of columns to reflect the instance state data using RGBA CSS color codes, and how to keep the table refreshed by firing off the list EC2 instances query as a success query trigger.
I hope you enjoyed this post, and that it has inspired you to explore Retool further and use it to create your own custom admin panels for managing virtual servers, databases, or other infrastructure resources.
If you’d like to build a similar app, here’s the JSON you can import into your own free Retool instance. You can even build a DevOps dashboard.
Thanks for reading, and happy Retooling!
Reader