diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7cbabd0..0064109 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
- rev: v1.61.0
+ rev: v1.62.0
hooks:
- id: terraform_fmt
- id: terraform_validate
diff --git a/README.md b/README.md
index 572adac..6d5f29a 100644
--- a/README.md
+++ b/README.md
@@ -28,12 +28,11 @@ module "notify_slack" {
sns_topic_name = "slack-topic"
slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
- slack_channel = "aws-notification"
slack_username = "reporter"
}
```
-## Using with Terraform Cloud Agents
+### Using with Terraform Cloud Agents
[Terraform Cloud Agents](https://www.terraform.io/docs/cloud/workspaces/agent.html) are a paid feature, available as part of the Terraform Cloud for Business upgrade package.
@@ -45,19 +44,15 @@ RUN apt-get -y update && apt-get -y install python3.9 python3-pip
ENTRYPOINT ["/bin/tfc-agent"]
```
-## Use existing SNS topic or create new
+### Local Development and Testing
-If you want to subscribe the AWS Lambda Function created by this module to an existing SNS topic you should specify `create_sns_topic = false` as an argument and specify the name of existing SNS topic name in `sns_topic_name`.
+See the [functions](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/tree/master/functions) for further details.
## Examples
- [notify-slack-simple](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/tree/master/examples/notify-slack-simple) - Creates SNS topic which sends messages to Slack channel.
- [cloudwatch-alerts-to-slack](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/tree/master/examples/cloudwatch-alerts-to-slack) - End to end example which shows how to send AWS Cloudwatch alerts to Slack channel and use KMS to encrypt webhook URL.
-## Local Development and Testing
-
-See the [functions](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/tree/master/functions) for further details.
-
## Requirements
@@ -96,10 +91,11 @@ See the [functions](https://github.com/terraform-aws-modules/terraform-aws-notif
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | The ARN of the KMS Key to use when encrypting log data for Lambda | `string` | `null` | no |
-| [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Specifies the number of days you want to retain log events in log group for Lambda | `number` | `0` | no |
+| [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Specifies the number of days you want to retain log events in log group for Lambda | `number` | `7` | no |
| [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | Additional tags for the Cloudwatch log group | `map(string)` | `{}` | no |
| [create](#input\_create) | Whether to create all resources | `bool` | `true` | no |
| [create\_sns\_topic](#input\_create\_sns\_topic) | Whether to create new SNS topic | `bool` | `true` | no |
+| [environment\_variables](#input\_environment\_variables) | A map that defines environment variables for the Lambda Function | `map(string)` | `{}` | no |
| [iam\_policy\_path](#input\_iam\_policy\_path) | Path of policies to that should be added to IAM role for Lambda Function | `string` | `null` | no |
| [iam\_role\_boundary\_policy\_arn](#input\_iam\_role\_boundary\_policy\_arn) | The ARN of the policy that is used to set the permissions boundary for the role | `string` | `null` | no |
| [iam\_role\_name\_prefix](#input\_iam\_role\_name\_prefix) | A unique role name beginning with the specified prefix | `string` | `"lambda"` | no |
@@ -117,13 +113,10 @@ See the [functions](https://github.com/terraform-aws-modules/terraform-aws-notif
| [lambda\_role](#input\_lambda\_role) | IAM role attached to the Lambda Function. If this is set then a role will not be created for you | `string` | `""` | no |
| [lambda\_runtime](#input\_lambda\_runtime) | Lambda Function runtime | `string` | `"python3.9"` | no |
| [lambda\_timeout](#input\_lambda\_timeout) | The amount of time your Lambda Function has to run in seconds | `number` | `30` | no |
-| [log\_events](#input\_log\_events) | Boolean flag to enabled/disable logging of incoming events | `bool` | `false` | no |
| [recreate\_missing\_package](#input\_recreate\_missing\_package) | Whether to recreate missing Lambda package if it is missing locally or not | `bool` | `true` | no |
| [reserved\_concurrent\_executions](#input\_reserved\_concurrent\_executions) | The amount of reserved concurrent executions for this lambda function. A value of 0 disables lambda from being triggered and -1 removes any concurrency limitations | `number` | `-1` | no |
-| [slack\_channel](#input\_slack\_channel) | The name of the channel in Slack for notifications | `string` | n/a | yes |
-| [slack\_emoji](#input\_slack\_emoji) | A custom emoji that will appear on Slack messages | `string` | `":aws:"` | no |
-| [slack\_username](#input\_slack\_username) | The username that will appear on Slack messages | `string` | n/a | yes |
-| [slack\_webhook\_url](#input\_slack\_webhook\_url) | The URL of Slack webhook | `string` | n/a | yes |
+| [slack\_webhook\_url\_secret\_name](#input\_slack\_webhook\_url\_secret\_name) | Name of Secrets Manager secret that contains the Slack webhook URL. If not provided, `slack_webhook_url_ssm_parameter_name` must be provided | `string` | `""` | no |
+| [slack\_webhook\_url\_ssm\_parameter\_name](#input\_slack\_webhook\_url\_ssm\_parameter\_name) | Name of SSM parameter that contains the Slack webhook URL (must be type `SecureString`). If not provided, `slack_webhook_url_secret_name` must be provided | `string` | `""` | no |
| [sns\_topic\_kms\_key\_id](#input\_sns\_topic\_kms\_key\_id) | ARN of the KMS key used for enabling SSE on the topic | `string` | `""` | no |
| [sns\_topic\_name](#input\_sns\_topic\_name) | The name of the SNS topic to create | `string` | n/a | yes |
| [sns\_topic\_tags](#input\_sns\_topic\_tags) | Additional tags for the SNS topic | `map(string)` | `{}` | no |
diff --git a/examples/cloudwatch-alerts-to-slack/main.tf b/examples/cloudwatch-alerts-to-slack/main.tf
index c05f6c7..0b26829 100644
--- a/examples/cloudwatch-alerts-to-slack/main.tf
+++ b/examples/cloudwatch-alerts-to-slack/main.tf
@@ -16,12 +16,16 @@ module "notify_slack" {
lambda_function_name = "notify_slack_${each.value}"
- slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
- slack_channel = "aws-notification"
- slack_username = "reporter"
+ # Note: this needs to exist in your account already in SSM
+ # and should be set as a SecureString
+ slack_webhook_url_ssm_parameter_name = "/example/notify_slack/webhook_url"
+ environment_variables = {
+ SLACK_USERNAME = "reporter"
+ SLACK_EMOJI = ":wave:"
+ LOG_EVENTS = "True"
+ }
lambda_description = "Lambda function which sends notifications to Slack"
- log_events = true
# VPC
# lambda_function_vpc_subnet_ids = module.vpc.intra_subnets
diff --git a/examples/notify-slack-simple/main.tf b/examples/notify-slack-simple/main.tf
index b695fd9..c78bbb4 100644
--- a/examples/notify-slack-simple/main.tf
+++ b/examples/notify-slack-simple/main.tf
@@ -30,10 +30,14 @@ module "notify_slack" {
sns_topic_name = aws_sns_topic.example.name
create_sns_topic = false
- slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
- slack_channel = "aws-notification"
- slack_username = "reporter"
- log_events = true
+ # Note: this needs to exist in your account already in SSM
+ # and should be set as a SecureString
+ slack_webhook_url_ssm_parameter_name = "/example/notify_slack/webhook_url"
+ environment_variables = {
+ SLACK_USERNAME = "reporter"
+ SLACK_EMOJI = ":wave:"
+ LOG_EVENTS = "True"
+ }
tags = local.tags
}
diff --git a/functions/README.md b/functions/README.md
index 996f202..a6b6c03 100644
--- a/functions/README.md
+++ b/functions/README.md
@@ -83,7 +83,6 @@ To run the unit tests:
```hcl
slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
- slack_channel = "aws-notification"
slack_username = "reporter"
```
diff --git a/functions/notify_slack.py b/functions/notify_slack.py
index 4ed1750..279baf3 100644
--- a/functions/notify_slack.py
+++ b/functions/notify_slack.py
@@ -16,6 +16,7 @@
from urllib.error import HTTPError
from aws_lambda_powertools import Logger # type: ignore
+from aws_lambda_powertools.utilities import parameters
from aws_lambda_powertools.utilities.data_classes import SNSEvent, event_source
from cloudwatch import get_slack_attachment as get_cloudwatch_slack_attachment
@@ -66,12 +67,10 @@ def get_slack_message_payload(message: Union[str, Dict], region: str, subject: O
:returns: Slack message payload
"""
- slack_channel = os.environ["SLACK_CHANNEL"]
slack_username = os.environ["SLACK_USERNAME"]
slack_emoji = os.environ["SLACK_EMOJI"]
payload = {
- "channel": slack_channel,
"username": slack_username,
"icon_emoji": slack_emoji,
}
@@ -111,9 +110,21 @@ def send_slack_notification(payload: Dict[str, Any]) -> str:
:returns: response details from sending notification
"""
- slack_url = os.environ["SLACK_WEBHOOK_URL"]
+ # Pull from SSM parameter
+ webhook_url_ssm_param_name = os.environ.get("SLACK_WEBHOOK_URL_SSM_PARAM_NAME")
+ if webhook_url_ssm_param_name:
+ slack_webhook_url = parameters.get_parameter(webhook_url_ssm_param_name, decrypt=True, max_age=300)
+
+ # Pull from Secrets Manager
+ webhook_url_secret_name = os.environ.get("SLACK_WEBHOOK_URL_SECRET_NAME")
+ if webhook_url_secret_name:
+ slack_webhook_url = parameters.get_secret(webhook_url_secret_name, max_age=300)
+
+ if not slack_webhook_url:
+ raise KeyError("One of `SLACK_WEBHOOK_URL_SSM_PARAM_NAME` or `SLACK_WEBHOOK_URL_SECRET_NAME` must be provided")
+
data = urllib.parse.urlencode({"payload": json.dumps(payload)}).encode("utf-8")
- req = urllib.request.Request(slack_url)
+ req = urllib.request.Request(cast(str, slack_webhook_url))
try:
result = urllib.request.urlopen(req, data)
diff --git a/functions/tests/integration_test.py b/functions/tests/integration_test.py
index 92636f2..75595b0 100644
--- a/functions/tests/integration_test.py
+++ b/functions/tests/integration_test.py
@@ -70,7 +70,6 @@ def publish_event_to_sns_topic():
response = sns_client.publish(
TopicArn=SNS_TOPIC_ARN,
Message=msg,
- Subject=event,
)
pprint(response)
diff --git a/functions/tests/notify_slack_test.py b/functions/tests/notify_slack_test.py
index 18aeca8..cb07c75 100644
--- a/functions/tests/notify_slack_test.py
+++ b/functions/tests/notify_slack_test.py
@@ -21,10 +21,8 @@
@pytest.fixture(autouse=True)
def mock_settings_env_vars(monkeypatch):
- monkeypatch.setenv("SLACK_CHANNEL", "slack_testing_sandbox")
monkeypatch.setenv("SLACK_USERNAME", "notify_slack_test")
monkeypatch.setenv("SLACK_EMOJI", ":aws:")
- monkeypatch.setenv("SLACK_WEBHOOK_URL", "https://hooks.slack.com/services/YOUR/WEBOOK/URL")
def test_sns_get_slack_message_payload_snapshots(snapshot, monkeypatch):
@@ -99,10 +97,8 @@ def test_environment_variables_missing(monkeypatch):
Should pass since environment variables are NOT provided and
will raise a `KeyError`
"""
- monkeypatch.delenv("SLACK_CHANNEL")
monkeypatch.delenv("SLACK_USERNAME")
monkeypatch.delenv("SLACK_EMOJI")
- monkeypatch.delenv("SLACK_WEBHOOK_URL")
with pytest.raises(KeyError):
# will raise before parsing/validation
diff --git a/functions/tests/snapshots/snap_notify_slack_test.py b/functions/tests/snapshots/snap_notify_slack_test.py
index 11d9316..a7e0af8 100644
--- a/functions/tests/snapshots/snap_notify_slack_test.py
+++ b/functions/tests/snapshots/snap_notify_slack_test.py
@@ -6,378 +6,439 @@
snapshots = Snapshot()
-snapshots[
- "test_event_get_slack_message_payload_snapshots event_cloudwatch_alarm.json"
-] = [
+snapshots['test_event_get_slack_message_payload_snapshots event_cloudwatch_alarm.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "color": "danger",
- "fallback": "Alarm Example triggered",
- "fields": [
- {"short": True, "title": "Alarm Name", "value": "`Example`"},
+ 'color': 'danger',
+ 'fallback': 'Alarm Example triggered',
+ 'fields': [
{
- "short": False,
- "title": "Alarm Description",
- "value": "`Example alarm description.`",
+ 'short': True,
+ 'title': 'Alarm Name',
+ 'value': '`Example`'
},
{
- "short": False,
- "title": "Alarm reason",
- "value": "`Threshold Crossed`",
+ 'short': False,
+ 'title': 'Alarm Description',
+ 'value': '`Example alarm description.`'
},
- {"short": True, "title": "Old State", "value": "`OK`"},
- {"short": True, "title": "Current State", "value": "`ALARM`"},
{
- "short": False,
- "title": "Link to Alarm",
- "value": "https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#alarm:alarmFilter=ANY;name=Example",
+ 'short': False,
+ 'title': 'Alarm reason',
+ 'value': '`Threshold Crossed`'
},
+ {
+ 'short': True,
+ 'title': 'Old State',
+ 'value': '`OK`'
+ },
+ {
+ 'short': True,
+ 'title': 'Current State',
+ 'value': '`ALARM`'
+ },
+ {
+ 'short': False,
+ 'title': 'Link to Alarm',
+ 'value': 'https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#alarm:alarmFilter=ANY;name=Example'
+ }
],
- "text": "AWS CloudWatch notification - Example",
+ 'text': 'AWS CloudWatch notification - Example'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots[
- "test_event_get_slack_message_payload_snapshots event_guardduty_finding_high.json"
-] = [
+snapshots['test_event_get_slack_message_payload_snapshots event_guardduty_finding_high.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "color": "danger",
- "fallback": "GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
- "fields": [
+ 'color': 'danger',
+ 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
+ 'fields': [
{
- "short": False,
- "title": "Description",
- "value": "`EC2 instance has an unprotected port which is being probed by a known malicious host.`",
+ 'short': False,
+ 'title': 'Description',
+ 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
},
{
- "short": False,
- "title": "Finding Type",
- "value": "`Recon:EC2 PortProbeUnprotectedPort`",
+ 'short': False,
+ 'title': 'Finding Type',
+ 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
},
{
- "short": True,
- "title": "First Seen",
- "value": "`2020-01-02T01:02:03Z`",
+ 'short': True,
+ 'title': 'First Seen',
+ 'value': '`2020-01-02T01:02:03Z`'
},
{
- "short": True,
- "title": "Last Seen",
- "value": "`2020-01-03T01:02:03Z`",
+ 'short': True,
+ 'title': 'Last Seen',
+ 'value': '`2020-01-03T01:02:03Z`'
},
- {"short": True, "title": "Severity", "value": "`High`"},
- {"short": True, "title": "Count", "value": "`1234`"},
{
- "short": False,
- "title": "Link to Finding",
- "value": "https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2",
+ 'short': True,
+ 'title': 'Severity',
+ 'value': '`High`'
},
+ {
+ 'short': True,
+ 'title': 'Count',
+ 'value': '`1234`'
+ },
+ {
+ 'short': False,
+ 'title': 'Link to Finding',
+ 'value': 'https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2'
+ }
],
- "text": "AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
+ 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots[
- "test_event_get_slack_message_payload_snapshots event_guardduty_finding_low.json"
-] = [
+snapshots['test_event_get_slack_message_payload_snapshots event_guardduty_finding_low.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "color": "#777777",
- "fallback": "GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
- "fields": [
+ 'color': '#777777',
+ 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
+ 'fields': [
+ {
+ 'short': False,
+ 'title': 'Description',
+ 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
+ },
{
- "short": False,
- "title": "Description",
- "value": "`EC2 instance has an unprotected port which is being probed by a known malicious host.`",
+ 'short': False,
+ 'title': 'Finding Type',
+ 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
},
{
- "short": False,
- "title": "Finding Type",
- "value": "`Recon:EC2 PortProbeUnprotectedPort`",
+ 'short': True,
+ 'title': 'First Seen',
+ 'value': '`2020-01-02T01:02:03Z`'
},
{
- "short": True,
- "title": "First Seen",
- "value": "`2020-01-02T01:02:03Z`",
+ 'short': True,
+ 'title': 'Last Seen',
+ 'value': '`2020-01-03T01:02:03Z`'
},
{
- "short": True,
- "title": "Last Seen",
- "value": "`2020-01-03T01:02:03Z`",
+ 'short': True,
+ 'title': 'Severity',
+ 'value': '`Low`'
},
- {"short": True, "title": "Severity", "value": "`Low`"},
- {"short": True, "title": "Count", "value": "`1234`"},
{
- "short": False,
- "title": "Link to Finding",
- "value": "https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2",
+ 'short': True,
+ 'title': 'Count',
+ 'value': '`1234`'
},
+ {
+ 'short': False,
+ 'title': 'Link to Finding',
+ 'value': 'https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2'
+ }
],
- "text": "AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
+ 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots[
- "test_event_get_slack_message_payload_snapshots event_guardduty_finding_medium.json"
-] = [
+snapshots['test_event_get_slack_message_payload_snapshots event_guardduty_finding_medium.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "color": "warning",
- "fallback": "GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
- "fields": [
+ 'color': 'warning',
+ 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
+ 'fields': [
{
- "short": False,
- "title": "Description",
- "value": "`EC2 instance has an unprotected port which is being probed by a known malicious host.`",
+ 'short': False,
+ 'title': 'Description',
+ 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
},
{
- "short": False,
- "title": "Finding Type",
- "value": "`Recon:EC2 PortProbeUnprotectedPort`",
+ 'short': False,
+ 'title': 'Finding Type',
+ 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
},
{
- "short": True,
- "title": "First Seen",
- "value": "`2020-01-02T01:02:03Z`",
+ 'short': True,
+ 'title': 'First Seen',
+ 'value': '`2020-01-02T01:02:03Z`'
},
{
- "short": True,
- "title": "Last Seen",
- "value": "`2020-01-03T01:02:03Z`",
+ 'short': True,
+ 'title': 'Last Seen',
+ 'value': '`2020-01-03T01:02:03Z`'
},
- {"short": True, "title": "Severity", "value": "`Medium`"},
- {"short": True, "title": "Count", "value": "`1234`"},
{
- "short": False,
- "title": "Link to Finding",
- "value": "https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2",
+ 'short': True,
+ 'title': 'Severity',
+ 'value': '`Medium`'
},
+ {
+ 'short': True,
+ 'title': 'Count',
+ 'value': '`1234`'
+ },
+ {
+ 'short': False,
+ 'title': 'Link to Finding',
+ 'value': 'https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2'
+ }
],
- "text": "AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
+ 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots[
- "test_sns_get_slack_message_payload_snapshots message_cloudwatch_alarm.json"
-] = [
+snapshots['test_sns_get_slack_message_payload_snapshots message_cloudwatch_alarm.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "color": "good",
- "fallback": "Alarm DBMigrationRequired triggered",
- "fields": [
+ 'color': 'good',
+ 'fallback': 'Alarm DBMigrationRequired triggered',
+ 'fields': [
+ {
+ 'short': True,
+ 'title': 'Alarm Name',
+ 'value': '`DBMigrationRequired`'
+ },
{
- "short": True,
- "title": "Alarm Name",
- "value": "`DBMigrationRequired`",
+ 'short': False,
+ 'title': 'Alarm Description',
+ 'value': '`App is reporting "A JPA error occurred(Unable to build EntityManagerFactory)"`'
},
{
- "short": False,
- "title": "Alarm Description",
- "value": '`App is reporting "A JPA error occurred(Unable to build EntityManagerFactory)"`',
+ 'short': False,
+ 'title': 'Alarm reason',
+ 'value': '`Threshold Crossed: 1 datapoint [1.0 (12/02/19 15:44:00)] was not less than the threshold (1.0).`'
},
{
- "short": False,
- "title": "Alarm reason",
- "value": "`Threshold Crossed: 1 datapoint [1.0 (12/02/19 15:44:00)] was not less than the threshold (1.0).`",
+ 'short': True,
+ 'title': 'Old State',
+ 'value': '`ALARM`'
},
- {"short": True, "title": "Old State", "value": "`ALARM`"},
- {"short": True, "title": "Current State", "value": "`OK`"},
{
- "short": False,
- "title": "Link to Alarm",
- "value": "https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#alarm:alarmFilter=ANY;name=DBMigrationRequired",
+ 'short': True,
+ 'title': 'Current State',
+ 'value': '`OK`'
},
+ {
+ 'short': False,
+ 'title': 'Link to Alarm',
+ 'value': 'https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#alarm:alarmFilter=ANY;name=DBMigrationRequired'
+ }
],
- "text": "AWS CloudWatch notification - DBMigrationRequired",
+ 'text': 'AWS CloudWatch notification - DBMigrationRequired'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots[
- "test_sns_get_slack_message_payload_snapshots message_dms_notification.json"
-] = [
+snapshots['test_sns_get_slack_message_payload_snapshots message_dms_notification.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "fallback": "A new message",
- "fields": [
+ 'fallback': 'A new message',
+ 'fields': [
{
- "short": True,
- "title": "Event Source",
- "value": "`replication-task`",
+ 'short': True,
+ 'title': 'Event Source',
+ 'value': '`replication-task`'
},
{
- "short": True,
- "title": "Event Time",
- "value": "`2019-02-12 15:45:24.091`",
+ 'short': True,
+ 'title': 'Event Time',
+ 'value': '`2019-02-12 15:45:24.091`'
},
{
- "short": False,
- "title": "Identifier Link",
- "value": "`https://console.aws.amazon.com/dms/home?region=us-east-1#tasks:ids=hello-world`",
+ 'short': False,
+ 'title': 'Identifier Link',
+ 'value': '`https://console.aws.amazon.com/dms/home?region=us-east-1#tasks:ids=hello-world`'
},
- {"short": True, "title": "SourceId", "value": "`hello-world`"},
{
- "short": False,
- "title": "Event ID",
- "value": "`http://docs.aws.amazon.com/dms/latest/userguide/CHAP_Events.html#DMS-EVENT-0079 `",
+ 'short': True,
+ 'title': 'SourceId',
+ 'value': '`hello-world`'
},
{
- "short": False,
- "title": "Event Message",
- "value": "`Replication task has stopped.`",
+ 'short': False,
+ 'title': 'Event ID',
+ 'value': '`http://docs.aws.amazon.com/dms/latest/userguide/CHAP_Events.html#DMS-EVENT-0079 `'
},
+ {
+ 'short': False,
+ 'title': 'Event Message',
+ 'value': '`Replication task has stopped.`'
+ }
+ ],
+ 'mrkdwn_in': [
+ 'value'
],
- "mrkdwn_in": ["value"],
- "text": "AWS notification",
- "title": "DMS Notification Message",
+ 'text': 'AWS notification',
+ 'title': 'DMS Notification Message'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots[
- "test_sns_get_slack_message_payload_snapshots message_glue_notification.json"
-] = [
+snapshots['test_sns_get_slack_message_payload_snapshots message_glue_notification.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "fallback": "A new message",
- "fields": [
- {"short": True, "title": "version", "value": "`0`"},
+ 'fallback': 'A new message',
+ 'fields': [
+ {
+ 'short': True,
+ 'title': 'version',
+ 'value': '`0`'
+ },
+ {
+ 'short': False,
+ 'title': 'id',
+ 'value': '`ad3c3da1-148c-d5da-9a6a-79f1bc9a8a2e`'
+ },
+ {
+ 'short': True,
+ 'title': 'detail-type',
+ 'value': '`Glue Job State Change`'
+ },
+ {
+ 'short': True,
+ 'title': 'source',
+ 'value': '`aws.glue`'
+ },
{
- "short": False,
- "title": "id",
- "value": "`ad3c3da1-148c-d5da-9a6a-79f1bc9a8a2e`",
+ 'short': True,
+ 'title': 'account',
+ 'value': '`000000000000`'
},
{
- "short": True,
- "title": "detail-type",
- "value": "`Glue Job State Change`",
+ 'short': True,
+ 'title': 'time',
+ 'value': '`2021-06-18T12:34:06Z`'
},
- {"short": True, "title": "source", "value": "`aws.glue`"},
- {"short": True, "title": "account", "value": "`000000000000`"},
- {"short": True, "title": "time", "value": "`2021-06-18T12:34:06Z`"},
- {"short": True, "title": "region", "value": "`us-east-2`"},
- {"short": True, "title": "resources", "value": "`[]`"},
{
- "short": False,
- "title": "detail",
- "value": '`{"jobName": "test_job", "severity": "ERROR", "state": "FAILED", "jobRunId": "jr_ca2144d747b45ad412d3c66a1b6934b6b27aa252be9a21a95c54dfaa224a1925", "message": "SystemExit: 1"}`',
+ 'short': True,
+ 'title': 'region',
+ 'value': '`us-east-2`'
},
+ {
+ 'short': True,
+ 'title': 'resources',
+ 'value': '`[]`'
+ },
+ {
+ 'short': False,
+ 'title': 'detail',
+ 'value': '`{"jobName": "test_job", "severity": "ERROR", "state": "FAILED", "jobRunId": "jr_ca2144d747b45ad412d3c66a1b6934b6b27aa252be9a21a95c54dfaa224a1925", "message": "SystemExit: 1"}`'
+ }
+ ],
+ 'mrkdwn_in': [
+ 'value'
],
- "mrkdwn_in": ["value"],
- "text": "AWS notification",
- "title": "Message",
+ 'text': 'AWS notification',
+ 'title': 'Message'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots[
- "test_sns_get_slack_message_payload_snapshots message_guardduty_finding.json"
-] = [
+snapshots['test_sns_get_slack_message_payload_snapshots message_guardduty_finding.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "color": "danger",
- "fallback": "GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
- "fields": [
+ 'color': 'danger',
+ 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
+ 'fields': [
{
- "short": False,
- "title": "Description",
- "value": "`EC2 instance has an unprotected port which is being probed by a known malicious host.`",
+ 'short': False,
+ 'title': 'Description',
+ 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
},
{
- "short": False,
- "title": "Finding Type",
- "value": "`Recon:EC2 PortProbeUnprotectedPort`",
+ 'short': False,
+ 'title': 'Finding Type',
+ 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
},
{
- "short": True,
- "title": "First Seen",
- "value": "`2020-01-02T01:02:03Z`",
+ 'short': True,
+ 'title': 'First Seen',
+ 'value': '`2020-01-02T01:02:03Z`'
},
{
- "short": True,
- "title": "Last Seen",
- "value": "`2020-01-03T01:02:03Z`",
+ 'short': True,
+ 'title': 'Last Seen',
+ 'value': '`2020-01-03T01:02:03Z`'
},
- {"short": True, "title": "Severity", "value": "`High`"},
- {"short": True, "title": "Count", "value": "`1234`"},
{
- "short": False,
- "title": "Link to Finding",
- "value": "https://console.amazonaws-us-gov.com/guardduty/home?region=us-gov-east-1#/findings?search=id%3Dsample-id-2",
+ 'short': True,
+ 'title': 'Severity',
+ 'value': '`High`'
},
+ {
+ 'short': True,
+ 'title': 'Count',
+ 'value': '`1234`'
+ },
+ {
+ 'short': False,
+ 'title': 'Link to Finding',
+ 'value': 'https://console.amazonaws-us-gov.com/guardduty/home?region=us-gov-east-1#/findings?search=id%3Dsample-id-2'
+ }
],
- "text": "AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
+ 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
-snapshots["test_sns_get_slack_message_payload_snapshots message_text_message.json"] = [
+snapshots['test_sns_get_slack_message_payload_snapshots message_text_message.json'] = [
{
- "attachments": [
+ 'attachments': [
{
- "fallback": "A new message",
- "fields": [
+ 'fallback': 'A new message',
+ 'fields': [
{
- "short": False,
- "value": """This
+ 'short': False,
+ 'value': '''This
is
a typical multi-line
message from SNS!
-Have a ~good~ amazing day! :)""",
+Have a ~good~ amazing day! :)'''
}
],
- "mrkdwn_in": ["value"],
- "text": "AWS notification",
- "title": "All Fine",
+ 'mrkdwn_in': [
+ 'value'
+ ],
+ 'text': 'AWS notification',
+ 'title': 'All Fine'
}
],
- "channel": "slack_testing_sandbox",
- "icon_emoji": ":aws:",
- "username": "notify_slack_test",
+ 'icon_emoji': ':aws:',
+ 'username': 'notify_slack_test'
}
]
diff --git a/main.tf b/main.tf
index c9c259e..6b3394d 100644
--- a/main.tf
+++ b/main.tf
@@ -3,13 +3,16 @@ data "aws_partition" "current" {}
data "aws_region" "current" {}
locals {
+ account_id = data.aws_caller_identity.current.account_id
+ partition_id = data.aws_partition.current.id
+ region = data.aws_region.current.name
sns_topic_arn = try(
aws_sns_topic.this[0].arn,
- "arn:${data.aws_partition.current.id}:sns:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${var.sns_topic_name}",
+ "arn:${local.partition_id}:sns:${local.region}:${local.account_id}:${var.sns_topic_name}",
""
)
- aws_lambda_powertools_layer = substr(data.aws_region.current.name, 0, 6) != "us-gov-" ? "arn:aws:lambda:${data.aws_region.current.name}:017000801446:layer:AWSLambdaPowertoolsPython:4" : ""
+ aws_lambda_powertools_layer = contains(["aws-us-gov", "aws-cn"], local.partition_id) ? "" : "arn:${local.partition_id}:lambda:${local.region}:017000801446:layer:AWSLambdaPowertoolsPython:4"
lambda_layers = compact(distinct(concat(var.lambda_layers, [local.aws_lambda_powertools_layer])))
}
@@ -69,6 +72,10 @@ module "lambda" {
timeout = var.lambda_timeout
layers = local.lambda_layers
+ environment_variables = merge(var.environment_variables, {
+ SLACK_WEBHOOK_URL_SSM_PARAM_NAME = var.slack_webhook_url_ssm_parameter_name
+ SLACK_WEBHOOK_URL_SECRET_NAME = var.slack_webhook_url_secret_name
+ })
kms_key_arn = var.kms_key_arn
reserved_concurrent_executions = var.reserved_concurrent_executions
@@ -76,14 +83,6 @@ module "lambda" {
# InvalidParameterValueException: We currently do not support adding policies for $LATEST."
publish = true
- environment_variables = {
- SLACK_WEBHOOK_URL = var.slack_webhook_url
- SLACK_CHANNEL = var.slack_channel
- SLACK_USERNAME = var.slack_username
- SLACK_EMOJI = var.slack_emoji
- LOG_EVENTS = var.log_events ? "True" : "False"
- }
-
create_role = var.lambda_role == ""
lambda_role = var.lambda_role
role_name = "${var.iam_role_name_prefix}-${var.lambda_function_name}"
@@ -93,6 +92,30 @@ module "lambda" {
policy_path = var.iam_policy_path
attach_network_policy = var.lambda_function_vpc_subnet_ids != null
+ attach_policy_json = true
+ policy_json = <<-EOT
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ %{if length(var.slack_webhook_url_ssm_parameter_name) > 1~}
+ {
+ "Sid": "GetSsmWebhookUrl",
+ "Effect": "Allow",
+ "Action": ["ssm:GetParameter"],
+ "Resource": ["arn:${local.partition_id}:ssm:${local.region}:${local.account_id}:parameter${var.slack_webhook_url_ssm_parameter_name}"]
+ }
+ %{endif~}
+ %{if length(var.slack_webhook_url_secret_name) > 1~}
+ {
+ "Sid": "GetSecretWebhookUrl",
+ "Effect": "Allow",
+ "Action": ["secretsmanager:GetSecretValue"],
+ "Resource": ["arn:${local.partition_id}:secretsmanager:${local.region}:${local.account_id}:secret:${var.slack_webhook_url_secret_name}-*"]
+ }
+ %{endif~}
+ ]
+ }
+ EOT
allowed_triggers = {
AllowExecutionFromSNS = {
diff --git a/variables.tf b/variables.tf
index 40afa9b..5fcbfd8 100644
--- a/variables.tf
+++ b/variables.tf
@@ -101,25 +101,22 @@ variable "lambda_function_s3_bucket" {
default = null
}
-variable "slack_webhook_url" {
- description = "The URL of Slack webhook"
- type = string
-}
-
-variable "slack_channel" {
- description = "The name of the channel in Slack for notifications"
+variable "slack_webhook_url_ssm_parameter_name" {
+ description = "Name of SSM parameter that contains the Slack webhook URL (must be type `SecureString`). If not provided, `slack_webhook_url_secret_name` must be provided"
type = string
+ default = ""
}
-variable "slack_username" {
- description = "The username that will appear on Slack messages"
+variable "slack_webhook_url_secret_name" {
+ description = "Name of Secrets Manager secret that contains the Slack webhook URL. If not provided, `slack_webhook_url_ssm_parameter_name` must be provided"
type = string
+ default = ""
}
-variable "slack_emoji" {
- description = "A custom emoji that will appear on Slack messages"
- type = string
- default = ":aws:"
+variable "environment_variables" {
+ description = "A map that defines environment variables for the Lambda Function"
+ type = map(string)
+ default = {}
}
variable "kms_key_arn" {
@@ -134,12 +131,6 @@ variable "recreate_missing_package" {
default = true
}
-variable "log_events" {
- description = "Boolean flag to enabled/disable logging of incoming events"
- type = bool
- default = false
-}
-
variable "reserved_concurrent_executions" {
description = "The amount of reserved concurrent executions for this lambda function. A value of 0 disables lambda from being triggered and -1 removes any concurrency limitations"
type = number
@@ -195,7 +186,7 @@ variable "iam_policy_path" {
variable "cloudwatch_log_group_retention_in_days" {
description = "Specifies the number of days you want to retain log events in log group for Lambda"
type = number
- default = 0
+ default = 7
}
variable "cloudwatch_log_group_kms_key_id" {