├── .github └── workflows │ └── validate.yml ├── .gitignore ├── .markdownlintrc ├── .pre-commit-config.yaml ├── .terraform-docs.yml ├── LICENSE ├── README.md ├── bin └── make-test ├── event-pattern.json ├── examples ├── no-pagerduty │ ├── main.tf │ ├── providers.tf │ └── variables.tf └── simple │ ├── main.tf │ ├── providers.tf │ └── variables.tf ├── main.tf ├── renovate.json ├── variables.tf └── versions.tf /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: validate-tf 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | validate-tf: 13 | uses: trussworks/shared-actions/.github/workflows/validate-tf.yml@main 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .terraform 3 | terraform.tfstate 4 | terraform.tfstate.backup 5 | terraform.tfstate.*.backup 6 | -------------------------------------------------------------------------------- /.markdownlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "first-header-h1": false, 4 | "first-line-h1": false, 5 | "line_length": false, 6 | "no-multiple-blanks": false, 7 | "no-inline-html": false 8 | } 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.4.0 4 | hooks: 5 | - id: check-json 6 | - id: check-merge-conflict 7 | - id: check-yaml 8 | - id: detect-private-key 9 | - id: pretty-format-json 10 | args: 11 | - --autofix 12 | - id: trailing-whitespace 13 | - id: check-symlinks 14 | - id: end-of-file-fixer 15 | - id: mixed-line-ending 16 | 17 | - repo: https://github.com/executablebooks/mdformat 18 | rev: 0.7.16 19 | hooks: 20 | - id: mdformat 21 | additional_dependencies: 22 | - mdformat-gfm 23 | - mdformat-toc 24 | # mdformat fights with terraform_docs 25 | exclude: README.m(ark)?d(own)? 26 | 27 | - repo: https://github.com/igorshubovych/markdownlint-cli 28 | rev: v0.33.0 29 | hooks: 30 | - id: markdownlint 31 | 32 | - repo: https://github.com/detailyang/pre-commit-shell 33 | rev: 1.0.5 34 | hooks: 35 | - id: shell-lint 36 | 37 | - repo: https://github.com/antonbabenko/pre-commit-terraform 38 | rev: v1.77.1 39 | hooks: 40 | - id: terraform_fmt 41 | -------------------------------------------------------------------------------- /.terraform-docs.yml: -------------------------------------------------------------------------------- 1 | settings: 2 | html: false 3 | anchor: false 4 | formatter: "markdown table" 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, TrussWorks, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Enable AWS GuardDuty and configures any findings to be sent to and SNS topic. 2 | 3 | Creates the following resources: 4 | 5 | - CloudWatch event rule to filter GuardDuty Findings 6 | - CloudWatch event target to send to SNS topic formatted as `GuardDuty finding: ` 7 | 8 | Optionally, it can also create the GuardDuty detector as well. 9 | 10 | ## Usage 11 | 12 | ```hcl 13 | module "guardduty-notifications" { 14 | source = "trussworks/guardduty-notifications/aws" 15 | version = "5.0.0" 16 | 17 | sns_topic_slack_arn = aws_sns_topic.slack.arn 18 | sns_topic_pagerduty_arn = aws_sns_topic.pagerduty.arn 19 | } 20 | ``` 21 | 22 | ## Upgrade Notice v4.x.x to v5.x.x 23 | 24 | - The `sns_topic_slack` and `sns_topic_pagerduty` variables have been 25 | renamed to `sns_topic_slack_arn` and `sns_topic_pagerduty_arn`; they 26 | are also taking ARNs as values, and not `aws_sns_topic` objects. We 27 | made this change to better handle the outputs of the `notify-slack` 28 | Terraform module, which outputs names and ARNs, but not objects. 29 | 30 | ## Upgrade Notice v2.x.x to v3.x.x 31 | 32 | Version 3 makes a number of changes to the module that will break if it 33 | is updated in place. Specifically: 34 | 35 | - The GuardDuty detector is now an optional part of the module, and 36 | defaults to off; if you are leaving the GuardDuty detector in this 37 | module, you will need to add "create_detector = true" as a parameter 38 | and do a `terraform state mv` of the detector like so: 39 | 40 | ```console 41 | terraform state mv module.module_name.aws_guardduty_detector.main module.module_name.aws_guardduty_detector.main[0] 42 | ``` 43 | 44 | - The `sns_topic_name_slack` and `sns_topic_name_pagerduty` variables 45 | have been renamed `sns_topic_slack` and `sns_topic_pagerduty` because 46 | they are not actually names, but the actual SNS topic objects. 47 | 48 | <!-- BEGIN_TF_DOCS --> 49 | ## Requirements 50 | 51 | | Name | Version | 52 | |------|---------| 53 | | terraform | >= 1.0 | 54 | | aws | >= 3.0 | 55 | 56 | ## Providers 57 | 58 | | Name | Version | 59 | |------|---------| 60 | | aws | >= 3.0 | 61 | 62 | ## Modules 63 | 64 | No modules. 65 | 66 | ## Resources 67 | 68 | | Name | Type | 69 | |------|------| 70 | | [aws_cloudwatch_event_rule.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | 71 | | [aws_cloudwatch_event_target.pagerduty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | 72 | | [aws_cloudwatch_event_target.slack](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | 73 | | [aws_guardduty_detector.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/guardduty_detector) | resource | 74 | 75 | ## Inputs 76 | 77 | | Name | Description | Type | Default | Required | 78 | |------|-------------|------|---------|:--------:| 79 | | create\_detector | Create GuardDuty detector | `bool` | `false` | no | 80 | | pagerduty\_notifications | Enable PagerDuty notifications for GuardDuty findings | `bool` | `true` | no | 81 | | slack\_notifications | Enable Slack notifications for GuardDuty findings | `bool` | `true` | no | 82 | | sns\_topic\_pagerduty\_arn | PagerDuty SNS Topic ARN | `string` | `""` | no | 83 | | sns\_topic\_slack\_arn | Slack SNS Topic ARN | `string` | `""` | no | 84 | 85 | ## Outputs 86 | 87 | No outputs. 88 | <!-- END_TF_DOCS --> 89 | 90 | ## Developer Setup 91 | 92 | Install dependencies (macOS) 93 | 94 | ```shell 95 | brew install pre-commit go terraform terraform-docs 96 | pre-commit install --install-hooks 97 | ``` 98 | -------------------------------------------------------------------------------- /bin/make-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | go_test_output="/tmp/go-test.out" 6 | 7 | go test -short -count 1 -v -timeout 90m github.com/trussworks/terraform-aws-guardduty-notifications/test/... | tee "${go_test_output}" 8 | 9 | # Check if we are running tests inside of CircleCI by checking for a $CIRCLECI 10 | # environment variable. The dash after $CIRCLECI substitutes a null value if 11 | # CIRCLECI is unset. This prevents unbound variable errors 12 | if [[ -n ${CIRCLECI-} ]]; then 13 | mkdir -p "${TEST_RESULTS}"/gotest 14 | go-junit-report < "${go_test_output}" \ 15 | > "${TEST_RESULTS}/gotest/go-test-report.xml" 16 | fi 17 | -------------------------------------------------------------------------------- /event-pattern.json: -------------------------------------------------------------------------------- 1 | { 2 | "detail-type": [ 3 | "GuardDuty Finding" 4 | ], 5 | "source": [ 6 | "aws.guardduty" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/no-pagerduty/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_sns_topic" "slack" { 2 | name = var.sns_topic_name_slack 3 | } 4 | 5 | module "guardduty-notifications" { 6 | source = "../../" 7 | 8 | create_detector = false 9 | 10 | pagerduty_notifications = false 11 | 12 | sns_topic_slack_arn = aws_sns_topic.slack.arn 13 | } 14 | -------------------------------------------------------------------------------- /examples/no-pagerduty/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 3.0" 3 | } 4 | -------------------------------------------------------------------------------- /examples/no-pagerduty/variables.tf: -------------------------------------------------------------------------------- 1 | variable "sns_topic_name_slack" { 2 | type = string 3 | } 4 | -------------------------------------------------------------------------------- /examples/simple/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_sns_topic" "slack" { 2 | name = var.sns_topic_name_slack 3 | } 4 | 5 | resource "aws_sns_topic" "pagerduty" { 6 | name = var.sns_topic_name_pagerduty 7 | } 8 | 9 | 10 | module "guardduty-notifications" { 11 | source = "../../" 12 | 13 | create_detector = false 14 | 15 | sns_topic_slack_arn = aws_sns_topic.slack.arn 16 | sns_topic_pagerduty_arn = aws_sns_topic.pagerduty.arn 17 | } 18 | -------------------------------------------------------------------------------- /examples/simple/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 3.0" 3 | } 4 | -------------------------------------------------------------------------------- /examples/simple/variables.tf: -------------------------------------------------------------------------------- 1 | variable "sns_topic_name_slack" { 2 | type = string 3 | } 4 | 5 | variable "sns_topic_name_pagerduty" { 6 | type = string 7 | } 8 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | # 2 | # GuardDuty 3 | # 4 | 5 | resource "aws_guardduty_detector" "main" { 6 | count = var.create_detector ? 1 : 0 7 | 8 | enable = true 9 | } 10 | 11 | # 12 | # CloudWatch Event 13 | # 14 | 15 | resource "aws_cloudwatch_event_rule" "main" { 16 | name = "guardduty-finding-events" 17 | description = "AWS GuardDuty event findings" 18 | event_pattern = file("${path.module}/event-pattern.json") 19 | } 20 | 21 | # More details about the response syntax can be found here: 22 | # https://docs.aws.amazon.com/guardduty/latest/ug/get-findings.html#get-findings-response-syntax 23 | resource "aws_cloudwatch_event_target" "slack" { 24 | count = var.slack_notifications ? 1 : 0 25 | 26 | rule = aws_cloudwatch_event_rule.main.name 27 | target_id = "send-to-sns-slack" 28 | arn = var.sns_topic_slack_arn 29 | 30 | input_transformer { 31 | input_paths = { 32 | title = "$.detail.title" 33 | description = "$.detail.description" 34 | eventTime = "$.detail.service.eventFirstSeen" 35 | region = "$.detail.region" 36 | } 37 | 38 | input_template = "\"GuardDuty finding in <region> first seen at <eventTime>: <title> <description>\"" 39 | } 40 | } 41 | 42 | resource "aws_cloudwatch_event_target" "pagerduty" { 43 | count = var.pagerduty_notifications ? 1 : 0 44 | 45 | rule = aws_cloudwatch_event_rule.main.name 46 | target_id = "send-to-sns-pagerduty" 47 | arn = var.sns_topic_pagerduty_arn 48 | } 49 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | ":disableDependencyDashboard" 5 | ], 6 | "labels": [ 7 | "dependencies" 8 | ], 9 | "packageRules": [ 10 | { 11 | "automerge": true, 12 | "description": "Automerge all updates except major versions", 13 | "matchUpdateTypes": [ 14 | "patch", 15 | "pin", 16 | "digest", 17 | "minor" 18 | ] 19 | }, 20 | { 21 | "description": "Tag the waddlers Github Team for major updates", 22 | "matchUpdateTypes": [ 23 | "major" 24 | ], 25 | "reviewers": [ 26 | "team:waddlers" 27 | ] 28 | }, 29 | { 30 | "automerge": true, 31 | "description": "Group minor and patch updates into a single PR", 32 | "groupName": "dependencies", 33 | "managers": [ 34 | "terraform", 35 | "pre-commit", 36 | "dockerfile", 37 | "github-actions" 38 | ], 39 | "matchUpdateTypes": [ 40 | "minor", 41 | "patch" 42 | ] 43 | } 44 | ], 45 | "schedule": [ 46 | "every weekend" 47 | ], 48 | "separateMinorPatch": true, 49 | "timezone": "America/Los_Angeles" 50 | } 51 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "create_detector" { 2 | description = "Create GuardDuty detector" 3 | type = bool 4 | default = false 5 | } 6 | 7 | variable "pagerduty_notifications" { 8 | description = "Enable PagerDuty notifications for GuardDuty findings" 9 | type = bool 10 | default = true 11 | } 12 | 13 | variable "slack_notifications" { 14 | description = "Enable Slack notifications for GuardDuty findings" 15 | type = bool 16 | default = true 17 | } 18 | 19 | variable "sns_topic_slack_arn" { 20 | description = "Slack SNS Topic ARN" 21 | type = string 22 | default = "" 23 | } 24 | 25 | variable "sns_topic_pagerduty_arn" { 26 | description = "PagerDuty SNS Topic ARN" 27 | type = string 28 | default = "" 29 | } 30 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = ">= 3.0" 6 | } 7 | } 8 | --------------------------------------------------------------------------------