AWS re/Start Lab · Monitoring & Compliance

Monitoring Infrastructure with CloudWatch Agent and AWS Config

Instrumented an EC2 web server with the CloudWatch Agent via Systems Manager Run Command, captured Apache access/error logs into CloudWatch Logs, fired a metric-filter alarm on HTTP 404s, wired an EventBridge rule on EC2 state changes, and audited compliance with two AWS Config rules.

Lab Summary

A pre-provisioned Web Server EC2 instance was instrumented end-to-end: the CloudWatch Agent was pushed via SSM Run Command using a JSON configuration stored in Parameter Store. Apache logs flowed into CloudWatch Logs, a metric filter on the access log triggered an SNS-backed alarm when 404 errors crossed a threshold, an EventBridge rule reacted to EC2 state changes, and AWS Config evaluated the account against two managed rules.

Agent installation via SSM

Used AWS-ConfigureAWSPackage to install the agent, AmazonCloudWatch-ManageAgent to start it, and Parameter Store to hold the configuration centrally.

Logs, alarms, and compliance

Apache access logs → metric filter on status_code 404 → CloudWatch Alarm → SNS topic → email. In parallel, AWS Config detected missing project tags and unused EBS volumes.

Monitoring Architecture

Four monitoring layers, each handled by a different service. The CloudWatch Agent unifies the first two; the rest run independently against the same EC2 resources.

Layer What it watches Service Source
System metrics CPU per mode, memory, disk used %, swap, disk I/O CloudWatch Metrics (CWAgent namespace) CloudWatch Agent (inside the OS)
Application logs access_log and error_log from Apache CloudWatch Logs CloudWatch Agent tailing files
Infrastructure events EC2 state-change notifications (stopped, terminated) EventBridge (CloudWatch Events) EC2 service emitting events
Compliance Required tags, EBS volume attachment state AWS Config (managed rules) Continuous configuration evaluation
Naming note: the lab guide calls it CloudWatch Events, but the AWS Console now exposes the feature as Amazon EventBridge. They are the same product — the rules API is identical and existing CloudWatch Events rules are visible from the EventBridge console.

Step-by-Step Walkthrough

Five tasks, with screenshots where the lab produced visual evidence (Tasks 2 and 5). The remaining tasks are documented in prose without padding.

01

Install the CloudWatch Agent via SSM Run Command

From Systems Manager → Run Command, ran the AWS-managed document AWS-ConfigureAWSPackage against the Web Server instance with Action=Install, Name=AmazonCloudWatchAgent, and Version=latest. The command finished as Success, which means the agent binary is on disk but not yet configured — nothing is being collected until the configuration is provided and the agent is started in Task 1b.

No screenshot was captured for this step; the Run Command result page only shows a success status with no service-specific details worth preserving.

1b

Store agent configuration in Parameter Store and start the agent

Created the SSM parameter Monitor-Web-Server (String type) holding the full CloudWatch Agent configuration in JSON. Two log files are tailed (/var/log/httpd/access_log and /var/log/httpd/error_log) and five metric groups are collected every 10 seconds (cpu, disk, diskio, mem, swap).

{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "log_group_name": "HttpAccessLog",
            "file_path": "/var/log/httpd/access_log",
            "log_stream_name": "{instance_id}",
            "timestamp_format": "%b %d %H:%M:%S"
          },
          {
            "log_group_name": "HttpErrorLog",
            "file_path": "/var/log/httpd/error_log",
            "log_stream_name": "{instance_id}",
            "timestamp_format": "%b %d %H:%M:%S"
          }
        ]
      }
    }
  },
  "metrics": {
    "metrics_collected": {
      "cpu": {
        "measurement": [
          "cpu_usage_idle",
          "cpu_usage_iowait",
          "cpu_usage_user",
          "cpu_usage_system"
        ],
        "metrics_collection_interval": 10,
        "totalcpu": false
      },
      "disk": {
        "measurement": [
          "used_percent",
          "inodes_free"
        ],
        "metrics_collection_interval": 10,
        "resources": ["*"]
      },
      "diskio": {
        "measurement": ["io_time"],
        "metrics_collection_interval": 10,
        "resources": ["*"]
      },
      "mem": {
        "measurement": ["mem_used_percent"],
        "metrics_collection_interval": 10
      },
      "swap": {
        "measurement": ["swap_used_percent"],
        "metrics_collection_interval": 10
      }
    }
  }
}

With the configuration parameter in place, ran a second Run Command using AmazonCloudWatch-ManageAgent with Action=configure, Mode=ec2, Optional Configuration Source=ssm, Optional Configuration Location=Monitor-Web-Server, and Optional Restart=yes. The script reads the JSON from Parameter Store and starts the agent on the target instance.

Why Parameter Store: the same JSON can now be referenced from every Run Command target without baking it into each instance's user-data or AMI. Updating the parameter and re-running AmazonCloudWatch-ManageAgent reconfigures the fleet in place.
02

Capture Apache logs and alarm on HTTP 404 errors

Generated traffic against the web server's public IP from a browser, hitting both valid and non-existent paths. Within seconds the CloudWatch Agent forwarded the access log entries to the HttpAccessLog log group, in a log stream named after the instance ID.

On the access log group, created a metric filter with the space-delimited pattern [ip, id, user, timestamp, request, status_code=404, size] — this matches the Apache common log format and pins position 6 to 404. The filter publishes the metric 404Errors into the namespace LogMetrics with value 1 per matching line.

Built an alarm on LogMetrics/404Errors: statistic Sum, period 60 seconds, threshold ≥ 5, treat missing data as missing. The alarm action publishes to a new SNS topic whose subscription is an email address — confirmed via the standard SNS confirmation link before generating traffic.

Triggered the alarm by hitting /start repeatedly. As soon as the count of 404s in a one-minute window crossed five, the alarm transitioned from OK to ALARM and SNS delivered the notification email.

End-to-end proof: log → metric filter → alarm → SNS → email closes the loop in a single screenshot. The MetricNamespace, MetricName, period and threshold visible in the email body all match what was configured.
03

Inspect CloudWatch Agent metrics (CWAgent namespace)

Navigated to CloudWatch → Metrics → All metrics and confirmed the CWAgent namespace had appeared alongside the default AWS/EC2 namespace. CWAgent exposes the metrics the agent collects from inside the instance: mem_used_percent, disk_used_percent, swap_used_percent, diskio_io_time, and per-mode cpu_usage_*.

Source Namespace Captures Sees what
EC2 hypervisor AWS/EC2 CPU utilization, network in/out, disk read/write bytes The instance from outside
CloudWatch Agent CWAgent Memory %, disk free %, swap %, per-process and per-mode CPU The OS from inside

No screenshot of the metric explorer was captured for this task. The presence of the CWAgent namespace is implicit evidence the agent started successfully in Task 1b — without a running agent, that namespace simply would not exist in the account.

04

EventBridge rule on EC2 state changes

Created a CloudWatch Events / EventBridge rule named Instance_Stopped_Terminated. Service EC2, event type EC2 Instance State-change Notification, specific states stopped, terminated. The target is the existing SNS topic Default_CloudWatch_Alarms_Topic — reusing the topic already wired in Task 2 so stop/terminate events are delivered to the same email inbox.

The rule was created but not exercised (no instance was stopped or terminated during the lab window), so no confirmation email was generated and no screenshot was captured. The configuration matches the canonical example in the AWS docs: rules on EC2 state changes are commonly used to trigger remediation Lambdas or notifications when fleet membership changes.

05

Audit compliance with AWS Config

Opened AWS Config → Rules and added two AWS-managed rules in DETECTIVE mode:

  • required-tags with parameter tag1Key=project — flags every resource that does not carry a project tag.
  • ec2-volume-inuse-check (no parameters) — flags EBS volumes that exist but are not attached to any running instance.
Detective vs preventive: Config rules in DETECTIVE mode report non-compliance after the fact; they do not block a resource from being created. For enforcement at create time the alternatives are Service Control Policies, IAM permission boundaries, or CloudFormation Guard / Hooks during deployment.

Key Learnings

What was actually learned

The CloudWatch Agent collects metrics and logs from inside the OS; the native EC2 namespace only sees what the hypervisor exposes.
Storing the agent configuration in Parameter Store decouples it from the instance — one parameter can drive an entire fleet.
Metric filters turn unstructured log lines into CloudWatch metrics that can drive alarms; a single pattern [..., status_code=404, ...] is enough.
CloudWatch Events in the docs is the same product as EventBridge in the console.
AWS Config is detective by default — it reports drift, it does not prevent it.

Technical conclusion

This lab is a small reference architecture for "observing one server end-to-end". The four layers — system metrics, application logs, infrastructure events, configuration compliance — cover the bulk of what monitoring means in AWS at the EC2 level, and each layer can be extended without touching the others.

The most reusable artifact from this lab is the CloudWatch Agent JSON configuration above: the same file, lifted unchanged into Parameter Store, can be applied to any Amazon Linux instance running Apache. Replacing the file paths is enough to repoint it at NGINX, an application server, or any other log source.