feat(CAD-2252): target both OUs and single accounts for AWS org-level Agentless - #1855
Open
PengyuanZhao wants to merge 5 commits into
Open
feat(CAD-2252): target both OUs and single accounts for AWS org-level Agentless#1855PengyuanZhao wants to merge 5 commits into
PengyuanZhao wants to merge 5 commits into
Conversation
…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
force-pushed
the
pzhao/CAD-2252
branch
from
August 13, 2026 19:13
ef4d813 to
d932a25
Compare
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, andcreateAgentlessdropped anything notou-/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_MANAGEDStackSet does not — it can narrow an ancestor OU to a specific account list viaaccount_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_accountsalready 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/awsAgentlessOrganizationRootID+WithAgentlessOrganizationRootID.snapshot_role_accountstargeting the root with anINTERSECTIONaccount filter. When OUs are also selected, the first instance gains aDIFFERENCEfilter so the two target provably disjoint sets, anddepends_onserializes them.lwpreflight/awsorganizations:ListRoots,ListOrganizationalUnitsForParent) were already inRequiredPermissionsForOrg[Agentless]and already granted by the customer-facing policy JSON — no policy change needed.ListAccountsandListOrganizationalUnitsForParent, which silently truncated at 20 entries.Why
depends_onis load-bearingCloudFormation permits one operation per stack set at a time and the AWS provider does not retry
OperationInProgressExceptionon create. Without it the mixed case fails nearly every time, not intermittently.Backwards compatibility
Output is unchanged for every existing caller:
TestGenerationAgentlessOrganizationand its golden are untouched and still pass.integration/aws_generation_test.gois untouched.TestGenerationAgentlessOrganizationRootNoChurnForOrgUnitTargetsgenerates the OU-only case twice, with and without the root, and asserts the outputs are byte-identical — this is what protects existing deployments, whosedeployment_targetsare allForceNew.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 withgofmt,go build ./...andgo vet.Consumed by self-deployment (services) and the Automated Configuration wizard (rainbow) in follow-up PRs under the same ticket.
🤖 Generated with Claude Code