Repository navigation
AWS Savings Plans purchase path is USD-only; aws-cn accounts can onboard but can't buy SPs #7
Description
Summary
The AWS Savings Plans purchase path is hardcoded to USD, but the onboarding layer accepts aws-cn (AWS China) role ARNs. A China account can therefore be added to CUDly but cannot correctly purchase Savings Plans, since SPs in the aws-cn partition are priced in CNY, not USD.
This is an onboarding-vs-capability mismatch, not a regression. It surfaced while reviewing the Currencies: [USD] offering filter added in LeanerCloud/cloud-commitments-cli#1039 (finding 08-L1).
Evidence
- Onboarding accepts the China partition:
internal/api/handler.go:672→case "aws", "aws-cn", "aws-us-gov":internal/api/validation.go:42→ ARN regex allowsaws-cn.
- SP purchase path assumes USD throughout:
providers/aws/services/savingsplans/client.go:408→Currency: "USD"hardcoded on the returnedOfferingDetails(pre-existing, predates fix(aws/savingsplans): paginate GetExistingCommitments via NextToken loop cloud-commitments-cli#1039).findOfferingIDnow pinsCurrencies: [types.CurrencyCodeUsd]onDescribeSavingsPlansOfferings(fix(aws/savingsplans): paginate GetExistingCommitments via NextToken loop cloud-commitments-cli#1039). In anaws-cnregion this returns zero offerings, so the purchase fails to resolve an offering.- SP
GetExistingCommitmentsdoes not set aCurrencyon returned commitments — USD is implicit.
- Same USD-hardcoding exists for
memorydb/client.go:425andelasticache/client.go:394.
Why this is not a LeanerCloud/cloud-commitments-cli#1039 regression
Before LeanerCloud/cloud-commitments-cli#1039 the SP purchase path was already USD-only (client.go:408). A China lookup could previously return a CNY offering that downstream then mislabelled as USD. LeanerCloud/cloud-commitments-cli#1039's USD filter makes the lookup return nothing instead, so the flow now fails cleanly/early rather than silently picking a CNY offering and treating it as USD. Commercial-AWS customers (including those billed in EUR/GBP/JPY) are unaffected, because commercial SP offerings are always USD-denominated regardless of invoice currency — the filter excludes nothing for them.
Scope of impact
- Only
aws-cn. GovCloud (aws-us-gov) is USD. All commercial regions are USD at the offering layer regardless of billing/display currency. - Likely no China customers today, hence low priority — but the gap should be tracked rather than implicit, and the onboarding layer should not advertise a capability the purchase path can't deliver.
Proposed direction (if/when China support is wanted)
Derive the SP currency from the account's partition/region rather than hardcoding USD, and thread it consistently through:
- the
Currenciesfilter infindOfferingID(CNY forcn-*, USD otherwise), - the hardcoded
Currency: "USD"atclient.go:408, - the commitment / cost math that assumes USD.
Note: CUDly already reads the real CurrencyCode from AWS for EC2/RDS/OpenSearch/Redshift and the RI-exchange path, so the codebase is partially currency-aware; SP/MemoryDB/ElastiCache are the USD-hardcoded outliers.
Interim option
If China SP purchasing is explicitly out of scope, reject aws-cn onboarding for the SP purchase capability (or surface a clear "unsupported partition" error) so the onboarding layer doesn't advertise a capability the purchase path can't deliver.
Refs
- PR fix(aws/savingsplans): paginate GetExistingCommitments via NextToken loop cloud-commitments-cli#1039 (
fix/aws-sp-pagination, finding 08-L1) introduced the USD offering filter. - Related USD-hardcoded paths:
memorydb/client.go:425,elasticache/client.go:394.
Activity
cristim commentedon Oct 17, 2026
Decision needed (one line): support aws-cn Savings Plans purchases (CNY), or reject them? Safe default: reject with an explicit unsupported-partition error in the savingsplans client (USD-only today at
client.go~:347 and ~:689) and revisit when a China customer exists; the onboarding check lives in the platform repo. Markingstatus/needs-info.cristim commentedon Oct 20, 2026
Claiming [claude-cc-libs]
fix(aws/savingsplans): reject aws-cn before any AWS call
fix(aws/savingsplans): also reject cn regions carried in plan details
test(aws/savingsplans): cover CE-supplied OfferingID in the aws-cn guard
fix(aws/savingsplans): reject aws-cn before any AWS call
fix(aws/savingsplans): also reject cn regions carried in plan details
test(aws/savingsplans): cover CE-supplied OfferingID in the aws-cn guard
fix(aws/savingsplans): reject aws-cn before any AWS call (LeanerCloud…