# EBS Volume Monitoring through CloudWatchAgent and Setting an Alarm

## **Introduction:**

Monitoring storage utilization is critical to ensure application stability and avoid unexpected outages. In AWS, Amazon EBS does not provide built-in metrics for volume-level disk usage, which makes proactive alerting a challenge. To address this, we can install a CloudWatch agent that publishes custom disk utilization metrics to Amazon CloudWatch, enabling us to configure alarms and receive email notifications when EBS usage reaches critical thresholds. In this blog we will setup an alarm if our EBS Volume usage is 80% and above.

## Architecture Diagram:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744048368976/416dcb7e-a69d-497b-9ad0-f3d289d5b08f.png align="center")

## Procedure:

**Step 1: Launch the EC2-Instance**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744020888885/f2ce157d-ebe1-4244-b2f3-c23666aea112.png align="center")

**Step 2: Copy the Public-IP address of the Instance.**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744021018323/d0ae6423-7547-44c2-acf6-e434f01a41f3.png align="center")

**Step 3: Connect the server using ssh key**

```bash
ssh -i /path/<Keyfile.pem> ec2-user@<public Ip>
#example:
ssh -i max-demo-key.pem ec2-user@18.207.190.7
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744021253689/8eee269d-c993-40d0-9cfc-5443e108d7a4.png align="center")

**Step 4: Install and configure the cloudwatch agent**

you can do  it **manually** or configure with with a custom script.

* make a script [**install.sh**](http://install.sh) and place the following code.
    

```bash
#!/bin/bash

# Update package list
echo "Updating system packages..."
sudo yum update -y || sudo apt-get update -y

# Install CloudWatch Agent
echo "Installing Amazon CloudWatch Agent..."
sudo yum install -y amazon-cloudwatch-agent || sudo apt-get install -y amazon-cloudwatch-agent

# Create CloudWatch Agent Configuration
echo "Creating CloudWatch Agent Configuration for EBS volume monitoring..."
CONFIG_FILE="/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json"

sudo tee $CONFIG_FILE > /dev/null <<EOF
{
    "agent": {
        "metrics_collection_interval": 60,
        "run_as_user": "root"
    },
    "metrics": {
        "metrics_collected": {
            "cpu": {
                "measurement": [
                    "cpu_usage_idle",
                    "cpu_usage_iowait",
                    "cpu_usage_user",
                    "cpu_usage_system"
                ],
                "metrics_collection_interval": 60,
                "totalcpu": false
            },
            "disk": {
                "measurement": [
                    "used_percent",
                    "inodes_free"
                ],
                "metrics_collection_interval": 60,
                "resources": [
                    "*"
                ]
            },
            "diskio": {
                "measurement": [
                    "io_time",
                    "write_bytes",
                    "read_bytes",
                    "writes",
                    "reads"
                ],
                "metrics_collection_interval": 60,
                "resources": [
                    "*"
                ]
            },
            "mem": {
                "measurement": [
                    "mem_used_percent"
                ],
                "metrics_collection_interval": 60
            },
            "netstat": {
                "measurement": [
                    "tcp_established",
                    "tcp_time_wait"
                ],
                "metrics_collection_interval": 60
            },
            "swap": {
                "measurement": [
                    "swap_used_percent"
                ],
                "metrics_collection_interval": 60
            },
            "custom_metrics": {
                "measurement": [
                    "CustomMetricExample"
                ],
                "metrics_collection_interval": 60,
                "resources": [
                    "*"
                ],
                "default": {
                    "CustomMetricExample": 3
                }
            }
        }
    }
}

EOF
```

**Step 5: make the script executable and run the script**

```bash
chmod +x install.sh
./install.sh
```

**Step 6: Enable and Start CloudWatch Agent**

```bash
sudo systemctl enable amazon-cloudwatch-agent
sudo systemctl start amazon-cloudwatch-agent
#verify the agent is running, if required restart the agent
sudo systemctl status amazon-cloudwatch-agent --no-pager
```

**Step 7: Create a role for agent to put metric about the resources :**

* Search for **IAM &gt;** Select **Policy &gt;** **Create Policy**
    

### CloudWatchAgentPolicy

```bash
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"cloudwatch:PutMetricData",
				"cloudwatch:DescribeAlarms",
				"cloudwatch:GetDashboard",
                "cloudwatch:ListMetrics",
				"cloudwatch:ListDashboards",
				"cloudwatch:PutMetricAlarm",
				"logs:PutLogEvents",
				"logs:CreateLogGroup",
				"logs:CreateLogStream",
				"logs:DescribeLogStreams",
				"logs:DescribeLogGroups",
				"logs:PutRetentionPolicy"
			],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"ec2:DescribeInstances",
				"ec2:DescribeTags",
				"ec2:DescribeVolumes",
				"ec2:DescribeVolumeStatus",
				"ec2:DescribeInstanceStatus",
				"ec2:DescribeRegions"
			],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"logs:DescribeLogGroups",
				"logs:DescribeLogStreams",
				"logs:PutLogEvents"
			],
			"Resource": "*"
		}
	]
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744022818926/c3768c8f-6926-44ea-a246-7853a77cb421.png align="center")

* **Create Policy.**
    
    **Now**
    
* Select **Role** and Create **New Role**
    
* **Trusted entity type to Custom trust policy**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744023171879/46184958-11f5-4316-b0e6-eaef732be93d.png align="center")
    

```bash
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Principal": {
				"Service": "ec2.amazonaws.com"
			},
			"Action": "sts:AssumeRole"
		}
	]
}
```

* Click on **Next**
    
* Select the Permission you have created Recently i.e **CloudWatchAgentPolicy**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744049690566/3a5158e8-5664-45b2-b674-6e5445a92dd1.png align="center")
    
* Click on **Next**
    
* Enter the **Role Name**
    
* Click on **Create Role**
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744023433122/74c150f7-64f7-4ec9-9dd0-a73429887c74.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744023550262/fe5239f1-aaf0-4982-8ec0-4777d1af17e3.png align="center")

**Step 8: Attach the Role to the Instance**

* On the Instances
    
* Select the **Action** &gt; choose **Security** &gt; Click on **Modify IAM role**
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744022073602/df460ae9-a49a-42c6-b483-e50069b885f2.png align="center")

* Choose the **CloudWatchAgentInstanceProfile**
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744022052799/d9df41cb-b733-43ec-922e-30a203bcb296.png align="center")

**Step 9: Restart the Agent**

```bash
sudo systemctl restart amazon-cloudwatch-agent
```

* view the logs of the agent
    

```bash
sudo journalctl -u amazon-cloudwatch-agent --no-pager -n 50
sudo cat /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log
```

**Step 10: Create  a SNS Topic and make Subscription using required email address.**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744024408439/e81ea738-9c5d-4cc1-ae9d-880ea93f4ff6.png align="center")

* Click on **Create Topic**
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744024673838/1a8fa655-19a4-44d5-8e1a-36520106d237.png align="center")
    
    Copy the **Arn** value
    

**Step 11: Command to create a cloudwatch alarm.**

* Change the required value also, CloudWatch metric parameter must match the dimensions.
    

**Example: &lt;Private\_IP&gt; and &lt;SNS\_ARN\_Topic&gt;**

```bash
aws cloudwatch put-metric-alarm \
  --alarm-name "Volume 80% Utilization" \
  --metric-name "disk_used_percent" \
  --namespace "CWAgent" \
  --statistic "Average" \
  --dimensions Name=device,Value=xvda1 Name=fstype,Value=xfs Name=host,Value=ip-<Private-IP>.ec2.internal Name=path,Value=/ \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1 \
  --period 300 \
  --alarm-actions <SNS_ARN_Topic> \
  --region us-east-1
```

### Or manually set an alarm.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744028267838/fd126506-cb42-402b-a14c-01d03fb0edf5.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744028284185/0d31b583-e0c2-49e6-8292-968ad028899f.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744028325961/96104046-7ac0-436b-950e-8a852a5b3efc.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744028356571/11806c0d-1523-47ca-b607-30efe34b9e59.png align="left")

Now,

* Go to the **CloudWatch**
    
* Select **All Alarms.**
    
* Copy the **ARN** of the CloudWatch Alarm.
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744026348042/61a7db2b-ce84-47b6-a806-b01d3669518a.png align="center")
    

**Step 12: Attach the Access policy to the SNS Topic**

* CloudWatch need permission to publish the message,
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744024673838/1a8fa655-19a4-44d5-8e1a-36520106d237.png align="left")

* Click on **Edit** in SNS Topic
    
* Under **Access Policy** replace the policy and **change &lt;SNS ARN&gt;**
    
* ```bash
            {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Principal": {
                    "Service": "cloudwatch.amazonaws.com"
                  },
                  "Action": "SNS:Publish",
                  "Resource": "<SNS ARN>",
                  "Condition": {
                    "ArnLike": {
                      "aws:SourceArn": "CLOUDWATCH ARN"
                    }
                  }
                }
              ]
            }
    ```
    
    **Step 13: Restart the Agent and check the metric list.**
    

```bash
#restart t-status
sudo systemctl restart amazon-cloudwatch-agent
#checking the metric being pushed by the CWAgent
aws cloudwatch list-metrics --namespace "CWAgent" --metric-name "disk_used_percent" --region us-east-1
```

**Step 14:** **Creating Subscriptions for your SNS Topic**

* Go the **SNS TOPIC**
    
* **Create Subscriptions**
    
* **Protocal: Email**
    
* **EndPoint: “Enter Your Email address”**
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744027098455/fc0e0d9a-091e-4242-85e9-1e66e3bd6320.png align="center")

* Open your Email and **Confirm Subscription**
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744027287720/a5eda22d-ac2f-47c9-b131-936c773b27c5.png align="center")

**Step 15: Triggering the Event by Manually utilizing the EBS Volume.**

* Creating a **testfile.img** with size of **5GB**
    

```bash
sudo fallocate -l 5G /testfile.img
#view  the storage of the system 
df -h
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744027655307/26b3f049-c05d-4994-92f3-961b5b3b1efa.png align="center")

As you can see the disk utilization is 88% on /dev/xvda1 now, we should receive an alarm.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744028189861/4a9601d0-1d53-463a-bed9-c2b23b95a537.png align="center")

### Congratulation!!!

You did a great job. you have now successfully demonstrated monitoring EBS volume using CloudWatch agent and setup an alarm when the threshold met.

## Conclusion:

By publishing custom disk utilization metrics to Amazon CloudWatch, we gained visibility into EBS volume usage that wasn’t available by default. This enables timely alerts and proactive storage management, helping prevent performance issues and downtime.
