├── .editorconfig
├── .github
└── workflows
│ ├── lock.yml
│ ├── pr-title.yml
│ ├── pre-commit.yml
│ ├── release.yml
│ ├── stale-actions.yaml
│ └── unit-test.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .releaserc.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── examples
├── cloudwatch-alerts-to-slack
│ ├── README.md
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
└── notify-slack-simple
│ ├── README.md
│ ├── custom-lambda.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── functions
├── .flake8
├── .pyproject.toml
├── Pipfile
├── README.md
├── events
│ ├── aws_health_event.json
│ ├── cloudwatch_alarm.json
│ ├── guardduty_finding_high.json
│ ├── guardduty_finding_low.json
│ └── guardduty_finding_medium.json
├── integration_test.py
├── messages
│ ├── backup.json
│ ├── cloudwatch_alarm.json
│ ├── dms_notification.json
│ ├── glue_notification.json
│ ├── guardduty_finding.json
│ └── text_message.json
├── mylambda.py
├── notify_slack.py
├── notify_slack_test.py
└── snapshots
│ ├── __init__.py
│ └── snap_notify_slack_test.py
├── iam.tf
├── main.tf
├── outputs.tf
├── variables.tf
└── versions.tf
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 | # Uses editorconfig to maintain consistent coding styles
3 |
4 | # top-most EditorConfig file
5 | root = true
6 |
7 | # Unix-style newlines with a newline ending every file
8 | [*]
9 | charset = utf-8
10 | end_of_line = lf
11 | indent_size = 2
12 | indent_style = space
13 | insert_final_newline = true
14 | max_line_length = 80
15 | trim_trailing_whitespace = true
16 |
17 | [*.{tf,tfvars}]
18 | indent_size = 2
19 | indent_style = space
20 |
21 | [*.md]
22 | max_line_length = 0
23 | trim_trailing_whitespace = false
24 |
25 | [Makefile]
26 | tab_width = 2
27 | indent_style = tab
28 |
29 | [COMMIT_EDITMSG]
30 | max_line_length = 0
31 |
--------------------------------------------------------------------------------
/.github/workflows/lock.yml:
--------------------------------------------------------------------------------
1 | name: 'Lock Threads'
2 |
3 | on:
4 | schedule:
5 | - cron: '50 1 * * *'
6 |
7 | jobs:
8 | lock:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: dessant/lock-threads@v5
12 | with:
13 | github-token: ${{ secrets.GITHUB_TOKEN }}
14 | issue-comment: >
15 | I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.
16 | If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
17 | issue-inactive-days: '30'
18 | pr-comment: >
19 | I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.
20 | If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
21 | pr-inactive-days: '30'
22 |
--------------------------------------------------------------------------------
/.github/workflows/pr-title.yml:
--------------------------------------------------------------------------------
1 | name: 'Validate PR title'
2 |
3 | on:
4 | pull_request_target:
5 | types:
6 | - opened
7 | - edited
8 | - synchronize
9 |
10 | jobs:
11 | main:
12 | name: Validate PR title
13 | runs-on: ubuntu-latest
14 | steps:
15 | # Please look up the latest version from
16 | # https://github.com/amannn/action-semantic-pull-request/releases
17 | - uses: amannn/action-semantic-pull-request@v5.5.3
18 | env:
19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20 | with:
21 | # Configure which types are allowed.
22 | # Default: https://github.com/commitizen/conventional-commit-types
23 | types: |
24 | fix
25 | feat
26 | docs
27 | ci
28 | chore
29 | # Configure that a scope must always be provided.
30 | requireScope: false
31 | # Configure additional validation for the subject based on a regex.
32 | # This example ensures the subject starts with an uppercase character.
33 | subjectPattern: ^[A-Z].+$
34 | # If `subjectPattern` is configured, you can use this property to override
35 | # the default error message that is shown when the pattern doesn't match.
36 | # The variables `subject` and `title` can be used within the message.
37 | subjectPatternError: |
38 | The subject "{subject}" found in the pull request title "{title}"
39 | didn't match the configured pattern. Please ensure that the subject
40 | starts with an uppercase character.
41 | # For work-in-progress PRs you can typically use draft pull requests
42 | # from Github. However, private repositories on the free plan don't have
43 | # this option and therefore this action allows you to opt-in to using the
44 | # special "[WIP]" prefix to indicate this state. This will avoid the
45 | # validation of the PR title and the pull request checks remain pending.
46 | # Note that a second check will be reported if this is enabled.
47 | wip: true
48 | # When using "Squash and merge" on a PR with only one commit, GitHub
49 | # will suggest using that commit message instead of the PR title for the
50 | # merge commit, and it's easy to commit this by mistake. Enable this option
51 | # to also validate the commit message for one commit PRs.
52 | validateSingleCommit: false
53 |
--------------------------------------------------------------------------------
/.github/workflows/pre-commit.yml:
--------------------------------------------------------------------------------
1 | name: Pre-Commit
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | - master
8 |
9 | env:
10 | TERRAFORM_DOCS_VERSION: v0.19.0
11 | TFLINT_VERSION: v0.53.0
12 |
13 | jobs:
14 | collectInputs:
15 | name: Collect workflow inputs
16 | runs-on: ubuntu-latest
17 | outputs:
18 | directories: ${{ steps.dirs.outputs.directories }}
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v4
22 |
23 | - name: Get root directories
24 | id: dirs
25 | uses: clowdhaus/terraform-composite-actions/directories@v1.9.0
26 |
27 | preCommitMinVersions:
28 | name: Min TF pre-commit
29 | needs: collectInputs
30 | runs-on: ubuntu-latest
31 | strategy:
32 | matrix:
33 | directory: ${{ fromJson(needs.collectInputs.outputs.directories) }}
34 | steps:
35 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449
36 | - name: Delete huge unnecessary tools folder
37 | run: |
38 | rm -rf /opt/hostedtoolcache/CodeQL
39 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk
40 | rm -rf /opt/hostedtoolcache/Ruby
41 | rm -rf /opt/hostedtoolcache/go
42 |
43 | - name: Checkout
44 | uses: actions/checkout@v4
45 |
46 | - name: Terraform min/max versions
47 | id: minMax
48 | uses: clowdhaus/terraform-min-max@v1.3.1
49 | with:
50 | directory: ${{ matrix.directory }}
51 |
52 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
53 | # Run only validate pre-commit check on min version supported
54 | if: ${{ matrix.directory != '.' }}
55 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1
56 | with:
57 | terraform-version: ${{ steps.minMax.outputs.minVersion }}
58 | tflint-version: ${{ env.TFLINT_VERSION }}
59 | args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*'
60 |
61 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }}
62 | # Run only validate pre-commit check on min version supported
63 | if: ${{ matrix.directory == '.' }}
64 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1
65 | with:
66 | terraform-version: ${{ steps.minMax.outputs.minVersion }}
67 | tflint-version: ${{ env.TFLINT_VERSION }}
68 | args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)'
69 |
70 | preCommitMaxVersion:
71 | name: Max TF pre-commit
72 | runs-on: ubuntu-latest
73 | needs: collectInputs
74 | steps:
75 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449
76 | - name: Delete huge unnecessary tools folder
77 | run: |
78 | rm -rf /opt/hostedtoolcache/CodeQL
79 | rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk
80 | rm -rf /opt/hostedtoolcache/Ruby
81 | rm -rf /opt/hostedtoolcache/go
82 |
83 | - name: Checkout
84 | uses: actions/checkout@v4
85 | with:
86 | ref: ${{ github.event.pull_request.head.ref }}
87 | repository: ${{github.event.pull_request.head.repo.full_name}}
88 |
89 | - name: Terraform min/max versions
90 | id: minMax
91 | uses: clowdhaus/terraform-min-max@v1.3.1
92 |
93 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }}
94 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.11.1
95 | with:
96 | terraform-version: ${{ steps.minMax.outputs.maxVersion }}
97 | tflint-version: ${{ env.TFLINT_VERSION }}
98 | terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }}
99 | install-hcledit: true
100 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 | - master
9 | paths:
10 | - '**/*.tpl'
11 | - '**/*.py'
12 | - '**/*.tf'
13 | - '.github/workflows/release.yml'
14 |
15 | jobs:
16 | release:
17 | name: Release
18 | runs-on: ubuntu-latest
19 | # Skip running release workflow on forks
20 | if: github.repository_owner == 'terraform-aws-modules'
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | with:
25 | persist-credentials: false
26 | fetch-depth: 0
27 |
28 | - name: Release
29 | uses: cycjimmy/semantic-release-action@v4
30 | with:
31 | semantic_version: 23.0.2
32 | extra_plugins: |
33 | @semantic-release/changelog@6.0.3
34 | @semantic-release/git@10.0.1
35 | conventional-changelog-conventionalcommits@7.0.2
36 | env:
37 | GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/stale-actions.yaml:
--------------------------------------------------------------------------------
1 | name: 'Mark or close stale issues and PRs'
2 | on:
3 | schedule:
4 | - cron: '0 0 * * *'
5 |
6 | jobs:
7 | stale:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/stale@v9
11 | with:
12 | repo-token: ${{ secrets.GITHUB_TOKEN }}
13 | # Staling issues and PR's
14 | days-before-stale: 30
15 | stale-issue-label: stale
16 | stale-pr-label: stale
17 | stale-issue-message: |
18 | This issue has been automatically marked as stale because it has been open 30 days
19 | with no activity. Remove stale label or comment or this issue will be closed in 10 days
20 | stale-pr-message: |
21 | This PR has been automatically marked as stale because it has been open 30 days
22 | with no activity. Remove stale label or comment or this PR will be closed in 10 days
23 | # Not stale if have this labels or part of milestone
24 | exempt-issue-labels: bug,wip,on-hold
25 | exempt-pr-labels: bug,wip,on-hold
26 | exempt-all-milestones: true
27 | # Close issue operations
28 | # Label will be automatically removed if the issues are no longer closed nor locked.
29 | days-before-close: 10
30 | delete-branch: true
31 | close-issue-message: This issue was automatically closed because of stale in 10 days
32 | close-pr-message: This PR was automatically closed because of stale in 10 days
33 |
--------------------------------------------------------------------------------
/.github/workflows/unit-test.yml:
--------------------------------------------------------------------------------
1 | name: Unit Test
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | - master
8 | paths:
9 | - 'functions/**'
10 | - '.github/workflows/unit-test.yml'
11 |
12 | defaults:
13 | run:
14 | working-directory: functions
15 |
16 | jobs:
17 | test:
18 | name: Execute unit tests
19 | runs-on: ubuntu-latest
20 |
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 |
25 | - name: Set up Python 3.11
26 | uses: actions/setup-python@v5
27 | with:
28 | python-version: 3.11
29 |
30 | - name: Install pipenv
31 | run: |
32 | python -m pip install --upgrade pip
33 | python -m pip install pipenv
34 |
35 | - name: Install local deps
36 | run: pipenv install --dev
37 |
38 | - name: Lint check
39 | run: pipenv run lint:ci
40 |
41 | - name: Type check
42 | run: pipenv run typecheck
43 |
44 | - name: Unit tests
45 | run: pipenv run test
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Local .terraform directories
2 | **/.terraform/*
3 |
4 | # Terraform lockfile
5 | .terraform.lock.hcl
6 |
7 | # .tfstate files
8 | *.tfstate
9 | *.tfstate.*
10 |
11 | # Crash log files
12 | crash.log
13 |
14 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as
15 | # password, private keys, and other secrets. These should not be part of version
16 | # control as they are data points which are potentially sensitive and subject
17 | # to change depending on the environment.
18 | *.tfvars
19 |
20 | # Ignore override files as they are usually used to override resources locally and so
21 | # are not checked in
22 | override.tf
23 | override.tf.json
24 | *_override.tf
25 | *_override.tf.json
26 |
27 | # Ignore CLI configuration files
28 | .terraformrc
29 | terraform.rc
30 |
31 | # Locals
32 | .swp
33 | .idea
34 | .idea*
35 | .vscode/*
36 | *.DS_Store
37 | *.zip
38 | .env
39 | .envrc
40 |
41 | # Byte-compiled / optimized / DLL files
42 | __pycache__/
43 | *.py[cod]
44 | *$py.class
45 |
46 | # Distribution / packaging
47 | .Python
48 | env/
49 | build/
50 | develop-eggs/
51 | dist/
52 | downloads/
53 | eggs/
54 | .eggs/
55 | lib/
56 | lib64/
57 | parts/
58 | sdist/
59 | var/
60 | *.egg-info/
61 | .installed.cfg
62 | *.egg
63 |
64 | # Unit test / coverage reports
65 | .pytest*
66 | htmlcov/
67 | .tox/
68 | .coverage
69 | .coverage.*
70 | .cache
71 | nosetests.xml
72 | coverage.xml
73 | *.cover
74 | *.coverage
75 | .hypothesis/
76 | .mypy_cache/
77 |
78 | # Lockfile
79 | Pipfile.lock
80 |
81 | # Lambda directories
82 | builds/
83 | functions/pytest.ini
84 |
85 | # Integration testing file
86 | .int.env
87 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/antonbabenko/pre-commit-terraform
3 | rev: v1.96.1
4 | hooks:
5 | - id: terraform_fmt
6 | - id: terraform_docs
7 | args:
8 | - '--args=--lockfile=false'
9 | - id: terraform_tflint
10 | args:
11 | - '--args=--only=terraform_deprecated_interpolation'
12 | - '--args=--only=terraform_deprecated_index'
13 | - '--args=--only=terraform_unused_declarations'
14 | - '--args=--only=terraform_comment_syntax'
15 | - '--args=--only=terraform_documented_outputs'
16 | - '--args=--only=terraform_documented_variables'
17 | - '--args=--only=terraform_typed_variables'
18 | - '--args=--only=terraform_module_pinned_source'
19 | - '--args=--only=terraform_naming_convention'
20 | - '--args=--only=terraform_required_version'
21 | - '--args=--only=terraform_required_providers'
22 | - '--args=--only=terraform_standard_module_structure'
23 | - '--args=--only=terraform_workspace_remote'
24 | - id: terraform_validate
25 | - repo: https://github.com/pre-commit/pre-commit-hooks
26 | rev: v5.0.0
27 | hooks:
28 | - id: check-merge-conflict
29 | - id: end-of-file-fixer
30 | - id: trailing-whitespace
31 |
--------------------------------------------------------------------------------
/.releaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "branches": [
3 | "main",
4 | "master"
5 | ],
6 | "ci": false,
7 | "plugins": [
8 | [
9 | "@semantic-release/commit-analyzer",
10 | {
11 | "preset": "conventionalcommits"
12 | }
13 | ],
14 | [
15 | "@semantic-release/release-notes-generator",
16 | {
17 | "preset": "conventionalcommits"
18 | }
19 | ],
20 | [
21 | "@semantic-release/github",
22 | {
23 | "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:",
24 | "labels": false,
25 | "releasedLabels": false
26 | }
27 | ],
28 | [
29 | "@semantic-release/changelog",
30 | {
31 | "changelogFile": "CHANGELOG.md",
32 | "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file."
33 | }
34 | ],
35 | [
36 | "@semantic-release/git",
37 | {
38 | "assets": [
39 | "CHANGELOG.md"
40 | ],
41 | "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
42 | }
43 | ]
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | ## [6.6.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.5.2...v6.6.0) (2025-03-12)
6 |
7 |
8 | ### Features
9 |
10 | * Support for Security Hub ([#242](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/242)) ([3aef5ba](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/3aef5badc119568f8986eb4fea91b76aef99c4df))
11 |
12 | ## [6.5.2](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.5.1...v6.5.2) (2025-02-25)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * Modify logging for security inspector issue ([#249](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/249)) ([b3cd40f](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/b3cd40f6e90fa628e0481b5093d60a302e58f155))
18 |
19 | ## [6.5.1](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.5.0...v6.5.1) (2025-01-06)
20 |
21 |
22 | ### Bug Fixes
23 |
24 | * Reverts endpoint variable change from e95cde8 ([#240](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/240)) ([81e4b81](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/81e4b816f39c34fab8a5d78e8b854c43aed7dbd2))
25 | * Update CI workflow versions to latest ([#239](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/239)) ([50b951a](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/50b951a333ebab734c5afba984f0584fd1b43dd7))
26 |
27 | ## [6.5.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.4.1...v6.5.0) (2024-09-03)
28 |
29 |
30 | ### Features
31 |
32 | * Add variable to allow disabling the package timestamp trigger ([#233](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/233)) ([b3016e2](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/b3016e2f059ffa4ce12745acd4c131c3744faf44))
33 |
34 | ## [6.4.1](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.4.0...v6.4.1) (2024-09-03)
35 |
36 |
37 | ### Bug Fixes
38 |
39 | * Update `aws_sns_topic_subscription` endpoint to use qualified arn ([#231](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/231)) ([e95cde8](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/e95cde8acdaf221e74595daa2238b75f0682ea06)), closes [#230](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/230)
40 |
41 | ## [6.4.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.3.0...v6.4.0) (2024-04-24)
42 |
43 |
44 | ### Features
45 |
46 | * Improved AWS backup notification readability ([#222](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/222)) ([27d1c46](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/27d1c464f80708740d8d155e7cb11367b41bab6c))
47 |
48 | ## [6.3.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.2.0...v6.3.0) (2024-04-22)
49 |
50 |
51 | ### Features
52 |
53 | * Update Python lambda runtime from `3.8` to `3.11` ([#225](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/225)) ([b4ef4e4](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/b4ef4e45e9f3dafb774ccf62d9473b338de68f3f))
54 |
55 | ## [6.2.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.1.2...v6.2.0) (2024-04-22)
56 |
57 |
58 | ### Features
59 |
60 | * Added architecture variable ([#224](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/224)) ([1ae3ab7](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/1ae3ab7e084341e7a1fd3acccb15d2971020fce5))
61 |
62 | ## [6.1.2](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.1.1...v6.1.2) (2024-03-26)
63 |
64 |
65 | ### Bug Fixes
66 |
67 | * Correct assume role permissions for SNS service to assume IAM role ([#220](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/220)) ([dae0c0f](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/dae0c0f49d41cf98c5e31af7912ed406eea81c83))
68 |
69 | ## [6.1.1](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.1.0...v6.1.1) (2024-03-06)
70 |
71 |
72 | ### Bug Fixes
73 |
74 | * Update CI workflow versions to remove deprecated runtime warnings ([#218](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/218)) ([44edd19](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/44edd191bac2812951faea9562c685fbeeeefee8))
75 |
76 | ## [6.1.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v6.0.0...v6.1.0) (2023-12-11)
77 |
78 |
79 | ### Features
80 |
81 | * Expose `hash_extra` variable from Lambda module ([#211](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/211)) ([ee30bb3](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/ee30bb3f5c0da7c118c8c09fbb195a7c0e607f73))
82 |
83 | ## [6.0.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.6.0...v6.0.0) (2023-05-18)
84 |
85 |
86 | ### ⚠ BREAKING CHANGES
87 |
88 | * Added variable to filter body of message on SNS level and bumped min Terraform version to 1.0 (#196)
89 |
90 | ### Features
91 |
92 | * Added variable to filter body of message on SNS level and bumped min Terraform version to 1.0 ([#196](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/196)) ([ab660f7](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/ab660f7e86aec7a4f134036460b98eeb92c6c4c8))
93 |
94 | ## [5.6.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.5.0...v5.6.0) (2023-01-26)
95 |
96 |
97 | ### Features
98 |
99 | * Add account ID to GuardDuty event notification ([#187](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/187)) ([e3452b4](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/e3452b424a0e5ccdaf69935094e9fb7785fb315b))
100 |
101 | ## [5.5.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.4.1...v5.5.0) (2022-12-07)
102 |
103 |
104 | ### Features
105 |
106 | * Add SNS topic delivery status IAM role ([#178](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/178)) ([2863105](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/2863105fd6e07ea0f16500928242968c4b4873cb))
107 |
108 | ### [5.4.1](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.4.0...v5.4.1) (2022-11-07)
109 |
110 |
111 | ### Bug Fixes
112 |
113 | * Update CI configuration files to use latest version ([#181](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/181)) ([6ca4605](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/6ca4605be57c4dd17c3daf87867b6e98136b0914))
114 |
115 | ## [5.4.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.3.0...v5.4.0) (2022-10-21)
116 |
117 |
118 | ### Features
119 |
120 | * Add lambda dead-letter queue variables ([#180](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/180)) ([010aa89](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/010aa89147f91eeb95e7d842d90eccc3beac6265))
121 |
122 | ## [5.3.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.2.0...v5.3.0) (2022-06-17)
123 |
124 |
125 | ### Features
126 |
127 | * Added support for AWS Health events ([#170](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/170)) ([3d38bfa](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/3d38bfa524541a6497ebcc77051ef78253cc4a3e))
128 |
129 | ## [5.2.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.1.0...v5.2.0) (2022-06-14)
130 |
131 |
132 | ### Features
133 |
134 | * Added support for custom lambda function ([#172](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/172)) ([4a9d0b0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/4a9d0b02a9421ff52b392145aaa2aea0c7317a51))
135 |
136 | ## [5.1.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v5.0.0...v5.1.0) (2022-05-04)
137 |
138 |
139 | ### Features
140 |
141 | * Added ephemeral_storage_size variable ([#167](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/167)) ([c82299a](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/c82299aaec22f301c62f220d8446675647168ff4))
142 |
143 | ## [5.0.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.24.0...v5.0.0) (2022-03-31)
144 |
145 |
146 | ### ⚠ BREAKING CHANGES
147 |
148 | * - Update lambda module to 3.1.0 to support AWS provider version 4.8+
149 |
150 | ### Features
151 |
152 | * Update lambda module to 3.1.0 to support AWS provider version 4.8+ ([#166](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/166)) ([ea822a3](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/ea822a3dbd4ac24803385cabae43538c9a3b10f3))
153 |
154 | # [4.24.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.23.0...v4.24.0) (2021-12-14)
155 |
156 |
157 | ### Features
158 |
159 | * Revert incorrectly removed output this_slack_topic_arn ([#159](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/159)) ([24ec027](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/24ec027e1b6fe708eb4a6d7788a64d9452ecbfe0))
160 |
161 | # [4.23.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.22.0...v4.23.0) (2021-12-11)
162 |
163 |
164 | ### Features
165 |
166 | * add support for recreating package locally if not missing/not present ([#158](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/158)) ([912e11d](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/912e11dc38416650ac07e0762a5e469a030032bd))
167 |
168 | # [4.22.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.21.0...v4.22.0) (2021-12-10)
169 |
170 |
171 | ### Features
172 |
173 | * add lint and unit test workflow checks for pull requests ([#152](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/152)) ([d2675ec](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/d2675eca91f3ca4bc8b7a18912ae84b36b7922f1))
174 |
175 | # [4.21.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.20.0...v4.21.0) (2021-12-10)
176 |
177 |
178 | ### Features
179 |
180 | * Added policy path variable to lambda module IAM role policy ([#153](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/153)) ([b3179a9](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/b3179a9f025943da60daf39d3ce73e88ed57e9ba))
181 |
182 | # [4.20.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.19.0...v4.20.0) (2021-12-09)
183 |
184 |
185 | ### Features
186 |
187 | * Update lambda module and bump Terraform/AWS provider versions ([#151](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/151)) ([0a1fae8](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/0a1fae86060248353eea2ededad26f43774e500e))
188 |
189 | # [4.19.0](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.18.0...v4.19.0) (2021-12-09)
190 |
191 |
192 | ### Bug Fixes
193 |
194 | * update CI/CD process to enable auto-release workflow ([#149](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/149)) ([f7dd0a3](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/f7dd0a35d1c140a3465564740abe3579c9e12b48))
195 |
196 |
197 | ### Features
198 |
199 | * Added path input variable for lambda module IAM role ([#150](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/150)) ([fc0c120](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/commit/fc0c120bd379be65177745637ad402b46334cda5))
200 |
201 |
202 | ## [v4.18.0] - 2021-10-01
203 |
204 | - feat: Added support for GuardDuty Findings format ([#143](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/143))
205 |
206 |
207 |
208 | ## [v4.17.0] - 2021-06-28
209 |
210 | - feat: Allow custom attachement ([#123](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/123))
211 |
212 |
213 |
214 | ## [v4.16.0] - 2021-06-28
215 |
216 | - feat: add support for nested messages ([#142](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/142))
217 |
218 |
219 |
220 | ## [v4.15.0] - 2021-05-25
221 |
222 | - chore: Remove check boxes that don't render properly in module doc ([#140](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/140))
223 | - chore: update CI/CD to use stable `terraform-docs` release artifact and discoverable Apache2.0 license ([#138](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/138))
224 |
225 |
226 |
227 | ## [v4.14.0] - 2021-04-19
228 |
229 | - feat: Updated code to support Terraform 0.15 ([#136](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/136))
230 | - chore: update documentation and pin `terraform_docs` version to avoid future changes ([#134](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/134))
231 |
232 |
233 |
234 | ## [v4.13.0] - 2021-03-12
235 |
236 | - fix: use the current aws partition ([#133](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/133))
237 | - chore: align ci-cd static checks to use individual minimum Terraform versions ([#131](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/131))
238 | - fix: Remove data resource for sns topic to avoid race condition ([#81](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/81))
239 | - chore: add ci-cd workflow for pre-commit checks ([#128](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/128))
240 | - feat: Improve slack message formatting for generic messages ([#124](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/124))
241 | - chore: update documentation based on latest `terraform-docs` which includes module and resource sections ([#126](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/126))
242 | - feat: add support for GovCloud URLs ([#114](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/114))
243 | - feat: Allow Lambda function to be VPC bound ([#122](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/122))
244 | - feat: Updated version of terraform-aws-lambda module to 1.28.0 ([#119](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/119))
245 | - feat: Updated version of Terraform AWS Lambda module to support multiple copies ([#117](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/117))
246 | - fix: Typo on subscription_filter_policy variable ([#113](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/113))
247 | - docs: Added a note about using with Terraform Cloud Agents ([#108](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/108))
248 | - feat: allow reuse of existing lambda_role ([#85](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/85))
249 | - fix: Fix regression with aws_cloudwatch_log_group resource after upgrade of AWS provider 3.0 ([#106](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/106))
250 | - docs: Updated version of module to use for Terraform 0.12 users
251 | - fix: Updated version requirements to be Terraform 0.13 only ([#101](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/101))
252 | - feat: Updated Lambda module to work with Terraform 0.13 ([#99](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/99))
253 | - fix: Bump version of lambda module that supports Terraform 13 and AWS Provider 3.x ([#96](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/96))
254 |
255 |
256 |
257 | ## [v3.6.0] - 2021-03-01
258 |
259 | - fix: Fix regression with aws_cloudwatch_log_group resource after upgrade of AWS provider 3.0 ([#106](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/106)) ([#130](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/130))
260 | - feat: Updated version of Lambda module to allow AWS provider version 3
261 |
262 |
263 |
264 | ## [v4.12.0] - 2021-03-01
265 |
266 | - fix: Remove data resource for sns topic to avoid race condition ([#81](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/81))
267 | - chore: add ci-cd workflow for pre-commit checks ([#128](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/128))
268 |
269 |
270 |
271 | ## [v4.11.0] - 2021-02-21
272 |
273 | - feat: Improve slack message formatting for generic messages ([#124](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/124))
274 |
275 |
276 |
277 | ## [v4.10.0] - 2021-02-20
278 |
279 | - chore: update documentation based on latest `terraform-docs` which includes module and resource sections ([#126](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/126))
280 |
281 |
282 |
283 | ## [v4.9.0] - 2020-12-18
284 |
285 | - feat: add support for GovCloud URLs ([#114](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/114))
286 |
287 |
288 |
289 | ## [v4.8.0] - 2020-12-18
290 |
291 | - feat: Allow Lambda function to be VPC bound ([#122](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/122))
292 |
293 |
294 |
295 | ## [v4.7.0] - 2020-11-17
296 |
297 | - feat: Updated version of terraform-aws-lambda module to 1.28.0 ([#119](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/119))
298 |
299 |
300 |
301 | ## [v4.6.0] - 2020-11-05
302 |
303 | - feat: Updated version of Terraform AWS Lambda module to support multiple copies ([#117](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/117))
304 |
305 |
306 |
307 | ## [v4.5.0] - 2020-10-15
308 |
309 | - fix: Typo on subscription_filter_policy variable ([#113](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/113))
310 |
311 |
312 |
313 | ## [v4.4.0] - 2020-10-08
314 |
315 | - docs: Added a note about using with Terraform Cloud Agents ([#108](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/108))
316 |
317 |
318 |
319 | ## [v4.3.0] - 2020-09-07
320 |
321 | - feat: allow reuse of existing lambda_role ([#85](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/85))
322 |
323 |
324 |
325 | ## [v4.2.0] - 2020-09-07
326 |
327 | - fix: Fix regression with aws_cloudwatch_log_group resource after upgrade of AWS provider 3.0 ([#106](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/106))
328 | - docs: Updated version of module to use for Terraform 0.12 users
329 | - fix: Updated version requirements to be Terraform 0.13 only ([#101](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/101))
330 | - feat: Updated Lambda module to work with Terraform 0.13 ([#99](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/99))
331 | - fix: Bump version of lambda module that supports Terraform 13 and AWS Provider 3.x ([#96](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/96))
332 |
333 |
334 |
335 | ## [v3.5.0] - 2020-08-14
336 |
337 | - feat: Updated version of Lambda module to allow AWS provider version 3
338 |
339 |
340 |
341 | ## [v4.1.0] - 2020-08-14
342 |
343 | - fix: Updated version requirements to be Terraform 0.13 only ([#101](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/101))
344 |
345 |
346 |
347 | ## [v4.0.0] - 2020-08-13
348 |
349 | - feat: Updated Lambda module to work with Terraform 0.13 ([#99](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/99))
350 | - fix: Bump version of lambda module that supports Terraform 13 and AWS Provider 3.x ([#96](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/96))
351 |
352 |
353 |
354 | ## [v3.4.0] - 2020-08-13
355 |
356 | - feat: update required version of aws provider to allow 3.0 ([#95](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/95))
357 |
358 |
359 |
360 | ## [v3.3.0] - 2020-06-19
361 |
362 | - Updated README
363 | - feat: Add support for SSE on the topic ([#82](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/82))
364 |
365 |
366 |
367 | ## [v3.2.0] - 2020-06-11
368 |
369 | - feat: Updated version of Lambda module
370 |
371 |
372 |
373 | ## [v3.1.0] - 2020-06-10
374 |
375 | - fix: Upgraded version of Lambda module (fix [#84](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/84))
376 |
377 |
378 |
379 | ## [v3.0.0] - 2020-06-07
380 |
381 | - Updated pre-commit hooks
382 | - feat: Rewrote module to handle Lambda resources properly with terraform-aws-lambda module ([#83](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/83))
383 | - chore: Removed stale.yml from .github folder
384 | - fix: Stale bot should process only issues for now ([#79](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/79))
385 |
386 |
387 |
388 | ## [v2.15.0] - 2020-04-13
389 |
390 | - docs: Updated required versions in README
391 | - Add support fro IAM role boundary policy ([#61](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/61))
392 |
393 |
394 |
395 | ## [v2.14.0] - 2020-04-13
396 |
397 | - docs: Updated README after [#62](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/62)
398 | - feat: Add support for custom name prefixes for IAM role and policy ([#62](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/62))
399 | - fix: Move stale.yml to .github ([#78](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/78))
400 | - feat: Add Stale Bot Config ([#77](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/77))
401 |
402 |
403 |
404 | ## [v2.13.0] - 2020-03-19
405 |
406 |
407 |
408 |
409 | ## [v2.12.0] - 2020-03-19
410 |
411 |
412 |
413 |
414 | ## [v2.11.0] - 2020-03-19
415 |
416 | - Add subsription filter policy support ([#74](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/74))
417 |
418 |
419 |
420 | ## [v2.10.0] - 2020-01-21
421 |
422 | - Updated pre-commit-terraform with terraform-docs 0.8.0 support ([#65](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/65))
423 |
424 |
425 |
426 | ## [v2.9.0] - 2020-01-16
427 |
428 | - Fix empty tuple error ([#64](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/64))
429 |
430 |
431 |
432 | ## [v2.8.0] - 2019-12-21
433 |
434 | - Added lambda description and improved Lambda IAM policy for KMS ([#56](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/56))
435 |
436 |
437 |
438 | ## [v2.7.0] - 2019-12-20
439 |
440 | - Added support for multiline messages ([#55](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/55))
441 |
442 |
443 |
444 | ## [v2.6.0] - 2019-12-20
445 |
446 | - Added pytest and logging (based on [#27](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/27)) ([#54](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/54))
447 |
448 |
449 |
450 | ## [v2.5.0] - 2019-12-20
451 |
452 | - Updated formatting
453 | - use 0.12 syntax for depends_on ([#51](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/51))
454 |
455 |
456 |
457 | ## [v2.4.0] - 2019-12-10
458 |
459 | - Use urllib.parse.quote for the alarm name ([#35](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/35))
460 | - Updated simple example a bit
461 | - Create AWS Cloudwatch log group and give explicit access to it ([#40](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/40))
462 | - Added support for reserved_concurrent_executions
463 | - Updated docs, python3.7
464 | - Add support for resource tagging ([#45](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/45))
465 | - Upgraded module to support Terraform 0.12 ([#36](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/36))
466 |
467 |
468 |
469 | ## [v1.14.0] - 2019-11-08
470 |
471 | - Updated pre-commit hooks
472 | - Reduce scope of IAM Policy for CloudWatch Logs ([#44](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/44))
473 |
474 |
475 |
476 | ## [v2.3.0] - 2019-11-08
477 |
478 | - Create AWS Cloudwatch log group and give explicit access to it ([#40](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/40))
479 |
480 |
481 |
482 | ## [v2.2.0] - 2019-11-08
483 |
484 | - Added support for reserved_concurrent_executions
485 |
486 |
487 |
488 | ## [v2.1.0] - 2019-11-08
489 |
490 | - Updated docs, python3.7
491 | - Add support for resource tagging ([#45](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/45))
492 |
493 |
494 |
495 | ## [v2.0.0] - 2019-06-12
496 |
497 | - Upgraded module to support Terraform 0.12 ([#36](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/36))
498 |
499 |
500 |
501 | ## [v1.13.0] - 2019-02-22
502 |
503 | - need to convert from json string to dict when extracting message from event ([#30](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/30))
504 |
505 |
506 |
507 | ## [v1.12.0] - 2019-02-21
508 |
509 | - Pass the subject ot default_notification ([#29](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/29))
510 |
511 |
512 |
513 | ## [v1.11.0] - 2018-12-28
514 |
515 | - No longer parsing the SNS event as incoming JSON ([#23](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/23))
516 |
517 |
518 |
519 | ## [v1.10.0] - 2018-08-20
520 |
521 | - Fixed bug which causes apply failure when create = false ([#19](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/19))
522 |
523 |
524 |
525 | ## [v1.9.0] - 2018-06-21
526 |
527 | - Allow computed KMS key value (fixed [#10](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/10)) ([#18](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/18))
528 |
529 |
530 |
531 | ## [v1.8.0] - 2018-06-20
532 |
533 | - include short alarm name in slack notification text ([#14](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/14))
534 |
535 |
536 |
537 | ## [v1.7.0] - 2018-06-20
538 |
539 | - Renamed enable to create, minor fixes after [#15](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/15)
540 | - Add flag to enable/disable creation of resources ([#15](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/15))
541 |
542 |
543 |
544 | ## [v1.6.0] - 2018-06-19
545 |
546 | - Fixed formatting
547 | - Fix Lambda path in shared state ([#17](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/17))
548 | - Fixed spelling a bit
549 | - Cirumvent TF's path.module limitation for lambda filenames
550 | - Cirumvent TF's path.module limitation for lambda filenames
551 | - Cirumvent TF's path.module limitation for lambda filenames
552 |
553 |
554 |
555 | ## [v1.5.0] - 2018-06-06
556 |
557 | - Fixed formatting (ran 'pre-commit run -a')
558 | - Add in slack emoji support ([#11](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/11))
559 | - Update comments in examples/ about aws_kms_ciphertext ([#12](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/12))
560 |
561 |
562 |
563 | ## [v1.4.0] - 2018-06-05
564 |
565 | - Ignore `last_modified` timestamp deciding whether to do an update ([#9](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/9))
566 | - Updated formatting in examples
567 |
568 |
569 |
570 | ## [v1.3.0] - 2018-05-29
571 |
572 | - Ignore changes in filename (fixed [#6](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/6))
573 |
574 |
575 |
576 | ## [v1.2.0] - 2018-05-16
577 |
578 | - Added pre-commit hook to autogenerate terraform-docs ([#7](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/7))
579 |
580 |
581 |
582 | ## [v1.1.0] - 2018-03-22
583 |
584 | - Feature/lambda function name variable ([#5](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/5))
585 |
586 |
587 |
588 | ## [v1.0.1] - 2018-02-22
589 |
590 | - Fix mismatch in alarm state labels and values ([#4](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/4))
591 |
592 |
593 |
594 | ## [v1.0.0] - 2018-02-15
595 |
596 | - Added better code, examples, docs ([#2](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/issues/2))
597 |
598 |
599 |
600 | ## v0.0.1 - 2018-02-12
601 |
602 | - Add encrypted webhook URL example
603 | - Fix decryption of webhook URL
604 | - Update readme
605 | - Add basic example
606 | - Make KMS optional
607 | - Add README description
608 | - Add preliminary cloudwatch event handling lambda
609 | - Initial commit
610 |
611 |
612 | [Unreleased]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.18.0...HEAD
613 | [v4.18.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.17.0...v4.18.0
614 | [v4.17.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.16.0...v4.17.0
615 | [v4.16.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.15.0...v4.16.0
616 | [v4.15.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.14.0...v4.15.0
617 | [v4.14.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.13.0...v4.14.0
618 | [v4.13.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v3.6.0...v4.13.0
619 | [v3.6.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.12.0...v3.6.0
620 | [v4.12.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.11.0...v4.12.0
621 | [v4.11.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.10.0...v4.11.0
622 | [v4.10.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.9.0...v4.10.0
623 | [v4.9.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.8.0...v4.9.0
624 | [v4.8.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.7.0...v4.8.0
625 | [v4.7.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.6.0...v4.7.0
626 | [v4.6.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.5.0...v4.6.0
627 | [v4.5.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.4.0...v4.5.0
628 | [v4.4.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.3.0...v4.4.0
629 | [v4.3.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.2.0...v4.3.0
630 | [v4.2.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v3.5.0...v4.2.0
631 | [v3.5.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.1.0...v3.5.0
632 | [v4.1.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v4.0.0...v4.1.0
633 | [v4.0.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v3.4.0...v4.0.0
634 | [v3.4.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v3.3.0...v3.4.0
635 | [v3.3.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v3.2.0...v3.3.0
636 | [v3.2.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v3.1.0...v3.2.0
637 | [v3.1.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v3.0.0...v3.1.0
638 | [v3.0.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.15.0...v3.0.0
639 | [v2.15.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.14.0...v2.15.0
640 | [v2.14.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.13.0...v2.14.0
641 | [v2.13.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.12.0...v2.13.0
642 | [v2.12.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.11.0...v2.12.0
643 | [v2.11.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.10.0...v2.11.0
644 | [v2.10.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.9.0...v2.10.0
645 | [v2.9.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.8.0...v2.9.0
646 | [v2.8.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.7.0...v2.8.0
647 | [v2.7.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.6.0...v2.7.0
648 | [v2.6.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.5.0...v2.6.0
649 | [v2.5.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.4.0...v2.5.0
650 | [v2.4.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.14.0...v2.4.0
651 | [v1.14.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.3.0...v1.14.0
652 | [v2.3.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.2.0...v2.3.0
653 | [v2.2.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.1.0...v2.2.0
654 | [v2.1.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v2.0.0...v2.1.0
655 | [v2.0.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.13.0...v2.0.0
656 | [v1.13.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.12.0...v1.13.0
657 | [v1.12.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.11.0...v1.12.0
658 | [v1.11.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.10.0...v1.11.0
659 | [v1.10.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.9.0...v1.10.0
660 | [v1.9.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.8.0...v1.9.0
661 | [v1.8.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.7.0...v1.8.0
662 | [v1.7.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.6.0...v1.7.0
663 | [v1.6.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.5.0...v1.6.0
664 | [v1.5.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.4.0...v1.5.0
665 | [v1.4.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.3.0...v1.4.0
666 | [v1.3.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.2.0...v1.3.0
667 | [v1.2.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.1.0...v1.2.0
668 | [v1.1.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.0.1...v1.1.0
669 | [v1.0.1]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v1.0.0...v1.0.1
670 | [v1.0.0]: https://github.com/terraform-aws-modules/terraform-aws-notify-slack/compare/v0.0.1...v1.0.0
671 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AWS Notify Slack Terraform module
2 |
3 | This module creates an SNS topic (or uses an existing one) and an AWS Lambda function that sends notifications to Slack using the [incoming webhooks API](https://api.slack.com/incoming-webhooks).
4 |
5 | Start by setting up an [incoming webhook integration](https://my.slack.com/services/new/incoming-webhook/) in your Slack workspace.
6 |
7 | Doing serverless with Terraform? Check out [serverless.tf framework](https://serverless.tf), which aims to simplify all operations when working with the serverless in Terraform.
8 |
9 | ## Supported Features
10 |
11 | - AWS Lambda runtime Python 3.11
12 | - Create new SNS topic or use existing one
13 | - Support plaintext and encrypted version of Slack webhook URL
14 | - Most of Slack message options are customizable
15 | - Custom Lambda function
16 | - Various event types are supported, even generic messages:
17 | - AWS CloudWatch Alarms
18 | - AWS CloudWatch LogMetrics Alarms
19 | - AWS GuardDuty Findings
20 |
21 | ## Usage
22 |
23 | ```hcl
24 | module "notify_slack" {
25 | source = "terraform-aws-modules/notify-slack/aws"
26 | version = "~> 5.0"
27 |
28 | sns_topic_name = "slack-topic"
29 |
30 | slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
31 | slack_channel = "aws-notification"
32 | slack_username = "reporter"
33 | }
34 | ```
35 |
36 | ## Using with Terraform Cloud Agents
37 |
38 | [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.
39 |
40 | This module requires Python 3.11. You can customize [tfc-agent](https://hub.docker.com/r/hashicorp/tfc-agent) to include Python using this sample `Dockerfile`:
41 |
42 | ```Dockerfile
43 | FROM hashicorp/tfc-agent:latest
44 | RUN apt-get -y update && apt-get -y install python3.11 python3-pip
45 | ENTRYPOINT ["/bin/tfc-agent"]
46 | ```
47 |
48 | ## Use existing SNS topic or create new
49 |
50 | 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`.
51 |
52 | ## Examples
53 |
54 | - [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.
55 | - [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.
56 |
57 | ## Local Development and Testing
58 |
59 | See the [functions](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/tree/master/functions) for further details.
60 |
61 |
62 | ## Requirements
63 |
64 | | Name | Version |
65 | |------|---------|
66 | | [terraform](#requirement\_terraform) | >= 1.0 |
67 | | [aws](#requirement\_aws) | >= 4.8 |
68 |
69 | ## Providers
70 |
71 | | Name | Version |
72 | |------|---------|
73 | | [aws](#provider\_aws) | >= 4.8 |
74 |
75 | ## Modules
76 |
77 | | Name | Source | Version |
78 | |------|--------|---------|
79 | | [lambda](#module\_lambda) | terraform-aws-modules/lambda/aws | 6.8.0 |
80 |
81 | ## Resources
82 |
83 | | Name | Type |
84 | |------|------|
85 | | [aws_cloudwatch_log_group.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
86 | | [aws_iam_role.sns_feedback_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
87 | | [aws_sns_topic.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
88 | | [aws_sns_topic_subscription.sns_notify_slack](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
89 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
90 | | [aws_iam_policy_document.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
91 | | [aws_iam_policy_document.sns_feedback](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
92 | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
93 | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
94 |
95 | ## Inputs
96 |
97 | | Name | Description | Type | Default | Required |
98 | |------|-------------|------|---------|:--------:|
99 | | [architectures](#input\_architectures) | Instruction set architecture for your Lambda function. Valid values are ["x86\_64"] and ["arm64"]. | `list(string)` | `null` | no |
100 | | [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 |
101 | | [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 |
102 | | [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | Additional tags for the Cloudwatch log group | `map(string)` | `{}` | no |
103 | | [create](#input\_create) | Whether to create all resources | `bool` | `true` | no |
104 | | [create\_sns\_topic](#input\_create\_sns\_topic) | Whether to create new SNS topic | `bool` | `true` | no |
105 | | [enable\_sns\_topic\_delivery\_status\_logs](#input\_enable\_sns\_topic\_delivery\_status\_logs) | Whether to enable SNS topic delivery status logs | `bool` | `false` | no |
106 | | [hash\_extra](#input\_hash\_extra) | The string to add into hashing function. Useful when building same source path for different functions. | `string` | `""` | no |
107 | | [iam\_policy\_path](#input\_iam\_policy\_path) | Path of policies to that should be added to IAM role for Lambda Function | `string` | `null` | no |
108 | | [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 |
109 | | [iam\_role\_name\_prefix](#input\_iam\_role\_name\_prefix) | A unique role name beginning with the specified prefix | `string` | `"lambda"` | no |
110 | | [iam\_role\_path](#input\_iam\_role\_path) | Path of IAM role to use for Lambda Function | `string` | `null` | no |
111 | | [iam\_role\_tags](#input\_iam\_role\_tags) | Additional tags for the IAM role | `map(string)` | `{}` | no |
112 | | [kms\_key\_arn](#input\_kms\_key\_arn) | ARN of the KMS key used for decrypting slack webhook url | `string` | `""` | no |
113 | | [lambda\_attach\_dead\_letter\_policy](#input\_lambda\_attach\_dead\_letter\_policy) | Controls whether SNS/SQS dead letter notification policy should be added to IAM role for Lambda Function | `bool` | `false` | no |
114 | | [lambda\_dead\_letter\_target\_arn](#input\_lambda\_dead\_letter\_target\_arn) | The ARN of an SNS topic or SQS queue to notify when an invocation fails. | `string` | `null` | no |
115 | | [lambda\_description](#input\_lambda\_description) | The description of the Lambda function | `string` | `null` | no |
116 | | [lambda\_function\_ephemeral\_storage\_size](#input\_lambda\_function\_ephemeral\_storage\_size) | Amount of ephemeral storage (/tmp) in MB your Lambda Function can use at runtime. Valid value between 512 MB to 10,240 MB (10 GB). | `number` | `512` | no |
117 | | [lambda\_function\_name](#input\_lambda\_function\_name) | The name of the Lambda function to create | `string` | `"notify_slack"` | no |
118 | | [lambda\_function\_s3\_bucket](#input\_lambda\_function\_s3\_bucket) | S3 bucket to store artifacts | `string` | `null` | no |
119 | | [lambda\_function\_store\_on\_s3](#input\_lambda\_function\_store\_on\_s3) | Whether to store produced artifacts on S3 or locally. | `bool` | `false` | no |
120 | | [lambda\_function\_tags](#input\_lambda\_function\_tags) | Additional tags for the Lambda function | `map(string)` | `{}` | no |
121 | | [lambda\_function\_vpc\_security\_group\_ids](#input\_lambda\_function\_vpc\_security\_group\_ids) | List of security group ids when Lambda Function should run in the VPC. | `list(string)` | `null` | no |
122 | | [lambda\_function\_vpc\_subnet\_ids](#input\_lambda\_function\_vpc\_subnet\_ids) | List of subnet ids when Lambda Function should run in the VPC. Usually private or intra subnets. | `list(string)` | `null` | no |
123 | | [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 |
124 | | [lambda\_source\_path](#input\_lambda\_source\_path) | The source path of the custom Lambda function | `string` | `null` | no |
125 | | [log\_events](#input\_log\_events) | Boolean flag to enabled/disable logging of incoming events | `bool` | `false` | no |
126 | | [putin\_khuylo](#input\_putin\_khuylo) | Do you agree that Putin doesn't respect Ukrainian sovereignty and territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo! | `bool` | `true` | no |
127 | | [recreate\_missing\_package](#input\_recreate\_missing\_package) | Whether to recreate missing Lambda package if it is missing locally or not | `bool` | `true` | no |
128 | | [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 |
129 | | [slack\_channel](#input\_slack\_channel) | The name of the channel in Slack for notifications | `string` | n/a | yes |
130 | | [slack\_emoji](#input\_slack\_emoji) | A custom emoji that will appear on Slack messages | `string` | `":aws:"` | no |
131 | | [slack\_username](#input\_slack\_username) | The username that will appear on Slack messages | `string` | n/a | yes |
132 | | [slack\_webhook\_url](#input\_slack\_webhook\_url) | The URL of Slack webhook | `string` | n/a | yes |
133 | | [sns\_topic\_feedback\_role\_description](#input\_sns\_topic\_feedback\_role\_description) | Description of IAM role to use for SNS topic delivery status logging | `string` | `null` | no |
134 | | [sns\_topic\_feedback\_role\_force\_detach\_policies](#input\_sns\_topic\_feedback\_role\_force\_detach\_policies) | Specifies to force detaching any policies the IAM role has before destroying it. | `bool` | `true` | no |
135 | | [sns\_topic\_feedback\_role\_name](#input\_sns\_topic\_feedback\_role\_name) | Name of the IAM role to use for SNS topic delivery status logging | `string` | `null` | no |
136 | | [sns\_topic\_feedback\_role\_path](#input\_sns\_topic\_feedback\_role\_path) | Path of IAM role to use for SNS topic delivery status logging | `string` | `null` | no |
137 | | [sns\_topic\_feedback\_role\_permissions\_boundary](#input\_sns\_topic\_feedback\_role\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the IAM role used by SNS topic delivery status logging | `string` | `null` | no |
138 | | [sns\_topic\_feedback\_role\_tags](#input\_sns\_topic\_feedback\_role\_tags) | A map of tags to assign to IAM the SNS topic feedback role | `map(string)` | `{}` | no |
139 | | [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 |
140 | | [sns\_topic\_lambda\_feedback\_role\_arn](#input\_sns\_topic\_lambda\_feedback\_role\_arn) | IAM role for SNS topic delivery status logs. If this is set then a role will not be created for you. | `string` | `""` | no |
141 | | [sns\_topic\_lambda\_feedback\_sample\_rate](#input\_sns\_topic\_lambda\_feedback\_sample\_rate) | The percentage of successful deliveries to log | `number` | `100` | no |
142 | | [sns\_topic\_name](#input\_sns\_topic\_name) | The name of the SNS topic to create | `string` | n/a | yes |
143 | | [sns\_topic\_tags](#input\_sns\_topic\_tags) | Additional tags for the SNS topic | `map(string)` | `{}` | no |
144 | | [subscription\_filter\_policy](#input\_subscription\_filter\_policy) | (Optional) A valid filter policy that will be used in the subscription to filter messages seen by the target resource. | `string` | `null` | no |
145 | | [subscription\_filter\_policy\_scope](#input\_subscription\_filter\_policy\_scope) | (Optional) A valid filter policy scope MessageAttributes\|MessageBody | `string` | `null` | no |
146 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
147 | | [trigger\_on\_package\_timestamp](#input\_trigger\_on\_package\_timestamp) | (Optional) Whether or not to ignore the file timestamp when deciding to create the archive | `bool` | `false` | no |
148 |
149 | ## Outputs
150 |
151 | | Name | Description |
152 | |------|-------------|
153 | | [lambda\_cloudwatch\_log\_group\_arn](#output\_lambda\_cloudwatch\_log\_group\_arn) | The Amazon Resource Name (ARN) specifying the log group |
154 | | [lambda\_iam\_role\_arn](#output\_lambda\_iam\_role\_arn) | The ARN of the IAM role used by Lambda function |
155 | | [lambda\_iam\_role\_name](#output\_lambda\_iam\_role\_name) | The name of the IAM role used by Lambda function |
156 | | [notify\_slack\_lambda\_function\_arn](#output\_notify\_slack\_lambda\_function\_arn) | The ARN of the Lambda function |
157 | | [notify\_slack\_lambda\_function\_invoke\_arn](#output\_notify\_slack\_lambda\_function\_invoke\_arn) | The ARN to be used for invoking Lambda function from API Gateway |
158 | | [notify\_slack\_lambda\_function\_last\_modified](#output\_notify\_slack\_lambda\_function\_last\_modified) | The date Lambda function was last modified |
159 | | [notify\_slack\_lambda\_function\_name](#output\_notify\_slack\_lambda\_function\_name) | The name of the Lambda function |
160 | | [notify\_slack\_lambda\_function\_version](#output\_notify\_slack\_lambda\_function\_version) | Latest published version of your Lambda function |
161 | | [slack\_topic\_arn](#output\_slack\_topic\_arn) | The ARN of the SNS topic from which messages will be sent to Slack |
162 | | [sns\_topic\_feedback\_role\_arn](#output\_sns\_topic\_feedback\_role\_arn) | The Amazon Resource Name (ARN) of the IAM role used for SNS delivery status logging |
163 | | [this\_slack\_topic\_arn](#output\_this\_slack\_topic\_arn) | The ARN of the SNS topic from which messages will be sent to Slack (backward compatibility for version 4.x) |
164 |
165 |
166 | ## Authors
167 |
168 | Module is maintained by [Anton Babenko](https://github.com/antonbabenko) with help from [these awesome contributors](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/graphs/contributors).
169 |
170 | ## License
171 |
172 | Apache 2 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-notify-slack/tree/master/LICENSE) for full details.
173 |
--------------------------------------------------------------------------------
/examples/cloudwatch-alerts-to-slack/README.md:
--------------------------------------------------------------------------------
1 | # CloudWatch alerts to Slack
2 |
3 | Configuration in this directory creates a VPC, an SNS topic that sends messages to a Slack channel with Slack webhook URL encrypted using KMS and a CloudWatch Alarm that monitors the duration of lambda execution.
4 |
5 | ## KMS keys
6 |
7 | There are 3 ways to define KMS key which should be used by Lambda function:
8 |
9 | 1. Create [aws_kms_key resource](https://www.terraform.io/docs/providers/aws/r/kms_key.html) and put ARN of it as `kms_key_arn` argument to this module
10 | 1. Use [aws_kms_alias data-source](https://www.terraform.io/docs/providers/aws/d/kms_alias.html) to get an existing KMS key alias and put ARN of it as `kms_key_arn` argument to this module
11 | 1. Hard-code the ARN of KMS key
12 |
13 | ### Option 1:
14 |
15 | ```hcl
16 | resource "aws_kms_key" "this" {
17 | description = "KMS key for notify-slack test"
18 | }
19 |
20 | resource "aws_kms_alias" "this" {
21 | name = "alias/kms-test-key"
22 | target_key_id = aws_kms_key.this.id
23 | }
24 |
25 | // kms_key_arn = aws_kms_key.this.arn
26 | ```
27 |
28 | ### Option 2:
29 |
30 | ```
31 | data "aws_kms_alias" "this" {
32 | name = "alias/kms-test-key"
33 | }
34 |
35 | // kms_key_arn = data.aws_kms_alias.this.target_key_arn
36 | ```
37 |
38 | ### Option 3:
39 |
40 | ```
41 | // kms_key_arn = "arn:aws:kms:eu-west-1:835367859851:key/054b4846-95fe-4537-94f2-1dfd255238cf"
42 | ```
43 |
44 | ## Usage
45 |
46 | To run this example you need to execute:
47 |
48 | ```bash
49 | $ terraform init
50 | $ terraform plan
51 | $ terraform apply
52 | ```
53 |
54 | Note that in practice, encryption of the Slack webhook URL should happen differently (outside of this module).
55 |
56 | Note that this example may create resources which can cost money. Run `terraform destroy` when you don't need these resources.
57 |
58 |
59 | ## Requirements
60 |
61 | | Name | Version |
62 | |------|---------|
63 | | [terraform](#requirement\_terraform) | >= 1.0 |
64 | | [aws](#requirement\_aws) | >= 4.8 |
65 | | [random](#requirement\_random) | >= 2.0 |
66 |
67 | ## Providers
68 |
69 | | Name | Version |
70 | |------|---------|
71 | | [aws](#provider\_aws) | >= 4.8 |
72 | | [random](#provider\_random) | >= 2.0 |
73 |
74 | ## Modules
75 |
76 | | Name | Source | Version |
77 | |------|--------|---------|
78 | | [notify\_slack](#module\_notify\_slack) | ../../ | n/a |
79 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | n/a |
80 |
81 | ## Resources
82 |
83 | | Name | Type |
84 | |------|------|
85 | | [aws_cloudwatch_metric_alarm.lambda_duration](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |
86 | | [aws_kms_ciphertext.slack_url](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_ciphertext) | resource |
87 | | [aws_kms_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
88 | | [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource |
89 |
90 | ## Inputs
91 |
92 | No inputs.
93 |
94 | ## Outputs
95 |
96 | | Name | Description |
97 | |------|-------------|
98 | | [lambda\_iam\_role\_arn](#output\_lambda\_iam\_role\_arn) | The ARN of the IAM role used by Lambda function |
99 | | [lambda\_iam\_role\_name](#output\_lambda\_iam\_role\_name) | The name of the IAM role used by Lambda function |
100 | | [notify\_slack\_lambda\_function\_arn](#output\_notify\_slack\_lambda\_function\_arn) | The ARN of the Lambda function |
101 | | [notify\_slack\_lambda\_function\_invoke\_arn](#output\_notify\_slack\_lambda\_function\_invoke\_arn) | The ARN to be used for invoking Lambda function from API Gateway |
102 | | [notify\_slack\_lambda\_function\_last\_modified](#output\_notify\_slack\_lambda\_function\_last\_modified) | The date Lambda function was last modified |
103 | | [notify\_slack\_lambda\_function\_name](#output\_notify\_slack\_lambda\_function\_name) | The name of the Lambda function |
104 | | [notify\_slack\_lambda\_function\_version](#output\_notify\_slack\_lambda\_function\_version) | Latest published version of your Lambda function |
105 | | [sns\_topic\_arn](#output\_sns\_topic\_arn) | The ARN of the SNS topic from which messages will be sent to Slack |
106 |
107 |
--------------------------------------------------------------------------------
/examples/cloudwatch-alerts-to-slack/main.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = "eu-west-1"
3 | }
4 |
5 | resource "aws_kms_key" "this" {
6 | description = "KMS key for notify-slack test"
7 | }
8 |
9 | # Encrypt the URL, storing encryption here will show it in logs and in tfstate
10 | # https://www.terraform.io/docs/state/sensitive-data.html
11 | resource "aws_kms_ciphertext" "slack_url" {
12 | plaintext = "https://hooks.slack.com/services/AAA/BBB/CCC"
13 | key_id = aws_kms_key.this.arn
14 | }
15 |
16 | module "notify_slack" {
17 | source = "../../"
18 |
19 | for_each = toset([
20 | "develop",
21 | "release",
22 | "test",
23 | ])
24 |
25 | sns_topic_name = "slack-topic"
26 | enable_sns_topic_delivery_status_logs = true
27 |
28 | # Specify the ARN of the pre-defined feedback role or leave blank to have the module create it
29 | #sns_topic_lambda_feedback_role_arn = "arn:aws:iam::111122223333:role/sns-delivery-status"
30 |
31 | lambda_function_name = "notify_slack_${each.value}"
32 |
33 | slack_webhook_url = aws_kms_ciphertext.slack_url.ciphertext_blob
34 | slack_channel = "aws-notification"
35 | slack_username = "reporter"
36 |
37 | kms_key_arn = aws_kms_key.this.arn
38 |
39 | lambda_description = "Lambda function which sends notifications to Slack"
40 | log_events = true
41 |
42 | # VPC
43 | # lambda_function_vpc_subnet_ids = module.vpc.intra_subnets
44 | # lambda_function_vpc_security_group_ids = [module.vpc.default_security_group_id]
45 |
46 | tags = {
47 | Name = "cloudwatch-alerts-to-slack"
48 | }
49 | }
50 |
51 | resource "aws_cloudwatch_metric_alarm" "lambda_duration" {
52 | alarm_name = "NotifySlackDuration"
53 | comparison_operator = "GreaterThanOrEqualToThreshold"
54 | evaluation_periods = "1"
55 | metric_name = "Duration"
56 | namespace = "AWS/Lambda"
57 | period = "60"
58 | statistic = "Average"
59 | threshold = "5000"
60 | alarm_description = "Duration of notifying slack exceeds threshold"
61 |
62 | alarm_actions = [module.notify_slack["develop"].slack_topic_arn]
63 |
64 | dimensions = {
65 | FunctionName = module.notify_slack["develop"].notify_slack_lambda_function_name
66 | }
67 | }
68 |
69 | ######
70 | # VPC
71 | ######
72 | resource "random_pet" "this" {
73 | length = 2
74 | }
75 |
76 | module "vpc" {
77 | source = "terraform-aws-modules/vpc/aws"
78 |
79 | name = random_pet.this.id
80 | cidr = "10.10.0.0/16"
81 |
82 | azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
83 | intra_subnets = ["10.10.101.0/24", "10.10.102.0/24", "10.10.103.0/24"]
84 | }
85 |
--------------------------------------------------------------------------------
/examples/cloudwatch-alerts-to-slack/outputs.tf:
--------------------------------------------------------------------------------
1 | output "sns_topic_arn" {
2 | description = "The ARN of the SNS topic from which messages will be sent to Slack"
3 | value = module.notify_slack["develop"].slack_topic_arn
4 | }
5 |
6 | output "lambda_iam_role_arn" {
7 | description = "The ARN of the IAM role used by Lambda function"
8 | value = module.notify_slack["develop"].lambda_iam_role_arn
9 | }
10 |
11 | output "lambda_iam_role_name" {
12 | description = "The name of the IAM role used by Lambda function"
13 | value = module.notify_slack["develop"].lambda_iam_role_name
14 | }
15 |
16 | output "notify_slack_lambda_function_arn" {
17 | description = "The ARN of the Lambda function"
18 | value = module.notify_slack["develop"].notify_slack_lambda_function_arn
19 | }
20 |
21 | output "notify_slack_lambda_function_name" {
22 | description = "The name of the Lambda function"
23 | value = module.notify_slack["develop"].notify_slack_lambda_function_name
24 | }
25 |
26 | output "notify_slack_lambda_function_invoke_arn" {
27 | description = "The ARN to be used for invoking Lambda function from API Gateway"
28 | value = module.notify_slack["develop"].notify_slack_lambda_function_invoke_arn
29 | }
30 |
31 | output "notify_slack_lambda_function_last_modified" {
32 | description = "The date Lambda function was last modified"
33 | value = module.notify_slack["develop"].notify_slack_lambda_function_last_modified
34 | }
35 |
36 | output "notify_slack_lambda_function_version" {
37 | description = "Latest published version of your Lambda function"
38 | value = module.notify_slack["develop"].notify_slack_lambda_function_version
39 | }
40 |
--------------------------------------------------------------------------------
/examples/cloudwatch-alerts-to-slack/variables.tf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-notify-slack/0a6a45ee485f3fae6773b2ce9a0bbf24cbbb42a6/examples/cloudwatch-alerts-to-slack/variables.tf
--------------------------------------------------------------------------------
/examples/cloudwatch-alerts-to-slack/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 4.8"
8 | }
9 | random = {
10 | source = "hashicorp/random"
11 | version = ">= 2.0"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/notify-slack-simple/README.md:
--------------------------------------------------------------------------------
1 | Basic Slack notification
2 | ========================
3 |
4 | Configuration in this directory creates an SNS topic that sends messages to a Slack channel.
5 |
6 | Note, this example does not use KMS key.
7 |
8 | Usage
9 | =====
10 |
11 | To run this example you need to execute:
12 |
13 | ```bash
14 | $ terraform init
15 | $ terraform plan
16 | $ terraform apply
17 | ```
18 |
19 | Note that this example may create resources which can cost money (AWS Elastic IP, for example). Run `terraform destroy` when you don't need these resources.
20 |
21 |
22 | ## Requirements
23 |
24 | | Name | Version |
25 | |------|---------|
26 | | [terraform](#requirement\_terraform) | >= 1.0 |
27 | | [aws](#requirement\_aws) | >= 4.8 |
28 | | [local](#requirement\_local) | >= 2.0 |
29 |
30 | ## Providers
31 |
32 | | Name | Version |
33 | |------|---------|
34 | | [aws](#provider\_aws) | >= 4.8 |
35 | | [local](#provider\_local) | >= 2.0 |
36 |
37 | ## Modules
38 |
39 | | Name | Source | Version |
40 | |------|--------|---------|
41 | | [custom\_lambda](#module\_custom\_lambda) | ../../ | n/a |
42 | | [notify\_slack](#module\_notify\_slack) | ../../ | n/a |
43 |
44 | ## Resources
45 |
46 | | Name | Type |
47 | |------|------|
48 | | [aws_sns_topic.custom_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
49 | | [aws_sns_topic.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
50 | | [local_file.integration_testing](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
51 |
52 | ## Inputs
53 |
54 | No inputs.
55 |
56 | ## Outputs
57 |
58 | | Name | Description |
59 | |------|-------------|
60 | | [lambda\_cloudwatch\_log\_group\_arn](#output\_lambda\_cloudwatch\_log\_group\_arn) | The Amazon Resource Name (ARN) specifying the log group |
61 | | [lambda\_iam\_role\_arn](#output\_lambda\_iam\_role\_arn) | The ARN of the IAM role used by Lambda function |
62 | | [lambda\_iam\_role\_name](#output\_lambda\_iam\_role\_name) | The name of the IAM role used by Lambda function |
63 | | [notify\_slack\_lambda\_function\_arn](#output\_notify\_slack\_lambda\_function\_arn) | The ARN of the Lambda function |
64 | | [notify\_slack\_lambda\_function\_invoke\_arn](#output\_notify\_slack\_lambda\_function\_invoke\_arn) | The ARN to be used for invoking Lambda function from API Gateway |
65 | | [notify\_slack\_lambda\_function\_last\_modified](#output\_notify\_slack\_lambda\_function\_last\_modified) | The date Lambda function was last modified |
66 | | [notify\_slack\_lambda\_function\_name](#output\_notify\_slack\_lambda\_function\_name) | The name of the Lambda function |
67 | | [notify\_slack\_lambda\_function\_version](#output\_notify\_slack\_lambda\_function\_version) | Latest published version of your Lambda function |
68 | | [sns\_topic\_arn](#output\_sns\_topic\_arn) | The ARN of the SNS topic from which messages will be sent to Slack |
69 |
70 |
--------------------------------------------------------------------------------
/examples/notify-slack-simple/custom-lambda.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | custom = {
3 | name = "ex-${replace(basename(path.cwd), "_", "-")}-custom"
4 | tags = merge({ "Type" = "custom" }, local.tags)
5 | }
6 | }
7 |
8 | ################################################################################
9 | # Supporting Resources
10 | ################################################################################
11 |
12 | resource "aws_sns_topic" "custom_lambda" {
13 | name = local.custom.name
14 | tags = local.custom.tags
15 | }
16 |
17 | ################################################################################
18 | # Slack Notify Module
19 | ################################################################################
20 |
21 | module "custom_lambda" {
22 | source = "../../"
23 |
24 | lambda_function_name = "custom_lambda"
25 | lambda_source_path = "../../functions/mylambda.py"
26 |
27 | iam_role_name_prefix = "custom"
28 |
29 | sns_topic_name = aws_sns_topic.custom_lambda.name
30 |
31 | slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
32 | slack_channel = "aws-notification"
33 | slack_username = "reporter"
34 |
35 | tags = local.custom.tags
36 | }
37 |
--------------------------------------------------------------------------------
/examples/notify-slack-simple/main.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = local.region
3 | }
4 |
5 | locals {
6 | name = "ex-${replace(basename(path.cwd), "_", "-")}"
7 | region = "eu-west-1"
8 | tags = {
9 | Owner = "user"
10 | Environment = "dev"
11 | }
12 | }
13 |
14 | ################################################################################
15 | # Supporting Resources
16 | ################################################################################
17 |
18 | resource "aws_sns_topic" "example" {
19 | name = local.name
20 | tags = local.tags
21 | }
22 |
23 | ################################################################################
24 | # Slack Notify Module
25 | ################################################################################
26 |
27 | module "notify_slack" {
28 | source = "../../"
29 |
30 | sns_topic_name = aws_sns_topic.example.name
31 | create_sns_topic = false
32 |
33 | slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
34 | slack_channel = "aws-notification"
35 | slack_username = "reporter"
36 |
37 | tags = local.tags
38 | }
39 |
40 | ################################################################################
41 | # Integration Testing Support
42 | # This populates a file that is gitignored to aid in executing the integration tests locally
43 | ################################################################################
44 |
45 | resource "local_file" "integration_testing" {
46 | filename = "${path.module}/../../functions/.int.env"
47 | content = <<-EOT
48 | REGION=${local.region}
49 | LAMBDA_FUNCTION_NAME=${module.notify_slack.notify_slack_lambda_function_name}
50 | SNS_TOPIC_ARN=${aws_sns_topic.example.arn}
51 | EOT
52 | }
53 |
--------------------------------------------------------------------------------
/examples/notify-slack-simple/outputs.tf:
--------------------------------------------------------------------------------
1 | output "sns_topic_arn" {
2 | description = "The ARN of the SNS topic from which messages will be sent to Slack"
3 | value = module.notify_slack.slack_topic_arn
4 | }
5 |
6 | output "lambda_iam_role_arn" {
7 | description = "The ARN of the IAM role used by Lambda function"
8 | value = module.notify_slack.lambda_iam_role_arn
9 | }
10 |
11 | output "lambda_iam_role_name" {
12 | description = "The name of the IAM role used by Lambda function"
13 | value = module.notify_slack.lambda_iam_role_name
14 | }
15 |
16 | output "notify_slack_lambda_function_arn" {
17 | description = "The ARN of the Lambda function"
18 | value = module.notify_slack.notify_slack_lambda_function_arn
19 | }
20 |
21 | output "notify_slack_lambda_function_name" {
22 | description = "The name of the Lambda function"
23 | value = module.notify_slack.notify_slack_lambda_function_name
24 | }
25 |
26 | output "notify_slack_lambda_function_invoke_arn" {
27 | description = "The ARN to be used for invoking Lambda function from API Gateway"
28 | value = module.notify_slack.notify_slack_lambda_function_invoke_arn
29 | }
30 |
31 | output "notify_slack_lambda_function_last_modified" {
32 | description = "The date Lambda function was last modified"
33 | value = module.notify_slack.notify_slack_lambda_function_last_modified
34 | }
35 |
36 | output "notify_slack_lambda_function_version" {
37 | description = "Latest published version of your Lambda function"
38 | value = module.notify_slack.notify_slack_lambda_function_version
39 | }
40 |
41 | output "lambda_cloudwatch_log_group_arn" {
42 | description = "The Amazon Resource Name (ARN) specifying the log group"
43 | value = module.notify_slack.lambda_cloudwatch_log_group_arn
44 | }
45 |
--------------------------------------------------------------------------------
/examples/notify-slack-simple/variables.tf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-notify-slack/0a6a45ee485f3fae6773b2ce9a0bbf24cbbb42a6/examples/notify-slack-simple/variables.tf
--------------------------------------------------------------------------------
/examples/notify-slack-simple/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 4.8"
8 | }
9 | local = {
10 | source = "hashicorp/local"
11 | version = ">= 2.0"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/functions/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-complexity = 10
3 | max-line-length = 120
4 | exclude =
5 | .pytest_cache
6 | __pycache__/
7 | *tests/
8 | events/
9 | messages/
10 | snapshots/
11 |
--------------------------------------------------------------------------------
/functions/.pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.black]
2 | line-length = 120
3 | target-version = ['py311']
4 | include = '\.pyi?$'
5 | verbose = true
6 | exclude = '''
7 | /(
8 | | \.git
9 | | \.mypy_cache
10 | | dist
11 | | \.pants\.d
12 | | virtualenvs
13 | | \.venv
14 | | _build
15 | | build
16 | | dist
17 | | snapshots
18 | )/
19 | '''
20 |
21 | [tool.isort]
22 | line_length = 120
23 | skip = '.terraform'
24 | sections = 'FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER'
25 | known_third_party = 'aws_lambda_powertools,boto3,botocore,pytest,snapshottest'
26 | known_first_party = 'events,notify_slack'
27 | indent = ' '
28 |
29 | [tool.mypy]
30 | namespace_packages = true
31 | explicit_package_bases = true
32 |
33 | no_implicit_optional = true
34 | implicit_reexport = false
35 | strict_equality = true
36 |
37 | warn_unused_configs = true
38 | warn_unused_ignores = true
39 | warn_return_any = true
40 | warn_redundant_casts = true
41 | warn_unreachable = true
42 |
43 | pretty = true
44 | show_column_numbers = true
45 | show_error_context = true
46 | show_error_codes = true
47 | show_traceback = true
48 |
49 | [tool.coverage.run]
50 | branch = true
51 | omit = ["*_test.py", "tests/*", "events/*", "messages/*", "snapshots/*", "venv/*", ".mypy_cache/*", ".pytest_cache/*"]
52 |
53 | [tool.coverage.report]
54 | show_missing = true
55 | skip_covered = true
56 | skip_empty = true
57 | sort = "-Miss"
58 | fail_under = 75
59 |
--------------------------------------------------------------------------------
/functions/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 |
8 | [dev-packages]
9 | boto3 = "~=1.34"
10 | botocore = "~=1.34"
11 | black = "*"
12 | flake8 = "*"
13 | isort = "*"
14 | mypy = "*"
15 | pytest = "*"
16 | pytest-cov = "*"
17 | radon = "*"
18 | snapshottest = "~=0.6"
19 |
20 | [requires]
21 | python_version = "3.11"
22 |
23 | [scripts]
24 | test = "python3 -m pytest --cov --cov-report=term"
25 | 'test:updatesnapshots' = "python3 -m pytest --snapshot-update"
26 | cover = "python3 -m coverage html"
27 | complexity = "python3 -m radon cc notify_slack.py -a"
28 | halstead = "python3 -m radon hal notify_slack.py"
29 | typecheck = "python3 -m mypy . --ignore-missing-imports"
30 | lint = "python3 -m flake8 . --count --statistics --benchmark --exit-zero --config=.flake8"
31 | 'lint:ci' = "python3 -m flake8 . --config=.flake8"
32 | imports = "python3 -m isort . --profile black"
33 | format = "python3 -m black ."
34 |
35 | [pipenv]
36 | allow_prereleases = true
37 |
--------------------------------------------------------------------------------
/functions/README.md:
--------------------------------------------------------------------------------
1 | # Slack Notify Lambda Functions
2 |
3 | ## Conventions
4 |
5 | The following tools and conventions are used within this project:
6 |
7 | - [pipenv](https://github.com/pypa/pipenv) for managing Python dependencies and development virtualenv
8 | - [flake8](https://github.com/PyCQA/flake8) & [radon](https://github.com/rubik/radon) for linting and static code analysis
9 | - [isort](https://github.com/timothycrosley/isort) for import statement formatting
10 | - [black](https://github.com/ambv/black) for code formatting
11 | - [mypy](https://github.com/python/mypy) for static type checking
12 | - [pytest](https://github.com/pytest-dev/pytest) and [snapshottest](https://github.com/syrusakbary/snapshottest) for unit testing and snapshot testing
13 |
14 | ## Getting Started
15 |
16 | The following instructions will help you get setup for local development and testing purposes.
17 |
18 | ### Prerequisites
19 |
20 | #### [Pipenv](https://github.com/pypa/pipenv)
21 |
22 | Pipenv is used to help manage the python dependencies and local virtualenv for local testing and development. To install `pipenv` please refer to the project [installation documentation](https://github.com/pypa/pipenv#installation).
23 |
24 | Install the projects Python dependencies (with development dependencies) locally by running the following command.
25 |
26 | ```bash
27 | $ pipenv install --dev
28 | ```
29 |
30 | If you add/change/modify any of the Pipfile dependencies, you can update your local virtualenv using:
31 |
32 | ```bash
33 | $ pipenv update
34 | ```
35 |
36 | ### Testing
37 |
38 | #### Sample Payloads
39 |
40 | In the `functions/` directory there are two folders that contain sample message payloads used for testing and validation:
41 |
42 | 1. `functions/events/` contains raw events as provided by AWS. You can see a more in-depth list of example events in the (AWS documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html)
43 | 2. `functions/messages/` contains SNS message payloads in the form that they are delivered to the Slack notify lambda function. The `Message` attribute field is where the payload is stored that will be parsed and sent to Slack; this can be events like those described above in #1, or any string/stringified-JSON
44 |
45 | #### Unit Tests
46 |
47 | There are a number of pipenv scripts that are provided to aid in testing and ensuring the codebase is formatted properly.
48 |
49 | - `pipenv run test`: execute unit tests defined using pytest and show test coverage
50 | - `pipenv run lint`: show linting errors and static analysis of codebase
51 | - `pipenv run format`: auto-format codebase according to configurations provided
52 | - `pipenv run imports`: auto-format import statements according to configurations provided
53 | - `pipenv run typecheck`: show typecheck analysis of codebase
54 |
55 | See the `[scripts]` section of the `Pipfile` for the complete list of script commands.
56 |
57 | #### Snapshot Testing
58 |
59 | Snapshot testing is used to compare a set of given inputs to generated output snapshots to aid in unit testing. The tests are run in conjunction with the standard unit tests and the output will be shown in the cumulative output from `pipenv run test`. In theory, however, the snapshots themselves should not change unless:
60 |
61 | 1. The expected output of the message payload has changed
62 | 2. Event/message payloads have been added to or removed from the project
63 |
64 | When a change is required to update the snapshots, please do the following:
65 |
66 | 1. Update the snapshots by running:
67 |
68 | ```bash
69 | $ pipenv run test:updatesnapshots
70 | $ pipenv run format # this is necessary since the generated code follows its own style
71 | ```
72 |
73 | 2. Provide a clear reasoning within your pull request as to why the snapshots have changed
74 |
75 | #### Integration Tests
76 |
77 | Integration tests require setting up a live Slack webhook
78 |
79 | To run the unit tests:
80 |
81 | 1. Set up a dedicated slack channel as a test sandbox with it's own webhook. See [Slack Incoming Webhooks docs](https://api.slack.com/messaging/webhooks) for details.
82 | 2. From within the `examples/notify-slack-simple/` directory, update the `slack_*` variables to use your values:
83 |
84 | ```hcl
85 | slack_webhook_url = "https://hooks.slack.com/services/AAA/BBB/CCC"
86 | slack_channel = "aws-notification"
87 | slack_username = "reporter"
88 | ```
89 |
90 | 3. Deploy the resources in the `examples/notify-slack-simple/` project using Terraform
91 |
92 | ```bash
93 | $ terraform init && terraform apply -y
94 | ```
95 |
96 | 4. From within the `functions/` directory, execute the integration tests locally:
97 |
98 | ```bash
99 | $ pipenv run python integration_test.py
100 | ```
101 |
102 | Within the Slack channel that is associated to the webhook URL provided, you should see all of the messages arriving. You can compared the messages to the payloads in the `functions/events/` and `functions/messages` directories; there should be one Slack message per event payload/file.
103 |
104 | 5. Do not forget to clean up your provisioned resources by returning to the `example/notify-slack-simple/` directory and destroying using Terraform:
105 |
106 | ```bash
107 | $ terraform destroy -y
108 | ```
109 |
110 | ## Supporting Additional Events
111 |
112 | To add new events with custom message formatting, the general workflow will consist of (ignoring git actions for brevity):
113 |
114 | 1. Add a new example event paylod to the `functions/events/` directory; please name the file, using snake casing, in the form `_.json` such as `guardduty_finding.json` or `cloudwatch_alarm.json`
115 | 2. In the `functions/notify_slack.py` file, add the new formatting function, following a similar naming pattern like in step #1 where the function name is `format__()` such as `format_guardduty_finding()` or `format_cloudwatch_alarm()`
116 | 3. (Optional) Ff there are different "severity" type levels that are to be mapped to Slack message color bars, create an enum that maps the possible serverity values to the appropriate colors. See the `CloudWatchAlarmState` and `GuardDutyFindingSeverity` for examples. The enum name should follow pascal case, Python standard, in the form of ``
117 | 4. Update the snapshots to include your new event payload and expected output. Note - the other snapshots should not be affected by your change, the snapshot diff should only show your new event:
118 |
119 | ```bash
120 | $ pipenv run test:updatesnapshots
121 | $ pipenv run format # this is necessary since the generated code follows its own style
122 | ```
123 |
--------------------------------------------------------------------------------
/functions/events/aws_health_event.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "121345678-1234-1234-1234-123456789012",
4 | "detail-type": "AWS Health Event",
5 | "source": "aws.health",
6 | "account": "123456789012",
7 | "time": "2016-06-05T06:27:57Z",
8 | "region": "us-west-2",
9 | "resources": [
10 | "i-abcd1111"
11 | ],
12 | "detail": {
13 | "eventArn": "arn:aws:health:us-west-2::event/AWS_EC2_INSTANCE_STORE_DRIVE_PERFORMANCE_DEGRADED_90353408594353980",
14 | "service": "EC2",
15 | "eventTypeCode": "AWS_EC2_INSTANCE_STORE_DRIVE_PERFORMANCE_DEGRADED",
16 | "eventTypeCategory": "issue",
17 | "startTime": "Sat, 05 Jun 2016 15:10:09 GMT",
18 | "eventDescription": [
19 | {
20 | "language": "en_US",
21 | "latestDescription": "A description of the event will be provided here"
22 | }
23 | ],
24 | "affectedEntities": [
25 | {
26 | "entityValue": "i-abcd1111",
27 | "tags": {
28 | "stage": "prod",
29 | "app": "my-app"
30 | }
31 | }
32 | ]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/functions/events/cloudwatch_alarm.json:
--------------------------------------------------------------------------------
1 | {
2 | "AlarmName": "Example",
3 | "AlarmDescription": "Example alarm description.",
4 | "AWSAccountId": "000000000000",
5 | "NewStateValue": "ALARM",
6 | "NewStateReason": "Threshold Crossed",
7 | "StateChangeTime": "2017-01-12T16:30:42.236+0000",
8 | "Region": "EU - Ireland",
9 | "OldStateValue": "OK"
10 | }
11 |
--------------------------------------------------------------------------------
/functions/events/guardduty_finding_high.json:
--------------------------------------------------------------------------------
1 | {
2 | "detail-type": "GuardDuty Finding",
3 | "region": "us-east-1",
4 | "detail": {
5 | "id": "sample-id-2",
6 | "title": "SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
7 | "severity": 9,
8 | "accountId": "123456789",
9 | "description": "EC2 instance has an unprotected port which is being probed by a known malicious host.",
10 | "type": "Recon:EC2 PortProbeUnprotectedPort",
11 | "service": {
12 | "eventFirstSeen": "2020-01-02T01:02:03Z",
13 | "eventLastSeen": "2020-01-03T01:02:03Z",
14 | "count": 1234
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/functions/events/guardduty_finding_low.json:
--------------------------------------------------------------------------------
1 | {
2 | "detail-type": "GuardDuty Finding",
3 | "region": "us-east-1",
4 | "detail": {
5 | "id": "sample-id-2",
6 | "title": "SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
7 | "severity": 2,
8 | "accountId": "123456789",
9 | "description": "EC2 instance has an unprotected port which is being probed by a known malicious host.",
10 | "type": "Recon:EC2 PortProbeUnprotectedPort",
11 | "service": {
12 | "eventFirstSeen": "2020-01-02T01:02:03Z",
13 | "eventLastSeen": "2020-01-03T01:02:03Z",
14 | "count": 1234
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/functions/events/guardduty_finding_medium.json:
--------------------------------------------------------------------------------
1 | {
2 | "detail-type": "GuardDuty Finding",
3 | "region": "us-east-1",
4 | "detail": {
5 | "id": "sample-id-2",
6 | "title": "SAMPLE Unprotected port on EC2 instance i-123123123 is being probed",
7 | "severity": 5,
8 | "accountId": "123456789",
9 | "description": "EC2 instance has an unprotected port which is being probed by a known malicious host.",
10 | "type": "Recon:EC2 PortProbeUnprotectedPort",
11 | "service": {
12 | "eventFirstSeen": "2020-01-02T01:02:03Z",
13 | "eventLastSeen": "2020-01-03T01:02:03Z",
14 | "count": 1234
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/functions/integration_test.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Integration Test
4 | ----------------
5 |
6 | Executes tests against live Slack webhook
7 |
8 | """
9 |
10 | import os
11 | from pprint import pprint
12 | from typing import List
13 |
14 | import boto3
15 | import pytest
16 |
17 |
18 | @pytest.mark.skip(reason="Execute with`pytest run python integration_test.py`")
19 | def _get_files(directory: str) -> List[str]:
20 | """
21 | Helper function to get list of files under `directory`
22 |
23 | :params directory: directory to pull list of files from
24 | :returns: list of files names under directory specified
25 | """
26 | return [
27 | os.path.join(directory, f)
28 | for f in os.listdir(directory)
29 | if os.path.isfile(os.path.join(directory, f))
30 | ]
31 |
32 |
33 | @pytest.mark.skip(reason="Execute with`pytest run python integration_test.py`")
34 | def invoke_lambda_handler():
35 | """
36 | Invoke lambda handler with sample SNS messages
37 |
38 | Messages should arrive at the live webhook specified
39 | """
40 | lambda_client = boto3.client("lambda", region_name=REGION)
41 |
42 | # These are SNS messages that invoke the lambda handler;
43 | # the event payload is in the `message` field
44 | messages = _get_files(directory="./messages")
45 |
46 | for message in messages:
47 | with open(message, "r") as mfile:
48 | msg = mfile.read()
49 | response = lambda_client.invoke(
50 | FunctionName=LAMBDA_FUNCTION_NAME,
51 | InvocationType="Event",
52 | Payload=msg,
53 | )
54 | pprint(response)
55 |
56 |
57 | @pytest.mark.skip(reason="Execute with`pytest run python integration_test.py`")
58 | def publish_event_to_sns_topic():
59 | """
60 | Publish sample events to SNS topic created
61 |
62 | Messages should arrive at the live webhook specified
63 | """
64 | sns_client = boto3.client("sns", region_name=REGION)
65 |
66 | # These are event payloads that will get published
67 | events = _get_files(directory="./events")
68 |
69 | for event in events:
70 | with open(event, "r") as efile:
71 | msg = efile.read()
72 | response = sns_client.publish(
73 | TopicArn=SNS_TOPIC_ARN,
74 | Message=msg,
75 | Subject=event,
76 | )
77 | pprint(response)
78 |
79 |
80 | if __name__ == "__main__":
81 | # Sourcing env vars set by `notify-slack-simple` example
82 | with open(".int.env", "r") as envvarfile:
83 | for line in envvarfile.readlines():
84 | (_var, _val) = line.strip().split("=")
85 | os.environ[_var] = _val
86 |
87 | # Not using .get() so it fails loudly if not set (`KeyError`)
88 | REGION = os.environ["REGION"]
89 | LAMBDA_FUNCTION_NAME = os.environ["LAMBDA_FUNCTION_NAME"]
90 | SNS_TOPIC_ARN = os.environ["SNS_TOPIC_ARN"]
91 |
92 | invoke_lambda_handler()
93 | publish_event_to_sns_topic()
94 |
--------------------------------------------------------------------------------
/functions/messages/backup.json:
--------------------------------------------------------------------------------
1 | {
2 | "Records": [
3 | {
4 | "EventSource": "aws:sns",
5 | "EventVersion": "1.0",
6 | "EventSubscriptionArn": "arn:aws:sns:...-a3802aa1ed45",
7 | "Sns": {
8 | "Type": "Notification",
9 | "MessageId": "12345678-abcd-123a-def0-abcd1a234567",
10 | "TopicArn": "arn:aws:sns:us-west-1:123456789012:backup-2sqs-sns-topic",
11 | "Subject": "Notification from AWS Backup",
12 | "Message": "An AWS Backup job was completed successfully. Recovery point ARN: arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012d. Resource ARN : arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012e. BackupJob ID : 1b2345b2-f22c-4dab-5eb6-bbc7890ed123",
13 | "Timestamp": "2019-08-02T18:46:02.788Z",
14 | "MessageAttributes": {
15 | "EventType": {
16 | "Type": "String",
17 | "Value": "BACKUP_JOB"
18 | },
19 | "State": {
20 | "Type": "String",
21 | "Value": "COMPLETED"
22 | },
23 | "AccountId": {
24 | "Type": "String",
25 | "Value": "123456789012"
26 | },
27 | "Id": {
28 | "Type": "String",
29 | "Value": "1b2345b2-f22c-4dab-5eb6-bbc7890ed123"
30 | },
31 | "StartTime": {
32 | "Type": "String",
33 | "Value": "2019-09-02T13:48:52.226Z"
34 | }
35 | }
36 | }
37 | },
38 | {
39 | "EventSource": "aws:sns",
40 | "EventVersion": "1.0",
41 | "EventSubscriptionArn": "arn:aws:sns:...-a3802aa1ed45",
42 | "Sns": {
43 | "Type": "Notification",
44 | "MessageId": "12345678-abcd-123a-def0-abcd1a234567",
45 | "TopicArn": "arn:aws:sns:us-west-1:123456789012:backup-2sqs-sns-topic",
46 | "Subject": "Notification from AWS Backup",
47 | "Message": "An AWS Backup job failed. Resource ARN : arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012e. BackupJob ID : 1b2345b2-f22c-4dab-5eb6-bbc7890ed123",
48 | "Timestamp": "2019-08-02T18:46:02.788Z",
49 | "MessageAttributes": {
50 | "EventType": {
51 | "Type": "String",
52 | "Value": "BACKUP_JOB"
53 | },
54 | "State": {
55 | "Type": "String",
56 | "Value": "FAILED"
57 | },
58 | "AccountId": {
59 | "Type": "String",
60 | "Value": "123456789012"
61 | },
62 | "Id": {
63 | "Type": "String",
64 | "Value": "1b2345b2-f22c-4dab-5eb6-bbc7890ed123"
65 | },
66 | "StartTime": {
67 | "Type": "String",
68 | "Value": "2019-09-02T13:48:52.226Z"
69 | }
70 | }
71 | }
72 | },
73 | {
74 | "EventSource": "aws:sns",
75 | "EventVersion": "1.0",
76 | "EventSubscriptionArn": "arn:aws:sns:...-a3802aa1ed45",
77 | "Sns": {
78 | "Type": "Notification",
79 | "MessageId": "12345678-abcd-123a-def0-abcd1a234567",
80 | "TopicArn": "arn:aws:sns:us-west-1:123456789012:backup-2sqs-sns-topic",
81 | "Subject": "Notification from AWS Backup",
82 | "Message": "An AWS Backup job failed to complete in time. Resource ARN : arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012e. BackupJob ID : 1b2345b2-f22c-4dab-5eb6-bbc7890ed123",
83 | "Timestamp": "2019-08-02T18:46:02.788Z",
84 | "MessageAttributes": {
85 | "EventType": {
86 | "Type": "String",
87 | "Value": "BACKUP_JOB"
88 | },
89 | "State": {
90 | "Type": "String",
91 | "Value": "EXPIRED"
92 | },
93 | "AccountId": {
94 | "Type": "String",
95 | "Value": "123456789012"
96 | },
97 | "Id": {
98 | "Type": "String",
99 | "Value": "1b2345b2-f22c-4dab-5eb6-bbc7890ed123"
100 | },
101 | "StartTime": {
102 | "Type": "String",
103 | "Value": "2019-09-02T13:48:52.226Z"
104 | }
105 | }
106 | }
107 | }
108 | ]
109 | }
110 |
--------------------------------------------------------------------------------
/functions/messages/cloudwatch_alarm.json:
--------------------------------------------------------------------------------
1 | {
2 | "Records": [{
3 | "EventSource": "aws:sns",
4 | "EventVersion": "1.0",
5 | "EventSubscriptionArn": "arn:aws:sns:us-east-1::ExampleTopic",
6 | "Sns": {
7 | "Type": "Notification",
8 | "MessageId": "f86e3c5b-cd17-1ab8-80e9-c0776d4f1e7a",
9 | "TopicArn": "arn:aws:sns:us-east-1:123456789012:ExampleTopic",
10 | "Subject": "'OK: \"DBMigrationRequired\" in EU (London)",
11 | "Message": "{\"AlarmName\": \"DBMigrationRequired\",\"AlarmDescription\": \"App is reporting \\\"A JPA error occurred(Unable to build EntityManagerFactory)\\\"\",\"AWSAccountId\": \"735598076380\",\"NewStateValue\": \"OK\",\"NewStateReason\": \"Threshold Crossed: 1 datapoint [1.0 (12\/02\/19 15:44:00)] was not less than the threshold (1.0).\",\"StateChangeTime\": \"2019-02-12T15:45:24.006+0000\",\"Region\": \"US (Virginia)\",\"OldStateValue\": \"ALARM\",\"Trigger\": {\"MetricName\": \"DBMigrationRequired\",\"Namespace\": \"LogMetrics\",\"StatisticType\": \"Statistic\",\"Statistic\": \"SUM\",\"Unit\": null,\"Dimensions\": [],\"Period\": 60,\"EvaluationPeriods\": 1,\"ComparisonOperator\": \"LessThanThreshold\",\"Threshold\": 1.0,\"TreatMissingData\": \"- TreatMissingData:NonBreaching\",\"EvaluateLowSampleCountPercentile\": \"\"}}",
12 | "Timestamp": "2019-02-12T15:45:24.091Z",
13 | "SignatureVersion": "1",
14 | "Signature": "EXAMPLE",
15 | "SigningCertUrl": "EXAMPLE",
16 | "UnsubscribeUrl": "EXAMPLE",
17 | "MessageAttributes": {}
18 | }
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/functions/messages/dms_notification.json:
--------------------------------------------------------------------------------
1 | {
2 | "Records": [{
3 | "EventSource": "aws:sns",
4 | "EventVersion": "1.0",
5 | "EventSubscriptionArn": "arn:aws:sns:eu-west-1::ExampleTopic",
6 | "Sns": {
7 | "Type": "Notification",
8 | "MessageId": "f86e3c5b-cd17-1ab8-80e9-c0776d4f1e7a",
9 | "TopicArn": "arn:aws:sns:eu-west-1:123456789012:ExampleTopic",
10 | "Subject": "DMS Notification Message",
11 | "Message": "{\"Event Source\": \"replication-task\",\"Event Time\": \"2019-02-12 15:45:24.091\",\"Identifier Link\": \"https:\/\/console.aws.amazon.com\/dms\/home?region=us-east-1#tasks:ids=hello-world\",\"SourceId\": \"hello-world\",\"Event ID\": \"http:\/\/docs.aws.amazon.com\/dms\/latest\/userguide\/CHAP_Events.html#DMS-EVENT-0079 \",\"Event Message\": \"Replication task has stopped.\"}",
12 | "Timestamp": "2019-02-12T15:45:24.091Z",
13 | "SignatureVersion": "1",
14 | "Signature": "EXAMPLE",
15 | "SigningCertUrl": "EXAMPLE",
16 | "UnsubscribeUrl": "EXAMPLE",
17 | "MessageAttributes": {}
18 | }
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/functions/messages/glue_notification.json:
--------------------------------------------------------------------------------
1 | {
2 | "Records": [{
3 | "EventSource": "aws:sns",
4 | "EventVersion": "1.0",
5 | "EventSubscriptionArn": "arn:aws:sns:us-east-2::ExampleTopic",
6 | "Sns": {
7 | "Type": "Notification",
8 | "MessageId": "00337b3f-0982-5cb1-9138-22799c885da9",
9 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:ExampleTopic",
10 | "Subject": "",
11 | "Message": "{\"version\": \"0\",\"id\": \"ad3c3da1-148c-d5da-9a6a-79f1bc9a8a2e\",\"detail-type\": \"Glue Job State Change\",\"source\": \"aws.glue\",\"account\": \"000000000000\",\"time\": \"2021-06-18T12:34:06Z\",\"region\": \"us-east-2\",\"resources\": [],\"detail\": {\"jobName\": \"test_job\",\"severity\": \"ERROR\",\"state\": \"FAILED\",\"jobRunId\": \"jr_ca2144d747b45ad412d3c66a1b6934b6b27aa252be9a21a95c54dfaa224a1925\",\"message\": \"SystemExit: 1\"}}",
12 | "Timestamp": "2021-06-18T12:34:09.509Z",
13 | "SignatureVersion": "1",
14 | "Signature": "EXAMPLE",
15 | "SigningCertUrl": "EXAMPLE",
16 | "UnsubscribeUrl": "EXAMPLE",
17 | "MessageAttributes": {}
18 | }
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/functions/messages/guardduty_finding.json:
--------------------------------------------------------------------------------
1 | {
2 | "Records": [{
3 | "EventSource": "aws:sns",
4 | "EventVersion": "1.0",
5 | "EventSubscriptionArn": "arn:aws:sns:us-gov-east-1::ExampleTopic",
6 | "Sns": {
7 | "Type": "Notification",
8 | "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
9 | "TopicArn": "arn:aws:sns:us-gov-east-1:123456789012:ExampleTopic",
10 | "Subject": "GuardDuty Finding",
11 | "Message": "{\"detail-type\": \"GuardDuty Finding\",\"region\": \"us-gov-east-1\",\"detail\": {\"id\": \"sample-id-2\",\"title\": \"SAMPLE Unprotected port on EC2 instance i-123123123 is being probed\",\"severity\": 9,\"accountId\":\"123456789\",\"description\": \"EC2 instance has an unprotected port which is being probed by a known malicious host.\",\"type\": \"Recon:EC2 PortProbeUnprotectedPort\",\"service\": {\"eventFirstSeen\": \"2020-01-02T01:02:03Z\",\"eventLastSeen\": \"2020-01-03T01:02:03Z\",\"count\": 1234}}}",
12 | "Timestamp": "1970-01-01T00:00:00.000Z",
13 | "SignatureVersion": "1",
14 | "Signature": "EXAMPLE",
15 | "SigningCertUrl": "EXAMPLE",
16 | "UnsubscribeUrl": "EXAMPLE",
17 | "MessageAttributes": {}
18 | }
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/functions/messages/text_message.json:
--------------------------------------------------------------------------------
1 | {
2 | "Records": [{
3 | "EventSource": "aws:sns",
4 | "EventVersion": "1.0",
5 | "EventSubscriptionArn": "arn:aws:sns:us-gov-west-1::ExampleTopic",
6 | "Sns": {
7 | "Type": "Notification",
8 | "MessageId": "f86e3c5b-cd17-1ab8-80e9-c0776d4f1e7a",
9 | "TopicArn": "arn:aws:sns:us-gov-west-1:123456789012:ExampleTopic",
10 | "Subject": "All Fine",
11 | "Message": "This\nis\na typical multi-line\nmessage from SNS!\n\nHave a ~good~ amazing day! :)",
12 | "Timestamp": "2019-02-12T15:45:24.091Z",
13 | "SignatureVersion": "1",
14 | "Signature": "EXAMPLE",
15 | "SigningCertUrl": "EXAMPLE",
16 | "UnsubscribeUrl": "EXAMPLE",
17 | "MessageAttributes": {}
18 | }
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/functions/mylambda.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3.6
2 | # CUSTOM LAMBDA FUNCTION
3 |
4 | import json
5 | import os
6 |
7 | import urllib3
8 |
9 | http = urllib3.PoolManager()
10 |
11 |
12 | def lambda_handler(event, context):
13 | url = os.environ["SLACK_WEBHOOK_URL"]
14 | msg = {
15 | "channel": "#channel-name",
16 | "username": "Prometheus",
17 | "text": event["Records"][0]["Sns"]["Message"],
18 | "icon_emoji": "",
19 | }
20 |
21 | encoded_msg = json.dumps(msg).encode("utf-8")
22 | resp = http.request("POST", url, body=encoded_msg)
23 | print(
24 | {
25 | "message": event["Records"][0]["Sns"]["Message"],
26 | "status_code": resp.status,
27 | "response": resp.data,
28 | }
29 | )
30 |
--------------------------------------------------------------------------------
/functions/notify_slack.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Notify Slack
4 | ------------
5 |
6 | Receives event payloads that are parsed and sent to Slack
7 |
8 | """
9 |
10 | import base64
11 | import json
12 | import logging
13 | import os
14 | import re
15 | import urllib.parse
16 | import urllib.request
17 | from enum import Enum
18 | from typing import Any, Dict, Optional, Union, cast
19 | from urllib.error import HTTPError
20 |
21 | import boto3
22 |
23 | # Set default region if not provided
24 | REGION = os.environ.get("AWS_REGION", "us-east-1")
25 |
26 | # Create client so its cached/frozen between invocations
27 | KMS_CLIENT = boto3.client("kms", region_name=REGION)
28 |
29 | SECURITY_HUB_CLIENT = boto3.client('securityhub', region_name=REGION)
30 |
31 |
32 | class AwsService(Enum):
33 | """AWS service supported by function"""
34 |
35 | cloudwatch = "cloudwatch"
36 | guardduty = "guardduty"
37 | securityhub = "securityhub"
38 |
39 |
40 | def decrypt_url(encrypted_url: str) -> str:
41 | """Decrypt encrypted URL with KMS
42 |
43 | :param encrypted_url: URL to decrypt with KMS
44 | :returns: plaintext URL
45 | """
46 | try:
47 | decrypted_payload = KMS_CLIENT.decrypt(
48 | CiphertextBlob=base64.b64decode(encrypted_url)
49 | )
50 | return decrypted_payload["Plaintext"].decode()
51 | except Exception:
52 | logging.exception("Failed to decrypt URL with KMS")
53 | return ""
54 |
55 |
56 | def get_service_url(region: str, service: str) -> str:
57 | """Get the appropriate service URL for the region
58 |
59 | :param region: name of the AWS region
60 | :param service: name of the AWS service
61 | :returns: AWS console url formatted for the region and service provided
62 | """
63 | try:
64 | service_name = AwsService[service].value
65 | if region.startswith("us-gov-"):
66 | return f"https://console.amazonaws-us-gov.com/{service_name}/home?region={region}"
67 | else:
68 | return f"https://console.aws.amazon.com/{service_name}/home?region={region}"
69 |
70 | except KeyError:
71 | print(f"Service {service} is currently not supported")
72 | raise
73 |
74 |
75 | class CloudWatchAlarmState(Enum):
76 | """Maps CloudWatch notification state to Slack message format color"""
77 |
78 | OK = "good"
79 | INSUFFICIENT_DATA = "warning"
80 | ALARM = "danger"
81 |
82 |
83 | def format_cloudwatch_alarm(message: Dict[str, Any], region: str) -> Dict[str, Any]:
84 | """Format CloudWatch alarm event into Slack message format
85 |
86 | :params message: SNS message body containing CloudWatch alarm event
87 | :region: AWS region where the event originated from
88 | :returns: formatted Slack message payload
89 | """
90 |
91 | cloudwatch_url = get_service_url(region=region, service="cloudwatch")
92 | alarm_name = message["AlarmName"]
93 |
94 | return {
95 | "color": CloudWatchAlarmState[message["NewStateValue"]].value,
96 | "fallback": f"Alarm {alarm_name} triggered",
97 | "fields": [
98 | {"title": "Alarm Name", "value": f"`{alarm_name}`", "short": True},
99 | {
100 | "title": "Alarm Description",
101 | "value": f"`{message['AlarmDescription']}`",
102 | "short": False,
103 | },
104 | {
105 | "title": "Alarm reason",
106 | "value": f"`{message['NewStateReason']}`",
107 | "short": False,
108 | },
109 | {
110 | "title": "Old State",
111 | "value": f"`{message['OldStateValue']}`",
112 | "short": True,
113 | },
114 | {
115 | "title": "Current State",
116 | "value": f"`{message['NewStateValue']}`",
117 | "short": True,
118 | },
119 | {
120 | "title": "Link to Alarm",
121 | "value": f"{cloudwatch_url}#alarm:alarmFilter=ANY;name={urllib.parse.quote(alarm_name)}",
122 | "short": False,
123 | },
124 | ],
125 | "text": f"AWS CloudWatch notification - {message['AlarmName']}",
126 | }
127 |
128 |
129 | def format_aws_security_hub(message: Dict[str, Any], region: str) -> Dict[str, Any]:
130 | """
131 | Format AWS Security Hub finding event into Slack message format
132 |
133 | :params message: SNS message body containing SecurityHub finding event
134 | :params region: AWS region where the event originated from
135 | :returns: formatted Slack message payload
136 | """
137 | service_url = get_service_url(region=region, service="securityhub")
138 | finding = message["detail"]["findings"][0]
139 |
140 | # Switch Status From New To Notified To Prevent Repeated Messages
141 | try:
142 | compliance_status = finding["Compliance"].get("Status", "UNKNOWN")
143 | workflow_status = finding["Workflow"].get("Status", "UNKNOWN")
144 | if compliance_status == "FAILED" and workflow_status == "NEW":
145 | notified = SECURITY_HUB_CLIENT.batch_update_findings(
146 | FindingIdentifiers=[{
147 | 'Id': finding.get('Id'),
148 | 'ProductArn': finding.get("ProductArn")
149 | }],
150 | Workflow={"Status": "NOTIFIED"}
151 | )
152 | logging.warning(f"Successfully updated finding status to NOTIFIED: {json.dumps(notified)}")
153 | except Exception as e:
154 | logging.error(f"Failed to update finding status: {str(e)}")
155 | pass
156 |
157 | if finding.get("ProductName") == "Inspector":
158 | severity = finding["Severity"].get("Label", "INFORMATIONAL")
159 | compliance_status = finding["Compliance"].get("Status", "UNKNOWN")
160 |
161 | Id = finding.get("Id", "No ID Provided")
162 | title = finding.get("Title", "No Title Provided")
163 | description = finding.get("Description", "No Description Provided")
164 | control_id = finding['ProductFields'].get('ControlId', 'N/A')
165 | control_url = service_url + f"#/controls/{control_id}"
166 | aws_account_id = finding.get('AwsAccountId', 'Unknown Account')
167 | first_observed = finding.get('FirstObservedAt', 'Unknown Date')
168 | last_updated = finding.get('UpdatedAt', 'Unknown Date')
169 | affected_resource = finding['Resources'][0].get('Id', 'Unknown Resource')
170 | remediation_url = finding.get("Remediation", {}).get("Recommendation", {}).get("Url", "#")
171 |
172 | finding_base_path = "#/findings?search=Id%3D%255Coperator%255C%253AEQUALS%255C%253A"
173 | double_encoded_id = urllib.parse.quote(urllib.parse.quote(Id, safe=''), safe='')
174 | finding_url = f"{service_url}{finding_base_path}{double_encoded_id}"
175 | generator_id = finding.get("GeneratorId", "Unknown Generator")
176 |
177 | color = SecurityHubSeverity.get(severity.upper(), SecurityHubSeverity.INFORMATIONAL).value
178 | if compliance_status == "PASSED":
179 | color = "#4BB543"
180 |
181 | slack_message = {
182 | "color": color,
183 | "fallback": f"Inspector Finding: {title}",
184 | "fields": [
185 | {"title": "Title", "value": f"`{title}`", "short": False},
186 | {"title": "Description", "value": f"`{description}`", "short": False},
187 | {"title": "Compliance Status", "value": f"`{compliance_status}`", "short": True},
188 | {"title": "Severity", "value": f"`{severity}`", "short": True},
189 | {"title": "Control ID", "value": f"`{control_id}`", "short": True},
190 | {"title": "Account ID", "value": f"`{aws_account_id}`", "short": True},
191 | {"title": "First Observed", "value": f"`{first_observed}`", "short": True},
192 | {"title": "Last Updated", "value": f"`{last_updated}`", "short": True},
193 | {"title": "Affected Resource", "value": f"`{affected_resource}`", "short": False},
194 | {"title": "Generator", "value": f"`{generator_id}`", "short": False},
195 | {"title": "Control Url", "value": f"`{control_url}`", "short": False},
196 | {"title": "Finding Url", "value": f"`{finding_url}`", "short": False},
197 | {"title": "Remediation", "value": f"`{remediation_url}`", "short": False},
198 | ],
199 | "text": f"AWS Inspector Finding - {title}",
200 | }
201 |
202 | return slack_message
203 |
204 | if finding.get("ProductName") == "Security Hub":
205 | severity = finding["Severity"].get("Label", "INFORMATIONAL")
206 | compliance_status = finding["Compliance"].get("Status", "UNKNOWN")
207 |
208 | Id = finding.get("Id", "No ID Provided")
209 | title = finding.get("Title", "No Title Provided")
210 | description = finding.get("Description", "No Description Provided")
211 | control_id = finding['ProductFields'].get('ControlId', 'N/A')
212 | control_url = service_url + f"#/controls/{control_id}"
213 | aws_account_id = finding.get('AwsAccountId', 'Unknown Account')
214 | first_observed = finding.get('FirstObservedAt', 'Unknown Date')
215 | last_updated = finding.get('UpdatedAt', 'Unknown Date')
216 | affected_resource = finding['Resources'][0].get('Id', 'Unknown Resource')
217 | remediation_url = finding.get("Remediation", {}).get("Recommendation", {}).get("Url", "#")
218 | generator_id = finding.get("GeneratorId", "Unknown Generator")
219 |
220 | finding_base_path = "#/findings?search=Id%3D%255Coperator%255C%253AEQUALS%255C%253A"
221 | double_encoded_id = urllib.parse.quote(urllib.parse.quote(Id, safe=''), safe='')
222 | finding_url = f"{service_url}{finding_base_path}{double_encoded_id}"
223 |
224 | color = SecurityHubSeverity.get(severity.upper(), SecurityHubSeverity.INFORMATIONAL).value
225 | if compliance_status == "PASSED":
226 | color = "#4BB543"
227 |
228 | slack_message = {
229 | "color": color,
230 | "fallback": f"Security Hub Finding: {title}",
231 | "fields": [
232 | {"title": "Title", "value": f"`{title}`", "short": False},
233 | {"title": "Description", "value": f"`{description}`", "short": False},
234 | {"title": "Compliance Status", "value": f"`{compliance_status}`", "short": True},
235 | {"title": "Severity", "value": f"`{severity}`", "short": True},
236 | {"title": "Control ID", "value": f"`{control_id}`", "short": True},
237 | {"title": "Account ID", "value": f"`{aws_account_id}`", "short": True},
238 | {"title": "First Observed", "value": f"`{first_observed}`", "short": True},
239 | {"title": "Last Updated", "value": f"`{last_updated}`", "short": True},
240 | {"title": "Affected Resource", "value": f"`{affected_resource}`", "short": False},
241 | {"title": "Generator", "value": f"`{generator_id}`", "short": False},
242 | {"title": "Control Url", "value": f"`{control_url}`", "short": False},
243 | {"title": "Finding Url", "value": f"`{finding_url}`", "short": False},
244 | {"title": "Remediation", "value": f"`{remediation_url}`", "short": False},
245 | ],
246 | "text": f"AWS Security Hub Finding - {title}",
247 | }
248 |
249 | return slack_message
250 |
251 | return format_default(message=message)
252 |
253 |
254 | class SecurityHubSeverity(Enum):
255 | """Maps Security Hub finding severity to Slack message format color"""
256 |
257 | CRITICAL = "danger"
258 | HIGH = "danger"
259 | MEDIUM = "warning"
260 | LOW = "#777777"
261 | INFORMATIONAL = "#439FE0"
262 |
263 | @staticmethod
264 | def get(name, default):
265 | try:
266 | return SecurityHubSeverity[name]
267 | except KeyError:
268 | return default
269 |
270 |
271 | class GuardDutyFindingSeverity(Enum):
272 | """Maps GuardDuty finding severity to Slack message format color"""
273 |
274 | Low = "#777777"
275 | Medium = "warning"
276 | High = "danger"
277 |
278 |
279 | def format_guardduty_finding(message: Dict[str, Any], region: str) -> Dict[str, Any]:
280 | """
281 | Format GuardDuty finding event into Slack message format
282 |
283 | :params message: SNS message body containing GuardDuty finding event
284 | :params region: AWS region where the event originated from
285 | :returns: formatted Slack message payload
286 | """
287 |
288 | guardduty_url = get_service_url(region=region, service="guardduty")
289 | detail = message["detail"]
290 | service = detail.get("service", {})
291 | severity_score = detail.get("severity")
292 |
293 | if severity_score < 4.0:
294 | severity = "Low"
295 | elif severity_score < 7.0:
296 | severity = "Medium"
297 | else:
298 | severity = "High"
299 |
300 | return {
301 | "color": GuardDutyFindingSeverity[severity].value,
302 | "fallback": f"GuardDuty Finding: {detail.get('title')}",
303 | "fields": [
304 | {
305 | "title": "Description",
306 | "value": f"`{detail['description']}`",
307 | "short": False,
308 | },
309 | {
310 | "title": "Finding Type",
311 | "value": f"`{detail['type']}`",
312 | "short": False,
313 | },
314 | {
315 | "title": "First Seen",
316 | "value": f"`{service['eventFirstSeen']}`",
317 | "short": True,
318 | },
319 | {
320 | "title": "Last Seen",
321 | "value": f"`{service['eventLastSeen']}`",
322 | "short": True,
323 | },
324 | {"title": "Severity", "value": f"`{severity}`", "short": True},
325 | {"title": "Account ID", "value": f"`{detail['accountId']}`", "short": True},
326 | {
327 | "title": "Count",
328 | "value": f"`{service['count']}`",
329 | "short": True,
330 | },
331 | {
332 | "title": "Link to Finding",
333 | "value": f"{guardduty_url}#/findings?search=id%3D{detail['id']}",
334 | "short": False,
335 | },
336 | ],
337 | "text": f"AWS GuardDuty Finding - {detail.get('title')}",
338 | }
339 |
340 |
341 | class AwsHealthCategory(Enum):
342 | """Maps AWS Health eventTypeCategory to Slack message format color
343 |
344 | eventTypeCategory
345 | The category code of the event. The possible values are issue,
346 | accountNotification, and scheduledChange.
347 | """
348 |
349 | accountNotification = "#777777"
350 | scheduledChange = "warning"
351 | issue = "danger"
352 |
353 |
354 | def format_aws_health(message: Dict[str, Any], region: str) -> Dict[str, Any]:
355 | """
356 | Format AWS Health event into Slack message format
357 |
358 | :params message: SNS message body containing AWS Health event
359 | :params region: AWS region where the event originated from
360 | :returns: formatted Slack message payload
361 | """
362 |
363 | aws_health_url = (
364 | f"https://phd.aws.amazon.com/phd/home?region={region}#/dashboard/open-issues"
365 | )
366 | detail = message["detail"]
367 | resources = message.get("resources", "")
368 | service = detail.get("service", "")
369 |
370 | return {
371 | "color": AwsHealthCategory[detail["eventTypeCategory"]].value,
372 | "text": f"New AWS Health Event for {service}",
373 | "fallback": f"New AWS Health Event for {service}",
374 | "fields": [
375 | {"title": "Affected Service", "value": f"`{service}`", "short": True},
376 | {
377 | "title": "Affected Region",
378 | "value": f"`{message.get('region')}`",
379 | "short": True,
380 | },
381 | {
382 | "title": "Code",
383 | "value": f"`{detail.get('eventTypeCode')}`",
384 | "short": False,
385 | },
386 | {
387 | "title": "Event Description",
388 | "value": f"`{detail['eventDescription'][0]['latestDescription']}`",
389 | "short": False,
390 | },
391 | {
392 | "title": "Affected Resources",
393 | "value": f"`{', '.join(resources)}`",
394 | "short": False,
395 | },
396 | {
397 | "title": "Start Time",
398 | "value": f"`{detail.get('startTime', '')}`",
399 | "short": True,
400 | },
401 | {
402 | "title": "End Time",
403 | "value": f"`{detail.get('endTime', '')}`",
404 | "short": True,
405 | },
406 | {
407 | "title": "Link to Event",
408 | "value": f"{aws_health_url}",
409 | "short": False,
410 | },
411 | ],
412 | }
413 |
414 |
415 | def aws_backup_field_parser(message: str) -> Dict[str, str]:
416 | """
417 | Parser for AWS Backup event message. It extracts the fields from the message and returns a dictionary.
418 |
419 | :params message: message containing AWS Backup event
420 | :returns: dictionary containing the fields extracted from the message
421 | """
422 | # Order is somewhat important, working in reverse order of the message payload
423 | # to reduce right most matched values
424 | field_names = {
425 | "BackupJob ID": r"(BackupJob ID : ).*",
426 | "Resource ARN": r"(Resource ARN : ).*[.]",
427 | "Recovery point ARN": r"(Recovery point ARN: ).*[.]",
428 | }
429 | fields = {}
430 |
431 | for fname, freg in field_names.items():
432 | match = re.search(freg, message)
433 | if match:
434 | value = match.group(0).split(" ")[-1]
435 | fields[fname] = value.removesuffix(".")
436 |
437 | # Remove the matched field from the message
438 | message = message.replace(match.group(0), "")
439 |
440 | return fields
441 |
442 |
443 | def format_aws_backup(message: str) -> Dict[str, Any]:
444 | """
445 | Format AWS Backup event into Slack message format
446 |
447 | :params message: SNS message body containing AWS Backup event
448 | :returns: formatted Slack message payload
449 | """
450 |
451 | fields: list[Dict[str, Any]] = []
452 | attachments = {}
453 |
454 | title = message.split(".")[0]
455 |
456 | if "failed" in title:
457 | title = f"⚠️ {title}"
458 |
459 | if "completed" in title:
460 | title = f"✅ {title}"
461 |
462 | fields.append({"title": title})
463 |
464 | backup_fields = aws_backup_field_parser(message)
465 |
466 | for k, v in backup_fields.items():
467 | fields.append({"value": k, "short": False})
468 | fields.append({"value": f"`{v}`", "short": False})
469 |
470 | attachments["fields"] = fields # type: ignore
471 |
472 | return attachments
473 |
474 |
475 | def format_default(
476 | message: Union[str, Dict], subject: Optional[str] = None
477 | ) -> Dict[str, Any]:
478 | """
479 | Default formatter, converting event into Slack message format
480 |
481 | :params message: SNS message body containing message/event
482 | :returns: formatted Slack message payload
483 | """
484 |
485 | attachments = {
486 | "fallback": "A new message",
487 | "text": "AWS notification",
488 | "title": subject if subject else "Message",
489 | "mrkdwn_in": ["value"],
490 | }
491 | fields = []
492 |
493 | if type(message) is dict:
494 | for k, v in message.items():
495 | value = f"{json.dumps(v)}" if isinstance(v, (dict, list)) else str(v)
496 | fields.append({"title": k, "value": f"`{value}`", "short": len(value) < 25})
497 | else:
498 | fields.append({"value": message, "short": False})
499 |
500 | if fields:
501 | attachments["fields"] = fields # type: ignore
502 |
503 | return attachments
504 |
505 |
506 | def parse_notification(message: Dict[str, Any], subject: Optional[str], region: str) -> Optional[Dict]:
507 | """
508 | Parse notification message and format into Slack message payload
509 |
510 | :params message: SNS message body notification payload
511 | :params subject: Optional subject line for Slack notification
512 | :params region: AWS region where the event originated from
513 | :returns: Slack message payload
514 | """
515 | if "AlarmName" in message:
516 | return format_cloudwatch_alarm(message=message, region=region)
517 | if isinstance(message, Dict) and message.get("detail-type") == "GuardDuty Finding":
518 | return format_guardduty_finding(message=message, region=message["region"])
519 | if isinstance(message, Dict) and message.get("detail-type") == "Security Hub Findings - Imported":
520 | return format_aws_security_hub(message=message, region=message["region"])
521 | if isinstance(message, Dict) and message.get("detail-type") == "AWS Health Event":
522 | return format_aws_health(message=message, region=message["region"])
523 | if subject == "Notification from AWS Backup":
524 | return format_aws_backup(message=str(message))
525 | return format_default(message=message, subject=subject)
526 |
527 |
528 | def get_slack_message_payload(
529 | message: Union[str, Dict], region: str, subject: Optional[str] = None
530 | ) -> Dict:
531 | """
532 | Parse notification message and format into Slack message payload
533 |
534 | :params message: SNS message body notification payload
535 | :params region: AWS region where the event originated from
536 | :params subject: Optional subject line for Slack notification
537 | :returns: Slack message payload
538 | """
539 |
540 | slack_channel = os.environ["SLACK_CHANNEL"]
541 | slack_username = os.environ["SLACK_USERNAME"]
542 | slack_emoji = os.environ["SLACK_EMOJI"]
543 |
544 | payload = {
545 | "channel": slack_channel,
546 | "username": slack_username,
547 | "icon_emoji": slack_emoji,
548 | }
549 | attachment = None
550 |
551 | if isinstance(message, str):
552 | try:
553 | message = json.loads(message)
554 | except json.JSONDecodeError:
555 | logging.info("Not a structured payload, just a string message")
556 |
557 | message = cast(Dict[str, Any], message)
558 |
559 | if "attachments" in message or "text" in message:
560 | payload = {**payload, **message}
561 | else:
562 | attachment = parse_notification(message, subject, region)
563 |
564 | if attachment:
565 | payload["attachments"] = [attachment] # type: ignore
566 |
567 | return payload
568 |
569 |
570 | def send_slack_notification(payload: Dict[str, Any]) -> str:
571 | """
572 | Send notification payload to Slack
573 |
574 | :params payload: formatted Slack message payload
575 | :returns: response details from sending notification
576 | """
577 |
578 | slack_url = os.environ["SLACK_WEBHOOK_URL"]
579 | if not slack_url.startswith("http"):
580 | slack_url = decrypt_url(slack_url)
581 |
582 | data = urllib.parse.urlencode({"payload": json.dumps(payload)}).encode("utf-8")
583 | req = urllib.request.Request(slack_url)
584 |
585 | try:
586 | result = urllib.request.urlopen(req, data)
587 | return json.dumps({"code": result.getcode(), "info": result.info().as_string()})
588 |
589 | except HTTPError as e:
590 | logging.error(f"{e}: result")
591 | return json.dumps({"code": e.getcode(), "info": e.info().as_string()})
592 |
593 |
594 | def lambda_handler(event: Dict[str, Any], context: Dict[str, Any]) -> str:
595 | """
596 | Lambda function to parse notification events and forward to Slack
597 |
598 | :param event: lambda expected event object
599 | :param context: lambda expected context object
600 | :returns: none
601 | """
602 |
603 | if os.environ.get("LOG_EVENTS", "False") == "True":
604 | logging.info("Event logging enabled: %s", json.dumps(event))
605 |
606 | for record in event["Records"]:
607 | sns = record["Sns"]
608 | subject = sns["Subject"]
609 | message = sns["Message"]
610 | region = sns["TopicArn"].split(":")[3]
611 |
612 | payload = get_slack_message_payload(
613 | message=message, region=region, subject=subject
614 | )
615 | response = send_slack_notification(payload=payload)
616 |
617 | if json.loads(response)["code"] != 200:
618 | response_info = json.loads(response)["info"]
619 | logging.error(
620 | f"Error: received status `{response_info}` using event `{event}` and context `{context}`"
621 | )
622 |
623 | return response
624 |
--------------------------------------------------------------------------------
/functions/notify_slack_test.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Slack Notification Test
4 | -----------------------
5 |
6 | Unit tests for `notify_slack.py`
7 |
8 | """
9 |
10 | import ast
11 | import os
12 |
13 | import notify_slack
14 | import pytest
15 |
16 |
17 | def test_sns_get_slack_message_payload_snapshots(snapshot, monkeypatch):
18 | """
19 | Compare outputs of get_slack_message_payload() with snapshots stored
20 |
21 | Run `pipenv run test:updatesnapshots` to update snapshot images
22 | """
23 |
24 | monkeypatch.setenv("SLACK_CHANNEL", "slack_testing_sandbox")
25 | monkeypatch.setenv("SLACK_USERNAME", "notify_slack_test")
26 | monkeypatch.setenv("SLACK_EMOJI", ":aws:")
27 |
28 | # These are SNS messages that invoke the lambda handler; the event payload is in the
29 | # `message` field
30 | _dir = "./messages"
31 | messages = [f for f in os.listdir(_dir) if os.path.isfile(os.path.join(_dir, f))]
32 |
33 | for file in messages:
34 | with open(os.path.join(_dir, file), "r") as ofile:
35 | event = ast.literal_eval(ofile.read())
36 |
37 | attachments = []
38 | # These are as delivered wrapped in an SNS message payload so we unpack
39 | for record in event["Records"]:
40 | sns = record["Sns"]
41 | subject = sns["Subject"]
42 | message = sns["Message"]
43 | region = sns["TopicArn"].split(":")[3]
44 |
45 | attachment = notify_slack.get_slack_message_payload(
46 | message=message, region=region, subject=subject
47 | )
48 | attachments.append(attachment)
49 |
50 | filename = os.path.basename(file)
51 | snapshot.assert_match(attachments, f"message_{filename}")
52 |
53 |
54 | def test_event_get_slack_message_payload_snapshots(snapshot, monkeypatch):
55 | """
56 | Compare outputs of get_slack_message_payload() with snapshots stored
57 |
58 | Run `pipenv run test:updatesnapshots` to update snapshot images
59 | """
60 |
61 | monkeypatch.setenv("SLACK_CHANNEL", "slack_testing_sandbox")
62 | monkeypatch.setenv("SLACK_USERNAME", "notify_slack_test")
63 | monkeypatch.setenv("SLACK_EMOJI", ":aws:")
64 |
65 | # These are just the raw events that will be converted to JSON string and
66 | # sent via SNS message
67 | _dir = "./events"
68 | events = [f for f in os.listdir(_dir) if os.path.isfile(os.path.join(_dir, f))]
69 |
70 | for file in events:
71 | with open(os.path.join(_dir, file), "r") as ofile:
72 | event = ast.literal_eval(ofile.read())
73 |
74 | attachment = notify_slack.get_slack_message_payload(
75 | message=event, region="us-east-1", subject="bar"
76 | )
77 | attachments = [attachment]
78 |
79 | filename = os.path.basename(file)
80 | snapshot.assert_match(attachments, f"event_{filename}")
81 |
82 |
83 | def test_environment_variables_set(monkeypatch):
84 | """
85 | Should pass since environment variables are provided
86 | """
87 |
88 | monkeypatch.setenv("SLACK_CHANNEL", "slack_testing_sandbox")
89 | monkeypatch.setenv("SLACK_USERNAME", "notify_slack_test")
90 | monkeypatch.setenv("SLACK_EMOJI", ":aws:")
91 | monkeypatch.setenv(
92 | "SLACK_WEBHOOK_URL", "https://hooks.slack.com/services/YOUR/WEBOOK/URL"
93 | )
94 |
95 | with open(os.path.join("./messages/text_message.json"), "r") as efile:
96 | event = ast.literal_eval(efile.read())
97 |
98 | for record in event["Records"]:
99 | sns = record["Sns"]
100 | subject = sns["Subject"]
101 | message = sns["Message"]
102 | region = sns["TopicArn"].split(":")[3]
103 |
104 | notify_slack.get_slack_message_payload(
105 | message=message, region=region, subject=subject
106 | )
107 |
108 |
109 | def test_environment_variables_missing():
110 | """
111 | Should pass since environment variables are NOT provided and
112 | will raise a `KeyError`
113 | """
114 | with pytest.raises(KeyError):
115 | # will raise before parsing/validation
116 | notify_slack.get_slack_message_payload(message={}, region="foo", subject="bar")
117 |
118 |
119 | @pytest.mark.parametrize(
120 | "region,service,expected",
121 | [
122 | (
123 | "us-east-1",
124 | "cloudwatch",
125 | "https://console.aws.amazon.com/cloudwatch/home?region=us-east-1",
126 | ),
127 | (
128 | "us-gov-east-1",
129 | "cloudwatch",
130 | "https://console.amazonaws-us-gov.com/cloudwatch/home?region=us-gov-east-1",
131 | ),
132 | (
133 | "us-east-1",
134 | "guardduty",
135 | "https://console.aws.amazon.com/guardduty/home?region=us-east-1",
136 | ),
137 | (
138 | "us-gov-east-1",
139 | "guardduty",
140 | "https://console.amazonaws-us-gov.com/guardduty/home?region=us-gov-east-1",
141 | ),
142 | ],
143 | )
144 | def test_get_service_url(region, service, expected):
145 | assert notify_slack.get_service_url(region=region, service=service) == expected
146 |
147 |
148 | def test_get_service_url_exception():
149 | """
150 | Should raise error since service is not defined in enum
151 | """
152 | with pytest.raises(KeyError):
153 | notify_slack.get_service_url(region="us-east-1", service="athena")
154 |
--------------------------------------------------------------------------------
/functions/snapshots/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terraform-aws-modules/terraform-aws-notify-slack/0a6a45ee485f3fae6773b2ce9a0bbf24cbbb42a6/functions/snapshots/__init__.py
--------------------------------------------------------------------------------
/functions/snapshots/snap_notify_slack_test.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # snapshottest: v1 - https://goo.gl/zC4yUc
3 | from __future__ import unicode_literals
4 |
5 | from snapshottest import Snapshot
6 |
7 |
8 | snapshots = Snapshot()
9 |
10 | snapshots['test_event_get_slack_message_payload_snapshots event_aws_health_event.json'] = [
11 | {
12 | 'attachments': [
13 | {
14 | 'color': 'danger',
15 | 'fallback': 'New AWS Health Event for EC2',
16 | 'fields': [
17 | {
18 | 'short': True,
19 | 'title': 'Affected Service',
20 | 'value': '`EC2`'
21 | },
22 | {
23 | 'short': True,
24 | 'title': 'Affected Region',
25 | 'value': '`us-west-2`'
26 | },
27 | {
28 | 'short': False,
29 | 'title': 'Code',
30 | 'value': '`AWS_EC2_INSTANCE_STORE_DRIVE_PERFORMANCE_DEGRADED`'
31 | },
32 | {
33 | 'short': False,
34 | 'title': 'Event Description',
35 | 'value': '`A description of the event will be provided here`'
36 | },
37 | {
38 | 'short': False,
39 | 'title': 'Affected Resources',
40 | 'value': '`i-abcd1111`'
41 | },
42 | {
43 | 'short': True,
44 | 'title': 'Start Time',
45 | 'value': '`Sat, 05 Jun 2016 15:10:09 GMT`'
46 | },
47 | {
48 | 'short': True,
49 | 'title': 'End Time',
50 | 'value': '``'
51 | },
52 | {
53 | 'short': False,
54 | 'title': 'Link to Event',
55 | 'value': 'https://phd.aws.amazon.com/phd/home?region=us-west-2#/dashboard/open-issues'
56 | }
57 | ],
58 | 'text': 'New AWS Health Event for EC2'
59 | }
60 | ],
61 | 'channel': 'slack_testing_sandbox',
62 | 'icon_emoji': ':aws:',
63 | 'username': 'notify_slack_test'
64 | }
65 | ]
66 |
67 | snapshots['test_event_get_slack_message_payload_snapshots event_cloudwatch_alarm.json'] = [
68 | {
69 | 'attachments': [
70 | {
71 | 'color': 'danger',
72 | 'fallback': 'Alarm Example triggered',
73 | 'fields': [
74 | {
75 | 'short': True,
76 | 'title': 'Alarm Name',
77 | 'value': '`Example`'
78 | },
79 | {
80 | 'short': False,
81 | 'title': 'Alarm Description',
82 | 'value': '`Example alarm description.`'
83 | },
84 | {
85 | 'short': False,
86 | 'title': 'Alarm reason',
87 | 'value': '`Threshold Crossed`'
88 | },
89 | {
90 | 'short': True,
91 | 'title': 'Old State',
92 | 'value': '`OK`'
93 | },
94 | {
95 | 'short': True,
96 | 'title': 'Current State',
97 | 'value': '`ALARM`'
98 | },
99 | {
100 | 'short': False,
101 | 'title': 'Link to Alarm',
102 | 'value': 'https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#alarm:alarmFilter=ANY;name=Example'
103 | }
104 | ],
105 | 'text': 'AWS CloudWatch notification - Example'
106 | }
107 | ],
108 | 'channel': 'slack_testing_sandbox',
109 | 'icon_emoji': ':aws:',
110 | 'username': 'notify_slack_test'
111 | }
112 | ]
113 |
114 | snapshots['test_event_get_slack_message_payload_snapshots event_guardduty_finding_high.json'] = [
115 | {
116 | 'attachments': [
117 | {
118 | 'color': 'danger',
119 | 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
120 | 'fields': [
121 | {
122 | 'short': False,
123 | 'title': 'Description',
124 | 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
125 | },
126 | {
127 | 'short': False,
128 | 'title': 'Finding Type',
129 | 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
130 | },
131 | {
132 | 'short': True,
133 | 'title': 'First Seen',
134 | 'value': '`2020-01-02T01:02:03Z`'
135 | },
136 | {
137 | 'short': True,
138 | 'title': 'Last Seen',
139 | 'value': '`2020-01-03T01:02:03Z`'
140 | },
141 | {
142 | 'short': True,
143 | 'title': 'Severity',
144 | 'value': '`High`'
145 | },
146 | {
147 | 'short': True,
148 | 'title': 'Account ID',
149 | 'value': '`123456789`'
150 | },
151 | {
152 | 'short': True,
153 | 'title': 'Count',
154 | 'value': '`1234`'
155 | },
156 | {
157 | 'short': False,
158 | 'title': 'Link to Finding',
159 | 'value': 'https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2'
160 | }
161 | ],
162 | 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
163 | }
164 | ],
165 | 'channel': 'slack_testing_sandbox',
166 | 'icon_emoji': ':aws:',
167 | 'username': 'notify_slack_test'
168 | }
169 | ]
170 |
171 | snapshots['test_event_get_slack_message_payload_snapshots event_guardduty_finding_low.json'] = [
172 | {
173 | 'attachments': [
174 | {
175 | 'color': '#777777',
176 | 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
177 | 'fields': [
178 | {
179 | 'short': False,
180 | 'title': 'Description',
181 | 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
182 | },
183 | {
184 | 'short': False,
185 | 'title': 'Finding Type',
186 | 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
187 | },
188 | {
189 | 'short': True,
190 | 'title': 'First Seen',
191 | 'value': '`2020-01-02T01:02:03Z`'
192 | },
193 | {
194 | 'short': True,
195 | 'title': 'Last Seen',
196 | 'value': '`2020-01-03T01:02:03Z`'
197 | },
198 | {
199 | 'short': True,
200 | 'title': 'Severity',
201 | 'value': '`Low`'
202 | },
203 | {
204 | 'short': True,
205 | 'title': 'Account ID',
206 | 'value': '`123456789`'
207 | },
208 | {
209 | 'short': True,
210 | 'title': 'Count',
211 | 'value': '`1234`'
212 | },
213 | {
214 | 'short': False,
215 | 'title': 'Link to Finding',
216 | 'value': 'https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2'
217 | }
218 | ],
219 | 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
220 | }
221 | ],
222 | 'channel': 'slack_testing_sandbox',
223 | 'icon_emoji': ':aws:',
224 | 'username': 'notify_slack_test'
225 | }
226 | ]
227 |
228 | snapshots['test_event_get_slack_message_payload_snapshots event_guardduty_finding_medium.json'] = [
229 | {
230 | 'attachments': [
231 | {
232 | 'color': 'warning',
233 | 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
234 | 'fields': [
235 | {
236 | 'short': False,
237 | 'title': 'Description',
238 | 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
239 | },
240 | {
241 | 'short': False,
242 | 'title': 'Finding Type',
243 | 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
244 | },
245 | {
246 | 'short': True,
247 | 'title': 'First Seen',
248 | 'value': '`2020-01-02T01:02:03Z`'
249 | },
250 | {
251 | 'short': True,
252 | 'title': 'Last Seen',
253 | 'value': '`2020-01-03T01:02:03Z`'
254 | },
255 | {
256 | 'short': True,
257 | 'title': 'Severity',
258 | 'value': '`Medium`'
259 | },
260 | {
261 | 'short': True,
262 | 'title': 'Account ID',
263 | 'value': '`123456789`'
264 | },
265 | {
266 | 'short': True,
267 | 'title': 'Count',
268 | 'value': '`1234`'
269 | },
270 | {
271 | 'short': False,
272 | 'title': 'Link to Finding',
273 | 'value': 'https://console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?search=id%3Dsample-id-2'
274 | }
275 | ],
276 | 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
277 | }
278 | ],
279 | 'channel': 'slack_testing_sandbox',
280 | 'icon_emoji': ':aws:',
281 | 'username': 'notify_slack_test'
282 | }
283 | ]
284 |
285 | snapshots['test_sns_get_slack_message_payload_snapshots message_backup.json'] = [
286 | {
287 | 'attachments': [
288 | {
289 | 'fields': [
290 | {
291 | 'title': '✅ An AWS Backup job was completed successfully'
292 | },
293 | {
294 | 'short': False,
295 | 'value': 'BackupJob ID'
296 | },
297 | {
298 | 'short': False,
299 | 'value': '`1b2345b2-f22c-4dab-5eb6-bbc7890ed123`'
300 | },
301 | {
302 | 'short': False,
303 | 'value': 'Resource ARN'
304 | },
305 | {
306 | 'short': False,
307 | 'value': '`arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012e`'
308 | },
309 | {
310 | 'short': False,
311 | 'value': 'Recovery point ARN'
312 | },
313 | {
314 | 'short': False,
315 | 'value': '`arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012d`'
316 | }
317 | ]
318 | }
319 | ],
320 | 'channel': 'slack_testing_sandbox',
321 | 'icon_emoji': ':aws:',
322 | 'username': 'notify_slack_test'
323 | },
324 | {
325 | 'attachments': [
326 | {
327 | 'fields': [
328 | {
329 | 'title': '⚠️ An AWS Backup job failed'
330 | },
331 | {
332 | 'short': False,
333 | 'value': 'BackupJob ID'
334 | },
335 | {
336 | 'short': False,
337 | 'value': '`1b2345b2-f22c-4dab-5eb6-bbc7890ed123`'
338 | },
339 | {
340 | 'short': False,
341 | 'value': 'Resource ARN'
342 | },
343 | {
344 | 'short': False,
345 | 'value': '`arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012e`'
346 | }
347 | ]
348 | }
349 | ],
350 | 'channel': 'slack_testing_sandbox',
351 | 'icon_emoji': ':aws:',
352 | 'username': 'notify_slack_test'
353 | },
354 | {
355 | 'attachments': [
356 | {
357 | 'fields': [
358 | {
359 | 'title': '⚠️ An AWS Backup job failed to complete in time'
360 | },
361 | {
362 | 'short': False,
363 | 'value': 'BackupJob ID'
364 | },
365 | {
366 | 'short': False,
367 | 'value': '`1b2345b2-f22c-4dab-5eb6-bbc7890ed123`'
368 | },
369 | {
370 | 'short': False,
371 | 'value': 'Resource ARN'
372 | },
373 | {
374 | 'short': False,
375 | 'value': '`arn:aws:ec2:us-west-1:123456789012:volume/vol-012f345df6789012e`'
376 | }
377 | ]
378 | }
379 | ],
380 | 'channel': 'slack_testing_sandbox',
381 | 'icon_emoji': ':aws:',
382 | 'username': 'notify_slack_test'
383 | }
384 | ]
385 |
386 | snapshots['test_sns_get_slack_message_payload_snapshots message_cloudwatch_alarm.json'] = [
387 | {
388 | 'attachments': [
389 | {
390 | 'color': 'good',
391 | 'fallback': 'Alarm DBMigrationRequired triggered',
392 | 'fields': [
393 | {
394 | 'short': True,
395 | 'title': 'Alarm Name',
396 | 'value': '`DBMigrationRequired`'
397 | },
398 | {
399 | 'short': False,
400 | 'title': 'Alarm Description',
401 | 'value': '`App is reporting "A JPA error occurred(Unable to build EntityManagerFactory)"`'
402 | },
403 | {
404 | 'short': False,
405 | 'title': 'Alarm reason',
406 | 'value': '`Threshold Crossed: 1 datapoint [1.0 (12/02/19 15:44:00)] was not less than the threshold (1.0).`'
407 | },
408 | {
409 | 'short': True,
410 | 'title': 'Old State',
411 | 'value': '`ALARM`'
412 | },
413 | {
414 | 'short': True,
415 | 'title': 'Current State',
416 | 'value': '`OK`'
417 | },
418 | {
419 | 'short': False,
420 | 'title': 'Link to Alarm',
421 | 'value': 'https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#alarm:alarmFilter=ANY;name=DBMigrationRequired'
422 | }
423 | ],
424 | 'text': 'AWS CloudWatch notification - DBMigrationRequired'
425 | }
426 | ],
427 | 'channel': 'slack_testing_sandbox',
428 | 'icon_emoji': ':aws:',
429 | 'username': 'notify_slack_test'
430 | }
431 | ]
432 |
433 | snapshots['test_sns_get_slack_message_payload_snapshots message_dms_notification.json'] = [
434 | {
435 | 'attachments': [
436 | {
437 | 'fallback': 'A new message',
438 | 'fields': [
439 | {
440 | 'short': True,
441 | 'title': 'Event Source',
442 | 'value': '`replication-task`'
443 | },
444 | {
445 | 'short': True,
446 | 'title': 'Event Time',
447 | 'value': '`2019-02-12 15:45:24.091`'
448 | },
449 | {
450 | 'short': False,
451 | 'title': 'Identifier Link',
452 | 'value': '`https://console.aws.amazon.com/dms/home?region=us-east-1#tasks:ids=hello-world`'
453 | },
454 | {
455 | 'short': True,
456 | 'title': 'SourceId',
457 | 'value': '`hello-world`'
458 | },
459 | {
460 | 'short': False,
461 | 'title': 'Event ID',
462 | 'value': '`http://docs.aws.amazon.com/dms/latest/userguide/CHAP_Events.html#DMS-EVENT-0079 `'
463 | },
464 | {
465 | 'short': False,
466 | 'title': 'Event Message',
467 | 'value': '`Replication task has stopped.`'
468 | }
469 | ],
470 | 'mrkdwn_in': [
471 | 'value'
472 | ],
473 | 'text': 'AWS notification',
474 | 'title': 'DMS Notification Message'
475 | }
476 | ],
477 | 'channel': 'slack_testing_sandbox',
478 | 'icon_emoji': ':aws:',
479 | 'username': 'notify_slack_test'
480 | }
481 | ]
482 |
483 | snapshots['test_sns_get_slack_message_payload_snapshots message_glue_notification.json'] = [
484 | {
485 | 'attachments': [
486 | {
487 | 'fallback': 'A new message',
488 | 'fields': [
489 | {
490 | 'short': True,
491 | 'title': 'version',
492 | 'value': '`0`'
493 | },
494 | {
495 | 'short': False,
496 | 'title': 'id',
497 | 'value': '`ad3c3da1-148c-d5da-9a6a-79f1bc9a8a2e`'
498 | },
499 | {
500 | 'short': True,
501 | 'title': 'detail-type',
502 | 'value': '`Glue Job State Change`'
503 | },
504 | {
505 | 'short': True,
506 | 'title': 'source',
507 | 'value': '`aws.glue`'
508 | },
509 | {
510 | 'short': True,
511 | 'title': 'account',
512 | 'value': '`000000000000`'
513 | },
514 | {
515 | 'short': True,
516 | 'title': 'time',
517 | 'value': '`2021-06-18T12:34:06Z`'
518 | },
519 | {
520 | 'short': True,
521 | 'title': 'region',
522 | 'value': '`us-east-2`'
523 | },
524 | {
525 | 'short': True,
526 | 'title': 'resources',
527 | 'value': '`[]`'
528 | },
529 | {
530 | 'short': False,
531 | 'title': 'detail',
532 | 'value': '`{"jobName": "test_job", "severity": "ERROR", "state": "FAILED", "jobRunId": "jr_ca2144d747b45ad412d3c66a1b6934b6b27aa252be9a21a95c54dfaa224a1925", "message": "SystemExit: 1"}`'
533 | }
534 | ],
535 | 'mrkdwn_in': [
536 | 'value'
537 | ],
538 | 'text': 'AWS notification',
539 | 'title': 'Message'
540 | }
541 | ],
542 | 'channel': 'slack_testing_sandbox',
543 | 'icon_emoji': ':aws:',
544 | 'username': 'notify_slack_test'
545 | }
546 | ]
547 |
548 | snapshots['test_sns_get_slack_message_payload_snapshots message_guardduty_finding.json'] = [
549 | {
550 | 'attachments': [
551 | {
552 | 'color': 'danger',
553 | 'fallback': 'GuardDuty Finding: SAMPLE Unprotected port on EC2 instance i-123123123 is being probed',
554 | 'fields': [
555 | {
556 | 'short': False,
557 | 'title': 'Description',
558 | 'value': '`EC2 instance has an unprotected port which is being probed by a known malicious host.`'
559 | },
560 | {
561 | 'short': False,
562 | 'title': 'Finding Type',
563 | 'value': '`Recon:EC2 PortProbeUnprotectedPort`'
564 | },
565 | {
566 | 'short': True,
567 | 'title': 'First Seen',
568 | 'value': '`2020-01-02T01:02:03Z`'
569 | },
570 | {
571 | 'short': True,
572 | 'title': 'Last Seen',
573 | 'value': '`2020-01-03T01:02:03Z`'
574 | },
575 | {
576 | 'short': True,
577 | 'title': 'Severity',
578 | 'value': '`High`'
579 | },
580 | {
581 | 'short': True,
582 | 'title': 'Account ID',
583 | 'value': '`123456789`'
584 | },
585 | {
586 | 'short': True,
587 | 'title': 'Count',
588 | 'value': '`1234`'
589 | },
590 | {
591 | 'short': False,
592 | 'title': 'Link to Finding',
593 | 'value': 'https://console.amazonaws-us-gov.com/guardduty/home?region=us-gov-east-1#/findings?search=id%3Dsample-id-2'
594 | }
595 | ],
596 | 'text': 'AWS GuardDuty Finding - SAMPLE Unprotected port on EC2 instance i-123123123 is being probed'
597 | }
598 | ],
599 | 'channel': 'slack_testing_sandbox',
600 | 'icon_emoji': ':aws:',
601 | 'username': 'notify_slack_test'
602 | }
603 | ]
604 |
605 | snapshots['test_sns_get_slack_message_payload_snapshots message_text_message.json'] = [
606 | {
607 | 'attachments': [
608 | {
609 | 'fallback': 'A new message',
610 | 'fields': [
611 | {
612 | 'short': False,
613 | 'value': '''This
614 | is
615 | a typical multi-line
616 | message from SNS!
617 |
618 | Have a ~good~ amazing day! :)'''
619 | }
620 | ],
621 | 'mrkdwn_in': [
622 | 'value'
623 | ],
624 | 'text': 'AWS notification',
625 | 'title': 'All Fine'
626 | }
627 | ],
628 | 'channel': 'slack_testing_sandbox',
629 | 'icon_emoji': ':aws:',
630 | 'username': 'notify_slack_test'
631 | }
632 | ]
633 |
--------------------------------------------------------------------------------
/iam.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | create_sns_feedback_role = local.create && var.create_sns_topic && var.enable_sns_topic_delivery_status_logs && var.sns_topic_lambda_feedback_role_arn == ""
3 | }
4 |
5 | data "aws_iam_policy_document" "sns_feedback" {
6 | count = local.create_sns_feedback_role ? 1 : 0
7 |
8 | statement {
9 | sid = "SnsAssume"
10 | effect = "Allow"
11 |
12 | actions = [
13 | "sts:AssumeRole",
14 | "sts:TagSession",
15 | ]
16 |
17 | principals {
18 | type = "Service"
19 | identifiers = ["sns.amazonaws.com"]
20 | }
21 | }
22 | }
23 |
24 | resource "aws_iam_role" "sns_feedback_role" {
25 | count = local.create_sns_feedback_role ? 1 : 0
26 |
27 | name = var.sns_topic_feedback_role_name
28 | description = var.sns_topic_feedback_role_description
29 | path = var.sns_topic_feedback_role_path
30 | force_detach_policies = var.sns_topic_feedback_role_force_detach_policies
31 | permissions_boundary = var.sns_topic_feedback_role_permissions_boundary
32 | assume_role_policy = data.aws_iam_policy_document.sns_feedback[0].json
33 |
34 | tags = merge(
35 | var.tags,
36 | var.sns_topic_feedback_role_tags,
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/main.tf:
--------------------------------------------------------------------------------
1 | data "aws_caller_identity" "current" {}
2 | data "aws_partition" "current" {}
3 | data "aws_region" "current" {}
4 |
5 | locals {
6 | create = var.create && var.putin_khuylo
7 |
8 | sns_topic_arn = try(
9 | aws_sns_topic.this[0].arn,
10 | "arn:${data.aws_partition.current.id}:sns:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${var.sns_topic_name}",
11 | ""
12 | )
13 |
14 | sns_feedback_role = local.create_sns_feedback_role ? aws_iam_role.sns_feedback_role[0].arn : var.sns_topic_lambda_feedback_role_arn
15 | lambda_policy_document = {
16 | sid = "AllowWriteToCloudwatchLogs"
17 | effect = "Allow"
18 | actions = ["logs:CreateLogStream", "logs:PutLogEvents"]
19 | resources = [replace("${try(aws_cloudwatch_log_group.lambda[0].arn, "")}:*", ":*:*", ":*")]
20 | }
21 |
22 | lambda_policy_document_kms = {
23 | sid = "AllowKMSDecrypt"
24 | effect = "Allow"
25 | actions = ["kms:Decrypt"]
26 | resources = [var.kms_key_arn]
27 | }
28 |
29 | lambda_policy_document_securityhub = {
30 | sid = "AllowSecurityHub"
31 | effect = "Allow"
32 | actions = ["securityhub:BatchUpdateFindings"]
33 | resources = ["*"]
34 | }
35 |
36 | lambda_handler = try(split(".", basename(var.lambda_source_path))[0], "notify_slack")
37 | }
38 |
39 | data "aws_iam_policy_document" "lambda" {
40 | count = var.create ? 1 : 0
41 |
42 | dynamic "statement" {
43 | for_each = concat([local.lambda_policy_document,
44 | local.lambda_policy_document_securityhub], var.kms_key_arn != "" ? [local.lambda_policy_document_kms] : [])
45 | content {
46 | sid = statement.value.sid
47 | effect = statement.value.effect
48 | actions = statement.value.actions
49 | resources = statement.value.resources
50 | }
51 | }
52 | }
53 |
54 | resource "aws_cloudwatch_log_group" "lambda" {
55 | count = var.create ? 1 : 0
56 |
57 | name = "/aws/lambda/${var.lambda_function_name}"
58 | retention_in_days = var.cloudwatch_log_group_retention_in_days
59 | kms_key_id = var.cloudwatch_log_group_kms_key_id
60 |
61 | tags = merge(var.tags, var.cloudwatch_log_group_tags)
62 | }
63 |
64 | resource "aws_sns_topic" "this" {
65 | count = var.create_sns_topic && var.create ? 1 : 0
66 |
67 | name = var.sns_topic_name
68 |
69 | kms_master_key_id = var.sns_topic_kms_key_id
70 |
71 | lambda_failure_feedback_role_arn = var.enable_sns_topic_delivery_status_logs ? local.sns_feedback_role : null
72 | lambda_success_feedback_role_arn = var.enable_sns_topic_delivery_status_logs ? local.sns_feedback_role : null
73 | lambda_success_feedback_sample_rate = var.enable_sns_topic_delivery_status_logs ? var.sns_topic_lambda_feedback_sample_rate : null
74 |
75 | tags = merge(var.tags, var.sns_topic_tags)
76 | }
77 |
78 |
79 | resource "aws_sns_topic_subscription" "sns_notify_slack" {
80 | count = var.create ? 1 : 0
81 |
82 | topic_arn = local.sns_topic_arn
83 | protocol = "lambda"
84 | endpoint = module.lambda.lambda_function_arn
85 | filter_policy = var.subscription_filter_policy
86 | filter_policy_scope = var.subscription_filter_policy_scope
87 | }
88 |
89 | module "lambda" {
90 | source = "terraform-aws-modules/lambda/aws"
91 | version = "6.8.0"
92 |
93 | create = var.create
94 |
95 | function_name = var.lambda_function_name
96 | description = var.lambda_description
97 |
98 | hash_extra = var.hash_extra
99 | handler = "${local.lambda_handler}.lambda_handler"
100 | source_path = var.lambda_source_path != null ? "${path.root}/${var.lambda_source_path}" : "${path.module}/functions/notify_slack.py"
101 | recreate_missing_package = var.recreate_missing_package
102 | runtime = "python3.11"
103 | architectures = var.architectures
104 | timeout = 30
105 | kms_key_arn = var.kms_key_arn
106 | reserved_concurrent_executions = var.reserved_concurrent_executions
107 | ephemeral_storage_size = var.lambda_function_ephemeral_storage_size
108 | trigger_on_package_timestamp = var.trigger_on_package_timestamp
109 |
110 | # If publish is disabled, there will be "Error adding new Lambda Permission for notify_slack:
111 | # InvalidParameterValueException: We currently do not support adding policies for $LATEST."
112 | publish = true
113 |
114 | environment_variables = {
115 | SLACK_WEBHOOK_URL = var.slack_webhook_url
116 | SLACK_CHANNEL = var.slack_channel
117 | SLACK_USERNAME = var.slack_username
118 | SLACK_EMOJI = var.slack_emoji
119 | LOG_EVENTS = var.log_events ? "True" : "False"
120 | }
121 |
122 | create_role = var.lambda_role == ""
123 | lambda_role = var.lambda_role
124 | role_name = "${var.iam_role_name_prefix}-${var.lambda_function_name}"
125 | role_permissions_boundary = var.iam_role_boundary_policy_arn
126 | role_tags = var.iam_role_tags
127 | role_path = var.iam_role_path
128 | policy_path = var.iam_policy_path
129 |
130 | # Do not use Lambda's policy for cloudwatch logs, because we have to add a policy
131 | # for KMS conditionally. This way attach_policy_json is always true independenty of
132 | # the value of presense of KMS. Famous "computed values in count" bug...
133 | attach_cloudwatch_logs_policy = false
134 | attach_policy_json = true
135 | policy_json = try(data.aws_iam_policy_document.lambda[0].json, "")
136 |
137 | use_existing_cloudwatch_log_group = true
138 | attach_network_policy = var.lambda_function_vpc_subnet_ids != null
139 |
140 | dead_letter_target_arn = var.lambda_dead_letter_target_arn
141 | attach_dead_letter_policy = var.lambda_attach_dead_letter_policy
142 |
143 | allowed_triggers = {
144 | AllowExecutionFromSNS = {
145 | principal = "sns.amazonaws.com"
146 | source_arn = local.sns_topic_arn
147 | }
148 | }
149 |
150 | store_on_s3 = var.lambda_function_store_on_s3
151 | s3_bucket = var.lambda_function_s3_bucket
152 |
153 | vpc_subnet_ids = var.lambda_function_vpc_subnet_ids
154 | vpc_security_group_ids = var.lambda_function_vpc_security_group_ids
155 |
156 | tags = merge(var.tags, var.lambda_function_tags)
157 |
158 | depends_on = [aws_cloudwatch_log_group.lambda]
159 | }
160 |
--------------------------------------------------------------------------------
/outputs.tf:
--------------------------------------------------------------------------------
1 | output "slack_topic_arn" {
2 | description = "The ARN of the SNS topic from which messages will be sent to Slack"
3 | value = local.sns_topic_arn
4 | }
5 |
6 | # todo: Remove `this_slack_topic_arn` output during next major release 5.x
7 | output "this_slack_topic_arn" {
8 | description = "The ARN of the SNS topic from which messages will be sent to Slack (backward compatibility for version 4.x)"
9 | value = local.sns_topic_arn
10 | }
11 |
12 | output "lambda_iam_role_arn" {
13 | description = "The ARN of the IAM role used by Lambda function"
14 | value = module.lambda.lambda_role_arn
15 | }
16 |
17 | output "lambda_iam_role_name" {
18 | description = "The name of the IAM role used by Lambda function"
19 | value = module.lambda.lambda_role_name
20 | }
21 |
22 | output "notify_slack_lambda_function_arn" {
23 | description = "The ARN of the Lambda function"
24 | value = module.lambda.lambda_function_arn
25 | }
26 |
27 | output "notify_slack_lambda_function_name" {
28 | description = "The name of the Lambda function"
29 | value = module.lambda.lambda_function_name
30 | }
31 |
32 | output "notify_slack_lambda_function_invoke_arn" {
33 | description = "The ARN to be used for invoking Lambda function from API Gateway"
34 | value = module.lambda.lambda_function_invoke_arn
35 | }
36 |
37 | output "notify_slack_lambda_function_last_modified" {
38 | description = "The date Lambda function was last modified"
39 | value = module.lambda.lambda_function_last_modified
40 | }
41 |
42 | output "notify_slack_lambda_function_version" {
43 | description = "Latest published version of your Lambda function"
44 | value = module.lambda.lambda_function_version
45 | }
46 |
47 | output "lambda_cloudwatch_log_group_arn" {
48 | description = "The Amazon Resource Name (ARN) specifying the log group"
49 | value = try(aws_cloudwatch_log_group.lambda[0].arn, "")
50 | }
51 |
52 | output "sns_topic_feedback_role_arn" {
53 | description = "The Amazon Resource Name (ARN) of the IAM role used for SNS delivery status logging"
54 | value = local.sns_feedback_role
55 | }
56 |
--------------------------------------------------------------------------------
/variables.tf:
--------------------------------------------------------------------------------
1 | variable "putin_khuylo" {
2 | description = "Do you agree that Putin doesn't respect Ukrainian sovereignty and territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo!"
3 | type = bool
4 | default = true
5 | }
6 |
7 | variable "architectures" {
8 | description = "Instruction set architecture for your Lambda function. Valid values are [\"x86_64\"] and [\"arm64\"]."
9 | type = list(string)
10 | default = null
11 | }
12 |
13 | variable "create" {
14 | description = "Whether to create all resources"
15 | type = bool
16 | default = true
17 | }
18 |
19 | variable "create_sns_topic" {
20 | description = "Whether to create new SNS topic"
21 | type = bool
22 | default = true
23 | }
24 |
25 | variable "hash_extra" {
26 | description = "The string to add into hashing function. Useful when building same source path for different functions."
27 | type = string
28 | default = ""
29 | }
30 |
31 | variable "lambda_role" {
32 | description = "IAM role attached to the Lambda Function. If this is set then a role will not be created for you."
33 | type = string
34 | default = ""
35 | }
36 |
37 | variable "lambda_function_name" {
38 | description = "The name of the Lambda function to create"
39 | type = string
40 | default = "notify_slack"
41 | }
42 |
43 | variable "lambda_description" {
44 | description = "The description of the Lambda function"
45 | type = string
46 | default = null
47 | }
48 |
49 | variable "lambda_source_path" {
50 | description = "The source path of the custom Lambda function"
51 | type = string
52 | default = null
53 | }
54 |
55 | variable "lambda_dead_letter_target_arn" {
56 | description = "The ARN of an SNS topic or SQS queue to notify when an invocation fails."
57 | type = string
58 | default = null
59 | }
60 |
61 | variable "lambda_attach_dead_letter_policy" {
62 | description = "Controls whether SNS/SQS dead letter notification policy should be added to IAM role for Lambda Function"
63 | type = bool
64 | default = false
65 | }
66 |
67 | variable "sns_topic_name" {
68 | description = "The name of the SNS topic to create"
69 | type = string
70 | }
71 |
72 | variable "sns_topic_kms_key_id" {
73 | description = "ARN of the KMS key used for enabling SSE on the topic"
74 | type = string
75 | default = ""
76 | }
77 |
78 | variable "enable_sns_topic_delivery_status_logs" {
79 | description = "Whether to enable SNS topic delivery status logs"
80 | type = bool
81 | default = false
82 | }
83 |
84 | variable "sns_topic_lambda_feedback_role_arn" {
85 | description = "IAM role for SNS topic delivery status logs. If this is set then a role will not be created for you."
86 | type = string
87 | default = ""
88 | }
89 |
90 | variable "sns_topic_feedback_role_name" {
91 | description = "Name of the IAM role to use for SNS topic delivery status logging"
92 | type = string
93 | default = null
94 | }
95 |
96 | variable "sns_topic_feedback_role_description" {
97 | description = "Description of IAM role to use for SNS topic delivery status logging"
98 | type = string
99 | default = null
100 | }
101 |
102 | variable "sns_topic_feedback_role_path" {
103 | description = "Path of IAM role to use for SNS topic delivery status logging"
104 | type = string
105 | default = null
106 | }
107 |
108 | variable "sns_topic_feedback_role_force_detach_policies" {
109 | description = "Specifies to force detaching any policies the IAM role has before destroying it."
110 | type = bool
111 | default = true
112 | }
113 |
114 | variable "sns_topic_feedback_role_permissions_boundary" {
115 | description = "The ARN of the policy that is used to set the permissions boundary for the IAM role used by SNS topic delivery status logging"
116 | type = string
117 | default = null
118 | }
119 |
120 | variable "sns_topic_feedback_role_tags" {
121 | description = "A map of tags to assign to IAM the SNS topic feedback role"
122 | type = map(string)
123 | default = {}
124 | }
125 |
126 | variable "sns_topic_lambda_feedback_sample_rate" {
127 | description = "The percentage of successful deliveries to log"
128 | type = number
129 | default = 100
130 | }
131 |
132 | variable "slack_webhook_url" {
133 | description = "The URL of Slack webhook"
134 | type = string
135 | }
136 |
137 | variable "slack_channel" {
138 | description = "The name of the channel in Slack for notifications"
139 | type = string
140 | }
141 |
142 | variable "slack_username" {
143 | description = "The username that will appear on Slack messages"
144 | type = string
145 | }
146 |
147 | variable "slack_emoji" {
148 | description = "A custom emoji that will appear on Slack messages"
149 | type = string
150 | default = ":aws:"
151 | }
152 |
153 | variable "kms_key_arn" {
154 | description = "ARN of the KMS key used for decrypting slack webhook url"
155 | type = string
156 | default = ""
157 | }
158 |
159 | variable "recreate_missing_package" {
160 | description = "Whether to recreate missing Lambda package if it is missing locally or not"
161 | type = bool
162 | default = true
163 | }
164 |
165 | variable "log_events" {
166 | description = "Boolean flag to enabled/disable logging of incoming events"
167 | type = bool
168 | default = false
169 | }
170 |
171 | variable "reserved_concurrent_executions" {
172 | 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"
173 | type = number
174 | default = -1
175 | }
176 |
177 | variable "cloudwatch_log_group_retention_in_days" {
178 | description = "Specifies the number of days you want to retain log events in log group for Lambda."
179 | type = number
180 | default = 0
181 | }
182 |
183 | variable "cloudwatch_log_group_kms_key_id" {
184 | description = "The ARN of the KMS Key to use when encrypting log data for Lambda"
185 | type = string
186 | default = null
187 | }
188 |
189 | variable "tags" {
190 | description = "A map of tags to add to all resources"
191 | type = map(string)
192 | default = {}
193 | }
194 |
195 | variable "iam_role_tags" {
196 | description = "Additional tags for the IAM role"
197 | type = map(string)
198 | default = {}
199 | }
200 |
201 | variable "iam_role_boundary_policy_arn" {
202 | description = "The ARN of the policy that is used to set the permissions boundary for the role"
203 | type = string
204 | default = null
205 | }
206 |
207 | variable "iam_role_name_prefix" {
208 | description = "A unique role name beginning with the specified prefix"
209 | type = string
210 | default = "lambda"
211 | }
212 |
213 | variable "iam_role_path" {
214 | description = "Path of IAM role to use for Lambda Function"
215 | type = string
216 | default = null
217 | }
218 |
219 | variable "iam_policy_path" {
220 | description = "Path of policies to that should be added to IAM role for Lambda Function"
221 | type = string
222 | default = null
223 | }
224 |
225 | variable "lambda_function_tags" {
226 | description = "Additional tags for the Lambda function"
227 | type = map(string)
228 | default = {}
229 | }
230 |
231 | variable "lambda_function_vpc_subnet_ids" {
232 | description = "List of subnet ids when Lambda Function should run in the VPC. Usually private or intra subnets."
233 | type = list(string)
234 | default = null
235 | }
236 |
237 | variable "lambda_function_vpc_security_group_ids" {
238 | description = "List of security group ids when Lambda Function should run in the VPC."
239 | type = list(string)
240 | default = null
241 | }
242 |
243 | variable "lambda_function_store_on_s3" {
244 | description = "Whether to store produced artifacts on S3 or locally."
245 | type = bool
246 | default = false
247 | }
248 |
249 | variable "lambda_function_s3_bucket" {
250 | description = "S3 bucket to store artifacts"
251 | type = string
252 | default = null
253 | }
254 |
255 | variable "lambda_function_ephemeral_storage_size" {
256 | description = "Amount of ephemeral storage (/tmp) in MB your Lambda Function can use at runtime. Valid value between 512 MB to 10,240 MB (10 GB)."
257 | type = number
258 | default = 512
259 | }
260 |
261 | variable "sns_topic_tags" {
262 | description = "Additional tags for the SNS topic"
263 | type = map(string)
264 | default = {}
265 | }
266 |
267 | variable "cloudwatch_log_group_tags" {
268 | description = "Additional tags for the Cloudwatch log group"
269 | type = map(string)
270 | default = {}
271 | }
272 |
273 | variable "subscription_filter_policy" {
274 | description = "(Optional) A valid filter policy that will be used in the subscription to filter messages seen by the target resource."
275 | type = string
276 | default = null
277 | }
278 |
279 | variable "subscription_filter_policy_scope" {
280 | description = "(Optional) A valid filter policy scope MessageAttributes|MessageBody"
281 | type = string
282 | default = null
283 | }
284 |
285 | variable "trigger_on_package_timestamp" {
286 | description = "(Optional) Whether or not to ignore the file timestamp when deciding to create the archive"
287 | type = bool
288 | default = false
289 | }
290 |
--------------------------------------------------------------------------------
/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 4.8"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------