Skip to content

fix(E3692): allow Multi-AZ read replicas and omitted StorageType - #4963

Merged
kddejong merged 2 commits into
aws-cloudformation:mainfrom
hoganbr-amzn:fix/e3692-multiaz-read-replica
Nov 5, 2026
Merged

kddejong merged 2 commits into
aws-cloudformation:mainfrom
hoganbr-amzn:fix/e3692-multiaz-read-replica

Conversation

@hoganbr-amzn

@hoganbr-amzn hoganbr-amzn commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Issue #, if available: Fixes #4957, fixes #4962

Description of changes:

E3692 flagged two valid Multi-AZ DB cluster templates:

It also required Iops on every Multi-AZ DB cluster, although RDS accepts gp3 without it.

The rule now applies these requirements:

Property New Multi-AZ DB cluster Read replica (ReplicationSourceIdentifier set)
DBClusterInstanceClass required required
Iops required when StorageType is io1, io2 or omitted required when StorageType is io1, io2 or omitted
AllocatedStorage required not allowed
StorageType optional (still limited to io1, io2, gp3) optional (still limited to io1, io2, gp3)

Evidence, from us-east-1 in a personal AWS account. Clusters were PostgreSQL 17.10 on db.c6gd.medium with 100 GB, and replica sources were PostgreSQL 17.10 db.t4g.micro DB instances.

New Multi-AZ DB clusters:

  • StorageType: gp3 without Iops (CloudFormation stack, 2026-11-04) succeeds, with 3000 IOPS.
  • StorageType: io1 or io2 without Iops (CreateDBCluster API, 2026-11-04) fails with "The storage type io1 requires iops to be specified." (the same for io2).
  • Neither StorageType nor Iops (CloudFormation stack, and CreateDBCluster for PostgreSQL 17.10 and MySQL 8.4.11, 2026-11-04) fails with "You can't use storage type gp2".
  • The StorageType-omitted results with Iops set are in E3692 requires StorageType on a Multi-AZ DB cluster, but RDS defaults it to io1 #4962.

Read replicas:

  • Setting AllocatedStorage (CloudFormation stack, 2026-10-24, reproduced 2026-11-04) fails with "You can't specify allocated storage for a Multi-AZ DB cluster read replica. The Multi-AZ DB cluster read replica uses the same allocated storage value as the source DB instance." The same replica without AllocatedStorage, from the same source, reaches CREATE_COMPLETE.
  • Leaving out StorageType with Iops set (CreateDBCluster API, 2026-10-30) succeeds, and the cluster defaults to io1.
  • Leaving out DBClusterInstanceClass (CreateDBCluster API, 2026-10-30) fails with "DBClusterInstanceClass is required."
  • StorageType: io1 or io2 without Iops (CreateDBCluster API, 2026-10-30 for io1, 2026-11-04 for both) fails with "The storage type io1 requires iops to be specified." (the same for io2).
  • StorageType: gp3 without Iops (CloudFormation stack, 2026-11-04) succeeds, with 3000 IOPS.
  • Neither StorageType nor Iops (CloudFormation stack, 2026-11-04) fails with "You can't use storage type gp2".

Three cases get their own message:

  • AllocatedStorage on a read replica: 'AllocatedStorage' is not allowed on a Multi-AZ DB cluster read replica (ReplicationSourceIdentifier is set).
  • io1 or io2 without Iops: 'Iops' is a required property when 'StorageType' is 'io1'.
  • Neither StorageType nor Iops: Set 'Iops' or set 'StorageType' to 'gp3'; without either, RDS uses gp2 storage, which Multi-AZ DB clusters do not support.

The other properties that are not allowed keep the existing message.

Tests:

  • Unit cases for new clusters and read replicas: gp3 without Iops, io1 and io2 without Iops, an unresolved Ref for StorageType, no StorageType, an invalid StorageType, AllocatedStorage on a replica, and missing DBClusterInstanceClass and Iops.
  • Template tests with Fn::If: one switches between a primary and a replica with AWS::NoValue, setting AllocatedStorage only on the primary branch (clean) and on both branches (the replica error). The other switches StorageType between io1 and gp3 without Iops, which reports only the io1 branch.

Verification:

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@hoganbr-amzn
hoganbr-amzn marked this pull request as ready for review October 30, 2026 15:11

@kddejong kddejong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two fixes are right and the change is well scoped — the good-fixtures corpus is unchanged, the conditional Fn::If cases behave, and no template that is clean today starts failing.

One thing to add before merge. The rule still requires Iops unconditionally, but RDS only requires it for io1 and io2. A cluster with StorageType: gp3 and no Iops, or with no StorageType and no Iops, is still reported on this branch. The second case is the same shape as #4962 — once StorageType is optional, nothing requires Iops. Please gate Iops on StorageType being io1 or io2 rather than leaving it in the unconditional required list.

Also, could you confirm the AllocatedStorage-on-a-replica rejection is reproducible? It's the only part of this change that adds a finding, so it's worth pinning down.

@hoganbr-amzn

hoganbr-amzn commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I've pushed a follow-up commit: Iops is now required for io1 and io2, so gp3 without Iops is clean. I kept one case you asked about. With neither StorageType nor Iops, RDS falls back to gp2 and rejects the cluster, so that case is still reported, now with its own message. It differs from #4962, where Iops is set and RDS uses io1. I checked each case in us-east-1, in a personal AWS account, on 2026-11-04. Each CloudFormation cluster was PostgreSQL 17.10 on db.c6gd.medium with 100 GB, and each replica's source was a PostgreSQL 17.10 DB instance.

Case Method Result
New cluster, StorageType: gp3, no Iops CloudFormation stack CREATE_COMPLETE, 3000 IOPS and 125 MiB/s
Read replica, StorageType: gp3, no Iops CloudFormation stack CREATE_COMPLETE, 3000 IOPS and 125 MiB/s
New cluster or read replica, StorageType: io1 or io2, no Iops CreateDBCluster API "The storage type io1 requires iops to be specified." (same for io2)
New cluster, no StorageType, no Iops CloudFormation stack, and CreateDBCluster for PostgreSQL 17.10 and MySQL 8.4.11 "You can't use storage type gp2"
Read replica, no StorageType, no Iops CloudFormation stack "You can't use storage type gp2"
Read replica with AllocatedStorage CloudFormation stack "You can't specify allocated storage for a Multi-AZ DB cluster read replica. The Multi-AZ DB cluster read replica uses the same allocated storage value as the source DB instance."
The same read replica without AllocatedStorage CloudFormation stack, same source CREATE_COMPLETE

The message for the gp2 case is Set 'Iops' or set 'StorageType' to 'gp3'; without either, RDS uses gp2 storage, which Multi-AZ DB clusters do not support.

The AllocatedStorage rejection on a replica reproduced, and the control replica without it deployed from the same source.

E3692 treated ReplicationSourceIdentifier as a property that is not
allowed on a Multi-AZ DB cluster and required AllocatedStorage, so every
Multi-AZ read replica was flagged. RDS rejects AllocatedStorage on a
Multi-AZ DB cluster read replica, because the replica uses the source's
allocated storage.

The rule also required StorageType, although RDS defaults it to io1 for
Multi-AZ DB clusters.

AllocatedStorage is now required only when ReplicationSourceIdentifier
is absent and is not allowed when it is set, with a replica-specific
message. StorageType is optional but still limited to io1, io2 and gp3.

Fixes aws-cloudformation#4957
Fixes aws-cloudformation#4962

Signed-off-by: Brenden Hogan <hoganbr@amazon.com>
A Multi-AZ DB cluster with StorageType gp3 needs no Iops; RDS sets a
baseline of 3000 IOPS. Iops is now required only when StorageType is
io1 or io2, or when StorageType is omitted. With neither StorageType
nor Iops, RDS uses gp2, which Multi-AZ DB clusters do not support, so
that case gets its own message. The same rules apply to read replicas.

Signed-off-by: Brenden Hogan <hoganbr@amazon.com>
@kddejong
kddejong force-pushed the fix/e3692-multiaz-read-replica branch from ec8a28d to 5ce4b02 Compare November 5, 2026 20:11
@codecov

codecov Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.81%. Comparing base (0772607) to head (5ce4b02).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4963   +/-   ##
=======================================
  Coverage   94.81%   94.81%           
=======================================
  Files         441      441           
  Lines       16179    16188    +9     
  Branches     3137     3140    +3     
=======================================
+ Hits        15340    15349    +9     
  Misses        463      463           
  Partials      376      376           
Flag Coverage Δ
unittests 94.81% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kddejong
kddejong merged commit 3d499d3 into aws-cloudformation:main Nov 5, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants