Multi-cloud integration is fundamentally a translation problem.
Every cloud provider has evolved its own internal vocabulary for the same concepts. Support case statuses, resource naming conventions, billing line-item categories, SLA credit eligibility — they all use different words for the same things. When you build a platform that talks to all three simultaneously, you either build a translation layer or you write provider-specific code for every feature.
We chose a translation layer. But we got it wrong once first, and the consequences were exactly what you’d expect: valid SLA credits being silently marked as denied.
The Problem
When a customer files an SLA credit claim, we poll the provider’s support API daily to check whether the credit was approved. Each provider returns a status. The question is: what do you do with that status?
Here’s what each provider actually returns:
Notice the GCP subtlety: "CLOSED" means denied. "SOLUTION_PROVIDED" means approved. These are two completely different outcomes and GCP uses two different states to express them.
The Bug We Shipped
Our first implementation of the poll endpoint read the raw status from each provider and used it directly:
# Early (broken) implementation
status_result = connector.get_claim_status(case_id)
credit_status = status_result.get("status", "unknown")
if credit_status == "approved":
breach.status = "Credit Approved"
else:
breach.status = "Credit Denied" # ← everything not "approved" was denied
The problem: AWS get_claim_status() was returning the raw AWS status string "resolved", not our normalised vocabulary "approved". So a successfully resolved AWS case hit the else branch and got marked "Credit Denied".
This was a silent failure. No exception. No log warning. Just a wrong value in the database.
A code review caught it before it reached a real customer claim, but barely.
The Fix: Normalise at the Provider Layer
Each provider strategy is now responsible for mapping its own vocabulary to our internal set:
approved — credit confirmed
denied — claim rejected
pending — under review
unknown — can't determine (skip the update)
AWS normalisation:
_AWS_STATUS_MAP = {
"resolved": "approved",
"closed": "approved",
"unassigned": "pending",
"work-in-progress": "pending",
"pending-customer-action": "pending",
"reopened": "pending",
}
credit_status = _AWS_STATUS_MAP.get(raw_status, "unknown")
GCP normalisation:
if state_name == "SOLUTION_PROVIDED":
credit_status = "approved"
elif state_name == "CLOSED":
credit_status = "denied"
else:
credit_status = "pending"
Azure normalisation:
_AZURE_STATUS_MAP = {
"closed": "approved",
"open": "pending",
"in_review": "pending",
"waiting_for_customer":"pending",
}
credit_status = _AZURE_STATUS_MAP.get(raw_status.lower(), "pending")
Belt and Suspenders: The Poll Guard
Even with normalisation at the provider layer, we added a guard in the poll endpoint itself:
credit_status = status_result.get("status", "unknown")
# Only act on statuses we explicitly understand
if credit_status not in ("approved", "denied", "pending"):
continue # Skip — don't write anything
If a provider ever returns an unexpected value, we skip the update rather than writing wrong data. The breach stays in “Filed” status until the next poll run.
Why This Architecture
The alternative was to handle all status mapping in the poll endpoint itself:
# Everything in one place
if provider == "aws":
if raw_status in ("resolved", "closed"):
credit_status = "approved"
...
elif provider == "gcp":
if raw_state == "SOLUTION_PROVIDED":
credit_status = "approved"
...
This works but doesn’t scale. Every new provider requires changes to the poll endpoint. Every edge case in AWS’s vocabulary is mixed in with GCP’s edge cases.
By normalising at the provider strategy layer, the poll endpoint only ever sees approved/denied/pending/unknown. Adding a fourth provider means implementing the normalisation once in the new strategy class. Nothing in the poll endpoint changes.
The Broader Pattern
This is a general principle for any system that integrates with multiple external APIs: normalise at the boundary, not at the consumer.
The provider strategy is the boundary. It knows AWS’s vocabulary. It translates to our vocabulary. Everything upstream of it only speaks our vocabulary.
When AWS changes their status strings (it’s happened before), we update one map in one file. Not every place in the codebase that checks for AWS status values.
Fintropy is a multi-cloud FinOps platform in private beta. Learn more at nuvikatech.com