# The machine-generated inventory of every plane on which one part of the system reaches another — VPC peering connections, Transit Gateway attachments, the interface and gateway endpoints this account consumes, the endpoint connections other accounts have made INTO your endpoint service, and the security-group rules that name another group rather than a CIDR — each narrowed to the states that are actually live

> FedRAMP Consolidated Rules for 2026 v2026.07.14.01 · updated 2026-07-14
> Canonical page: /collect/internal-connection-inventory-and-authorization

Recipe id: `internal-connection-inventory-and-authorization` · cadence continuous · partial

> **Authored opinion.** AWS overlay v3.0.0, written
> against dataset 2026.07.14.01. The upstream
> FedRAMP rules name none of these tools; this mapping is ours.

## What it proves

- KSI `KSI-CNA-ULN`
- control `ca-9`

## Collection

Kind: `cli`

```sh
# describe-vpc-peering-connections
aws ec2 describe-vpc-peering-connections
# describe-transit-gateway-attachments
aws ec2 describe-transit-gateway-attachments
# describe-vpc-endpoints
aws ec2 describe-vpc-endpoints
# describe-vpc-endpoint-connections
aws ec2 describe-vpc-endpoint-connections
# describe-security-group-rules
aws ec2 describe-security-group-rules
```

## Expected output

All five responses are collected unprojected and per-Region — every one of these is a Regional call, so an inventory taken in one Region is an inventory of one Region. From describe-vpc-peering-connections, VpcPeeringConnections[] with VpcPeeringConnectionId, Tags, ExpirationTime, and RequesterVpcInfo and AccepterVpcInfo each carrying OwnerId, VpcId, Region, CidrBlock and CidrBlockSet — the account on the far side is OwnerId, and it is the field that says whether a connection is internal at all. Status.Code is one of initiating-request, pending-acceptance, active, deleted, rejected, failed, expired, provisioning, deleting: the list is a history, not a live inventory, and only `active` is a connection. From describe-transit-gateway-attachments, TransitGatewayAttachments[] with TransitGatewayAttachmentId, TransitGatewayId, TransitGatewayOwnerId, ResourceOwnerId, ResourceId, CreationTime, Tags, an Association carrying TransitGatewayRouteTableId and its own State, a ResourceType of vpc, vpn, vpn-concentrator, direct-connect-gateway, connect, peering, tgw-peering (deprecated) or client-vpn, and a State among initiating (deprecated), initiatingRequest, pendingAcceptance, rollingBack, pending, available, modifying, deleting, deleted, failed, rejected, rejecting and failing — `available` is the live one, and `pendingAcceptance` is a cross-account attachment nobody has adjudicated. From describe-vpc-endpoints, VpcEndpoints[] with VpcEndpointId, VpcEndpointType (Interface, Gateway, GatewayLoadBalancer, Resource, ServiceNetwork), VpcId, ServiceName, OwnerId, CreationTimestamp, PolicyDocument, PrivateDnsEnabled, RouteTableIds for gateway endpoints, SubnetIds and Groups for interface endpoints, Tags, and a State among PendingAcceptance, Pending, Available, Deleting, Deleted, Rejected, Failed, Expired and Partial. From describe-vpc-endpoint-connections — the provider side, and the only one of the five that shows connections INTO you — VpcEndpointConnections[] with ServiceId, VpcEndpointId, VpcEndpointOwner (the consumer's account id), CreationTimestamp, DnsEntries, IpAddressType and a VpcEndpointState of PendingAcceptance, Pending, Available, Deleting, Deleted, Rejected, Failed, Expired or Partial — PascalCase, unlike the Transit Gateway attachment state beside it, and unlike the lowercase forms the CLI page lists as --filters ARGUMENT values. From describe-security-group-rules, SecurityGroupRules[] with SecurityGroupRuleId, SecurityGroupRuleArn, GroupId, IsEgress, IpProtocol, FromPort, ToPort, Description, Tags, and a peer field naming what the rule permits traffic to or from — CidrIpv4, CidrIpv6, PrefixListId or ReferencedGroupInfo, and IsEgress is what says which direction that peer sits in; a rule carrying ReferencedGroupInfo is one component reaching another by identity rather than by address, which is the internal connection CA-09 is about.

## Assertions

- {"field":"describe-vpc-peering-connections.VpcPeeringConnections[?Status.Code=='active'] | [?!not_null(Tags[?Key=='ConnectionAuthorization'] | [0])]","op":"count_eq","value":0,"controls":["ca-9"],"description":"No ACTIVE peering connection is missing the tag naming the authorization record that permitted it. Narrowed by state because the response also carries deleted, rejected, expired and failed connections. Zero on an account with no peering connections, so read it beside the length of the list, never instead of it."}
- {"field":"describe-vpc-peering-connections.VpcPeeringConnections[?Status.Code=='pending-acceptance'] | [0].VpcPeeringConnectionId","op":"not_exists","controls":["ca-9"],"description":"No peering request is sitting unaccepted. An unaccepted request is not a connection, but it is an authorization decision nobody has made, and ExpirationTime means it resolves itself by lapsing if the review never happens. The pipe is load-bearing: without it the [0] indexes each matched object rather than the filtered list, and the expression is empty whether such a request exists or not."}
- {"field":"describe-transit-gateway-attachments.TransitGatewayAttachments[?State=='available'] | [?!not_null(Tags[?Key=='ConnectionAuthorization'] | [0])]","op":"count_eq","value":0,"controls":["ca-9"],"description":"No AVAILABLE Transit Gateway attachment is missing its authorization tag. ResourceOwnerId is what says whether the far side is inside the boundary, and no assertion can compare it to your account list."}
- {"field":"describe-transit-gateway-attachments.TransitGatewayAttachments[?State=='pendingAcceptance'] | [0].TransitGatewayAttachmentId","op":"not_exists","controls":["ca-9"],"description":"No cross-account Transit Gateway attachment is waiting to be accepted. Note the case: the attachment state is camelCase pendingAcceptance, while the VPC endpoint connection state below is PascalCase PendingAcceptance — the two enums are spelled differently and a literal copied from one to the other matches nothing while still returning a green result."}
- {"field":"describe-vpc-endpoint-connections.VpcEndpointConnections[?VpcEndpointState=='PendingAcceptance'] | [0].VpcEndpointId","op":"not_exists","controls":["ca-9"],"description":"No consumer account is waiting on a decision to connect INTO an endpoint service this account publishes. This is the only inbound clause in the recipe, and VpcEndpointOwner on such an entry is the account asking. The literal is PascalCase per the VpcEndpointConnection API type; the lowercase spelling on the CLI page is a --filters argument value, not an output value."}
- {"field":"describe-security-group-rules.SecurityGroupRules[?ReferencedGroupInfo && !Description]","op":"count_eq","value":0,"controls":["ca-9"],"description":"No security-group rule that reaches another group by identity rather than by address is left without a description. A documentation-completeness check, not evidence about the connection: Description is free text and AWS validates none of it — and in GovCloud it is a field AWS asks you to keep export-controlled data out of."}

## GovCloud

Amazon VPC, Transit Gateway and PrivateLink all operate in both AWS GovCloud (US) Regions and every call here is available; VPC, endpoint, attachment and security-group ARNs use partition arn:aws-us-gov. Two documented GovCloud differences touch this recipe and neither blocks it. Security group rule IDs are not shown in the Amazon VPC CONSOLE in GovCloud — the API returns SecurityGroupRuleId normally, so collect this evidence through the CLI and do not expect a reviewer to reconcile it against a console screenshot. And not all VPC endpoints in GovCloud support VPC endpoint policies, so PolicyDocument may be absent on an endpoint where the commercial Regions would carry one; read its absence as an unsupported endpoint type rather than as a removed control, and say which it was. Note also that VPC metadata in GovCloud is not permitted to contain export-controlled data, and AWS names security group rule descriptions, tag keys and values, and VPC endpoint service names among the free-text fields this applies to — which is a constraint on the very fields this recipe asks you to write an authorization reference into.

## Notes

CA-09 has four elements and this output serves parts of two of them. The part it holds: which internal connections exist. Every plane on which one component of the system reaches another leaves a resource behind — a peering connection, a Transit Gateway attachment, an endpoint, an endpoint connection, or a security-group rule that names another group instead of a CIDR — and five calls enumerate them completely for a Region. That enumeration is worth having on its own: it is the list the control's documentation requirement is written against, and most providers do not have it.

What it does not hold is everything CA-09 asks you to record about each connection. The interface characteristics, the security AND PRIVACY requirements agreed for the connection, the nature of the information communicated, and the authorization to connect at all — none of them is a field. Two further elements of the control are not addressed here at all and should not be read as covered: terminating internal connections when the stated conditions are met, and reviewing the continued need for each connection on a stated frequency. The second of those is the one this inventory most nearly serves — a continuously collected enumeration is the substrate a periodic review runs on — but the review itself is a decision with a date and a reviewer, and none of that is in the response. They live in an interconnection agreement or an internal approval record, and the reconciliation of that record against this inventory is the CA-09 artifact. This recipe produces the column you reconcile against, which is why it is partial and why no assertion below claims more than attachment and state.

Read every one of these lists as a history rather than an inventory. Peering connections persist in the response with Status.Code deleted, rejected, expired or failed; Transit Gateway attachments persist as deleted, failed or rejected; endpoints persist as Deleted or Rejected. A count taken over the raw response counts connections that do not exist, and — the direction that actually matters — a clause of the form 'every connection carries an authorization tag' evaluated over the raw response is answering about the dead ones too. Every assertion here is therefore narrowed by state, and the narrowing is the assertion.

The completeness clauses are written as OFFENDER LISTS — a filter that selects the rows which fail, asserted to be empty — and not as 'every connection carries a tag'. This is not a style preference. A field projection over a list drops the rows where the field is absent instead of reporting them as false, so an every-row clause of the obvious form evaluates only the rows that already comply and is green on an estate where half the connections are untagged. The offender form was verified against synthetic responses before it was written down.

The vacuity to state plainly: all five lists are empty on an account that has none of these, and an every-entry clause over an empty list is true. This evidence cannot distinguish 'no internal connections' from 'no connections in THIS Region' from 'the collection ran with a role that cannot see them'. Record the Region set the collection covered beside the output, because every one of these calls is Regional and nothing in the response says which Region produced it.

Two directions, and only one of them is intuitive. Four of the five calls show what this account reaches OUT to. describe-vpc-endpoint-connections is the other direction — it is the provider-side view of who has connected IN to an endpoint service you publish — and a pendingAcceptance entry there is a connection request nobody has adjudicated. The same is true of a peering connection in pending-acceptance: an unaccepted request is not yet a connection, but it is a decision someone owes, and it belongs in the review the KSIs describe as persistent rather than in a quarterly surprise.

OwnerId and ResourceOwnerId are what make 'internal' checkable at all, and they are also the join this grammar cannot do: deciding whether a peer account is inside the authorization boundary means comparing OwnerId against your own account list, and an assertion compares a field to a constant. Write the boundary's account set into the assessment and reconcile by hand; a tag naming the authorization record is the closest a field gets, which is what the assertions ask for.

Security-group rules are included for a reason worth stating: they are the only one of the five that shows a connection with no resource of its own. A rule whose ReferencedGroupInfo names another security group is one component permitted to reach another by identity, and it is invisible to any inventory that looks only for peering connections and attachments. Description is free text and no AWS call validates it, so treat the description assertion as a documentation-completeness check rather than as evidence about the connection itself.

## References

- {"title":"AWS CLI: ec2 describe-vpc-peering-connections (VpcPeeringConnections[] with Tags, ExpirationTime, RequesterVpcInfo/AccepterVpcInfo carrying OwnerId, VpcId, Region; Status.Code including pending-acceptance, active, deleted, rejected, expired)","url":"https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-vpc-peering-connections.html"}
- {"title":"AWS CLI: ec2 describe-transit-gateway-attachments (ResourceType and State enums, ResourceOwnerId, TransitGatewayOwnerId, Association, CreationTime, Tags)","url":"https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-transit-gateway-attachments.html"}
- {"title":"AWS CLI: ec2 describe-vpc-endpoints (VpcEndpointType and State enums, ServiceName, PolicyDocument, OwnerId, Groups, SubnetIds, RouteTableIds)","url":"https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-vpc-endpoints.html"}
- {"title":"AWS CLI: ec2 describe-vpc-endpoint-connections — the service-provider view of consumer connections, with VpcEndpointOwner and VpcEndpointState including pendingAcceptance","url":"https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-vpc-endpoint-connections.html"}
- {"title":"AWS CLI: ec2 describe-security-group-rules (SecurityGroupRuleId, IsEgress, Description, and the CidrIpv4 / CidrIpv6 / PrefixListId / ReferencedGroupInfo peer fields)","url":"https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-security-group-rules.html"}
- {"title":"AWS GovCloud (US) User Guide: Amazon VPC — security group rule IDs unavailable in the console, not all VPC endpoints support endpoint policies, and the free-text fields that must not carry export-controlled data","url":"https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/govcloud-vpc.html"}
- {"title":"Amazon EC2 API: VpcEndpointConnection — vpcEndpointState valid values PendingAcceptance | Pending | Available | Deleting | Deleted | Rejected | Failed | Expired | Partial (PascalCase)","url":"https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_VpcEndpointConnection.html"}
- {"title":"Amazon EC2 API: TransitGatewayAttachment — state valid values including initiatingRequest, pendingAcceptance, rollingBack, modifying, rejecting and failing (camelCase; initiating deprecated), and resourceType including vpn-concentrator (tgw-peering deprecated)","url":"https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_TransitGatewayAttachment.html"}
