Skip to content

Add notifications-sms module to support SMS notifications#256

Draft
jrpbc wants to merge 12 commits intomainfrom
johan/notifications-sms
Draft

Add notifications-sms module to support SMS notifications#256
jrpbc wants to merge 12 commits intomainfrom
johan/notifications-sms

Conversation

@jrpbc
Copy link

@jrpbc jrpbc commented Feb 9, 2026

Ticket

Resolves #976

Changes

What was added, updated, or removed in this PR.

- Implements PinpointSMSVoiceV2 service resources
- Supports SMS configuration sets and opt-out lists
- Includes VPC endpoint for secure connectivity
- Provides IAM policies for SMS sending permissions
- Supports delivery receipt logging to CloudWatch

Context for reviewers

Testing instructions, background context, more in-depth details of the implementation, and anything else you'd like to call out or ask reviewers.

Testing

Provide evidence that the code works as expected. Explain what was done for testing and the results of the test plan. Include screenshots, GIF demos, shell commands or output to help show the changes working as expected. ProTip: you can drag and drop or paste images into this textbox.

Waiting for AWS to approve requested phone number +18665684902.

  • Performed basic validation which includes:
    • Go to /sms-notifications -> use AWS provided receiver phone number +14254147755 (This phone number is for successful messages test); validated Cloudwatch logs for message success (TEXT_SUCCESS status)
    • Go to /sms-notifications -> use AWS provided receiver phone number +14254147167 (This phone number is for failure messages test); validated Cloudwatch logs for message failure (TEXT_BLOCKED status)

Preview environment for app-nextjs

Preview environment for app

Preview environment for app-flask

Preview environment for app-rails

@jrpbc jrpbc requested a review from doshitan February 13, 2026 14:12
Copy link
Contributor

@doshitan doshitan left a comment

Choose a reason for hiding this comment

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

This feels a little complex. Can you update the tech spec with tradeoffs/rationale and future considerations around some of the decisions? It looks like it's been updated a little bit, but a lot of context feels missing.

In particular, the "simulator" stuff doesn't feel very useful. Can we drop it?

@jrpbc
Copy link
Author

jrpbc commented Feb 18, 2026

Updated Tech Specs with: All Considerations; Implementation Options considered.

@jrpbc jrpbc requested a review from doshitan February 19, 2026 02:01
# Allow access to the phone pool created by CloudFormation
data.aws_cloudformation_stack.sms_config_set_outputs.outputs["PhonePoolArn"],
# Allow access to the configuration set created by this module
"arn:aws:sms-voice:*:${data.aws_caller_identity.current.account_id}:configuration-set/${var.name}-config-set"
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it not possible to get this value from the created resource directly?

Copy link
Author

Choose a reason for hiding this comment

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

Due to the use of CloudFormation (via aws_cloudformation_stack) and the inability to fully control the timing of its execution, I was intermittently encountering the following error:
`Error: Provider produced inconsistent final plan

When expanding the plan for module.notifications_sms[0].aws_cloudformation_stack.sms_config_set to include new values learned so far during apply, provider "registry.terraform.io/hashicorp/aws" produced an invalid new value for .outputs: was known, but now unknown.
`
With the use of Terraform "data" here allows us to better control the timing and sequence of execution for the CloudFormation stack, preventing the provider from re-evaluating stack outputs in an inconsistent state and eliminating the intermittent plan inconsistency error.

@jrpbc jrpbc requested a review from doshitan February 24, 2026 05:58
@jrpbc jrpbc force-pushed the johan/notifications-sms branch from b01e304 to 1a68e31 Compare March 12, 2026 19:20
@jrpbc jrpbc force-pushed the johan/notifications-sms branch 5 times, most recently from ae7a043 to e462bbc Compare March 13, 2026 05:34
Copy link
Contributor

@doshitan doshitan left a comment

Choose a reason for hiding this comment

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

A few notes mostly around docs and some cleanup. But I think we are good to create the template PR.

<form method="post">
<div>
<label for="phone_number">Receiver Phone Number (E.164 format: +1234567890):</label><br>
<input type="tel" id="phone_number" name="phone_number"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can make this a select element populated by the rendering endpoint, with only the predefined simulator numbers by default.

(longer term we should probably better lock down the email testing endpoint as well, we don't really want to be a vehicle to spam people)

Copy link
Author

Choose a reason for hiding this comment

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

Restricting this to the simulator numbers here will prevent from testing approved phone numbers. I added an extra message to this form, specifying the phone numbers that can be used for testing if using simulator phone number as originator.

# 1. Creates AWS End User Messaging SMS configuration set
# 2. Sets up IAM permissions for SMS sending
# 3. Configures SMS delivery tracking and opt-out management
enable_sms_notifications = true
Copy link
Contributor

Choose a reason for hiding this comment

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

The default in the template PR should be false.

Copy link
Author

Choose a reason for hiding this comment

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

Applied change

module.notifications_phone_pool_temp[0].phone_pool_id)
) : null

#SMS environment variables for notifications-sms module
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#SMS environment variables for notifications-sms module
# SMS environment variables for notifications-sms module

Copy link
Author

Choose a reason for hiding this comment

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

Applied change

- **SIMULATOR**: Test numbers for development (immediate approval, limited recipients)

### Simulator Phone Numbers
AWS Allows to use **simulator** phone numbers as originator to send text. This simulator phone numbers are only allowed to send SMS text to:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
AWS Allows to use **simulator** phone numbers as originator to send text. This simulator phone numbers are only allowed to send SMS text to:
AWS Allows to use **simulator** phone numbers as originator to send text. This simulator phone numbers are only allowed to send SMS text to:

Copy link
Author

Choose a reason for hiding this comment

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

Applied change

sms_number_type = null # Enter SMS Number Type (e.g: 'SHORT_CODE', 'LONG_CODE', 'TOLL_FREE', 'TEN_DLC', 'SIMULATOR') when available; otherwise leave empty to use simulator number type
}
```
*__Note:__* if an AWS End User SMS Registration ID is not provided, a simulator phone number will be automatically provisioned which will allow applications to send SMS test to:
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we point to the above Simulator Phone Numbers section rather than duplicating?

Copy link
Author

Choose a reason for hiding this comment

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

Applied change

*__Note:__* This assumes your AWS End User Messaging SMS Registration has been approved and configured in `infra/<APP_NAME>/app-config/env-config/dev.tf|staging.tf|prod.tf`
### 1. Verify Destination Numbers (Sandbox Accounts)

In sandbox environments, you can only send SMS to up to 10 verified phone numbers. Steps to register destination phone numbers:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
In sandbox environments, you can only send SMS to up to 10 verified phone numbers. Steps to register destination phone numbers:
In sandbox environments, you can only send SMS to up to 10 verified phone numbers. Steps to register destination phone numbers:

Copy link
Author

Choose a reason for hiding this comment

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

Applied change


## Requirements

1. **AWS End User Messaging SMS Registration**: All provisioned originating phone numbers require a AWS End User Messaging Registration form to be completed in the AWS End User Messaging Service, and approved by the AWS phone carrier. Without an approved Registration, originating phone numbers cannot be provisioned.
Copy link
Contributor

Choose a reason for hiding this comment

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

All provisioned originating phone numbers require a AWS End User Messaging Registration form to be completed

Still makes it sound like every phone number will need a registration form completed at first glance. Can we clarify?

Suggested change
1. **AWS End User Messaging SMS Registration**: All provisioned originating phone numbers require a AWS End User Messaging Registration form to be completed in the AWS End User Messaging Service, and approved by the AWS phone carrier. Without an approved Registration, originating phone numbers cannot be provisioned.
1. **AWS End User Messaging SMS Registration**: All provisioned originating phone numbers require a AWS End User Messaging Registration form to be completed in the AWS End User Messaging Service, and approved by the AWS phone carrier. Without an approved Registration, originating phone numbers cannot be provisioned.

Copy link
Author

Choose a reason for hiding this comment

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

Changed the description of the AWS End User Messaging Registration and its purpose and process

- Break notifications-sms module into two components:
  * notifications-phone-pool: manages phone pools and phone numbers (CloudFormation)
  * notifications-sms: manages configuration sets and IAM policies (CloudFormation + Terraform)
- Add logic to reuse phone pool on temporary environments or create one f none exists
- Add logic to create phone pool for production environment
- Update documentations
@jrpbc jrpbc force-pushed the johan/notifications-sms branch from e462bbc to 890c33d Compare March 17, 2026 04:28
    - Break notifications-sms module into two components:
      * notifications-phone-pool: manages phone pools and phone numbers (CloudFormation)
      * notifications-sms: manages configuration sets and IAM policies (CloudFormation + Terraform)
    - Add logic to reuse phone pool on temporary environments or create one f none exists
    - Add logic to create phone pool for production environment
    - Update documentations
@jrpbc jrpbc force-pushed the johan/notifications-sms branch from b1aa6cc to 599b06a Compare March 17, 2026 05:36
@jrpbc jrpbc requested a review from doshitan March 17, 2026 05:49
}
```
*__Note:__* if an AWS End User SMS Registration ID is not provided, a simulator phone number will be automatically provisioned.
### 3. Deploy SMS notification infrastructure
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
### 3. Deploy SMS notification infrastructure
### 3. Deploy SMS notification infrastructure

## Testing Setup

*__Note:__* This assumes your AWS End User Messaging SMS Registration has been approved and configured in `infra/<APP_NAME>/app-config/env-config/dev.tf|staging.tf|prod.tf`
### 1. Verify Destination Numbers (Sandbox Accounts)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
### 1. Verify Destination Numbers (Sandbox Accounts)
### 1. Verify Destination Numbers (Sandbox Accounts)


## Testing Setup

*__Note:__* This assumes your AWS End User Messaging SMS Registration has been approved and configured in `infra/<APP_NAME>/app-config/env-config/dev.tf|staging.tf|prod.tf`
Copy link
Contributor

Choose a reason for hiding this comment

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

The specific environments will vary by project, can we just point back up to the "Configure SMS settings" section?

- Registration and phone numbers are region-specific
- You must create separate registrations for each AWS region where you plan to send SMS
- Phone numbers provisioned in one region cannot be used in other regions
- Phone numbers must be provisioned by AWS through the End User Messaging service. You cannot bring existing phone numbers from other carriers or services.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Phone numbers must be provisioned by AWS through the End User Messaging service. You cannot bring existing phone numbers from other carriers or services.
- Phone numbers must be provisioned by AWS through the End User Messaging service. You cannot bring existing phone numbers from other carriers or services.


A company or entity must be registered and approved in AWS End User Messaging before using the service for SMS notifications.

**Important Regional Considerations:**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
**Important Regional Considerations:**
**Important Considerations:**

Comment on lines +19 to +20
- You must create separate registrations for each AWS region where you plan to send SMS
- Phone numbers provisioned in one region cannot be used in other regions
Copy link
Contributor

Choose a reason for hiding this comment

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

These feel like sub-points/clarifications of the first one, so indent?

Suggested change
- You must create separate registrations for each AWS region where you plan to send SMS
- Phone numbers provisioned in one region cannot be used in other regions
- You must create separate registrations for each AWS region where you plan to send SMS
- Phone numbers provisioned in one region cannot be used in other regions

A company or entity must be registered and approved in AWS End User Messaging before using the service for SMS notifications.

**Important Regional Considerations:**
- Registration and phone numbers are region-specific
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Registration and phone numbers are region-specific
- Registrations and phone numbers are region-specific

@@ -0,0 +1,48 @@
#!/bin/bash
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#!/bin/bash
#!/usr/bin/env bash

Comment on lines +22 to +48
REGION="${1}"

# Get first available pool without filtering by name
QUERY="Pools[0].{PoolId:PoolId,PoolArn:PoolArn}"

# List SMS phone pools using AWS CLI with specified region
pools=$(aws pinpoint-sms-voice-v2 describe-pools \
--region "$REGION" \
--query "$QUERY" \
--output json 2>/dev/null || echo '{}')

# Check if any pools were found
if [[ "$pools" == "{}" ]] || [[ "$pools" == "null" ]]; then
echo '{"pool_id":"","pool_arn":"","exists":"false"}'
exit 0
fi

# Extract pool details
pool_id=$(echo "$pools" | jq -r '.PoolId // ""')
pool_arn=$(echo "$pools" | jq -r '.PoolArn // ""')

# Validate that we have valid pool data
if [[ "$pool_id" == "" ]] || [[ "$pool_id" == "null" ]]; then
echo '{"pool_id":"","pool_arn":"","exists":"false"}'
else
echo "{\"pool_id\":\"$pool_id\",\"pool_arn\":\"$pool_arn\",\"exists\":\"true\"}"
fi No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

For formatting standards.

Suggested change
REGION="${1}"
# Get first available pool without filtering by name
QUERY="Pools[0].{PoolId:PoolId,PoolArn:PoolArn}"
# List SMS phone pools using AWS CLI with specified region
pools=$(aws pinpoint-sms-voice-v2 describe-pools \
--region "$REGION" \
--query "$QUERY" \
--output json 2>/dev/null || echo '{}')
# Check if any pools were found
if [[ "$pools" == "{}" ]] || [[ "$pools" == "null" ]]; then
echo '{"pool_id":"","pool_arn":"","exists":"false"}'
exit 0
fi
# Extract pool details
pool_id=$(echo "$pools" | jq -r '.PoolId // ""')
pool_arn=$(echo "$pools" | jq -r '.PoolArn // ""')
# Validate that we have valid pool data
if [[ "$pool_id" == "" ]] || [[ "$pool_id" == "null" ]]; then
echo '{"pool_id":"","pool_arn":"","exists":"false"}'
else
echo "{\"pool_id\":\"$pool_id\",\"pool_arn\":\"$pool_arn\",\"exists\":\"true\"}"
fi
# Get region parameter (always provided by Terraform)
region="${1}"
# Get first available pool without filtering by name
query="Pools[0].{PoolId:PoolId,PoolArn:PoolArn}"
# List SMS phone pools using AWS CLI with specified region
pools=$(aws pinpoint-sms-voice-v2 describe-pools \
--region "${region}" \
--query "${query}" \
--output json 2>/dev/null || echo '{}')
# Check if any pools were found
if [[ "${pools}" == "{}" ]] || [[ "${pools}" == "null" ]]; then
echo '{"pool_id":"","pool_arn":"","exists":"false"}'
exit 0
fi
# Extract pool details
pool_id=$(echo "${pools}" | jq -r '.PoolId // ""')
pool_arn=$(echo "${pools}" | jq -r '.PoolArn // ""')
# Validate that we have valid pool data
if [[ -z "${pool_id}" ]] || [[ "${pool_id}" == "null" ]]; then
echo '{"pool_id":"","pool_arn":"","exists":"false"}'
else
echo "{\"pool_id\":\"${pool_id}\",\"pool_arn\":\"${pool_arn}\",\"exists\":\"true\"}"
fi

Copy link
Contributor

@doshitan doshitan Mar 17, 2026

Choose a reason for hiding this comment

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

The script looks to only grab the first phone pool in a region? So the name doesn't feel quite right being plural, maybe more find-an-existing-pool.sh?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notifications: SMS support

2 participants