Skip to content

feat(CAD-2252): target both OUs and single accounts for AWS org-level Agentless - #1855

Open
PengyuanZhao wants to merge 5 commits into
mainfrom
pzhao/CAD-2252
Open

feat(CAD-2252): target both OUs and single accounts for AWS org-level Agentless#1855
PengyuanZhao wants to merge 5 commits into
mainfrom
pzhao/CAD-2252

Conversation

@PengyuanZhao

Copy link
Copy Markdown
Contributor

Linked JIRA issue(s) - Required

https://lacework.atlassian.net/browse/CAD-2252 (Mantis 1279468)

Description

An org-level Agentless integration can monitor the organization root, OUs, or individual accounts. Individual accounts were rejected: Validate() required a profile/region for every 12-digit ID, and createAgentless dropped anything not ou-/r- prefixed from the StackSet deployment targets.

That rule exists because the CLI provisions each monitored account's snapshot role through a per-account provider alias, which needs credentials for every account. A SERVICE_MANAGED StackSet does not — it can narrow an ancestor OU to a specific account list via account_filter_type, and the organization root is an ancestor of every account. So callers that can supply the root (self-deployment, which holds only org-level credentials) can now target individual accounts without per-account credentials.

Note organization.monitored_accounts already accepted all three forms — the module's own validation is ^ou-…$|^[0-9]{8,32}$|^r-…$. It is forwarded to the Lacework SaaS integration to say what to scan and needs no credentials. Only snapshot-role provisioning was the blocker.

Changes

lwgenerate/aws

  • Add AgentlessOrganizationRootID + WithAgentlessOrganizationRootID.
  • Relax the profile/region requirement only when that root is set, so existing CLI callers keep identical behavior and the identical error string.
  • Emit a second stack set instance snapshot_role_accounts targeting the root with an INTERSECTION account filter. When OUs are also selected, the first instance gains a DIFFERENCE filter so the two target provably disjoint sets, and depends_on serializes them.

lwpreflight/aws

  • Discover root/OU IDs for Agentless, not just Config. The permissions (organizations:ListRoots, ListOrganizationalUnitsForParent) were already in RequiredPermissionsForOrg[Agentless] and already granted by the customer-facing policy JSON — no policy change needed.
  • Paginate ListAccounts and ListOrganizationalUnitsForParent, which silently truncated at 20 entries.

Why depends_on is load-bearing

CloudFormation permits one operation per stack set at a time and the AWS provider does not retry OperationInProgressException on create. Without it the mixed case fails nearly every time, not intermittently.

Backwards compatibility

Output is unchanged for every existing caller:

  • TestGenerationAgentlessOrganization and its golden are untouched and still pass.
  • integration/aws_generation_test.go is untouched.
  • TestGenerationAgentlessOrganizationRootNoChurnForOrgUnitTargets generates the OU-only case twice, with and without the root, and asserts the outputs are byte-identical — this is what protects existing deployments, whose deployment_targets are all ForceNew.

New coverage: full goldens for the mixed and accounts-only cases, plus a five-case validation table.

How to test

go test ./lwgenerate/aws/... ./lwpreflight/... — all green, along with gofmt, go build ./... and go vet.

Consumed by self-deployment (services) and the Automated Configuration wizard (rainbow) in follow-up PRs under the same ticket.

🤖 Generated with Claude Code

@PengyuanZhao
PengyuanZhao requested a review from a team as a code owner August 13, 2026 18:31
…ning

An org-level Agentless integration can monitor the organization root, OUs, or
individual accounts. Individual accounts were rejected: Validate() required a
profile/region for every 12-digit ID, and createAgentless dropped anything that
was not ou-/r- prefixed from the StackSet deployment targets.

That rule exists because the CLI provisions each monitored account's snapshot
role through a per-account provider alias, which needs credentials for every
account. A SERVICE_MANAGED StackSet does not: it can narrow an ancestor OU to a
specific account list with account_filter_type, and the organization root is an
ancestor of every account. Callers that can supply the root (self-deployment,
which holds only org-level credentials) can now target individual accounts.

- Add AgentlessOrganizationRootID + WithAgentlessOrganizationRootID.
- Relax the profile/region requirement only when that root is set, so every
  existing CLI caller keeps the same behavior and the same error.
- Emit a second stack set instance, snapshot_role_accounts, targeting the root
  with an INTERSECTION account filter. When OUs are also selected the first
  instance gains a DIFFERENCE filter so the two target disjoint sets, and
  depends_on serializes them: CloudFormation permits one operation per stack set
  at a time and the AWS provider does not retry that on create.
- Discover the root and OU IDs for Agentless, not just Config, so callers can
  offer them. The required permissions were already requested and granted.
- Paginate ListAccounts and ListOrganizationalUnitsForParent, which silently
  truncated at 20 entries.

Output is unchanged for every existing caller: the OU-only path is asserted
byte-identical with and without the root, and the pre-existing goldens are
untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@PengyuanZhao PengyuanZhao changed the title feat(CAD-2252): target individual AWS accounts for org Agentless scanning feat(CAD-2252): target individual accounts for AWS org-level Agentless Aug 13, 2026
PengyuanZhao and others added 4 commits August 14, 2026 09:05
The four conditions gating the two stack set instances spelled the same idea
three different ways: the DIFFERENCE filter tested targetAccountsByStackSet and
depends_on tested len(OUIDs) > 0, but inside their respective blocks both mean
only "the other instance also exists".

Name that, and name the OU instance's own gate. No behavior change -- the
goldens for all three shapes are untouched and still pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The organizational_unit_ids list was built by filtering the monitored accounts,
so a monitored list of only individual accounts produced an instance targeting
an empty list. CloudFormation rejects that, and the stack set it belonged to had
nothing to deploy either: those accounts are reached through their own provider
aliases in AgentlessMonitoredAccounts, not through the stack set.

Decide what can be targeted first, and emit the stack set and its instances only
when at least one of them has a target. Callers that monitor at least one
organizational unit -- which is every caller the tests and examples cover --
generate exactly what they did before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The comment asserted that CreateStackInstances rejects UNION without saying why
that matters, so the reasoning had to be rediscovered to review the two-instance
design. Record it: UNION is the only account filter that widens the OU selection
to "these OUs plus these accounts", AWS does not accept it on create, and every
deployment_targets field is ForceNew in the AWS provider, so this resource is
only ever created -- never updated -- and UNION stays out of reach.

Also record why the organizational-unit instance deliberately omits
account_filter_type instead of spelling it NONE.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pair targetOUsByStackSet with targetAccountsByStackSet, and let each instance
test the other directly rather than going through a combined bothInstances --
inside either block the two are the same condition. Rename the instance that
carries the organizational units to snapshot_role_ous so it reads as a peer of
snapshot_role_accounts rather than as the original from which the other was
split off.

The rename changes a Terraform resource address, so the goldens move with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@PengyuanZhao PengyuanZhao changed the title feat(CAD-2252): target individual accounts for AWS org-level Agentless feat(CAD-2252): target both OUs and single accounts for AWS org-level Agentless Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant