Skip to content

fix(elbv2): hold an ACM certificate in use while a listener serves it - #6056

Merged
pgermosen merged 2 commits into
floci-io:mainfrom
drakeo338:claude/6040-fix
Oct 24, 2026
Merged

pgermosen merged 2 commits into
floci-io:mainfrom
drakeo338:claude/6040-fix

Conversation

@drakeo338

Copy link
Copy Markdown
Contributor

Summary

Refs #6040.

Covers the ELBv2 half of the registration list (listener create, modify, certificate add/remove, listener and load balancer delete) plus the 409 to 400 status fix. ELB Classic, CloudFront and the blank SSLCertificateId refusal are not covered.

A listener recorded its ACM certificate ARNs but never told ACM, so DeleteCertificate removed a certificate an HTTPS listener still served and InUseBy stayed empty. CertificateReferenceResolver now adds and releases the listener ARN as an InUseBy consumer, and DeleteCertificate answers ResourceInUseException with 400. Re-importing to an existing ARN used to reset InUseBy, so it now carries consumers over.

Type of change

  • Bug fix (fix:)
  • New feature (feat:)
  • Breaking change (feat!: or fix!:)
  • Docs / chore

AWS Compatibility

Before: delete succeeded while a listener served the certificate. After: 400 ResourceInUseException, as the API reference documents.

Checklist

  • ./mvnw test passes locally (only the affected test classes were run: 43 passed, 0 failed)
  • New or updated integration test added
  • Commit messages follow Conventional Commits
  • I have no more than 2 open, non-draft pull requests and no more than 4 open pull requests in total (drafts included) in this repository (maintainers and dependency bots are exempt).

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Keep ACM certificates in use while ELBv2 listeners serve them

🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Register ELBv2 listeners as ACM certificate consumers and release references when service ends.
• Return HTTP 400 when deleting an in-use certificate, and preserve consumers across re-imports.
• Test listener lifecycle, certificate replacement, IAM exclusion, and ACM deletion behavior.
Diagram

graph TD
  A["Listener changes"] --> B{"Serves ACM?"} -->|yes| C["Certificate resolver"] -->|add or release| D["ACM InUseBy"] -->|checked by| E["DeleteCertificate"] -->|in use| F["HTTP 400 refusal"]
  E -->|released| G["Deletion succeeds"]
  H["Certificate re-import"] -->|preserves consumers| D
Loading
High-Level Assessment

Registering listeners through the shared certificate resolver fits ACM’s existing InUseBy mechanism and keeps IAM certificate handling separate. Scanning listener references on every ACM delete would not populate InUseBy, so it would not address the full behavior.

Files changed (7) +384 / -4

Bug fix (3) +99 / -3
CertificateReferenceResolver.javaRegister and release ACM certificate consumers +42/-0

Register and release ACM certificate consumers

• Adds helpers that update InUseBy for ACM certificate ARNs while skipping IAM and invalid entries. Registration tolerates certificates that disappear between validation and the update.

src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java

AcmService.javaCorrect in-use deletion status and retain consumers on re-import +5/-2

Correct in-use deletion status and retain consumers on re-import

• DeleteCertificate now returns HTTP 400 for ResourceInUseException. Re-importing to an existing ARN carries its InUseBy consumers onto the replacement certificate.

src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java

ElbV2Service.javaTrack ACM consumers throughout the listener lifecycle +52/-1

Track ACM consumers throughout the listener lifecycle

• Listener creation, modification, and certificate additions register served ACM certificates. Certificate removal, protocol changes, listener deletion, and load balancer deletion release references that are no longer served.

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java

Tests (4) +285 / -1
AcmInUseByTest.javaTest deletion status and consumer retention on re-import +17/-0

Test deletion status and consumer retention on re-import

• Asserts that in-use deletion returns HTTP 400 and that replacing certificate material at an existing ARN preserves its consumer list.

src/test/java/io/github/hectorvent/floci/services/acm/AcmInUseByTest.java

CognitoUserPoolDomainIntegrationTest.javaExpect HTTP 400 for an in-use domain certificate +1/-1

Expect HTTP 400 for an in-use domain certificate

• Updates the Cognito integration assertion to match ACM’s corrected ResourceInUseException status.

src/test/java/io/github/hectorvent/floci/services/cognito/CognitoUserPoolDomainIntegrationTest.java

ElbV2AcmInUseByTest.javaExercise listener certificate holds against a real ACM service +174/-0

Exercise listener certificate holds against a real ACM service

• Adds integration tests for InUseBy and deletion refusal while an HTTPS listener serves a certificate. Covers release after listener deletion, certificate replacement, switching to HTTP, and load balancer deletion.

src/test/java/io/github/hectorvent/floci/services/elbv2/ElbV2AcmInUseByTest.java

ElbV2ServiceTest.javaVerify ACM registration and release calls from ELBv2 +93/-0

Verify ACM registration and release calls from ELBv2

• Adds service tests for listener creation, modification, certificate additions and removals, and load balancer deletion. Confirms IAM server certificates do not trigger ACM registration.

src/test/java/io/github/hectorvent/floci/services/elbv2/ElbV2ServiceTest.java

@qodo-code-review

qodo-code-review Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Stack updates that replace a certificate fail 🐞 Bug ≡ Correctness
Description
A listener now registers as an ACM consumer when it is created (markCertificatesInUse).
AcmCfnProvisioner.deletePriorOrUnwind deletes the superseded certificate during the certificate's
own provision step, and CloudFormation runs that step, in dependency order, before the listener that
refers to it is updated to the new ARN. In a stack where an AWS::ElasticLoadBalancingV2::Listener
refers to an AWS::CertificateManager::Certificate, any createOnly change to the certificate
(DomainName, SANs, KeyAlgorithm) now hits ResourceInUseException and the update rolls back. Before
this PR the same update succeeded.
Code

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[R570-574]

            requireListenerCertificates(region, protocol, certificates);
            regionListeners.put(listenerArn, listener);
            listeners.put(region, regionListeners);
+            markCertificatesInUse(listener);
        });
Relevance

●●● Strong

Recent CloudFormation reviews accepted findings where replacement cleanup could delete referenced
resources or break rollback.

PR-#4511
PR-#5656
PR-#5950

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
CloudFormationService topologically sorts resources so dependencies come first, and provisions
them one after another. The ACM provisioner creates the replacement certificate and deletes the old
one right away, while the listener still holds the old ARN because its modifyListener call comes
later. AcmService.deleteCertificate now refuses that delete because of the listener's InUseBy
entry. deletePriorOrUnwind then rethrows, and the update fails.

src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/AcmCfnProvisioner.java[68-105]
src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationService.java[4615-4658]
src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationService.java[2000-2073]
src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/ElbV2CfnProvisioner.java[318-330]
src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[345-356]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When a CloudFormation update replaces an ACM certificate that an ELBv2 listener in the same stack refers to, `AcmCfnProvisioner.deletePriorOrUnwind` deletes the old certificate before the listener has moved to the new ARN. Listeners now register as ACM InUseBy consumers, so that delete fails with ResourceInUseException and the stack update rolls back.

## Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/AcmCfnProvisioner.java[68-105]
- src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationService.java[2000-2073]

## Recommended Fix
Match real CloudFormation: delete the replaced physical resource in a cleanup phase that runs after every resource in the update has been provisioned. Record the prior ARN, for example in a cleanup list on the update context, instead of deleting it inline, and delete it once the dependents have been updated. Add a test that updates a stack with a listener referring to a replaced certificate and asserts the update succeeds and the old certificate is gone.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


2. Deletion can strand a serving listener ✓ Resolved
Description
addInUseBy catches AwsException, while runWhileHeld does not lock ACM certificates against
DeleteCertificate. If deletion runs between a listener's certificate check and registration, the
listener write can succeed with a certificate that ACM has already deleted.
Code

src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[R103-106]

+                acmService.addInUseBy(certificateArn, consumerArn, region);
+            } catch (AwsException e) {
+                LOG.debugv("ACM certificate {0} not marked in use by {1}: {2}", certificateArn,
+                        consumerArn, e.getMessage());
Relevance

●●● Strong

The race directly defeats this PR’s stated certificate-use protection; recent lifecycle concurrency
fixes were accepted.

PR-#5959
PR-#6025

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The listener checks and publishes before calling the new registration method. ACM deletion checks
the current consumer list and deletes independently; the resolver's existing guard covers IAM only,
and its new handler suppresses an ACM lookup failure.

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[567-574]
src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[63-76]
src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[100-108]
src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[342-355]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
A certificate can be deleted after an ELBv2 existence check but before ACM registration; registration failure is swallowed and the listener write succeeds.
### Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[100-108]
- src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[342-355]
- src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[567-574]
### Recommended Fix
Coordinate ACM deletion with the listener's final validation, publication, and registration using the same certificate-scoped guard. Do not silently complete a listener write when registration fails.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Concurrent edits can block certificate deletion ✓ Resolved
Description
modifyListener snapshots the certificates served before an edit and updates ACM after mutating a
shared listener without synchronizing competing edits. If two requests replace certificate A with B
and C, both can release A and register their respective replacements, leaving B marked in use even
when the listener ends on C.
Code

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[R703-709]

+            List<String> servedAfter = servedCertificates(listener);
+            List<String> released = new ArrayList<>(servedBefore);
+            released.removeAll(servedAfter);
+            if (certificateResolver != null) {
+                certificateResolver.removeInUseBy(released, listener.getListenerArn());
+            }
+            markCertificatesInUse(listener);
Relevance

●●● Strong

Recent accepted reviews address concurrent mutable-resource edits and require serialization to
prevent stale associations.

PR-#6025
PR-#4507

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The before snapshot, listener setters, release, and registration form separate operations on the
shared listener. The resolver serializes IAM certificate writes only, so ACM-only replacements can
execute concurrently.

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[686-709]
src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[63-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Competing edits of one listener can interleave their before/after snapshots and leave a certificate registered after the listener stops serving it.
### Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[686-709]
- src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[63-76]
### Recommended Fix
Serialize certificate-related transitions for each listener across the snapshot, mutation, ACM release, and ACM registration. Test two competing certificate replacements.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Load balancer deletion can miss a hold ✓ Resolved
Description
deleteLoadBalancer releases certificates only for listener ARNs already present in
lbToListeners, but createListener registers its certificate before adding the ARN to that index.
If load balancer deletion removes the index during that interval, the new release path never sees
the listener and its ACM hold remains after the load balancer is gone.
Code

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[R322-325]

+                Listener removed = regionListeners.remove(listenerArn);
+                if (removed != null) {
+                    releaseCertificates(removed);
+                }
Relevance

●●● Strong

Recent ELB concurrency findings were accepted where mutable indexes and lifecycle cleanup could
race.

PR-#4507
PR-#6025

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The cascade iterates only the removed index. Creation marks the certificate inside its publication
callback but appends the listener ARN to the index afterward, leaving a window in which deletion
skips the new release.

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[313-330]
src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[567-575]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Load balancer deletion can snapshot its listener index before a concurrently created, ACM-registered listener is added, bypassing certificate release.
### Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[313-330]
- src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[567-575]
### Recommended Fix
Make listener publication, index insertion, and load balancer deletion mutually coordinated, or derive the deletion cascade from a complete listener snapshot. Test deletion overlapping listener creation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Re-import can lose or restore holds ✓ Resolved
Description
importCertificate copies InUseBy from the existing certificate and later replaces the stored
certificate with that snapshot. A listener registration or release between those operations is
respectively lost or resurrected, causing ACM to permit deletion of a served certificate or refuse
deletion of an unused one.
Code

src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[423]

+            inUseBy.addAll(existing.getInUseBy());
Relevance

●●● Strong

Recent accepted reviews require preserving mutable state across replacement and concurrent updates,
matching this lost-hold scenario.

PR-#5656
PR-#5959

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Re-import creates a separate copy of the consumer list before its later store.put. ACM registration
and release mutate the previously stored certificate independently, so an intervening change is
absent from the replacement.

src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[420-456]
src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[364-390]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Re-import snapshots InUseBy before replacing the stored certificate, so concurrent consumer changes can be discarded or restored.
### Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[420-456]
- src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[364-390]
### Recommended Fix
Coordinate re-import and consumer updates per certificate, obtaining the current consumer list at the atomic replacement point. Test registration and release concurrent with re-import.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (2)
6. Concurrent listener writes can lose an in-use hold ✓ Resolved
Description
AcmService.addInUseBy and removeInUseBy mutate a certificate's plain ArrayList and update the
store without a shared lock, while deleteCertificate checks usage and deletes without that lock.
Concurrent listener registrations for the same ACM certificate can lose a consumer and leave the
certificate deletable while a listener still serves it; an add racing deletion can also write the
deleted certificate back to the store.
Code

src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[R100-108]

+    public void addInUseBy(Collection<String> certificateArns, String consumerArn) {
+        forEachAcmCertificate(certificateArns, (certificateArn, region) -> {
+            try {
+                acmService.addInUseBy(certificateArn, consumerArn, region);
+            } catch (AwsException e) {
+                LOG.debugv("ACM certificate {0} not marked in use by {1}: {2}", certificateArn,
+                        consumerArn, e.getMessage());
+            }
+        });
Relevance

●●● Strong

Recent accepted reviews consistently require shared locking for concurrent mutable state and
lifecycle operations.

PR-#5959
PR-#5996
PR-#6025

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Each listener write registers or releases its certificate through CertificateReferenceResolver,
but runWhileHeld takes IAM's lock only for IAM certificates, leaving ACM calls unguarded.
addInUseBy performs a get/contains/add/put sequence on the certificate's ArrayList, while
deleteCertificate checks inUseBy before deleting without a shared lock; these sequences permit
both lost registrations and a later put after deletion.

src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[345-387]
src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[69-75]
src/main/java/io/github/hectorvent/floci/services/acm/model/Certificate.java[184-190]
src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[567-575]
src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[63-76]
src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[364-390]
src/main/java/io/github/hectorvent/floci/services/acm/model/Certificate.java[185-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
ACM consumer registration and release, including their storage updates, are not atomic with `DeleteCertificate`'s in-use check and deletion. Concurrent listener registrations can lose a consumer, and an add racing deletion can write a deleted certificate back to the store.

## Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[345-390]
- src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[570-574]

## Recommended Fix
Guard `addInUseBy`, `removeInUseBy`, and `deleteCertificate` with the same per-certificate or service-wide lock, such as a `ReentrantLock` or synchronized methods, covering both list mutations and storage updates. Re-read the certificate inside `addInUseBy` while holding the lock so it cannot write back a certificate deleted before the lock was acquired. Add a concurrent registration test for listeners sharing one certificate.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. A deleted listener can retain its hold ✓ Resolved
Description
deleteListener releases ACM usage after removing the listener, but it has no guard shared with
createListener registration. If deletion reaches release after creation publishes the listener but
before creation registers its certificate, the release does nothing and the later registration
records the deleted listener.
Code

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[631]

+        releaseCertificates(listener);
Relevance

●●● Strong

Recent accepted concurrency reviews require synchronizing lifecycle transitions to prevent stale
cleanup and ownership state.

PR-#6025
PR-#4507

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Creation puts the listener in the map before the new registration call. Deletion can remove that
listener and invoke the new release in between; ACM release is a no-op when the consumer has not yet
been added.

src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[567-575]
src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[623-633]
src/main/java/io/github/hectorvent/floci/services/acm/AcmService.java[374-390]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
A delete between listener publication and certificate registration can release nothing, then leave ACM holding a deleted listener ARN.
### Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[567-575]
- src/main/java/io/github/hectorvent/floci/services/elbv2/ElbV2Service.java[623-633]
### Recommended Fix
Use a shared per-listener lifecycle guard so creation cannot register after deletion has released its certificates. Cover creation/deletion overlap with a test.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

8. Listeners can lose deletion protection ⊘ Outdated
Description
CertificateReferenceResolver.addInUseBy catches every AwsException and logs it instead of
rethrowing non-benign failures. Any storage or service failure while registering a listener's ACM
consumer is therefore treated as success, leaving the listener serving a certificate that
DeleteCertificate can remove.
Code

src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[R104-107]

+            } catch (AwsException e) {
+                LOG.debugv("ACM certificate {0} not marked in use by {1}: {2}", certificateArn,
+                        consumerArn, e.getMessage());
+            }
Relevance

● Weak

Recent reviews explicitly rejected broad exception swallowing and required propagation of non-benign
failures.

PR-#5976
PR-#5869
PR-#5845

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist requires non-benign failures to propagate and permits suppression only for a verified
idempotency or already-gone case. The changed addInUseBy implementation catches all AwsException
values and continues after logging, so failures other than a missing certificate are swallowed while
ELBv2 listeners are being registered.

Rule 3865600: Do not swallow exceptions broadly; suppress only known benign errors
src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[100-109]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`addInUseBy` suppresses every `AwsException`, although only an already-missing certificate is benign. Other ACM or storage failures can leave a serving listener unregistered in `InUseBy`, allowing certificate deletion to succeed incorrectly.

## Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/core/common/CertificateReferenceResolver.java[100-109]

## Recommended Fix
Catch `AwsException`, inspect its error code, and suppress only `ResourceNotFoundException`; rethrow all other failures so listener creation or certificate updates cannot complete without registering the ACM consumer.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 8 rules
✅ Cross-repo context — repo relationships
  Explored: repo: floci-io/floci-ui (sha: a58b5c5f) — View relationship
Review mode: Auto: 🧠 Deep: Moderate confidence; multiple lifecycle paths create independent edge-case risks.

Grey Divider

Tip of the day
💡 Did you know, you can choose which labels appear on a finding, and whether they show icons or text

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines 570 to 574
requireListenerCertificates(region, protocol, certificates);
regionListeners.put(listenerArn, listener);
listeners.put(region, regionListeners);
markCertificatesInUse(listener);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Stack updates that replace a certificate fail 🐞 Bug ≡ Correctness

A listener now registers as an ACM consumer when it is created (markCertificatesInUse).
AcmCfnProvisioner.deletePriorOrUnwind deletes the superseded certificate during the certificate's
own provision step, and CloudFormation runs that step, in dependency order, before the listener that
refers to it is updated to the new ARN. In a stack where an AWS::ElasticLoadBalancingV2::Listener
refers to an AWS::CertificateManager::Certificate, any createOnly change to the certificate
(DomainName, SANs, KeyAlgorithm) now hits ResourceInUseException and the update rolls back. Before
this PR the same update succeeded.
Agent Prompt
## Issue description
When a CloudFormation update replaces an ACM certificate that an ELBv2 listener in the same stack refers to, `AcmCfnProvisioner.deletePriorOrUnwind` deletes the old certificate before the listener has moved to the new ARN. Listeners now register as ACM InUseBy consumers, so that delete fails with ResourceInUseException and the stack update rolls back.

## Fix Focus Areas
- src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/AcmCfnProvisioner.java[68-105]
- src/main/java/io/github/hectorvent/floci/services/cloudformation/CloudFormationService.java[2000-2073]

## Recommended Fix
Match real CloudFormation: delete the replaced physical resource in a cleanup phase that runs after every resource in the update has been provisioned. Record the prior ARN, for example in a cleanup list on the update context, instead of deleting it inline, and delete it once the dependents have been updated. Add a test that updates a stack with a listener referring to a replaced certificate and asserts the update succeeds and the old certificate is gone.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗

@pgermosen pgermosen added acm AWS Certificate Manager (ACM) bug Something isn't working elbv2 labels Oct 20, 2026

@pgermosen pgermosen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@drakeo338 thanks for this. It conflicts with main now, though, and #5959, which just landed, already covers most of it: DeleteCertificate refuses a certificate a listener serves, reading the listeners' certificates live rather than registering them, and a re-import keeps InUseBy.

What main still lacks is the status. DeleteCertificate answers 409, where the ACM reference gives 400, as RevokeCertificate on main already does. Could you rebase and trim this down to that fix and its tests?

@pgermosen pgermosen added the waiting-author Review posted; waiting on the PR author to respond label Oct 20, 2026
drakeo338 and others added 2 commits October 23, 2026 23:30
ACM answers ResourceInUseException with HTTP 400, not 409, so
DeleteCertificate now does too, as RevokeCertificate already does.

Refs floci-io#6040
The ELBv2 HTTPS data plane test still asserted 409, and the ACM
CloudFormation provisioner test stubbed the refusal with 409.

@pgermosen pgermosen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@drakeo338 thanks for this. Since #5959 already holds the certificate while a listener serves it, I trimmed the branch down to your status fix: DeleteCertificate now answers 400 as the ACM reference gives, and I updated the two tests on main that still expected 409. The ELB Classic and CloudFront parts of #6040 stay open there.

@pgermosen pgermosen removed the waiting-author Review posted; waiting on the PR author to respond label Oct 24, 2026
@pgermosen
pgermosen merged commit 020015f into floci-io:main Oct 24, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

acm AWS Certificate Manager (ACM) bug Something isn't working elbv2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants