ADR-015: AWS Source System Tenancy Scheme
Status
Accepted (2026-03-11)
Context
The SecurityV0 entity model uses source_system to identify which connector produced an entity. Existing values are flat strings: entra_id, servicenow, azure_foundry. These work because each connector instance connects to exactly one logical tenant (one Entra directory, one ServiceNow instance).
AWS is structurally different:
- A single enterprise customer may have dozens of AWS accounts (prod, dev, staging, per-team, per-region sandbox accounts) all visible to the same connector configuration
- A single SecurityV0 deployment may serve multiple unrelated customers, each with their own AWS accounts
- AWS entity ARNs encode account ID:
arn:aws:lambda:eu-west-1:123456789012:function:my-fn— the account ID is part of the canonical identifier
Without account-scoped source_system values, the platform cannot distinguish:
Lambda Function "my-fn"in account123456789012fromLambda Function "my-fn"in account987654321098- A cross-account authority path (intentional, should trigger
deep_trust_chainfinding) from a path within a single account (normal, no finding) - An Inetum Lambda from a different customer's Lambda sharing the same function name
The cross-connector correlation problem
The cross-connector entity correlation research (2026-02-26) established that entities are correlated across connectors using external_id. For AWS entities, external_id is the full ARN (e.g., arn:aws:iam::123456789012:role/my-role). The ARN already encodes account scope, so external_id uniqueness is guaranteed.
However, source_system is used in:
- Entity deduplication (same source_system + external_id = same entity)
- API filtering (
GET /entities?source_system=aws_lambda) - UI source badges (the badge label and icon in authority path detail views)
- Connector scan scheduling (which connector instance handles which source_system)
If source_system is flat (aws_lambda), two entities with the same function name in different accounts would collide at deduplication if the ARN-based external_id is not checked. More critically, the scan scheduler cannot route "rescan account 123456789012" without an account-scoped identifier.
Decision
Use {service}:{account_id} as the source_system value for all AWS entities.
Format
aws_{service}:{account_id}
Examples:
aws_lambda:123456789012
aws_iam:123456789012
aws_ecr:123456789012
aws_ecs:123456789012
aws_s3:123456789012
aws_secretsmanager:123456789012
aws_bedrock:123456789012
aws_orgs:123456789012
For entities that span accounts (e.g., AWS Organizations SCPs which apply org-wide), use the management account ID:
aws_orgs:management_account_id
Entity examples
Entity(
type="workload",
subtype="lambda_function",
external_id="arn:aws:lambda:eu-west-1:123456789012:function:provision-user",
source_system="aws_lambda:123456789012",
display_name="provision-user",
tenant_id="inetum-prod"
)
Entity(
type="identity",
subtype="iam_role",
external_id="arn:aws:iam::123456789012:role/LambdaExecutionRole",
source_system="aws_iam:123456789012",
display_name="LambdaExecutionRole",
tenant_id="inetum-prod"
)
Connector configuration
Each AWS connector instance is configured with:
tenant_id— the SecurityV0 tenant (e.g.,inetum-prod)aws_account_ids: [str]— list of AWS account IDs this instance scansrole_arn_template: str—arn:aws:iam::{account_id}:role/SecurityV0ReadOnlyRoleexternal_id: str— per-tenant ExternalId for AssumeRole
The connector iterates aws_account_ids, assumes the role in each account, and stamps all emitted entities with source_system=aws_{service}:{account_id}.
API filtering
The API supports prefix filtering on source_system:
GET /entities?source_system=aws_lambda— all Lambda functions across all accountsGET /entities?source_system=aws_lambda:123456789012— Lambda functions in a specific accountGET /entities?source_system_prefix=aws_— all AWS entities
This requires the source_system index to support prefix queries. If the current MongoDB index does not support this, the platform team must add a source_system_prefix field or update the index strategy.
Cross-account path detection
Cross-account authority paths are detected when a path traverses entities with different account_id components in their source_system fields:
workload.source_system = "aws_lambda:111111111111"
identity.source_system = "aws_iam:222222222222" ← different account
This triggers auth_chain_depth + 1 and may trigger the cross_account_access finding if the destination account has higher sensitivity than the source account.
UI display
The source badge in the authority path detail page renders:
aws_lambda:123456789012→ badge:AWS Lambda · 123456789012(or last 4 digits for brevity)- Tooltip: full account ID + account alias (if available via
organizations:DescribeAccount)
Consequences
Positive:
- Entity uniqueness is guaranteed across accounts and customers — no collisions at deduplication
- Scan scheduling can target individual accounts for rescan without full connector restart
- Cross-account path detection is structural — no heuristic needed
- Consistent with how ARNs already encode account scope;
source_systemjust makes it explicit at the entity level
Negative / Trade-offs:
source_systemvalues for AWS are longer and less human-readable than flat strings- API consumers that filter by exact
source_systemmatch (e.g., dashboards hardcodingsource_system=aws_lambda) will break; they must use prefix filtering or update to the account-scoped form - The MongoDB index on
source_systemmust support prefix queries — verify before Phase 1 ships - If a customer reorganises their AWS accounts (splits, merges),
source_systemvalues change and path lineage (path_lineage_id) must be migrated
Related
- ADR-004: Import by Type Connectors — connector interface contract;
source_systemis defined here - Cross-Connector Entity Correlation Research —
external_idcorrelation across connectors - AWS Connector Research — Phase 1 implementation context