Skip to content
BytePatterns

CloudWatch vs CloudTrail vs AWS Config: Metrics, API Calls, or State

9 min readBytePatterns

CloudWatch vs CloudTrail vs AWS Config as three questions: is it healthy and how much, who called which API and when, and what a resource looks like now.

CloudWatch vs CloudTrail vs AWS Config in one line: CloudWatch tells you whether something is healthy and how much of it there is, CloudTrail tells you who called which API and when, and AWS Config tells you what a resource looks like now, how it got there and whether that is allowed. All three "monitor" an account, which is why they are confused, but they record different things: numbers and log lines, API calls, and resource configurations. Facts come from the AWS documentation listed at the end, as of October 2026.

ServiceThe question it answersThe record it keeps
CloudWatchis it healthy, how much, how fastmetrics per period, log events, alarms
CloudTrailwho did what, when, from whereone event per API call, with userIdentity
AWS Configwhat does it look like, was it ever compliantconfiguration items over time, rule evaluations

The problem it solves

At 14:02 someone opens port 22 of a security group to the whole internet. Three different people will ask three different questions about it. The on-call engineer asks whether anything is being attacked right now. The security team asks who made the change and from which role. The compliance owner asks whether that group is allowed to look like this, and how long it has looked like this. One service cannot answer all three, because the answers are different kinds of data.

The CloudWatch, Alarms and X-Ray lesson lives inside the first question. This article is the boundary between that question and the other two.

The intuition

CloudWatch is about measurements. A metric is a sequence of numbers over time, identified by a namespace, a name and dimensions: HTTPCode_Target_5XX_Count for one load balancer, CPUUtilization for one instance. Services publish them, and you can publish your own. An alarm compares a metric with a threshold over several periods and moves between OK, ALARM and INSUFFICIENT_DATA, with actions such as notifying an SNS topic or scaling a group. CloudWatch Logs stores log events in log groups, and a metric filter can turn a pattern in those logs into a metric. None of that records who did anything; it records what the system did. The alarm mechanics are in CloudWatch metrics, alarms, logs and X-Ray.

CloudTrail is about API calls. Almost everything in AWS is an API call, including clicks in the console, and CloudTrail records each one as an event: the time, the eventSource and eventName, the userIdentity that made it, the source IP and the request parameters. Management events, calls that change or read account resources, are recorded by default; the console's event history shows the last 90 days. A trail delivers events to an S3 bucket, and optionally to CloudWatch Logs, for as long as you keep them. Data events, such as GetObject on an S3 object or a Lambda invocation, are high-volume and are logged only if you turn them on. CloudTrail Lake adds an event data store you query with SQL. CloudTrail does not know whether the call was a good idea; it knows that it happened.

AWS Config is about resource state. It records a configuration item for each supported resource every time the configuration changes: the security group's rules, the bucket's encryption setting, the instance's type. The items form a timeline, so you can see what the group looked like at 13:00 and at 14:03. Config rules, AWS managed ones such as restricted-ssh or custom ones, evaluate resources and mark them COMPLIANT or NON_COMPLIANT; a remediation action can fix the resource automatically, and conformance packs bundle rules. A configuration item also lists the related CloudTrail events, so Config can point at the call that caused the change.

How they connect:

  • CloudTrail into CloudWatch is how you alarm on an API call. Send the trail to a log group, add a metric filter for ConsoleLogin by the root user or for AuthorizeSecurityGroupIngress, and put an alarm on the metric.
  • Config uses CloudTrail to attribute a change: the configuration item's related events are the API calls that produced it.
  • CloudWatch cannot replace either. A metric says that SSH attempts spiked; it does not say who opened the port or whether the port is allowed to be open.

Watch it run

The lesson animation runs the CloudWatch question end to end. The load balancer's 5XX count climbs after a bad deploy, and one noisy period is not enough; at the third period in breach the alarm goes from OK to ALARM and its action pages whoever is on call. A trace shows where the time went and a Logs Insights query finds the error behind it. Every frame is a measurement of what the system did. Nothing in it says who deployed, which is CloudTrail's UpdateService or CreateDeployment event, or whether the deployed configuration was allowed, which is a Config rule.

CloudWatch, Alarms & X-Ray

Step 1 of 11

Every incident asks three things in order: is it broken, where, and why. Each has its own tool.

The same interactive animation as the lesson — step through it with the controls.

The code

A toy model of one incident producing three records, not of any AWS API. The change is a single fact; each service keeps a different shape of it:

# one change: at 14:02 someone opens port 22 of sg-0a1b to the whole internet
CHANGE = {"when": "2026-10-24T14:02:11Z", "who": "arn:aws:iam::123456789012:user/dana",
          "sg": "sg-0a1b", "rule": {"port": 22, "cidr": "0.0.0.0/0"}}

def cloudtrail_event(c):
    """Toy model of a CloudTrail management event: the API call itself."""
    return {"eventTime": c["when"], "eventSource": "ec2.amazonaws.com",
            "eventName": "AuthorizeSecurityGroupIngress",
            "userIdentity": {"type": "IAMUser", "arn": c["who"]},
            "requestParameters": {"groupId": c["sg"], "ipPermissions": [c["rule"]]}}

def config_item(c, before):
    """Toy model of an AWS Config configuration item plus a rule evaluation."""
    after = before + [c["rule"]]
    return {"resourceId": c["sg"], "configurationItemCaptureTime": c["when"],
            "configuration": {"ipPermissions": after},
            "relatedEvents": ["AuthorizeSecurityGroupIngress"],
            "rule": "restricted-ssh",
            "compliance": "NON_COMPLIANT" if any(r["cidr"] == "0.0.0.0/0" and r["port"] == 22 for r in after) else "COMPLIANT"}

def cloudwatch_datapoints(ssh_attempts_per_minute):
    """Toy model of a CloudWatch metric: a number per period, then an alarm on it."""
    points = [{"minute": m, "value": v} for m, v in enumerate(ssh_attempts_per_minute)]
    breaches = sum(p["value"] > 50 for p in points[-3:])    # 3 periods, threshold 50
    return points, ("ALARM" if breaches == 3 else "OK")

The metric here is the kind a metric filter produces from the instance's auth log in CloudWatch Logs: SSH attempts per minute. Run the incident through all three:

trail = cloudtrail_event(CHANGE)
item = config_item(CHANGE, before=[{"port": 443, "cidr": "0.0.0.0/0"}])
points, state = cloudwatch_datapoints([2, 1, 3, 180, 240, 310])

print("CloudTrail -> who did what, when")
print(" ", trail["eventTime"], trail["userIdentity"]["arn"].split(":")[-1], trail["eventName"], trail["requestParameters"]["ipPermissions"])
print("Config     -> what does it look like now, and is that allowed")
print(" ", item["resourceId"], "rule", item["rule"], item["compliance"], "related:", item["relatedEvents"])
print("CloudWatch -> is it healthy, how much")
print(" ", [p["value"] for p in points], "alarm:", state)
# CloudTrail -> who did what, when
#   2026-10-24T14:02:11Z user/dana AuthorizeSecurityGroupIngress [{'port': 22, 'cidr': '0.0.0.0/0'}]
# Config     -> what does it look like now, and is that allowed
#   sg-0a1b rule restricted-ssh NON_COMPLIANT related: ['AuthorizeSecurityGroupIngress']
# CloudWatch -> is it healthy, how much
#   [2, 1, 3, 180, 240, 310] alarm: ALARM

Read the three lines against the three people. The security team reads the CloudTrail line: Dana, AuthorizeSecurityGroupIngress, at 14:02. The compliance owner reads the Config line: the group's current rules fail restricted-ssh, and the related event says which call did it. The on-call engineer reads the CloudWatch line: attempts went from single digits to hundreds a minute, and the alarm fired after three breaching periods. The same incident, three records, and none of them contains the other two's answer.

The complexity

The costs grow along different axes:

  • CloudWatch grows with the number of metrics and the volume of log data ingested and stored. Custom metrics with many dimension values multiply quickly.
  • CloudTrail grows with API volume. Management events are modest; data events on a busy bucket can be enormous, which is why they are opt-in and can be filtered by selector.
  • Config grows with the number of resources and how often they change, since each change is a configuration item, plus rule evaluations.

Retention also differs: CloudTrail's event history keeps 90 days without a trail; a trail in S3 and a log group in CloudWatch keep what you configure; Config keeps configuration history for the retention period you set.

Where it goes wrong

  • Looking in CloudWatch for "who did this". CloudWatch has no identity in its metrics. The answer is a CloudTrail event, or a Config item's related events.
  • Expecting CloudTrail to show object reads by default. GetObject and similar calls are data events and are logged only when enabled.
  • Treating CloudTrail as compliance. It proves a call happened; it does not evaluate the result. Whether a bucket may be public is a Config rule.
  • Alarming on nothing when the root user logs in. The event exists in CloudTrail, but an alarm needs the trail delivered to CloudWatch Logs with a metric filter.
  • Turning Config on and never adding rules. The timeline alone is useful; the compliance answer needs rules, and the fix needs remediation.

When it shows up in interviews

As an incident question: "a bucket became public last night, walk me through finding out what happened." The strong answer uses all three in order: Config shows the bucket's configuration changed and when, the related CloudTrail event names the caller and the call, and CloudWatch shows whether traffic to the bucket changed. Certification questions pair each service with one verb: measure, audit, assess.

How to say it in an interview

"CloudWatch is metrics, logs and alarms: it tells me the system's health and lets me page on symptoms. CloudTrail is an audit log of API calls: every event carries who made it, when and with what parameters, so it answers who changed what. AWS Config records each resource's configuration over time and evaluates it against rules, so it answers what a resource looks like, how it got there and whether that is compliant, and can remediate. I connect them by sending CloudTrail to CloudWatch Logs for alarms on sensitive calls, and Config already references the CloudTrail events behind each change."

The vendor-neutral split of what logs, metrics and traces each answer is in observability explained.

Frequently asked questions

What is the difference between CloudWatch and CloudTrail?

CloudWatch records measurements: metrics per period, log events and alarms on thresholds, answering whether the system is healthy and how much it is doing. CloudTrail records API calls as events with the caller's identity, time and parameters, answering who did what and when. CloudWatch is performance monitoring; CloudTrail is an audit log.

What is the difference between CloudTrail and AWS Config?

CloudTrail logs the calls that were made. AWS Config records the resulting resource configurations over time and evaluates them against rules, marking resources compliant or not and optionally remediating them. A Config configuration item lists the related CloudTrail events, so Config shows the state and CloudTrail shows the call that produced it.

Does CloudTrail log S3 object reads?

Not by default. Object-level calls such as GetObject and PutObject are data events, which CloudTrail records only when you enable them on a trail or event data store, because of their volume. Bucket-level calls such as CreateBucket are management events and are recorded automatically.

Sources