Lab 17: The Appliance Mode That Was Enabled And Ignored
Appliance mode is enabled. Route propagation is enabled on the right route table. Every check AWS documents passes. Half of same-zone traffic is still handed to the appliance in the wrong Availability Zone, and the bill shows it before anything else does.
- Debugging time
- ~40 min
- Reading time
- 11 min
- Reported by
- Cloud Platform
- Tier
- Specialty
Cross-zone data transfer on the inspection VPC is roughly half of all inspected traffic
Reported by Cloud Platform
Finance flagged cross-zone transfer on the inspection VPC. It is running at close to half the total inspected volume, which should be near zero — the workloads that talk to each other are deliberately co-located in the same Availability Zone precisely so inspection stays in-zone.
We enabled appliance mode when we built this, exactly for this reason. I have checked it: the
attachment reports ApplianceModeSupport: enable.
I also checked the prerequisite in the documentation, which says route propagation must be enabled on
the transit gateway route table associated with the appliance-mode attachment. It is enabled. Both
spoke attachments show enabled in get-transit-gateway-route-table-propagations.
So both requirements are met and the behaviour is wrong. There is no error anywhere, nothing has alarmed, and the only reason we know is the invoice.
What you are working with
One Transit Gateway, three VPCs, two Availability Zones.
| Resource | Configuration |
| --- | --- |
| Client | Spoke A, 10.180.1.50, first AZ |
| Target | Spoke B, 10.181.1.50, first AZ |
| Appliance A | Inspection VPC, first AZ |
| Appliance B | Inspection VPC, second AZ |
| Appliance attachment | ApplianceModeSupport: enable, subnets in both zones |
| Spoke route table | Both spoke prefixes → appliance attachment |
| Appliance route table propagations | Both spoke attachments, enabled |
| Appliance route table routes | Both spoke prefixes, present |
Source and destination are in the same Availability Zone. Per AWS's documented Scenario 1, appliance mode should hold that zone for the whole flow, so every flow should reach appliance A.
- EC2
Client, first AZ
10.180.1.50 → 10.181.1.50
- GW
Transit gateway
spoke route table sends the prefix to the appliance attachment
- RTB
Availability Zone selection
appliance mode enabled, propagation enabled
Dropped — The route actually in use for the destination prefix is static, and a static route carries no Availability Zone information. With nothing to reason over, the transit gateway hashes the flow and picks a zone.
- VPCE
Appliance A, first AZ
receives roughly half the flows
- VPCE
Appliance B, second AZ
receives roughly half the flows, and should receive none
Nothing fails. Each flow is inspected, consistently, by one appliance. Half of them cross a zone boundary to get there.
Scope and constraints
- In scope: why zone affinity is not happening when both documented requirements are met.
- Out of scope: appliance mode itself, the propagation configuration, the attachment subnets, and the spoke route table. All correct.
- The reporter is right on both counts. Appliance mode is enabled and propagation is enabled. Verify both yourself early so you stop re-checking them.
- Nothing is broken. No connection fails and no alarm fires. This is a cost and blast-radius finding.
- The fix removes configuration rather than adding it.
Deploy the broken state
cd lab-17-appliance-mode-static-shadow
terraform init
terraform applyThe Transit Gateway takes a few minutes and the attachments a few more — budget about eight minutes before anything is testable.
CLIENT=$(terraform output -raw client_instance_id)
APP_A=$(terraform output -raw appliance_a_instance_id)
APP_B=$(terraform output -raw appliance_b_instance_id)
RTB=$(terraform output -raw appliance_route_table_id)Confirm both documented requirements are met
Do this first, because it is what the ticket claims and it is true.
aws ec2 describe-transit-gateway-vpc-attachments \
--transit-gateway-attachment-ids "$(terraform output -raw appliance_attachment_id)" \
--query 'TransitGatewayVpcAttachments[0].[State,Options.ApplianceModeSupport]' --output text
# available enable
aws ec2 get-transit-gateway-route-table-propagations \
--transit-gateway-route-table-id "$RTB" \
--query 'TransitGatewayRouteTablePropagations[].[TransitGatewayAttachmentId,State]' --output table
# ----------------------------------------------------
# | tgw-attach-0616db2cd42ac0fe4 | vpc | enabled |
# | tgw-attach-0edecca4c33d992c6 | vpc | enabled |
# ----------------------------------------------------Appliance mode enabled. Propagation enabled on both spoke attachments. Both boxes ticked.
Measure, do not test
A single connection tells you nothing here, because each individual flow succeeds and is handled by exactly one appliance. You have to count.
Start a capture on each appliance. These run in the foreground and their output is the result, so launch both and leave them:
# On appliance A, and separately on appliance B
sudo timeout 80 tcpdump -ni any -nn -c 500 "tcp and dst host 10.181.1.50"Then, from the client, open sixty distinct flows. Varying the destination port varies the tuple, and therefore the hash:
for p in $(seq 4000 4059); do
timeout 1 bash -c "echo > /dev/tcp/10.181.1.50/$p" 2>/dev/null
done
echo doneCount the distinct destination ports each appliance saw:
# piped from each capture
sed -n 's/.*> 10\.181\.1\.50\.\([0-9]*\):.*/\1/p' | sort -un | wc -lMeasured on a real account:
appliance A (first AZ) 24 flows
appliance B (second AZ) 29 flowsBoth endpoints are in the first Availability Zone. Appliance B should have seen nothing. It saw more than half.
Note what is working
Two things worth registering before you go looking for the cause, because they narrow it sharply.
The port sets are disjoint. No flow appears on both appliances. Each flow is pinned to one appliance for its lifetime, which is appliance mode's flow stickiness working correctly. Whatever is broken is not the stickiness.
The split is close to even. That is the signature of a hash over a roughly uniform input, not of a partial failure or a health problem. Something is choosing between two zones at random rather than being told which zone to use.
So the failure is specifically in zone determination, and appliance mode is otherwise functioning.
Look at what the route table actually contains
Propagation is enabled. Look at the routes themselves rather than the propagation state:
aws ec2 search-transit-gateway-routes \
--transit-gateway-route-table-id "$RTB" \
--filters "Name=state,Values=active" \
--query 'Routes[].[DestinationCidrBlock,Type,State]' --output table
# ---------------------------------------
# | 10.180.0.0/16| static | active |
# | 10.181.0.0/16| static | active |
# ---------------------------------------static.
Propagation is enabled and there is not a single propagated route in the table. Two static routes for the same prefixes are taking precedence, so the propagated routes exist as configuration and are never the routes in use.
This debrief is part of Labs Pro
The root-cause analysis, packet-flow walkthrough, and Terraform remediation diff for this lab are available to Labs Pro members. One payment of $49, no subscription, and it covers every Pro lab now and later.
The brief, the reproduction steps, and the Terraform stay free — you can still solve this one yourself.
Already bought it? Sign in and it unlocks.