Repository: ansible-collections/community.aws

Issue:
sns_topic module always deletes and replaces SMS subscriptions
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Explain the problem briefly below -->
The `sns_topic` module always deletes and replaces SMS subscriptions, because the [`_canonicalize_endpoint()` function](https://github.com/ansible-collections/community.aws/blob/52fb23054101acbd78e13a45048ddcf935899586/plugins/modules/sns_topic.py#L353) strips all non-numeric characters. A leading `+` character is added by AWS when a new subscription is created, so the `sns_topic` module never sees its previously-created subscriptions.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
sns_topic

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.15
  config file = None
  configured module search path = ['$HOME/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = $HOME/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible
  executable location = $HOME/Dev/ansible-site/.venv/bin/ansible
  python version = 3.7.9 (default, Feb  1 2021, 15:19:48) [Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
```


##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
1. Run the following playbook
2. Confirm the SNS subscription
3. Run the playbook again

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: localhost
  gather_facts: no
  vars:
    aws_access_key_id: KEYID
    aws_secret_access_key: SECRET
    aws_region: REGION
    # Must include leading plus and country code (+1 for North America)
    mobile_number: YOUR_VALID_MOBILE

  tasks:
    - sns_topic:
      aws_access_key: '{{ aws_access_key_id }}'
      aws_secret_key:  '{{ aws_secret_access_key }}'
      region: '{{ aws_region }}'
      name: Test_Topic
      state: present
      display_name: Test Topic
      subscriptions:
      - protocol: sms
        endpoint: '{{ mobile_number }}'
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
On the first run, the task result is `changed`. On the second run, the task result is `ok`.

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
The task result is `changed` on both runs.

<!--- Paste verbatim command output between quotes -->
<details><summary>Verbatim command output</summary>

```console
$ ansible-playbook -vvvv sns_test.yml
ansible-playbook 2.9.15
  config file = HOMEDIR/Dev/ansible-site/ansible.cfg
  configured module search path = ['HOMEDIR/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible
  executable location = HOMEDIR/Dev/ansible-site/.venv/bin/ansible-playbook
  python version = 3.7.9 (default, Feb  1 2021, 15:19:48) [Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
Using HOMEDIR/Dev/ansible-site/ansible.cfg as config file
setting up inventory plugins
Parsed HOMEDIR/Dev/ansible-site/inventories/zee/hosts.yml inventory source with yaml plugin
Loading callback plugin default of type stdout, v2.0 from HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/plugins/callback/default.py

PLAYBOOK: sns_test.yml ********************************************************************************************************************************
Positional arguments: sns_test.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('HOMEDIR/Dev/ansible-site/inventories/DIRNAME',)
forks: 5
1 plays in sns_test.yml

PLAY [aws_client] *************************************************************************************************************************************
META: ran handlers
Trying secret ScriptVaultSecret(filename='HOMEDIR/bin/get_ansible_vault_password.sh') for vault_id=default
Trying secret ScriptVaultSecret(filename='HOMEDIR/bin/get_ansible_vault_password.sh') for vault_id=default

TASK [sns_topic] **************************************************************************************************************************************
task path: HOMEDIR/Dev/ansible-site/sns_test.yml:6
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: USERNAME
<localhost> EXEC /bin/sh -c 'echo ~USERNAME && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo HOMEDIR/.ansible/tmp `"&& mkdir "` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004 `" && echo ansible-tmp-1614793718.438763-70642-181606092157004="` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004 `" ) && sleep 0'
Using module file HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/modules/cloud/amazon/sns_topic.py
<localhost> PUT HOMEDIR/.ansible/tmp/ansible-local-70581b86ao8uh/tmp1hmf83g4 TO HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/AnsiballZ_sns_topic.py
<localhost> EXEC /bin/sh -c 'chmod u+x HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/ HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'HOMEDIR/Dev/ansible-site/.venv/bin/python3.7m HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/ > /dev/null 2>&1 && sleep 0'
changed: [aws_client] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "aws_access_key": "AKIAJA6ZHTNW5FLJYE3Q",
            "aws_secret_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "debug_botocore_endpoint_logs": false,
            "delivery_policy": null,
            "display_name": "Test Topic",
            "ec2_url": null,
            "name": "Test_Topic",
            "policy": null,
            "profile": null,
            "purge_subscriptions": true,
            "region": "us-east-1",
            "security_token": null,
            "state": "present",
            "subscriptions": [
                {
                    "endpoint": "+1VALIDMOBILE",
                    "protocol": "sms"
                }
            ],
            "validate_certs": true
        }
    },
    "sns_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
    "sns_topic": {
        "attributes_set": [
            "display_name"
        ],
        "check_mode": false,
        "delivery_policy": "{\"http\":{\"defaultHealthyRetryPolicy\":{\"minDelayTarget\":20,\"maxDelayTarget\":20,\"numRetries\":3,\"numMaxDelayRetries\":0,\"numNoDelayRetries\":0,\"numMinDelayRetries\":0,\"backoffFunction\":\"linear\"},\"disableSubscriptionOverrides\":false}}",
        "display_name": "Test Topic",
        "name": "Test_Topic",
        "owner": "AWS_ACCOUNT_ID",
        "policy": "{\"Version\":\"2008-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:GetTopicAttributes\",\"SNS:SetTopicAttributes\",\"SNS:AddPermission\",\"SNS:RemovePermission\",\"SNS:DeleteTopic\",\"SNS:Subscribe\",\"SNS:ListSubscriptionsByTopic\",\"SNS:Publish\",\"SNS:Receive\"],\"Resource\":\"arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":\"AWS_ACCOUNT_ID\"}}}]}",
        "state": "present",
        "subscriptions": [
            {
                "endpoint": "+1VALIDMOBILE",
                "owner": "AWS_ACCOUNT_ID",
                "protocol": "sms",
                "subscription_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic:19322f05-0265-479b-84c8-e5669a24ac0c",
                "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic"
            }
        ],
        "subscriptions_added": [
            [
                "sms",
                "1VALIDMOBILE"
            ]
        ],
        "subscriptions_confirmed": "0",
        "subscriptions_deleted": "0",
        "subscriptions_existing": [],
        "subscriptions_new": [
            {
                "endpoint": "+1VALIDMOBILE",
                "protocol": "sms"
            }
        ],
        "subscriptions_pending": "0",
        "subscriptions_purge": true,
        "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
        "topic_created": false,
        "topic_deleted": false
    }
}
META: ran handlers
META: ran handlers

PLAY RECAP ********************************************************************************************************************************************
aws_client                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


$ # Confirmed subscription in AWS console

$ ansible-playbook -vvvv sns_test.yml
ansible-playbook 2.9.15
  config file = HOMEDIR/Dev/ansible-site/ansible.cfg
  configured module search path = ['HOMEDIR/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible
  executable location = HOMEDIR/Dev/ansible-site/.venv/bin/ansible-playbook
  python version = 3.7.9 (default, Feb  1 2021, 15:19:48) [Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
Using HOMEDIR/Dev/ansible-site/ansible.cfg as config file
setting up inventory plugins
Parsed HOMEDIR/Dev/ansible-site/inventories/zee/hosts.yml inventory source with yaml plugin
Loading callback plugin default of type stdout, v2.0 from HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/plugins/callback/default.py

PLAYBOOK: sns_test.yml ********************************************************************************************************************************
Positional arguments: sns_test.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('HOMEDIR/Dev/ansible-site/inventories/zee',)
forks: 5
1 plays in sns_test.yml

PLAY [aws_client] *************************************************************************************************************************************
META: ran handlers
Trying secret ScriptVaultSecret(filename='HOMEDIR/.homesick/repos/dotfiles/home/bin/get_ansible_vault_password.sh') for vault_id=default
Trying secret ScriptVaultSecret(filename='HOMEDIR/.homesick/repos/dotfiles/home/bin/get_ansible_vault_password.sh') for vault_id=default

TASK [sns_topic] **************************************************************************************************************************************
task path: HOMEDIR/Dev/ansible-site/sns_test.yml:6
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: USERNAME
<localhost> EXEC /bin/sh -c 'echo ~USERNAME && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo HOMEDIR/.ansible/tmp `"&& mkdir "` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857 `" && echo ansible-tmp-1614793769.8479924-71437-25683009179857="` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857 `" ) && sleep 0'
Using module file HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/modules/cloud/amazon/sns_topic.py
<localhost> PUT HOMEDIR/.ansible/tmp/ansible-local-71384z34ghj5q/tmp19on15xu TO HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/AnsiballZ_sns_topic.py
<localhost> EXEC /bin/sh -c 'chmod u+x HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/ HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'HOMEDIR/Dev/ansible-site/.venv/bin/python3.7m HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/ > /dev/null 2>&1 && sleep 0'
changed: [aws_client] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "aws_access_key": "ACCESS_KEY_ID",
            "aws_secret_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "debug_botocore_endpoint_logs": false,
            "delivery_policy": null,
            "display_name": "Test Topic",
            "ec2_url": null,
            "name": "Test_Topic",
            "policy": null,
            "profile": null,
            "purge_subscriptions": true,
            "region": "us-east-1",
            "security_token": null,
            "state": "present",
            "subscriptions": [
                {
                    "endpoint": "+1VALIDMOBILE",
                    "protocol": "sms"
                }
            ],
            "validate_certs": true
        }
    },
    "sns_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
    "sns_topic": {
        "attributes_set": [],
        "check_mode": false,
        "delivery_policy": "{\"http\":{\"defaultHealthyRetryPolicy\":{\"minDelayTarget\":20,\"maxDelayTarget\":20,\"numRetries\":3,\"numMaxDelayRetries\":0,\"numNoDelayRetries\":0,\"numMinDelayRetries\":0,\"backoffFunction\":\"linear\"},\"disableSubscriptionOverrides\":false}}",
        "display_name": "Test Topic",
        "name": "Test_Topic",
        "owner": "AWS_ACCOUNT_ID",
        "policy": "{\"Version\":\"2008-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:GetTopicAttributes\",\"SNS:SetTopicAttributes\",\"SNS:AddPermission\",\"SNS:RemovePermission\",\"SNS:DeleteTopic\",\"SNS:Subscribe\",\"SNS:ListSubscriptionsByTopic\",\"SNS:Publish\",\"SNS:Receive\"],\"Resource\":\"arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":\"AWS_ACCOUNT_ID\"}}}]}",
        "state": "present",
        "subscriptions": [
            {
                "endpoint": "+1VALIDMOBILE",
                "owner": "AWS_ACCOUNT_ID",
                "protocol": "sms",
                "subscription_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic:8842de3c-25db-4f90-bc10-b7a51669ccc3",
                "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic"
            }
        ],
        "subscriptions_added": [
            [
                "sms",
                "1VALIDMOBILE"
            ]
        ],
        "subscriptions_confirmed": "0",
        "subscriptions_deleted": "0",
        "subscriptions_existing": [],
        "subscriptions_new": [
            {
                "endpoint": "+1VALIDMOBILE",
                "protocol": "sms"
            }
        ],
        "subscriptions_pending": "0",
        "subscriptions_purge": true,
        "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
        "topic_created": false,
        "topic_deleted": false
    }
}
META: ran handlers
META: ran handlers

PLAY RECAP ********************************************************************************************************************************************
aws_client                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
</details>
sns_topic module always deletes and replaces SMS subscriptions
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Explain the problem briefly below -->
The `sns_topic` module always deletes and replaces SMS subscriptions, because the [`_canonicalize_endpoint()` function](https://github.com/ansible-collections/community.aws/blob/52fb23054101acbd78e13a45048ddcf935899586/plugins/modules/sns_topic.py#L353) strips all non-numeric characters. A leading `+` character is added by AWS when a new subscription is created, so the `sns_topic` module never sees its previously-created subscriptions.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
sns_topic

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.15
  config file = None
  configured module search path = ['$HOME/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = $HOME/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible
  executable location = $HOME/Dev/ansible-site/.venv/bin/ansible
  python version = 3.7.9 (default, Feb  1 2021, 15:19:48) [Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
```


##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
1. Run the following playbook
2. Confirm the SNS subscription
3. Run the playbook again

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: localhost
  gather_facts: no
  vars:
    aws_access_key_id: KEYID
    aws_secret_access_key: SECRET
    aws_region: REGION
    # Must include leading plus and country code (+1 for North America)
    mobile_number: YOUR_VALID_MOBILE

  tasks:
    - sns_topic:
      aws_access_key: '{{ aws_access_key_id }}'
      aws_secret_key:  '{{ aws_secret_access_key }}'
      region: '{{ aws_region }}'
      name: Test_Topic
      state: present
      display_name: Test Topic
      subscriptions:
      - protocol: sms
        endpoint: '{{ mobile_number }}'
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
On the first run, the task result is `changed`. On the second run, the task result is `ok`.

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
The task result is `changed` on both runs.

<!--- Paste verbatim command output between quotes -->
<details><summary>Verbatim command output</summary>

```console
$ ansible-playbook -vvvv sns_test.yml
ansible-playbook 2.9.15
  config file = HOMEDIR/Dev/ansible-site/ansible.cfg
  configured module search path = ['HOMEDIR/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible
  executable location = HOMEDIR/Dev/ansible-site/.venv/bin/ansible-playbook
  python version = 3.7.9 (default, Feb  1 2021, 15:19:48) [Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
Using HOMEDIR/Dev/ansible-site/ansible.cfg as config file
setting up inventory plugins
Parsed HOMEDIR/Dev/ansible-site/inventories/zee/hosts.yml inventory source with yaml plugin
Loading callback plugin default of type stdout, v2.0 from HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/plugins/callback/default.py

PLAYBOOK: sns_test.yml ********************************************************************************************************************************
Positional arguments: sns_test.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('HOMEDIR/Dev/ansible-site/inventories/DIRNAME',)
forks: 5
1 plays in sns_test.yml

PLAY [aws_client] *************************************************************************************************************************************
META: ran handlers
Trying secret ScriptVaultSecret(filename='HOMEDIR/bin/get_ansible_vault_password.sh') for vault_id=default
Trying secret ScriptVaultSecret(filename='HOMEDIR/bin/get_ansible_vault_password.sh') for vault_id=default

TASK [sns_topic] **************************************************************************************************************************************
task path: HOMEDIR/Dev/ansible-site/sns_test.yml:6
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: USERNAME
<localhost> EXEC /bin/sh -c 'echo ~USERNAME && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo HOMEDIR/.ansible/tmp `"&& mkdir "` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004 `" && echo ansible-tmp-1614793718.438763-70642-181606092157004="` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004 `" ) && sleep 0'
Using module file HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/modules/cloud/amazon/sns_topic.py
<localhost> PUT HOMEDIR/.ansible/tmp/ansible-local-70581b86ao8uh/tmp1hmf83g4 TO HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/AnsiballZ_sns_topic.py
<localhost> EXEC /bin/sh -c 'chmod u+x HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/ HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'HOMEDIR/Dev/ansible-site/.venv/bin/python3.7m HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r HOMEDIR/.ansible/tmp/ansible-tmp-1614793718.438763-70642-181606092157004/ > /dev/null 2>&1 && sleep 0'
changed: [aws_client] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "aws_access_key": "AKIAJA6ZHTNW5FLJYE3Q",
            "aws_secret_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "debug_botocore_endpoint_logs": false,
            "delivery_policy": null,
            "display_name": "Test Topic",
            "ec2_url": null,
            "name": "Test_Topic",
            "policy": null,
            "profile": null,
            "purge_subscriptions": true,
            "region": "us-east-1",
            "security_token": null,
            "state": "present",
            "subscriptions": [
                {
                    "endpoint": "+1VALIDMOBILE",
                    "protocol": "sms"
                }
            ],
            "validate_certs": true
        }
    },
    "sns_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
    "sns_topic": {
        "attributes_set": [
            "display_name"
        ],
        "check_mode": false,
        "delivery_policy": "{\"http\":{\"defaultHealthyRetryPolicy\":{\"minDelayTarget\":20,\"maxDelayTarget\":20,\"numRetries\":3,\"numMaxDelayRetries\":0,\"numNoDelayRetries\":0,\"numMinDelayRetries\":0,\"backoffFunction\":\"linear\"},\"disableSubscriptionOverrides\":false}}",
        "display_name": "Test Topic",
        "name": "Test_Topic",
        "owner": "AWS_ACCOUNT_ID",
        "policy": "{\"Version\":\"2008-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:GetTopicAttributes\",\"SNS:SetTopicAttributes\",\"SNS:AddPermission\",\"SNS:RemovePermission\",\"SNS:DeleteTopic\",\"SNS:Subscribe\",\"SNS:ListSubscriptionsByTopic\",\"SNS:Publish\",\"SNS:Receive\"],\"Resource\":\"arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":\"AWS_ACCOUNT_ID\"}}}]}",
        "state": "present",
        "subscriptions": [
            {
                "endpoint": "+1VALIDMOBILE",
                "owner": "AWS_ACCOUNT_ID",
                "protocol": "sms",
                "subscription_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic:19322f05-0265-479b-84c8-e5669a24ac0c",
                "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic"
            }
        ],
        "subscriptions_added": [
            [
                "sms",
                "1VALIDMOBILE"
            ]
        ],
        "subscriptions_confirmed": "0",
        "subscriptions_deleted": "0",
        "subscriptions_existing": [],
        "subscriptions_new": [
            {
                "endpoint": "+1VALIDMOBILE",
                "protocol": "sms"
            }
        ],
        "subscriptions_pending": "0",
        "subscriptions_purge": true,
        "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
        "topic_created": false,
        "topic_deleted": false
    }
}
META: ran handlers
META: ran handlers

PLAY RECAP ********************************************************************************************************************************************
aws_client                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


$ # Confirmed subscription in AWS console

$ ansible-playbook -vvvv sns_test.yml
ansible-playbook 2.9.15
  config file = HOMEDIR/Dev/ansible-site/ansible.cfg
  configured module search path = ['HOMEDIR/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible
  executable location = HOMEDIR/Dev/ansible-site/.venv/bin/ansible-playbook
  python version = 3.7.9 (default, Feb  1 2021, 15:19:48) [Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
Using HOMEDIR/Dev/ansible-site/ansible.cfg as config file
setting up inventory plugins
Parsed HOMEDIR/Dev/ansible-site/inventories/zee/hosts.yml inventory source with yaml plugin
Loading callback plugin default of type stdout, v2.0 from HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/plugins/callback/default.py

PLAYBOOK: sns_test.yml ********************************************************************************************************************************
Positional arguments: sns_test.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('HOMEDIR/Dev/ansible-site/inventories/zee',)
forks: 5
1 plays in sns_test.yml

PLAY [aws_client] *************************************************************************************************************************************
META: ran handlers
Trying secret ScriptVaultSecret(filename='HOMEDIR/.homesick/repos/dotfiles/home/bin/get_ansible_vault_password.sh') for vault_id=default
Trying secret ScriptVaultSecret(filename='HOMEDIR/.homesick/repos/dotfiles/home/bin/get_ansible_vault_password.sh') for vault_id=default

TASK [sns_topic] **************************************************************************************************************************************
task path: HOMEDIR/Dev/ansible-site/sns_test.yml:6
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: USERNAME
<localhost> EXEC /bin/sh -c 'echo ~USERNAME && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo HOMEDIR/.ansible/tmp `"&& mkdir "` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857 `" && echo ansible-tmp-1614793769.8479924-71437-25683009179857="` echo HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857 `" ) && sleep 0'
Using module file HOMEDIR/Dev/ansible-site/.venv/lib/python3.7/site-packages/ansible/modules/cloud/amazon/sns_topic.py
<localhost> PUT HOMEDIR/.ansible/tmp/ansible-local-71384z34ghj5q/tmp19on15xu TO HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/AnsiballZ_sns_topic.py
<localhost> EXEC /bin/sh -c 'chmod u+x HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/ HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'HOMEDIR/Dev/ansible-site/.venv/bin/python3.7m HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/AnsiballZ_sns_topic.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r HOMEDIR/.ansible/tmp/ansible-tmp-1614793769.8479924-71437-25683009179857/ > /dev/null 2>&1 && sleep 0'
changed: [aws_client] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "aws_access_key": "ACCESS_KEY_ID",
            "aws_secret_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "debug_botocore_endpoint_logs": false,
            "delivery_policy": null,
            "display_name": "Test Topic",
            "ec2_url": null,
            "name": "Test_Topic",
            "policy": null,
            "profile": null,
            "purge_subscriptions": true,
            "region": "us-east-1",
            "security_token": null,
            "state": "present",
            "subscriptions": [
                {
                    "endpoint": "+1VALIDMOBILE",
                    "protocol": "sms"
                }
            ],
            "validate_certs": true
        }
    },
    "sns_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
    "sns_topic": {
        "attributes_set": [],
        "check_mode": false,
        "delivery_policy": "{\"http\":{\"defaultHealthyRetryPolicy\":{\"minDelayTarget\":20,\"maxDelayTarget\":20,\"numRetries\":3,\"numMaxDelayRetries\":0,\"numNoDelayRetries\":0,\"numMinDelayRetries\":0,\"backoffFunction\":\"linear\"},\"disableSubscriptionOverrides\":false}}",
        "display_name": "Test Topic",
        "name": "Test_Topic",
        "owner": "AWS_ACCOUNT_ID",
        "policy": "{\"Version\":\"2008-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:GetTopicAttributes\",\"SNS:SetTopicAttributes\",\"SNS:AddPermission\",\"SNS:RemovePermission\",\"SNS:DeleteTopic\",\"SNS:Subscribe\",\"SNS:ListSubscriptionsByTopic\",\"SNS:Publish\",\"SNS:Receive\"],\"Resource\":\"arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":\"AWS_ACCOUNT_ID\"}}}]}",
        "state": "present",
        "subscriptions": [
            {
                "endpoint": "+1VALIDMOBILE",
                "owner": "AWS_ACCOUNT_ID",
                "protocol": "sms",
                "subscription_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic:8842de3c-25db-4f90-bc10-b7a51669ccc3",
                "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic"
            }
        ],
        "subscriptions_added": [
            [
                "sms",
                "1VALIDMOBILE"
            ]
        ],
        "subscriptions_confirmed": "0",
        "subscriptions_deleted": "0",
        "subscriptions_existing": [],
        "subscriptions_new": [
            {
                "endpoint": "+1VALIDMOBILE",
                "protocol": "sms"
            }
        ],
        "subscriptions_pending": "0",
        "subscriptions_purge": true,
        "topic_arn": "arn:aws:sns:us-east-1:AWS_ACCOUNT_ID:Test_Topic",
        "topic_created": false,
        "topic_deleted": false
    }
}
META: ran handlers
META: ran handlers

PLAY RECAP ********************************************************************************************************************************************
aws_client                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
</details>


Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
@overhacked: Greetings! Thanks for taking the time to open this issue. In order for the community to handle your issue effectively, we need a bit more information.

Here are the items we could not find in your description:
- component name

Please set the description of this issue with this template:
https://raw.githubusercontent.com/ansible/ansible/devel/.github/ISSUE_TEMPLATE/bug_report.md

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: issue_missing_data --->
Files identified in the description:
* [`plugins/modules/sns_topic.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/sns_topic.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jillr @joelthompson @nand0p @s-hertel @tremble @willthames @wimnat
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
@overhacked: Greetings! Thanks for taking the time to open this issue. In order for the community to handle your issue effectively, we need a bit more information.

Here are the items we could not find in your description:
- component name

Please set the description of this issue with this template:
https://raw.githubusercontent.com/ansible/ansible/devel/.github/ISSUE_TEMPLATE/bug_report.md

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: issue_missing_data --->
Files identified in the description:
* [`plugins/modules/sns_topic.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/sns_topic.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jillr @joelthompson @nand0p @s-hertel @tremble @willthames @wimnat
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/sns_topic.py b/plugins/modules/sns_topic.py
--- a/plugins/modules/sns_topic.py
+++ b/plugins/modules/sns_topic.py
@@ -349,8 +349,11 @@ def _set_topic_attrs(self):
         return changed
 
     def _canonicalize_endpoint(self, protocol, endpoint):
+        # AWS SNS expects phone numbers in
+        # and canonicalizes to E.164 format
+        # See <https://docs.aws.amazon.com/sns/latest/dg/sms_publish-to-phone.html>
         if protocol == 'sms':
-            return re.sub('[^0-9]*', '', endpoint)
+            return re.sub('[^0-9+]*', '', endpoint)
         return endpoint
 
     def _set_topic_subs(self):


Test patch: