Skip to main content
Graduate Track

Using AWS CloudTrail Event History to Find Who Changed a Security Group

Who added or removed a rule on your AWS security group? Use CloudTrail Event history and aws cloudtrail lookup-events, then read userIdentity to find the caller.

Using AWS CloudTrail Event History to Find Who Changed a Security Group
KBY Technologies12 min readFoundation11 min

In this lesson

Advertising disclosure: this article includes partner links. KBY may earn a commission if you sign up or buy through one, at no extra cost to you. How we make money.

Share

Before you begin

  • An isolated lab or sandbox AWS account.
  • AWS CLI greater than 1.6.6, configured for the lab account and Region.
  • IAM permission to look up CloudTrail events and edit one lab security group's inbound rules.

Track this tutorial

Choose your current status and tick each safety check as you complete it. Sign in to sync progress between devices.

Current status

Before you apply the change

Confirm these production-safety controls during the tutorial.

0 of 6 safety checks completed

To find who changed a security group in the last 90 days, look up the change in AWS CloudTrail Event history in the Region where the group lives. Run aws cloudtrail lookup-events with one lookup attribute, such as EventName set to AuthorizeSecurityGroupIngress, then read the userIdentity block inside the returned event record: its type, arn and accessKeyId tell you which identity made the call, and sourceIPAddress and userAgent tell you where from and with what tool. The key constraint is scope: Event history covers one account, one Region and only the past 90 days, and each lookup accepts a single attribute.

That answer works because every Amazon EC2 API call, including the calls the EC2 console makes for you, is recorded by CloudTrail as a management event. A security group rule change is not a setting that silently flips; it is an API request made by an identity, and CloudTrail writes down the request, the caller and the time. This guide builds that mental model first, walks through a worked lookup, and then gives you a bounded lab exercise in which you make a harmless rule change, find your own event, and remove the rule again.

#Learning Objectives

  • Explain why a security group change appears in CloudTrail as an EC2 management event with an event name, an event source and a caller identity.
  • Query Event history with aws cloudtrail lookup-events using a single lookup attribute and a time range.
  • Read the userIdentity, sourceIPAddress and userAgent fields of an event record and state what each proves.
  • Recognise the limits of Event history and know when a trail or CloudTrail Lake event data store is needed instead.

#Prerequisites

  • An isolated lab or sandbox AWS account, not a production account.
  • The AWS CLI installed, at a version greater than 1.6.6, configured with aws configure for the lab account and the Region that holds your lab security group.
  • An IAM identity in the lab account allowed to look up CloudTrail events, describe security groups and edit the inbound rules of one lab security group.
  • A lab security group that no running workload depends on, and its group ID (for example sg-1234567890abcdef0).

#Content

#
The mental model: changes are API calls, and API calls are events

A security group is a set of rules that Amazon EC2 uses to decide whether to allow traffic to the network interfaces it is associated with. When several groups are associated with an instance, their rules are aggregated into one set. You can add, remove or edit rules at any time, from the console or from the AWS CLI with commands such as authorize-security-group-ingress, revoke-security-group-ingress and modify-security-group-rules. Whichever tool is used, the change reaches AWS as a request to the Amazon EC2 API.

CloudTrail is active in an AWS account from the moment it is created. It captures all Amazon EC2 API calls as events, including calls made by the console, and every EC2 API action is logged as a management event, which AWS also describes as a control plane operation. Each event records the requested operation, the date and time, the request parameters, the IP address the request came from and who generated it. That is why “who changed this rule?” is answerable at all: the question becomes “which identity called the EC2 API to change this group, and when?”

Three terms matter here. The event source is the service the request was made to; for EC2 it is ec2.amazonaws.com. The event name is the API action that was requested. In the AWS example of an instance being terminated, the event name is TerminateInstances, the action itself. By the same pattern, the CLI command authorize-security-group-ingress calls the AuthorizeSecurityGroupIngress action. The user identity is the block that describes the caller.

#
Event history: free, immutable and deliberately narrow

Event history is the record you get without configuring anything. It is a viewable, searchable, downloadable and immutable record of the past 90 days of management events in an AWS Region, and there is no CloudTrail charge for viewing it. You reach it on the Event history page of the CloudTrail console or with aws cloudtrail lookup-events, which shows events in the Region where they occurred.

Its narrowness is the trust boundary to remember. A lookup sees one account and one Region, nothing older than 90 days, and only one lookup attribute per command. The attributes you can filter on are AccessKeyId, EventId, EventName, EventSource, ReadOnly, ResourceName, ResourceType and Username, and the value names are case sensitive. If you need longer retention, AWS directs you to create a trail, which delivers log files to an Amazon S3 bucket, or a CloudTrail Lake event data store, which you query with SQL. A trail created in the console is multi-Region, and AWS recommends a multi-Region trail so that activity in every Region is captured.

#
Reading the identity correctly

The userIdentity block is where the answer lives, and reading it carefully avoids the most common wrong conclusion. Its type tells you whether the request used root user credentials, IAM user credentials, temporary security credentials for a role or federated user, an IAM Identity Center user, or another AWS service. Its arn names the principal, accountId names the account, and accessKeyId names the key that signed the request. Outside userIdentity, sourceIPAddress shows where the request came from and userAgent shows the tool: an AWS CLI request carries a user agent beginning aws-cli/.

The cause-and-effect point for a graduate is this: an event proves which credentials made a request, not which person was at the keyboard. If the type shows temporary credentials for a role, several people or an automated pipeline may be able to use that role, and the next question is who assumed it. That is the same reasoning used when an instance role is denied access in fixing S3 AccessDenied when an EC2 instance role reads a bucket: the role, not a human name, is the principal AWS evaluates.

Closeup of switch in server with connectors and adapters connected to plastic device in dark room on blurred background inside
Photo by Brett Sayles on Pexels

#Examples

#
Worked example: who opened port 22 on a web server group?

Suppose an instance that was unreachable over SSH yesterday is reachable today, and the inbound rules of its group now allow port 22. If you have worked through fixing an EC2 SSH connection that times out after launch, you know a missing inbound rule is one cause of a timeout; here the question is the reverse: who added the rule? With your CLI configured for the group’s Region, look up rule additions:

1aws cloudtrail lookup-events \
2  --lookup-attributes AttributeKey=EventName,AttributeValue=AuthorizeSecurityGroupIngress \
3  --start-time 2026-11-14T00:00:00Z \
4  --max-items 10

The output is a JSON object with an Events list, sorted with the latest event first. Each entry has EventId, EventName, EventTime in Unix time format, Username, a Resources list of referenced resources, and CloudTrailEvent, a JSON string holding the full event record. If a NextToken appears, there are more results; pass it with --next-token and exactly the same parameters to get the next page.

Open the CloudTrailEvent string of each entry and find the one whose record mentions your group ID and read it field by field. A record whose eventSource is ec2.amazonaws.com, whose eventName is AuthorizeSecurityGroupIngress, whose userIdentity.arn names an IAM user and whose userAgent starts with aws-cli/ supports one conclusion: that IAM user’s credentials added an inbound rule from the AWS CLI at the stated time. A role ARN with a pipeline-like user agent supports a different one, and sends you to the owners of that automation rather than to a colleague.

If nothing comes back, do not conclude that nobody changed the group. First check the Region, then the time range, then whether the change was made by a different action, such as ModifySecurityGroupRules for an edited rule, by repeating the lookup with that event name.

#Exercises

#
Exercise: make a harmless change, then find your own event

Objective: prove end to end that a security group rule change made in the console is attributed to your identity in Event history, then remove the change.

Safe setup: use the lab account and a lab security group that no workload depends on. Record its current inbound rules first; this record is your backup. The rule you add allows a single address from 192.0.2.0/24, a range reserved for documentation, so it opens nothing to a real client.

  1. Record the baseline: in the Amazon EC2 console, choose Security Groups, select the lab group and copy every rule on its Inbound rules tab into your notes. Expected evidence: a written list of type, protocol, port range and source for each rule. Stop if the group holds rules you do not recognise or is associated with anything you did not create.
  2. Note the time in UTC, then make the change. Open the Amazon EC2 console, choose Security Groups, select the lab group, choose Edit inbound rules, choose Add rule, set the type to a TCP port such as HTTPS with the source Custom 192.0.2.10/32, and choose Save rules. Expected evidence: the new rule appears on the Inbound rules tab.
  3. Look up write events since the time you noted, one attribute only:
    1aws cloudtrail lookup-events \
    2  --lookup-attributes AttributeKey=ReadOnly,AttributeValue=false \
    3  --start-time <the UTC time you noted> \
    4  --max-items 20

    Expected evidence: an entry with an EventName for a security group ingress change, such as AuthorizeSecurityGroupIngress, whose CloudTrailEvent shows eventSource ec2.amazonaws.com. If the list is empty, widen the time range and run it again rather than assuming the change was not recorded, and stay well under the limit of two lookup requests per second.

  4. Read the record. Write down eventName, eventTime, userIdentity.type, userIdentity.arn, sourceIPAddress and userAgent.
  5. Roll back: in the console, choose Edit inbound rules on the lab group, choose Delete on the rule you added, and choose Save rules.
  6. Verify the rollback: compare the Inbound rules tab with the baseline from step 1, then repeat step 3 to find the removal event.

Expected results: the ARN in the record is your own lab identity, the source IP normally matches the public address of the network you worked from, the event time matches the time you saved the rule, and a second write event records the removal. Pass condition: you can state, from the record alone, which identity changed which group, when and from where, and the final inbound rules match the baseline. Stop condition: stop and escalate if the record names an identity you do not recognise in your lab account, or if the rule cannot be deleted. Cleanup: the deletion in step 5 is the cleanup; confirm it in step 6.

#Validation Guidance

Validate the conclusion, not just the query. A good finding names the account, Region, event name, event time in UTC, the identity ARN and type, the access key ID, the source IP address and the user agent, plus the event ID so that anyone can retrieve the same record with AttributeKey=EventId. Check that the record’s awsRegion and recipientAccountId match the group you are investigating, because a group with the same name can exist in another Region or account.

Then check the state the event describes against the current state. If the group’s inbound rules no longer show the rule, look for a later revoke or modify event before reporting that the rule is still open. Distinguish what you observed in the record from what you infer: “this role’s credentials added the rule” is observed; “this engineer added the rule” is an inference that needs the role’s session history.

#Common Mistakes

A surprised man in a leather jacket peers through binoculars with a shocked expression.
Photo by Andrea Piacquadio on Pexels

#
Searching in the wrong Region

Event history records events in the Region where they happened. A lookup in the wrong Region returns an empty or unrelated list. Confirm the Region of the security group before you search.

#
Trying to combine filters

Each lookup-events command takes one attribute key and value pair, plus a time range. You cannot ask for “this event name and this user” in one lookup; filter on the most selective attribute and read the results, or, for investigations you repeat, query a CloudTrail Lake event data store with SQL.

#
Expecting more than 90 days

If the change is older than 90 days, Event history cannot show it. Without a trail or event data store created before the change, that history is gone. In production accounts, create the trail before you need it.

#
Reading a role as a person

An assumed role ARN names the role, not the human. Treat it as the start of the next lookup, not the end of the investigation.

#
Wrong case in attribute values

CloudTrail CLI commands and lookup value names are case sensitive, so authorizesecuritygroupingress will not match AuthorizeSecurityGroupIngress.

#Key Takeaways

  • Every EC2 API call, including console calls, is a CloudTrail management event, so security group changes are attributable.
  • Event history is free and immutable but limited to one account, one Region, 90 days and one lookup attribute per query.
  • The userIdentity block proves which credentials made a call; a role ARN needs a further step to reach a person.
  • A finding is complete only when it records the event ID, identity, time, source IP and user agent, and is checked against the current rules.

#Before You Investigate a Production Security Group

In production, the lookup itself is read-only, but the permissions are not trivial: anyone who can read CloudTrail events can see who did what across the account, so grant lookup access deliberately. Do not revert a suspicious rule on your own; a rule you did not expect may be carrying live traffic, and removing it can cause an outage. Record the evidence, tell the owner of the workload and your security contact, and let them decide on the change. Confirm that the account has a multi-Region trail or an event data store so the next investigation is not limited to 90 days, and pair it with ownership tags so a changed group leads you to a team quickly, as described in enforcing AWS resource tagging with tag policies and SCPs. The next safe decision is always to escalate with the event ID rather than to act on an assumption.

About this page

Credited to

Dates

Published

Sources

This page lists no external sources.

Corrections

No corrections are listed for this page. Some review sweeps corrected pages without naming each one; those are in the corrections log.

Reader Interaction

Comments

Add a thoughtful note on Using AWS CloudTrail Event History to Find Who Changed a Security Group. Comments are checked for spam and held for moderation before appearing.

Loading comments...

Discover more

Learn More About KBY

Was this useful?

Reports go to the site owner and are used to correct pages. Corrections are listed at /corrections.

Build practical engineering skills.

Receive new lessons, learning paths, practical exercises and early-career guidance.