Homeβ€Ί Blogβ€Ί AWS Architecture Series #106 β€” The group you act on is not the group you made…
AWS Architecture AWS Architecture Series

AWS Architecture Series #106 β€” The group you act on is not the group you made

Verified against current vendor documentation on 7 November 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.

Business Challenge

#104 governed the shape of tags. #105 measured which resources have none. Both treat tags as metadata — something you report on. Resource Groups is where tags stop being metadata and start being a control surface, because here a tag decides what an automation touches.

The definition is one sentence and every clause in it matters: “A resource group is a collection of AWS resources that are all in the same AWS Region, and that match the criteria specified in the group’s query.”

Not the resources you added. The resources that match. CloudFormation's reference for the same object is blunter: “The query used to dynamically define the members of a group.” A group is a saved question, and the answer is computed at the moment something asks.

1Editing a tag edits every group that queries it, and every automation behind them

“The group specifies tag key names and values, and any resources with tags that match are automatically part of the group.”

Read that in reverse, which is the direction that hurts. A resource whose tag stops matching stops being part of the group, automatically. If that group is what Systems Manager patches — “Gather operational insights and take bulk actions on your applications that are based on resource groups” — then removing a tag removes a host from the target set. The group is smaller, and nothing about that is an error condition — the next run simply has fewer targets to act on.

This inverts the usual review model. The automation is in version control and goes through change management. The tag is a text field in a console, editable by anyone holding tagging permissions on the resource, and it is the thing that decides scope. The blast radius of a maintenance window is governed by whichever of the two controls is weaker, and it is not the one in Git.

Fix

Where a group drives an action rather than a dashboard, treat its tag keys as part of the automation's interface and protect them accordingly — a tag policy from #104 for shape, and an IAM or service control policy condition on aws:TagKeys for who may change them. A tag that only feeds a cost report needs none of this; a tag that selects patch targets needs all of it.

2One group type grows when AWS ships, not when you deploy

A group can be built on a query for all supported resource types, and AWS attaches a warning to that choice which is worth quoting in full:

“A resource group based on a query for All supported resource types can add members automatically over time, as new resources are supported by Resource Groups. When you run automations or other bulk tasks on an existing resource group based on All supported resource types, be aware that the actions might run on many more resources than were in the group when you first created the group.”

The scope of your automation is now a function of AWS's release schedule. Nobody in your organisation changes anything; Resource Groups adds support for a resource type; the next run of a bulk task reaches a category of resource that did not exist in the group when the task was written and reviewed.

This is a reasonable default for a console view, where seeing more is the point. It is a poor default for anything that writes, and the two are the same object.

Fix

Enumerate resource types explicitly in any group that an automation acts on, in the documented AWS::{service}::{resource} form. Keep the all-supported-types query for human browsing and inventory, where new members arriving unannounced is a feature. The distinction is not between important and unimportant groups — it is between groups that are read and groups that are executed.

3There is no organisation-wide group, and nothing says so at the point of use

Two clauses bound a group tightly. Region, from the definition: “all in the same AWS Region”. Account, from the permissions model: “Resource Groups feature permissions are at the account level.” Even nesting stays inside the boundary — “Resource groups can be nested; a resource group can contain existing resource groups in the same region.”

So an estate of 14 accounts operating in 3 Regions does not have a production group. It has 42 of them: 42 independent queries, each created by a different person at a different time, all called something like prod, and free to diverge in resource types, tag filters and membership without any mechanism that compares them.

This is the exact inverse of #105. Resource Explorer aggregates across Regions and accounts, and the risk there is that its answer is quietly too small. Resource Groups cannot aggregate at all, and the risk is that you believe one name refers to one thing.

Fix

Create groups from a template rather than by hand — the CloudFormation resource type exists for this, and a group defined in a stack is a group whose query is reviewable. Then audit the query rather than the name: two groups called prod in two Regions are the same group only if their resource type lists and tag filters match, and nothing in the console tells you whether they do.

Architecture

Diagram: An AWS resource group stores a query rather than a list of members, and AWS describes it as the query used to dynamically define the members of a group. A group is a collection of resources all in the same AWS Region that match the criteria specified in the group's query, and Resource Groups permissions are at the account level, so a 14-account estate in 3 Regions holds 42 independent groups for one logical grouping. There are two query types: TAG_FILTERS_1_0 for tag-based groups, where any resources with tags that match are automatically part of the group, and CLOUDFORMATION_STACK_1_0, the default, which bases membership on a single CloudFormation stack. Membership can change through three routes that involve no edit to the group: someone adding a matching tag, someone removing one, and AWS adding support for a new resource type, where AWS warns that actions might run on many more resources than were in the group when you first created the group. Tag-based queries assign an AND operator to tags, so adding a tag filter narrows the group rather than widening it. Services act on whatever the query currently returns, including Systems Manager bulk actions, VPC Network Access Analyzer scope sources and destinations, and EC2 Capacity Reservation groups, where an instance is automatically matched with and moved into reserved capacity when a matching reservation is later added. Quotas are 100 resource groups per account in each supported Region and adjustable, 15 GetResources calls per second, 5 calls per second for the tagging write operations, and 50 user created tags per resource.

Two query types, two different kinds of drift

Tag-basedCloudFormation stack-based
Type value TAG_FILTERS_1_0 CLOUDFORMATION_STACK_1_0, “the default”
Membership follows Tags, which anyone holding tagging permissions on a resource can change A stack, which changes through a deployment
Scope limit The resource types you list, plus tag filters “You can base your query on only one CloudFormation stack”
Drift arrives via A console edit nobody records as a change A deployment, which is recorded

The stack-based type is the more conservative of the two and it is the default, which is the right way round. Its membership still moves, but it moves through a mechanism that leaves evidence. A tag-based group's membership moves through an edit that looks like metadata maintenance.

AND, not OR — so a more specific group is a smaller one

“Tag-based queries assign an AND operator to tags, so any resource that matches the specified resource types and all specified tags is returned by the query.”

Each tag filter you add narrows the result. For a dashboard that is obviously fine. For a patch group it means the instinct to be precise — adding Environment=prod and Team=payments and Compliance=pci — produces a group that excludes every resource missing any one of the three. The resource carrying two of the three tags is not an error and appears nowhere. It is simply not patched, and the group that was made more careful became less complete.

What acts on a group, and what that costs when membership moves

ServiceWhat it does with the groupConsequence of a membership change
Systems Manager “take bulk actions on your applications that are based on resource groups” A host leaves the group and stops being acted on, with nothing in the group itself recording that it did
VPC Network Access Analyzer “You can specify the sources and destinations for your network access requirements by using AWS Resource Groups” A tag edit changes what a security analysis considers in scope
EC2 Capacity Reservations Instances launch into a group holding reservations “If you later add a matching capacity reservation to the targeted group, the instance is automatically matched with and moved into the reserved capacity” — a billing change triggered by group membership
CloudWatch “Focus your view to display metrics and alarms from a single resource group” A dashboard silently narrows; the graph still looks healthy
Service Catalog AppRegistry “that service automatically creates an resource group for that application” Groups exist that nobody created by hand and nobody is reviewing

The Network Access Analyzer row is the one worth sitting with. It is a security control whose scope is a query over tags, which means what did we analyse has a different answer depending on when you asked it. The Capacity Reservations row is the one that moves money.

The quotas, and the contrast with #105

“Resource groups per account | Each supported Region: 100”, and this one is adjustable — which matters for the 42-group estate above, because a per-account, per-Region ceiling of 100 is generous for a human and worth checking before a templated rollout starts creating groups programmatically.

The tagging API is the tighter constraint. “Rate of calling the GetResources API operation | Maximum of 15 calls per second”, and for the write path — TagResources, UntagResources, GetTagKeys, GetTagValues — “Maximum of 5 calls per second”, with the note that these “limits are currently not adjustable using the Service Quotas console”.

So a remediation job that tags the untagged resources found by #105's tag:none query is paced at 5 calls per second on the write side. That is the practical bridge between the two posts: finding the gap runs against one quota, closing it runs against a tighter one.

The room you have per resource is “50 user created tags (AWS created tags don’t count against this limit)”, with keys to “maximum 128 Unicode characters in UTF-8” and values to “maximum of 256 Unicode characters in UTF-8”. The query itself carries a “Maximum” of 4096, which bounds how many resource types you can enumerate before an explicit list stops being practical — the point at which people reach for all supported types, and inherit the warning in challenge 2.

Why This Architecture Holds Up

A stored list would be wrong faster than a stored query

The alternative design is static membership: you add resources, they stay. In an estate where an Auto Scaling group replaces instances through the day, a static list is stale within the hour and silently excludes every resource created after someone last maintained it. Dynamic membership is what makes a group useful at all, and the failure mode it produces — too many members, or too few — is the price of the thing working.

Single-Region scope matches the resources, not the org chart

Most of what a group contains is regional, and most of what acts on a group is regional. A group that spanned Regions would have to resolve what a bulk action means when half its members sit in a Region the caller is not operating in. Keeping the boundary where AWS keeps every other regional boundary is consistent, even though it is the clause that produces the 42-group problem.

AWS wrote the warning in the right place

The all-supported-types caution is not in a footnote or a blog post — it is on the page listing which resource types work, which is the page you are on when you make that choice. Documenting a risk at the moment of the decision is the correct placement, and it is more than several of the controls in this fortnight of posts managed.

Key Architecture Decisions

DecisionChoose thisBecause
Group that drives an automation Enumerate resource types explicitly All supported types grows with AWS releases — “might run on many more resources than were in the group when you first created the group”
Group that drives a dashboard All supported types is fine, and better New members arriving unannounced is what you want from a view
Protecting the tags a group queries Condition on aws:TagKeys for the keys that select action targets Membership is “automatically” whatever matches, so the tag is the control surface
Precision in a tag filter Add filters deliberately, not defensively Tags are combined with AND, so each filter shrinks the group
One logical grouping across the estate Define groups in CloudFormation and deploy the same template everywhere Groups are per-account and per-Region, so the only thing making them equivalent is a shared definition
Bulk tag remediation Pace writes under 5 calls per second The tagging write quota is not adjustable in the Service Quotas console
Choosing between tag-based and stack-based Stack-based where the boundary really is a deployment Its membership moves through a recorded change rather than a console edit

The audit worth running

List your groups and sort them into read and execute. For each group, find what consumes it. A group feeding only a CloudWatch view carries almost no risk from drifting membership. A group named in a maintenance window, an automation document, or a Network Access Scope is a control, and should be treated like one.

Then check which of those execute groups use an all-supported-types query. That combination — a query that grows on AWS's schedule, feeding something that writes — is the specific pairing AWS warns about, and it is the one worth fixing first.

Finally, compare two groups with the same name in two accounts, query to query. Not the member counts, which will legitimately differ. The resource type lists and the tag filters. If those differ, the name is doing work the definition does not support, and every report that aggregates the two is adding up answers to different questions.

Closing Thought

The thread through the last several posts has been controls that are narrower than their names. A tag policy that cannot require a tag. A block that does not revoke. An index that answers completely and partially in the same format. This one is different in kind: the group is not narrower than its name, it is less fixed than its name. A group sounds like a set. It is a question.

That distinction is harmless right up to the moment something acts on it. A view computed fresh is better than a view computed once. An action computed fresh is an action whose scope was decided by whoever last edited a tag — or by an AWS release note.

The practical division is one line long and it resolves nearly all of this: groups you read can be broad and dynamic, groups you execute should be explicit and reviewed. The two look identical in the console, which is why the division has to be yours.

Next in this series

Cost & Governance — AWS Service Catalog AppRegistry: the resource groups it creates on your behalf, what an application boundary means when the service defines it rather than you, and why an inventory you did not author is still an inventory you own.

Official AWS Reference

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent