Skip to content

fix(sns-subscriptions): use regionalized service principal for opt-in region SQS subscriptions - #38339

Merged
mergify[bot] merged 9 commits into
aws:mainfrom
xkjjx:fix/sns-subscriptions-opt-in-region-principal
Aug 13, 2026
Merged

fix(sns-subscriptions): use regionalized service principal for opt-in region SQS subscriptions#38339
mergify[bot] merged 9 commits into
aws:mainfrom
xkjjx:fix/sns-subscriptions-opt-in-region-principal

Conversation

@xkjjx

@xkjjx xkjjx commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Issue # (if applicable)

Closes #32526.

Reason for this change

When subscribing an SQS queue or Lambda function to an SNS topic where one or both regions are opt-in regions, SqsSubscription and LambdaSubscription generate policies with the global sns.amazonaws.com service principal. Per the SNS cross-region delivery documentation, a regionalized service principal is required when opt-in regions are involved, otherwise messages are silently dropped.

Description of changes

Added a snsServicePrincipal() helper in aws-sns-subscriptions/lib/private/util.ts that determines the correct SNS service principal based on the topic and subscriber regions:

  • Default → opt-in region: uses sns.<subscriber-region>.amazonaws.com
  • Opt-in → default region: uses sns.<topic-region>.amazonaws.com
  • Opt-in → opt-in region: uses sns.<subscriber-region>.amazonaws.com
  • Default → default region: uses sns.amazonaws.com (unchanged)
  • Unresolved regions (tokens): falls back to sns.amazonaws.com (safe default)

The helper uses RegionInfo.get(region).isOptInRegion to determine whether regionalization is needed, and ServicePrincipal.fromStaticServicePrincipleName() to construct the regionalized principal directly. We use fromStaticServicePrincipleName rather than new ServicePrincipal('sns.amazonaws.com', { region }) because the standard ServicePrincipal region option relies on token resolution that compares the passed region against the stack's region at synth time — this fails when the policy lives in the same stack as the opt-in region (e.g., the subscriber is in the opt-in region), since stack.region === opts.region causes it to skip regionalization. Using the static method bypasses this entirely and makes the behavior deterministic.

Updated both SqsSubscription.bind() and LambdaSubscription.bind() to use this helper.

Description of how you validated changes

Added unit tests covering all scenarios:

  • Topic in opt-in region, queue in default region → regionalized principal
  • Queue in opt-in region, topic in default region → regionalized principal
  • Same region (both opt-in) → global principal
  • Cross-region default-to-default → global principal
  • Unresolved topic ARN → global principal (fallback)
  • Lambda subscription with topic in opt-in region → regionalized principal

Added integration test (integ.sns-sqs-cross-opt-in-region.ts) with snapshot verifying that a topic in ap-southeast-4 (opt-in) subscribing to a queue in us-east-1 produces the regionalized principal in the queue policy.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

…region subscriptions involving opt-in regions

When subscribing an SQS queue or Lambda function to an SNS topic where
one or both regions are opt-in regions, the generated policies now use
the correct regionalized service principal (e.g.,
sns.<region>.amazonaws.com) instead of always using the global
sns.amazonaws.com principal.

Per the SNS cross-region delivery documentation, the regionalized
principal is required when opt-in regions are involved:
- Default region to opt-in region: sns.<subscriber-region>.amazonaws.com
- Opt-in region to default region: sns.<topic-region>.amazonaws.com
- Opt-in region to opt-in region: sns.<subscriber-region>.amazonaws.com

Without this fix, cross-region SNS-to-SQS/Lambda delivery involving
opt-in regions silently fails with messages being dropped.

When regions cannot be determined at synth time (tokenized ARNs or
env-agnostic stacks), the behavior falls back to the global principal
to maintain backwards compatibility.

Closes aws#32526.
@github-actions github-actions Bot added bug This issue is a bug. effort/medium Medium work item – several days of effort beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK p2 labels Jul 17, 2026
@aws-cdk-automation
aws-cdk-automation requested a review from a team July 17, 2026 22:38

@aws-cdk-automation aws-cdk-automation left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

(This review is outdated)

… subscription

Adds an integration test that verifies the SQS queue policy uses a
regionalized service principal (sns.ap-southeast-4.amazonaws.com) when
the SNS topic is in an opt-in region and the queue is in a default
region.
@aws-cdk-automation
aws-cdk-automation dismissed their stale review July 17, 2026 22:47

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

…oth opt-in cases

Use the same explicit principal construction for both subscriber-opt-in
and topic-opt-in scenarios rather than mixing approaches. This avoids
relying on ServicePrincipalToken's resolve-time region comparison logic
and makes the behavior deterministic at construct time.
…le instead

Use the imported function name directly and rename the local variable
from snsServicePrincipal to principal to avoid the naming conflict.
@aws-cdk-automation aws-cdk-automation added the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Jul 21, 2026
@aws-cdk-automation aws-cdk-automation added the pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes. label Aug 6, 2026

@alvazjor alvazjor 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 principal selection matches the SNS cross-region delivery docs in every case, nice work.
This changes the synthesized template for existing stacks, which normally needs a feature flag. I'm fine shipping it without one here: the only templates that change belong to cross-region opt-in subscriptions, and those were already dropping messages with the global principal. We are not changing any working setup, so a flag would just keep broken stacks broken until users opt in. Non-opt-in setups synthesize exactly as before.

Comment on lines +61 to +68
function resolveTopicRegion(topic: sns.ITopic, subscriber: IResource): string | undefined {
if (topic instanceof sns.Topic) {
if (!Token.isUnresolved(topic.env.region)) {
return topic.env.region;
}
return undefined;
}
return Stack.of(subscriber).splitArn(topic.topicArn, ArnFormat.SLASH_RESOURCE_NAME).region;

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.

Two things on region resolution here:

  1. Guard the imported-topic ARN before splitting it. splitArn runs on topic.topicArn without a Token.isUnresolved(topic.topicArn) check first. Add the check so a token ARN resolves to undefined (global) on purpose rather than by accident, and validate the extracted region:
function resolveTopicRegion(topic: sns.ITopic, subscriber: IResource): string | undefined {
  if (topic instanceof sns.Topic) {
    return Token.isUnresolved(topic.env.region) ? undefined : topic.env.region;
  }
  if (Token.isUnresolved(topic.topicArn)) {
    return undefined;
  }
  const region = Stack.of(subscriber).splitArn(topic.topicArn, ArnFormat.SLASH_RESOURCE_NAME).region;
  return region && !Token.isUnresolved(region) ? region : undefined;
}
  1. Make the fallback limitation explicit in the docs. When either region is unresolved (env-agnostic stacks, or an imported token ARN), the principal stays global and the opt-in fix silently does not apply, so messages can still be dropped. Please spell this out in the snsServicePrincipal JSDoc: the stack needs an explicit env for the fix to take effect, and if that is not possible the user will need to add the regionalized sns.<region>.amazonaws.com principal to the resource policy themselves.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, I implemented the feedback.

this.fn.addPermission(`AllowInvoke:${Names.nodeUniqueId(topic.node)}`, {
sourceArn: topic.topicArn,
principal: new iam.ServicePrincipal('sns.amazonaws.com'),
principal: snsServicePrincipal(topic, this.fn),

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.

Question

There's a conflict between two AWS docs on which Lambda cross-region opt-in combinations are actually supported. The SNS cross-region delivery page lists all three opt-in combinations as supported for Lambda, but the Lambda-specific prerequisites page says the opposite:

"AWS doesn't support cross-region delivery to Lambda from a region that is enabled by default to an opt-in region. Also, cross-region forwarding of Amazon SNS messages from opt-in regions to other opt-in regions is not supported."

If the prereq page is current, then for a Lambda subscriber this helper emits a regionalized principal for combinations AWS does not support for delivery anyway (default to opt-in, and opt-in to opt-in). The principal itself is not wrong, so this is not harmful. Have you tested this end to end in an opt-in region to confirm whether the Lambda prereq page is right or wrong? If you have verified that default to opt-in or opt-in to opt-in delivery actually works for Lambda, that settles it. If it does not work, it may be worth scoping the Lambda path or noting the limitation, rather than implying full support.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, yeah I descoped the Lambda work from this PR. Do you know who we can contact to request clarification on these docs?

I'm not impacted by those changes, but it's still best if we can resolve the discrepancy.

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.

I will follow up with Lambda team, that is not a blocker for getting this merged. Thanks

@mergify
mergify Bot dismissed alvazjor’s stale review August 11, 2026 11:04

Pull request has been modified.

Kevin Johnson added 2 commits August 12, 2026 09:58
Revert the LambdaSubscription change and remove its unit test. AWS
documentation is inconsistent on whether SNS-to-Lambda cross-region
delivery is supported for all opt-in region combinations (the SNS
cross-region delivery page lists them as supported, while the Lambda
prerequisites page states default-to-opt-in and opt-in-to-opt-in are
not), so the Lambda path is descoped pending confirmation. The original
issue (aws#32526) concerns SQS.
Address review feedback on resolveTopicRegion: check Token.isUnresolved
on the imported topic's ARN before calling splitArn, and validate the
extracted region is a concrete non-empty string. Also document in the
snsServicePrincipal JSDoc that the opt-in regionalization only applies
when both regions are known at synth time, and how users can work around
env-agnostic stacks or tokenized ARNs.
@xkjjx xkjjx changed the title fix(sns-subscriptions): use regionalized service principal for opt-in regions fix(sns-subscriptions): use regionalized service principal for opt-in region SQS subscriptions Aug 12, 2026

@alvazjor alvazjor 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.

Thanks for the contribution

@mergify

mergify Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify

mergify Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-08-13 13:13 UTC · Rule: default-squash · triggered by rule automatic merge
  • Checks skipped · PR is already up-to-date
  • Merged2026-08-13 13:18 UTC · at 0c2d50a30d61ab5efddcba7251e8b369c18d743e · squash

This pull request spent 5 minutes 6 seconds in the queue, including 6 seconds running CI.

Required conditions to merge

@aws-cdk-automation aws-cdk-automation removed the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Aug 13, 2026
@mergify

mergify Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify
mergify Bot merged commit 67a7eb1 into aws:main Aug 13, 2026
28 of 29 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Aug 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK bug This issue is a bug. effort/medium Medium work item – several days of effort p2 pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

aws-sns-subscriptions: Adding SqsSubscription on SNS topic does not create correct queue policy for SQS when SNS topic is present in opt-in AWS regions

3 participants