Monitor and Auto-Heal an AWS Server
Provision an EC2 instance with Terraform and add CloudWatch alarms and dashboards.
Introduction
30 Second Summary
Think about how your phone alerts you when storage is running low or battery is dying. Servers in the cloud need the same kind of watchful eye, but they cannot alert themselves without someone setting up the monitoring.
In this project, you will write Terraform configuration to deploy an AWS server, wire up CloudWatch alarms that email you when CPU spikes, and build a live dashboard showing server health metrics in real time.
What You'll Build
You will run a single command and watch an entire monitored server come to life, complete with email alerts and a real-time health dashboard you can pull up and show to anyone.
By the end of this project, you'll have:
- A running EC2 instance you can create, modify, and destroy with a single Terraform command.
- An automated email alert powered by SNS that fires the moment your server's CPU crosses 80%.
- A live monitoring dashboard showing CPU, network traffic, and disk activity at a glance.
- Secret Mission: add an auto-recovery action so AWS automatically reboots your server if it becomes unresponsive.
Are there any prerequisites?
You will need an AWS account with console access. If you do not have one yet, you will create one in Step 1.
No prior experience with Terraform or CloudWatch is required. This project teaches both from scratch.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Install Terraform and AWS CLI
Every piece of infrastructure you build in this project starts as a text file on your machine. Terraform reads those files and translates them into real AWS resources.
To make that happen, you need two CLI tools installed and connected. Terraform handles the provisioning. The AWS CLI handles authentication to your AWS account.
In this step, get ready to:
- Install Terraform CLI on your Windows machine.
- Install and configure AWS CLI v2.
- Verify both tools can reach your AWS account.
Install Terraform on Windows
Terraform is distributed as a single binary file. You download it, place it in a folder, and tell Windows where to find it.
- Press the Windows key to open your search bar.
- Type PowerShell and press Enter to open it.
- Check if Terraform is already installed by running this command:
terraform -version
✔️ I see version 1.2 or higher
Terraform is ready to go. Skip ahead to the next substep where you install the AWS CLI.
ⓧ I see an older version
You have Terraform installed but the version is too old for this project. You need version 1.2 or higher.
- Open https://developer.hashicorp.com/terraform/install in your browser.
- Under the Windows section, click the AMD64 download link to get the latest binary.
- Extract the downloaded ZIP file.
- Replace your existing terraform.exe with the new one from the ZIP.
- Close and reopen PowerShell, then run terraform -version again to confirm the update.
ⓧ Command not found
Terraform is not installed yet. Follow these steps to set it up.
- Open https://developer.hashicorp.com/terraform/install in your browser.
- Under the Windows section, click the AMD64 download link to download the ZIP file.
- Create a new folder at C:\Terraform using File Explorer.
- Extract terraform.exe from the ZIP into C:\Terraform.
- Open the Start menu and search for Environment Variables.
- Click Edit the system environment variables.
- Click the Environment Variables button at the bottom of the window.
- Under System variables, find and select Path, then click Edit.
- Click New and type C:\Terraform.
- Click OK on all open dialogs to save your changes.
- Close PowerShell completely and reopen it so the new PATH takes effect.
- Confirm the installation by running this command:
terraform -version
You should see a version number like Terraform v1.15.8 printed in your terminal.
Still seeing 'command not found'?
Make sure you closed and reopened PowerShell after editing the PATH. The old window does not pick up PATH changes.
Double-check that C:\Terraform contains terraform.exe (not a nested subfolder with the file inside it).
Help me troubleshoot this
What is Terraform?
Terraform is an Infrastructure as Code tool made by HashiCorp. You write configuration files that describe the cloud resources you want, and Terraform creates, updates, or destroys them for you.
Instead of clicking through the AWS console, you run a single command and Terraform handles the rest.
Install AWS CLI on Windows
The AWS CLI is how your machine authenticates to your AWS account. Terraform uses these credentials behind the scenes when it creates resources.
- Check if the AWS CLI is already installed by running this command in PowerShell:
aws --version
✔️ I see aws-cli/2.x
AWS CLI v2 is already installed. Skip ahead to the configuration step below.
ⓧ I see aws-cli/1.x
You have an older version of the AWS CLI. This project requires version 2. Download the new installer to upgrade in place.
- Download the MSI installer from https://awscli.amazonaws.com/AWSCLIV2.msi.
- Run the downloaded AWSCLIV2.msi file and follow the on-screen prompts to complete the installation.
- Close and reopen PowerShell after the installer finishes.
- Run aws --version again to confirm it now shows aws-cli/2.x.
ⓧ Command not found
AWS CLI is not installed yet. Follow these steps to install it.
- Download the MSI installer from https://awscli.amazonaws.com/AWSCLIV2.msi.
- Run the downloaded AWSCLIV2.msi file.
- Follow the on-screen prompts: accept the license agreement and keep the default install location.
- Close and reopen PowerShell after the installer finishes.
- Confirm the installation by running:
aws --version
You should see output like aws-cli/2.27.41 Python/3.11.6 Windows/10 exe/AMD64 prompt/off.
AWS CLI command not recognized after install?
Make sure you closed and reopened PowerShell after the MSI installer finished. Windows needs a fresh terminal session to pick up the new PATH entry.
Help me fix this
Now connect the AWS CLI to your AWS account. This step is required whether you just installed the CLI or already had it.
- Run the configuration command:
aws configure
What will this command ask me?
The aws configure command prompts you for four values, one at a time:
- AWS Access Key ID (from your IAM user in the AWS console).
- AWS Secret Access Key (shown once when you create the access key).
- Default region name (enter us-east-1).
- Default output format (enter json).
- When prompted for your Access Key ID, paste the key from your IAM user.
- When prompted for your Secret Access Key, paste the secret from when you created the key.
- When prompted for the default region name, type us-east-1 and press Enter.
- When prompted for the default output format, type json and press Enter.
Where do I find my access keys?
In the AWS console, go to IAM and select your user. Under the Security credentials tab, click Create access key. Copy both the Access Key ID and Secret Access Key before closing the dialog. The secret is only shown once.
Verify both tools work together
Both tools are installed and configured. Time to confirm they can actually reach AWS.
Before you run the next command, what do you think it will return? Think about what AWS knows about your account identity.
- Test your AWS credentials by running:
aws sts get-caller-identity
You should see a JSON response containing your AWS account ID, user ID, and ARN. This proves the AWS CLI can authenticate with your account.
- Confirm Terraform is still accessible by running:
terraform -help
You should see Terraform's list of available commands printed in your terminal.
Seeing 'Unable to locate credentials'?
This means aws configure did not save your credentials correctly. Run aws configure again and double-check you pasted the Access Key ID and Secret Access Key without extra spaces.
Help me debug my credentials
Your environment is ready. Both tools are installed, configured, and talking to AWS. Next up, you will write your first Terraform configuration and deploy a real EC2 instance.
Deploy Your First EC2 Instance with Terraform
Your tools are installed and your AWS credentials are configured. Now it is time to use them to deploy a real server.
You need a running EC2 instance before you can monitor it. Deploying via Terraform rather than clicking through the AWS console means you can reproduce, modify, and destroy this server with a single command.
In this step, get ready to:
- Write Terraform configuration files that define your server.
- Initialize the Terraform workspace and download the AWS provider plugin.
- Deploy a live EC2 instance to AWS with a single command.
Create the Terraform configuration
Terraform reads .tf files from a project folder to understand what infrastructure you want. You will create two files: one that declares which provider plugin Terraform needs, and one that defines the actual resources to build.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir cloudops-monitor
cd cloudops-monitor
- Confirm the folder was created by running:
ls
The folder should be empty for now. You are about to fill it with your infrastructure code.
- Create a new file called terraform.tf in your cloudops-monitor folder using any text editor and paste the following code:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
What does this code do?
This file tells Terraform two things: which provider plugin to download (the AWS provider from HashiCorp, version 5.92 or newer in the 5.x range), and that your Terraform CLI must be version 1.2 or higher to use this configuration.
Think of this as a dependency lock. It prevents accidental use of an incompatible Terraform or provider version.
- Save terraform.tf.
- Confirm the file exists by running:
ls
You should see terraform.tf listed in the output.
Don't see the file?
Make sure you saved the file inside the cloudops-monitor folder on your Desktop, not somewhere else. Run pwd to confirm you are in the right directory.
help me find my terraform.tf file
- Create a second file called main.tf in the same cloudops-monitor folder. This is where you define the AWS provider and your EC2 instance. Paste the following code:
provider "aws" {
region = "us-east-1"
}
data "aws_ami" "amazon_linux" {
most_recent = true
filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["137112412989"] # Amazon
}
What does this code do?
The provider block tells Terraform to connect to AWS in the us-east-1 region.
The data block looks up the latest Amazon Linux 2023 AMI automatically. Instead of hardcoding an AMI ID that could go stale, Terraform queries AWS for the most recent image matching your filters every time you apply.
- Now add the EC2 instance resource below the data source in the same main.tf file:
resource "aws_instance" "server" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
tags = {
Name = "cloudops-monitor"
}
}
What does this code do?
This resource block declares a single EC2 instance. It uses the AMI ID found by the data source above, sets the instance type to t2.micro (free tier eligible), and tags it with the name "cloudops-monitor" so you can identify it in the AWS console.
- Save main.tf.
✔️ Awesome, I've got everything!
Great. Make sure you have saved both terraform.tf and main.tf in your cloudops-monitor folder.
ⓧ I'd like to double check the full code
Here is the complete terraform.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
And here is the complete main.tf:
provider "aws" {
region = "us-east-1"
}
data "aws_ami" "amazon_linux" {
most_recent = true
filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["137112412989"] # Amazon
}
resource "aws_instance" "server" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
tags = {
Name = "cloudops-monitor"
}
}
Initialize and apply
Your configuration files are ready. Now Terraform needs to download the AWS provider plugin before it can talk to AWS.
- Download the AWS provider plugin by running this command:
terraform init
You should see output that includes Installing hashicorp/aws v5.98.0 and ends with Terraform has been successfully initialized!.
What did terraform init do?
Terraform read your terraform.tf file, found that you need the hashicorp/aws provider, downloaded it into a hidden .terraform directory, and created a lock file (.terraform.lock.hcl) to pin the exact version for future runs.
Seeing an error during init?
Make sure your terminal is inside the cloudops-monitor folder. Run ls and confirm you see terraform.tf and main.tf listed. If not, cd into the correct folder and retry.
help me fix my terraform init error
- Preview what Terraform will create by running:
terraform plan
You should see a summary ending with Plan: 1 to add, 0 to change, 0 to destroy. This tells you Terraform will create one resource (your EC2 instance).
Before you run the next command, think about this: what do you expect to happen when Terraform creates this instance? Will it succeed or fail?
- Deploy your EC2 instance by running:
terraform apply
What happens when you run this?
Terraform will show the same plan you just previewed, then ask for confirmation. It will print:
Do you want to perform these actions? followed by Enter a value: waiting for your input.
- Type yes and press Enter to confirm.
Wait for Terraform to finish creating the instance. You should see output ending with Apply complete! Resources: 1 added, 0 changed, 0 destroyed..
Apply failed with an error?
If you see a credentials error, run aws sts get-caller-identity to check your AWS credentials are still valid. If that command fails, re-run aws configure and enter your credentials again.
If you see a syntax error referencing your .tf files, double check your code against the full code tab above.
help me fix my terraform apply error
Confirm your server is live
- View the details of your new instance by running:
terraform show
You should see your instance's details including its public IP, instance ID, and the tag Name = "cloudops-monitor".
- Open the AWS Management Console in your browser.
- Navigate to the EC2 service (search for "EC2" in the top search bar).
- Click Instances in the left sidebar.
- Confirm you see an instance named cloudops-monitor in the Running state.
Why Terraform instead of the console?
You could have clicked through the EC2 launch wizard to get the same result. The difference is that your .tf files are a permanent record of exactly what you built. You can version them, share them, and re-run them to recreate this server identically at any time.
This is the core idea behind Infrastructure as Code: your infrastructure is reproducible and auditable, not a one-off set of clicks you cannot repeat.
Your server is live. But right now you are flying blind. You cannot tell whether this instance is idle, overloaded, or about to crash. There is no alarm, no dashboard, and no notification set up. If something goes wrong, you would not know until a user complains.
You have a live EC2 instance fully managed by code. Next up, you will add a CloudWatch alarm that watches this server's CPU and emails you the moment it spikes.
Add a CloudWatch Alarm and Email Alert
Your EC2 instance is running, but you have no way of knowing if something goes wrong. If CPU spikes to 100% and the server becomes unresponsive, you would not find out until a user complains.
In this step, you will add a CloudWatch alarm that watches CPU utilization and an SNS email notification that alerts you when it crosses 80%. This closes the visibility gap from the previous step.
In this step, get ready to:
- Create an SNS topic and email subscription for alarm notifications.
- Add a CloudWatch alarm that monitors CPU utilization on your EC2 instance.
- Deploy the new resources and confirm the alarm shows an OK state.
Add an SNS topic for notifications
Before your alarm can email you, it needs a delivery channel. AWS Simple Notification Service (SNS) provides this through topics. You publish a message to a topic, and SNS delivers it to every subscriber on that topic.
- Open main.tf in your editor.
- Add the following code at the bottom of the file, after the aws_instance resource block:
# Create an SNS topic for alarm notifications
resource "aws_sns_topic" "alerts" {
name = "cloudops-alerts"
}
# Subscribe your email to the topic
resource "aws_sns_topic_subscription" "email" {
topic_arn = aws_sns_topic.alerts.arn
protocol = "email"
endpoint = "your-email-here"
}
- Replace your-email-here with your actual email address (keep the quotes around it).
What does this code do?
The aws_sns_topic resource creates a named notification channel called cloudops-alerts. Any AWS service can publish messages to this topic.
The aws_sns_topic_subscription resource tells SNS to forward every message on that topic to your email address using the email protocol.
- Save main.tf.
- Confirm Terraform can read your new configuration by running:
terraform plan
You should see a summary line that reads Plan: 2 to add, 0 to change, 0 to destroy. confirming Terraform detected the two new SNS resources.
Seeing a syntax error?
- Make sure the email address is wrapped in double quotes: endpoint = "you@example.com".
- Check that the previous resource block (aws_instance) has a closing } before your new code.
- Confirm there is a blank line separating each resource block.
Ask for help: Why is my terraform plan failing with a syntax error in main.tf?
Add a CloudWatch alarm for CPU utilization
Now you need a watcher that checks your server's CPU metric every five minutes. If the average CPU stays above 80% for two consecutive checks (10 minutes total), the alarm fires and publishes to your SNS topic.
- In main.tf, add the following code below the SNS subscription block you just created:
# Alarm when CPU exceeds 80% for two consecutive periods
resource "aws_cloudwatch_metric_alarm" "cpu_high" {
alarm_name = "cloudops-cpu-high"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
alarm_description = "Alarm when CPU exceeds 80% for 10 minutes"
dimensions = {
InstanceId = aws_instance.server.id
}
alarm_actions = [aws_sns_topic.alerts.arn]
}
What does this code do?
- metric_name and namespace tell CloudWatch which metric to watch. CPUUtilization in the AWS/EC2 namespace is a built-in metric that AWS publishes automatically for every EC2 instance.
- period is 300 seconds (5 minutes). evaluation_periods is 2. The alarm only fires when CPU exceeds 80% for two consecutive 5-minute windows (10 minutes total).
- dimensions scopes the alarm to your specific instance using aws_instance.server.id. Without this, CloudWatch would aggregate across all instances.
- alarm_actions points to your SNS topic. When the alarm fires, CloudWatch publishes a notification to that topic, which forwards it to your email.
- Save main.tf.
Deploy and confirm
You now have all three new resources defined. Time to deploy them and verify everything is wired up correctly.
- Preview the changes by running:
terraform plan
You should see Plan: 3 to add, 0 to change, 0 to destroy. confirming the SNS topic, SNS subscription, and CloudWatch alarm will be created.
- Deploy the resources by running:
terraform apply
- When prompted, type yes and press Enter.
Terraform creates all three resources and prints Apply complete! Resources: 3 added, 0 changed, 0 destroyed. at the end.
Why do I need to confirm my email?
SNS email subscriptions require manual confirmation to prevent spam. AWS sends a confirmation email to prove you own the address before delivering notifications to it. Until you click the link, the subscription stays in a pending state and no alerts reach you.
- Check your email inbox for a message from AWS with the subject line about confirming your subscription.
- Click the Confirm subscription link in that email.
- Your browser opens a page displaying a confirmation message with your subscription ARN.
Don't see the confirmation email?
- Check your spam or junk folder. AWS SNS emails sometimes land there.
- Verify you entered the correct email address in main.tf. If it is wrong, update the endpoint value, save the file, and run terraform apply again.
- The email arrives within a few minutes. Wait 2-3 minutes before troubleshooting further.
Ask for help: I'm not receiving the SNS subscription confirmation email after running terraform apply.
Before you check the alarm state, what status do you expect the alarm to show right now? Your server is idle with minimal CPU usage.
- Open the AWS Management Console in your browser.
- Navigate to CloudWatch by typing CloudWatch in the search bar and selecting the service.
- In the left sidebar, click Alarms, then All alarms.
- You should see your alarm named cloudops-cpu-high in the OK state.
The alarm shows OK because your server's CPU is well below the 80% threshold. If CPU ever crosses that line for 10 minutes, the alarm switches to ALARM and you get an email.
✔️ Awesome, I've got everything!
Great. Double check you have saved main.tf and that your alarm is visible in the CloudWatch console.
ⓧ I'd like to double check the full code
Your main.tf now has more than 30 lines. Scroll through it and confirm the following sections are present in this order:
- The provider "aws" block with region = "us-east-1".
- The data "aws_ami" "amazon_linux" block with the name filter and owner.
- The resource "aws_instance" "server" block with instance_type = "t2.micro" and the Name tag.
- The resource "aws_sns_topic" "alerts" block with name = "cloudops-alerts".
- The resource "aws_sns_topic_subscription" "email" block with your email address.
- The resource "aws_cloudwatch_metric_alarm" "cpu_high" block with threshold = 80 and alarm_actions pointing to the SNS topic.
If any section is missing or out of place, scroll up to the corresponding substep and copy the code block again.
You now have eyes on your server. Next up, you will build a dashboard that shows CPU, network, and disk metrics in real time so you can spot trends before they trigger an alarm.
Build a CloudWatch Dashboard
Your alarm tells you when CPU crosses a threshold. But it does not show you the trend leading up to that spike.
A CloudWatch dashboard gives you a real-time view of CPU, network, and disk activity so you can spot problems before they trigger an alarm. In this step, you will define a dashboard entirely in Terraform code and deploy it with a single command.
In this step, get ready to:
- Define a CloudWatch dashboard resource with metric widgets in Terraform.
- Deploy the dashboard and view live server metrics in the AWS console.
- Observe the difference between an idle server and what production traffic would look like.
Define the dashboard resource with a CPU widget
A CloudWatch dashboard is a JSON document that describes a grid of widgets. Each widget visualises one or more metrics at a position you choose.
Terraform's jsonencode() function lets you write this JSON structure directly in HCL, so Terraform can inject dynamic values like your instance ID.
- In main.tf, scroll to the bottom of the file (after the aws_cloudwatch_metric_alarm resource) and add the following dashboard resource with a single CPU Utilization widget:
resource "aws_cloudwatch_dashboard" "main" {
dashboard_name = "cloudops-monitor"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
x = 0
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/EC2", "CPUUtilization", "InstanceId", aws_instance.server.id]
]
period = 300
stat = "Average"
region = "us-east-1"
title = "CPU Utilization"
}
}
]
})
}
What does this code do?
This creates a CloudWatch dashboard named "cloudops-monitor" with one metric widget.
- dashboard_body = jsonencode(...) converts Terraform's HCL map into the JSON that CloudWatch expects.
- The metrics array tells CloudWatch to pull CPUUtilization from the AWS/EC2 namespace for your specific instance.
- aws_instance.server.id is a Terraform reference that automatically resolves to your running instance's ID at apply time.
- Save main.tf.
- Deploy the dashboard by running this command:
terraform apply
- Type yes when prompted. Terraform will show 1 resource to add.
- Open the AWS console, navigate to CloudWatch, then select Dashboards in the left sidebar.
- Click on the cloudops-monitor dashboard. You should see a single panel showing the CPU Utilization line graph.
Don't see the dashboard?
- Make sure you are in the us-east-1 region in the AWS console. Dashboards are region-specific.
- Check the terraform apply output for errors. A syntax error in the jsonencode block (missing comma or bracket) will prevent creation.
- help me troubleshoot my CloudWatch dashboard not appearing after terraform apply
Add network and disk widgets
A single CPU graph is helpful, but real operations teams watch multiple signals at once. You will add two more widgets: one for network traffic and one for disk read operations.
- In main.tf, find the closing of the first widget object inside the widgets array. It looks like this:
}
]
})
}
- Replace those closing lines with the following code, which adds a Network Traffic widget and keeps the array open for one more widget:
},
{
type = "metric"
x = 12
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/EC2", "NetworkIn", "InstanceId", aws_instance.server.id],
["AWS/EC2", "NetworkOut", "InstanceId", aws_instance.server.id]
]
period = 300
stat = "Average"
region = "us-east-1"
title = "Network Traffic"
}
},
What does this widget do?
This widget plots both NetworkIn and NetworkOut on the same graph so you can compare inbound and outbound traffic at a glance. The x = 12 position places it in the right half of the dashboard grid.
- Immediately after the Network Traffic widget, add the Disk Read Operations widget and close the resource:
{
type = "metric"
x = 0
y = 6
width = 12
height = 6
properties = {
metrics = [
["AWS/EC2", "DiskReadOps", "InstanceId", aws_instance.server.id]
]
period = 300
stat = "Sum"
region = "us-east-1"
title = "Disk Read Operations"
}
}
]
})
}
What does this widget do?
This widget shows total disk read operations using the Sum statistic instead of Average. The y = 6 position places it on the second row of the dashboard, below the CPU and Network widgets.
- Save main.tf.
- Before you apply, what do you think Terraform will do? It already created the dashboard with one widget. Will it create a new dashboard or update the existing one?
- Deploy the updated dashboard by running:
terraform apply
- Type yes when prompted. Terraform shows 1 resource to change (not create). It updates the existing dashboard in place.
- Return to the cloudops-monitor dashboard in the CloudWatch console and refresh the page.
- You should now see three panels: CPU Utilization (top left), Network Traffic (top right), and Disk Read Operations (bottom left). Values will be low or zero because the server is idle.
Why are the graphs nearly empty?
Your EC2 instance has no traffic hitting it and no applications running. In a production environment, these graphs would show real patterns: CPU spikes during deployments, network bursts during backups, and disk reads when a database warms its cache.
The dashboard is your single pane of glass for this server's health. Even with low values, it proves the wiring is correct and ready to capture real activity.
Only seeing one or two panels?
- Run terraform plan and check for syntax errors. A missing comma between widget objects is the most common cause.
- Make sure the widgets array contains exactly three objects separated by commas, with no trailing comma after the last one.
- help me fix my CloudWatch dashboard that is not showing all three widget panels
✔️ Awesome, I've got everything!
Great. Double check you have saved main.tf and that all three widgets appear in your CloudWatch dashboard.
ⓧ I'd like to double check the full code
Your main.tf should contain these sections in order. Scroll through your file and confirm each is present:
- The provider "aws" block (region us-east-1).
- The data "aws_ami" "amazon_linux" block.
- The resource "aws_instance" "server" block.
- The resource "aws_sns_topic" "alerts" block.
- The resource "aws_sns_topic_subscription" "email" block.
- The resource "aws_cloudwatch_metric_alarm" "cpu_high" block.
- The resource "aws_cloudwatch_dashboard" "main" block with three widgets inside the widgets array: CPU Utilization, Network Traffic, and Disk Read Operations.
Your server now has a complete monitoring story: an alarm that shouts when something is wrong, and a dashboard that shows you the full picture at any time. Next up, you will tear everything down with a single command to prove the power of Infrastructure as Code.
Secret mission
Add Auto-Recovery
Your alarm emails you when things go wrong. But what if your server could fix itself without waiting for a human? Add auto-recovery so AWS reboots your instance automatically when it detects a hardware failure.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses AWS free tier resources, so there are no ongoing costs as long as your free tier allowances remain active.
Cost warning
Your EC2 t2.micro instance consumes free tier hours (750 hours/month on legacy accounts created before July 15, 2025). If your free tier expires or you exceed the monthly allowance, you will be charged for the running instance.
If you are unsure about your free tier status, choose Pause or Delete below.
Resources you used:
- 1x EC2 instance (t2.micro) tagged cloudops-monitor.
- 1x SNS topic (cloudops-alerts) with email subscription.
- CloudWatch alarms (cloudops-cpu-high, plus cloudops-auto-recover if you completed the Secret Mission).
- 1x CloudWatch dashboard (cloudops-monitor).
- Local project folder cloudops-monitor on your Desktop with Terraform configuration files.
Keep everything running
No action needed. Choose this if you are still actively building or plan to experiment with the infrastructure further.
- Your EC2 instance will continue consuming free tier compute hours.
- Monitor your free tier usage in the AWS Billing console under Free Tier to avoid surprise charges.
- CloudWatch alarms and the dashboard remain active within free tier limits (10 alarms, 3 dashboards).
- Terraform and AWS CLI remain installed on your machine for future projects.
Pause - I'll come back to this later
Stop the running instance to free up compute hours while keeping your alarms, dashboard, and configuration intact.
- Stop your EC2 instance by running this command:
- Find your instance ID by running terraform show in your cloudops-monitor folder and looking for the id field.
aws ec2 stop-instances --instance-ids [[INSTANCE_ID="your EC2 instance ID"]]
- The stopped instance does not consume compute hours.
- Your CloudWatch alarms and dashboard remain but have no active data to display.
- To resume later, run terraform apply in your project folder to bring the instance back up.
Delete - I don't want to use this again
Remove all AWS resources and local project files permanently. Your Terraform files let you recreate everything from scratch if you change your mind.
- Open a terminal and navigate to your cloudops-monitor folder on your Desktop.
- Destroy all AWS resources by running:
terraform destroy
- Type yes when prompted to confirm destruction.
- Verify cleanup by opening the EC2 console and confirming no instances are running.
- Check CloudWatch > Alarms and CloudWatch > Dashboards to confirm they are removed.
- Delete the local project folder by running:
macOS
cd ~/Desktop
rm -rf cloudops-monitor
Windows
cd ~\Desktop
Remove-Item -Recurse -Force cloudops-monitor
What about Terraform and AWS CLI?
Both tools remain installed on your machine. They are reusable across projects and do not consume resources when idle.
To uninstall AWS CLI on Windows, open Settings > Apps > Apps & features, find AWS Command Line Interface, and select Uninstall. To remove Terraform, delete the terraform.exe file from the folder you added to your PATH.
That's a Wrap!
That's a Wrap!
Nice work! You just built a fully monitored AWS server entirely from code, complete with alerting and a real-time health dashboard.
You've learned how to:
- Provisioned and managed a live EC2 instance entirely through Terraform configuration files, replacing manual console clicks with repeatable code.
- Added a CloudWatch alarm with SNS email notifications that fires when CPU utilization crosses 80%.
- Destroyed all infrastructure with a single terraform destroy command and can recreate everything from scratch anytime, proving the power of Infrastructure as Code.
- Secret Mission: Added an auto-recovery alarm so AWS automatically reboots your server on system status check failure without human intervention.
Ready to quiz yourself?