AWS Cloud Resource Discovery¶
Automated discovery and interrogation of AWS cloud resources for cryptographic configuration collection.
Overview¶
AWS Cloud Resource Discovery reads your AWS account through the AWS management APIs with read-only credentials and lands the results in the same inventory as sensor-discovered assets. It covers two families of resource:
- TLS-terminating front ends — load balancers, API Gateway custom domains, CloudFront distributions. For these the platform reads the configured TLS policy and performs a live TLS handshake against publicly reachable endpoints to capture the served certificate chain.
- At-rest cryptographic posture — S3 bucket default encryption, RDS storage encryption, and the encryption of each Secrets Manager secret. These are configuration records, not reachable TLS endpoints.
- KMS keys — recorded in the key inventory (Inventory → Keys), and each one as a Key Store Infrastructure Asset with no endpoint.
Nothing is deployed inside your VPC. No traffic is mirrored. The platform only reads what the AWS control plane already exposes.
Supported Resources¶
Each type is either regional (scanned once per region you select) or global (listed account-wide, region selection does not apply). The Discovery → Cloud run dialog marks the global ones with a GLOBAL badge and disables the region picker when your selection contains only global types.
| Resource type | Scope | What is collected |
|---|---|---|
| Application Load Balancer (ALB) | Regional | SSL policy (permitted protocol versions and cipher suites), certificate bindings, listener configuration. One record per TLS listener. |
| Network Load Balancer (NLB) | Regional | TLS listener configuration and certificates. |
| Classic Load Balancer (ELB) | Regional | Basic SSL/TLS configuration only — see Limitations. |
| API Gateway (v2) | Regional | Per mapped custom domain: the domain's minimum TLS security policy, endpoint type, domain status and bound ACM certificate. |
| CloudFront | Global | Viewer-side minimum protocol version, SSL support method, certificate source and bound ACM certificate; plus one record per custom origin, including the origin protocol policy and the TLS versions permitted toward that origin. |
| KMS keys | Regional | Every key, customer-managed and AWS-managed (aws/s3, aws/ebs …), each labelled with who manages it: key spec / algorithm, state, usage, origin, rotation enabled and period, multi-Region flag, aliases. Metadata only — KMS never exposes key material. |
| S3 bucket encryption | Global | Default server-side encryption per bucket (SSE-S3, SSE-KMS, DSSE-KMS), the KMS key when customer-managed, whether S3 Bucket Keys are enabled, and the bucket's home region. |
| RDS instance encryption | Regional | Storage-encrypted flag, algorithm, KMS key, engine and version, Multi-AZ, Performance Insights KMS key. |
| Secrets Manager secrets | Regional | Per secret: name, whether rotation is enabled and when it last rotated, whether it is pending deletion, and its at-rest encryption: the AWS-managed aws/secretsmanager key or a customer-managed KMS key (which one). Shown in the Data Protection lens. Metadata only: the secret's value is never requested. |
What is enumerated¶
Alongside the cryptographic resources above, a run inventories the account's compute and network estate: the instances, the networks they sit in, and the security groups they belong to. This is inventory rather than cryptography — an EC2 instance negotiates no protocol and states no at-rest encryption — and it is what makes the rest of a run legible: a TLS listener on a machine that is not in your inventory is half an answer.
It is controlled by one switch per integration, Inventory compute instances and their networks in Discovery → Cloud → Edit integration. It is on by default, including for integrations created before this feature existed. Turning it off stops all four calls below.
Exact API calls¶
| Call | Per | Produces |
|---|---|---|
ec2:DescribeInstances |
Region, paginated | One asset per non-terminated instance, with its network interfaces (ENIs) |
ec2:DescribeVpcs |
Region, paginated | One prefix per VPC address range (Inventory → Networks) |
ec2:DescribeSubnets |
Region, paginated | One prefix per Subnet, inside its VPC's |
ec2:DescribeSecurityGroups |
Region, paginated | Group membership on the instances — never the rules |
Cost. Four extra calls per selected region per run, each paginated (the AWS default page size is 1,000 instances). A run over ten regions is forty extra calls plus their pages. Selecting fewer regions is the lever.
IAM actions to add¶
"ec2:DescribeInstances",
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups"
All four are in the AWS-managed AmazonEC2ReadOnlyAccess and ReadOnlyAccess
policies. If your policy already grants ec2:Describe*, nothing is needed.
What lands in the inventory¶
| AWS resource | Lands as | Identified by |
|---|---|---|
| EC2 instance | Compute instance asset | arn:aws:ec2:<region>:<account>:instance/<id> |
| ENI | an interface of the instance (type Virtual), named by its ENI id | the instance and the ENI id |
| VPC | a prefix per address range, in Inventory → Networks | the range, and the VPC's ARN as its cloud network |
Subnet |
a prefix inside its VPC's | the range, and the VPC's ARN as its cloud network |
| Security group | not an asset | recorded as membership on the instance |
A VPC and its address ranges are not assets: they are prefixes, the same objects your on-premises networks are, each naming the VPC it belongs to. Which network an instance is in is answered by the prefix its address falls in — open the prefix in Inventory → Networks to see the addresses in it.
Each instance's Interfaces tab lists its ENIs, each with its MAC and its
private addresses at the prefix length of the range they sit in. An ENI's
public (Elastic or auto-assigned) address is the NAT-outside side of a private
one, so it is recorded on the instance as NAT outside addresses rather than as
an interface address. The instance also carries its instance type and image id,
its tags, and the cloud.* placement facts (provider, account, region,
security groups). Because the prefixes are recorded before the instances, an
instance's private address resolves to the same asset when an agent or a sensor
sees it from inside.
Hostname. EC2 offers two private DNS name formats and only one of them is a
name. The resource-name format (i-0abc….ec2.internal) is built from the
instance id, is unique account-wide, and becomes the asset's hostname. The
IP-name format (ip-10-0-1-20.ec2.internal) is built from the private address
and is unique only inside one VPC — two instances with the same private address,
in two VPCs whose CIDRs overlap or across a terminated instance and its
replacement, get identical names — so it is not used as a hostname and would
otherwise merge two machines into one asset. It is still recorded on the
instance as private_dns_name, and the instance's Name tag is still its
display name.
New assets arrive in Pending approval like any other discovery. A re-run matches on the ARN and updates; it never creates a second asset.
Two instances that share a private address¶
A private address is not unique in an AWS account, and the inventory is built so that it never has to be.
- Two VPCs using the same address range — two VPCs created from the same
Terraform module both get
10.0.0.0/16and10.0.1.0/24— are two address spaces. Two instances at10.0.1.20in two different VPCs are two assets, and neither is affected by the other. (Inventory → Networks shows one prefix per range for now, named for the first VPC it was seen in.) - An address reused inside one VPC — EC2 gives a freed private address to the next instance launched — is genuinely the same address space, and the platform cannot tell from outside whether an address that changed hands means a new machine or a rebuilt one. So it does not guess: the new instance is recorded as its own asset in Pending approval, and a review item appears in Approvals asking whether it is the machine that used to answer to that address. You decide; nothing is merged on your behalf.
The previous asset is left exactly as it was, so its history, tags and findings stay with the instance they were recorded against.
What is deliberately NOT collected¶
- User data.
ec2:DescribeInstanceAttributewithuserDatais never called. Bootstrap scripts routinely carry database passwords, API tokens and private keys; the platform does not ask for them, so granting the action changes nothing. - Security group rules. Only a group's id and name are recorded — membership, not policy. A rule set is a map of your network.
- Block device mappings, IAM instance profiles, spot requests, licence specifications. Returned by the API, read by nothing.
- Guest operating system. EC2 states none.
PlatformDetailsis a billing string ("Linux/UNIX") and an image name is a label someone chose; neither is the product the machine runs, so the OS fields stay empty rather than carrying a guess. Guest OS comes from the host agent. - Whether a range is "public". Whether a range routes to an internet gateway
needs
ec2:DescribeRouteTables, which is a further grant and a further call.MapPublicIpOnLaunchis a different setting and is not used as a stand-in.
EC2 key pairs are recorded by name only. The name is a label; the private half never leaves your account and the public half is not requested.
Partitions¶
Only the AWS commercial partition (aws) is supported. GovCloud (aws-us-gov) and China (aws-cn) are not — they are separate partitions with separate credentials, separate account namespaces and separate service endpoints, and they are not selectable in the region picker.
The region list in the run dialog is a fixed list of commercial-partition regions. The platform does not enumerate the regions enabled on your account (that would require an additional IAM grant before the dialog could render).
Workflow¶
1. Add the AWS integration¶
UI: Discovery → Cloud → Add integration, choose provider AWS.
AWS authenticates one of two ways, selected by the Authentication dropdown. The fields change with the choice; only the fields on screen are submitted.
Access key (default)¶
| Field | Required | Notes |
|---|---|---|
| Access key ID | Yes | |
| Secret access key | Yes | |
| Session token | No | Only for temporary STS credentials. Leave blank for a long-lived IAM user key. |
Assume role (STS)¶
| Field | Required | Notes |
|---|---|---|
| Role ARN | Yes | The role in your account that the platform will assume. |
| External ID | No | The shared secret named in your role's trust policy. Recommended — see below. |
| Role session name | No | Appears in your CloudTrail records. Defaults to vistaplatform-discovery. |
| Access key ID / Secret access key | No | Optional. If supplied, the sts:AssumeRole call is signed with these. If both are blank, it is signed with the platform's own ambient AWS identity (environment credentials, IRSA, or the instance profile of the host running the platform). |
Also set Region (the default region used when a run selects none) and optionally Account ID.
Sensitive values — access key ID, secret access key, session token and external ID — are encrypted at rest and masked in API responses. On edit, leaving a masked field blank keeps the stored value. The role ARN and role session name are not secrets and are displayed.
Test the connection¶
Discovery → Cloud → the plug icon on the integration runs sts:GetCallerIdentity through exactly the same credential-assembly path discovery uses, so a green test proves discovery will authenticate the same way — including through the assume-role chain. Your policy must therefore allow sts:GetCallerIdentity; it is included in the policy below.
If a stored credential can no longer be read — it was saved under an encryption key the platform has since retired — the test says so and asks you to re-enter it, exactly as a discovery run would. It is never sent to AWS.
Turning an integration off¶
The integration's Enabled switch stops it being used: a Discover run or a scheduled run of a disabled integration fails at once with the integration is disabled, and it is not used to refresh identity evidence. Its stored credentials are kept, so switching it back on needs nothing else.
2. Cross-account setup for assume-role¶
Assume-role is the right choice when you do not want to hand out a long-lived IAM user key, or when you want the read to appear in CloudTrail as an identifiable session.
You create the role in your own AWS account. The platform is self-hosted, so there is no vendor-owned AWS account to trust: the principal you trust is whatever identity the platform itself calls AWS as — the IAM user whose access key you configured on the integration, or the role/instance profile of the host or pod running the platform.
Trust policy on the role you create (substitute the principal for your deployment):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<platform-account-id>:user/<platform-iam-user>"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<the-external-id-you-set-on-the-integration>"
}
}
}
]
}
Attach the permissions policy from IAM permissions required below to the same role.
If you do not set an external ID, omit the whole Condition block. Do not leave the condition in place with an empty value — the platform omits the external ID from the AssumeRole call entirely when the field is blank, and AWS rejects a call that omits an external ID the trust policy requires.
Why the external ID exists¶
The external ID solves the confused deputy problem. Your role's trust policy names a principal; anyone who can persuade that principal to call sts:AssumeRole on your role can read your account. If the same platform is used by several organizations, and one of them learns your role ARN, nothing but the external ID stops them from entering your ARN into their own integration and having the shared principal assume it on their behalf.
The external ID is a value only you and the platform operator know. AWS requires it to be presented on the AssumeRole call and matched against the trust policy, so knowing the role ARN alone is not enough. AWS recommends one whenever a third party assumes a role in your account. It is not a password for a human — it just has to be unpredictable and not reused across customers.
3. Run discovery¶
UI: Discovery → Cloud → the ▶ (play) icon on the integration.
Choose the resource types, and — if any regional type is selected — the regions. If your selection is only global types (S3, CloudFront), the region picker is disabled and no regions value is sent, because none would be applied.
API: POST /api/v1/device-interrogation-service/cloud/discover
{
"integration_id": "uuid-of-aws-integration",
"resource_types": ["alb", "elb", "nlb", "api_gateway", "cloudfront", "kms", "s3", "rds"],
"regions": ["us-east-1", "us-west-2"]
}
regions is optional and applies only to the regional types. When omitted, the integration's default region is used.
Scheduled runs¶
A schedule for this integration (Discovery → Scheduled Scans) runs the same collectors a Discover run with every type ticked would, and delivers exactly the same results: S3 buckets, RDS instances and KMS keys as well as the load balancers, with the same certificate quality flags, OCSP status, region and resource identity. A scheduled run's job detail reports per-type outcomes and counts the same way.
4. Automatic processing¶
The service creates a discovery job, enumerates and interrogates the resources, and writes discoveries to the sensor_discoveries table — the same pipeline sensor discoveries use. Each discovery carries its cloud_provider and cloud_region, so cloud resources are grouped into per-region cloud prefixes.
5. Reading the result: "found nothing" vs "could not look"¶
UI: Discovery → Discovery Jobs → click the job.
The job's detail lists every resource type the run asked for, and says which of four things happened to it:
| What you see | What it means |
|---|---|
| Collected — n found | The type was read successfully and there are n of them. |
| Collected — none in this account | The type was read successfully and there are none. A real zero. |
| Partly collected — n found in 1 of 3 regions | Some regions answered and some did not. The count is only what the working regions returned. |
| Could not collect — not measured | Nothing is known about this type. It is not a zero. |
| Not collected | You asked for a type this platform does not collect for this provider, so nothing looked. |
A failed type shows the region it failed in, the provider's own error code (AccessDeniedException, ThrottlingException, …) and what to do about it — a denial needs an IAM change, a throttle needs a re-run. The provider's message is stored with secret-shaped values stripped out.
The banner above the list gives the run's verdict: complete (everything requested was read), partly collected, or nothing could be collected. Only a complete run reports success: true, so a discovery whose KMS permission was revoked can no longer look like a clean run that found no keys.
The account-enumeration calls are listed there too — EC2 instances, VPCs, Network prefixes and Security group membership, per region. A region where ec2:DescribeInstances (or another ec2:Describe* call) was refused shows as Could not collect or Partly collected with AWS's UnauthorizedOperation, not as a region with no instances.
If the integration has account-wide compute enumeration switched off, or enumeration could not run, the job detail says so rather than leaving the instance and VPC counts silently at zero.
6. Review and approve¶
UI: Discovery → Approvals.
Cloud-discovered assets appear alongside sensor-discovered ones with hostname, port, protocol, protocol version, cipher suite and certificate detail. They carry discovery_method = 'cloud_api' and hang off the account's Cloud Account asset (a hosted_on relationship). They go straight to monitoring when the integration's Auto-approve resources switch is on (the default) and wait as pending_approval when it is off — the account decides, not a prefix; see Asset Approval.
Approved resources appear as Infrastructure Assets with their Crypto Configurations. Cloud resource types render with readable names ("AWS S3 bucket", "AWS KMS key") on Discovery → Sensors & Scanning → Managed Devices and in the job drawer, and each asset is given the class that says what it is: an ELB becomes a Cloud Load Balancer, an S3 bucket Object Storage, a KMS key a Key Store, an API Gateway domain an API Gateway, a CloudFront distribution a CDN Distribution, an RDS instance a Managed Database and an EC2 instance a Compute Instance. All of them sit under Cloud Resource in the class tree, so filtering the Inventory list by that one class shows every cloud thing this integration found, and a resource type the platform has not seen before is filed as a plain Cloud Resource rather than guessed into the wrong family.
Crypto Configuration Details¶
Reported TLS version is the minimum permitted, not the negotiated one¶
For every AWS-configured TLS surface, the protocol_version the platform records is the weakest version the endpoint still accepts, and the full permitted set is recorded alongside it.
This is deliberate. A listener on ELBSecurityPolicy-TLS-1-0-2015-04 negotiates TLS 1.2 with a modern client and still accepts TLS 1.0 from anyone who asks for it. Recording the negotiated version would hide exactly the finding this feature exists to produce.
- Load balancers — the permitted protocol set comes from the SSL policy's own protocol list.
- API Gateway — the domain's
SecurityPolicy(TLS_1_0/TLS_1_2) is a floor; the permitted set is everything at or above it. - CloudFront viewer —
MinimumProtocolVersionis likewise a floor. - CloudFront origins — the explicitly configured origin SSL protocols. An origin with an HTTP-only protocol policy is reported as HTTP on its HTTP port, with no TLS version, because that is what CloudFront actually uses to reach it.
Where a live TLS handshake also succeeds, the version our client negotiated is preserved separately as negotiated_protocol_version; it does not overwrite the permitted minimum. The negotiated cipher suite does come from the handshake, since that is a real measurement of the endpoint.
Anything AWS reports that the platform cannot map to a known protocol version is surfaced verbatim rather than guessed at or dropped.
Certificate Discovery¶
Cloud discovery performs TLS handshake verification against publicly accessible endpoints to extract full certificate chains, giving the same level of certificate detail as sensor-based discovery.
- After interrogating a resource via the AWS API, the service performs a TLS handshake against the resource's DNS name.
- The handshake extracts the full chain (leaf + intermediates): PEM, SHA-256/SHA-1 fingerprints, subject/issuer DN, serial, SANs, key algorithm, key size, signature algorithm, validity period, key usage and extended key usage, CA flag, chain order.
- Handshake certificates are enriched with ACM metadata by matching domain names against the ACM certificates bound to the resource.
- Certificates flow through
sensor_discoveries→ discovery-processor-service → inventory-service, creatingcertificatesrecords and Crypto Configurations with certificate linkages.
Fallback behaviour¶
- Private endpoints — an internal ALB whose DNS name is not publicly resolvable will fail the handshake gracefully. The device and its Crypto Configuration are still created from the API data (including the permitted TLS versions and ACM metadata), but without a full certificate record.
- Handshake timeout — 10 seconds, so unreachable endpoints do not block a run.
handshake_verifiedin the discovery metadata records whether the certificate came from a real handshake (true) or from API metadata only (false).
ACM metadata enrichment¶
Where a certificate matches an ACM certificate bound to the resource, the platform records the ACM ARN, status, certificate type (AMAZON_ISSUED, IMPORTED, …), renewal eligibility, in-use-by resources and domain validation options.
Certificate data sources¶
cloud_api— discovered via cloud TLS handshake + API enrichmentdiscovery— sensor-based discoverymanual— manually uploaded
S3 encryption: "could not determine" is a distinct answer¶
When the bucket encryption configuration cannot be read — AccessDenied, throttling, any API failure — the bucket is recorded as undetermined, not as encrypted. An unreadable bucket is not a compliant bucket.
The one case that does license a conclusion is S3's specific "this bucket has no bucket-level encryption configuration" response: since January 2023 such a bucket receives SSE-S3 by default, and it is recorded as sse-s3-default, flagged as an AWS default rather than as an explicit configuration you chose.
Each bucket's home region is resolved individually rather than assumed to be the integration's default region.
Example Discovery Result¶
{
"job_id": "uuid",
"devices": [
{
"id": "device-uuid",
"device_type": "aws_alb",
"vendor": "AWS",
"hostname": "my-alb-123456789.us-east-1.elb.amazonaws.com",
"metadata": {
"arn": "arn:aws:elasticloadbalancing:us-east-1:123456789:loadbalancer/app/my-alb/123456789",
"region": "us-east-1",
"crypto_configs": [
{
"protocol": "HTTPS",
"protocol_version": "TLS 1.0",
"negotiated_protocol_version": "TLS 1.3",
"tls_versions": ["TLS 1.0", "TLS 1.1", "TLS 1.2"],
"cipher_suite": "TLS_AES_128_GCM_SHA256",
"port": 443,
"handshake_verified": true,
"certificates": [
{
"subject_dn": "CN=example.com",
"issuer_dn": "CN=Amazon RSA 2048 M01,O=Amazon,C=US",
"serial_number": "0123456789abcdef",
"not_before": "2026-04-15T00:00:00Z",
"not_after": "2027-04-15T00:00:00Z",
"fingerprint_sha256": "abcdef1234...",
"key_algorithm": "RSA",
"key_size": 2048,
"chain_order": 0,
"acm_metadata": {
"arn": "arn:aws:acm:us-east-1:123456789:certificate/abc123",
"renewal_eligibility": "ELIGIBLE",
"status": "ISSUED"
}
}
],
"metadata": {
"ssl_policy": "ELBSecurityPolicy-TLS-1-0-2015-04",
"certificates": [
{ "arn": "arn:aws:acm:us-east-1:123456789:certificate/abc123" }
]
}
}
]
}
}
],
"count": 1
}
The example shows the point of the minimum-version rule: the listener negotiated TLS 1.3 with the platform's client, and is nonetheless reported as TLS 1.0 because that is what it still accepts.
Multi-Region Discovery¶
{
"integration_id": "uuid",
"resource_types": ["alb"],
"regions": ["us-east-1", "us-west-2", "eu-west-1"]
}
Regions are scanned sequentially, one after another, as are resource types. A run over many regions takes proportionally longer; every region you add costs another full pass. There is no parallel fan-out across regions today. For very large estates, prefer several smaller runs over one run covering every region.
S3 and CloudFront are listed account-wide and are enumerated once regardless of how many regions are selected.
Error Handling¶
- API errors — non-fatal. One resource type that fails never stops the others, and whatever the run did collect is kept.
- Permission errors — recorded against the resource type and the region they happened in, and shown on Discovery → Discovery Jobs → the job. See Reading the result.
- Retries and throttling — the platform does not configure its own retry or backoff policy. It uses the AWS SDK for Go v2 standard retryer defaults: up to 3 attempts per request, exponential backoff with jitter capped at 20 seconds, applied to throttling responses (
Throttling,TooManyRequestsException,SlowDown, …), request timeouts and HTTP 500/502/503/504, governed by a retry-token quota that stops a storm of retries. Requests that exhaust their attempts surface as ordinary API errors. There is no platform-level pacing across a run.
Security Considerations¶
Credential management¶
- Credentials are encrypted at rest in the
platform_integrationstable using the platform's master encryption key. - They are decrypted only when an API call needs them.
- They are never logged, and are masked in API responses.
- Only the credential fields are treated as secret; the role ARN and role session name are stored and displayed in the clear.
IAM permissions required¶
Attach this policy to the IAM user whose access key you configure, or to the role you let the platform assume. It is sufficient for every resource type; drop the statements for types you do not intend to discover.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:GetCallerIdentity",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeSSLPolicies",
"apigateway:GET",
"cloudfront:ListDistributions",
"cloudfront:GetDistributionConfig",
"acm:DescribeCertificate",
"kms:ListKeys",
"kms:DescribeKey",
"kms:GetKeyRotationStatus",
"kms:ListAliases",
"s3:ListAllMyBuckets",
"s3:GetBucketLocation",
"s3:GetEncryptionConfiguration",
"rds:DescribeDBInstances",
"secretsmanager:ListSecrets"
],
"Resource": "*"
}
]
}
Notes on the individual grants:
sts:GetCallerIdentity— called by the Test connection button. Without it the connection test fails on first use even though discovery itself would work.apigateway:GET— API Gateway v2 has noapigatewayv2:IAM namespace; its read operations authorize under theapigatewayservice prefix with HTTP-verb actions. Scope it down with a resource ARN (arn:aws:apigateway:<region>::/domainnames*,.../apis*) if you prefer.cloudfront:GetDistributionConfig— required for real CloudFront interrogation.GetDistributionConfigis used deliberately in preference toGetDistribution: it is the narrower response and does not return trusted-signer material.acm:DescribeCertificate— required for ACM enrichment of discovered certificates.s3:GetBucketLocation— required to place each bucket in its true region. This is one extra API call per bucket per run; on an account with thousands of buckets that is thousands of additional calls. If the call is denied, the bucket falls back to the integration's default region.s3:GetEncryptionConfiguration— the IAM action name for theGetBucketEncryptionoperation.secretsmanager:ListSecrets: lists each secret's metadata, including its KMS key. It never returns a secret's value, andGetSecretValueis never called.
All of these are read-only. Nothing in the policy can modify a load balancer, create or export a certificate, read object or database contents, or retrieve key material.
Limitations¶
Read this section before drawing conclusions from a run.
KMS keys appear in two places¶
Discovered KMS keys are listed in Inventory → Keys, with their spec, state, rotation setting and who manages them (you or AWS). Each key is also a Key Store Infrastructure Asset with no endpoint. The key inventory creates and refreshes that asset directly, without passing it through the discovery queue; it is approved automatically or held in Discovery → Approvals by its account's Auto-approve resources switch, like any other cloud resource, and a Key Store asset from an earlier release is the same asset, not a duplicate. A run's job detail still counts the keys it found under kms, and a run whose keys could not be recorded reports the KMS type as failed rather than complete.
S3, RDS and Secrets Manager records are not reachable endpoints¶
S3 buckets, RDS instances and Secrets Manager secrets are at-rest posture records, not TLS endpoints: they are inventoried with no network endpoint and no protocol, and nothing handshakes with them. They are identified by their ARN.
Other gaps¶
- Classic ELB detailed interrogation is basic only — enumeration and basic SSL/TLS configuration, without the listener-level SSL policy detail available for ALB and NLB.
- Not covered: EBS volume encryption, DynamoDB.
- Not covered: the GovCloud and China partitions (see Partitions above).
- Regions are not enumerated from your account — the region list is a fixed commercial-partition list, not the set of regions you have enabled.
- Discovery is sequential — see Multi-Region Discovery. Large multi-region runs take proportionally longer.
- Nothing in this feature has been verified against a live AWS account. The behaviour described here is what the implementation does; it has not been exercised end-to-end against real AWS resources.