Setting up AWS GuardDuty with Terraform
A comprehensive guide to configuring Amazon GuardDuty for threat detection using Terraform Infrastructure as Code
Setting up AWS GuardDuty with Terraform
Amazon GuardDuty is a threat detection service that continuously monitors for malicious activity and unauthorized behavior. This guide shows how to set up GuardDuty using Terraform.
Prerequisites
- AWS CLI configured
- Terraform installed
- Understanding of security monitoring
- Basic knowledge of AWS Organizations (optional)
Project Structure
aws-guardduty-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Basic GuardDuty Configuration
# main.tf
provider "aws" {
region = var.aws_region
}
# Enable GuardDuty
resource "aws_guardduty_detector" "main" {
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = true
}
}
malware_protection {
scan_ec2_instance_with_findings {
enable = true
}
}
}
finding_publishing_frequency = "FIFTEEN_MINUTES"
tags = {
Environment = var.environment
}
}
# SNS Topic for Notifications
resource "aws_sns_topic" "guardduty_findings" {
name = "${var.project_name}-guardduty-findings"
tags = {
Environment = var.environment
}
}
# SNS Topic Policy
resource "aws_sns_topic_policy" "guardduty_findings" {
arn = aws_sns_topic.guardduty_findings.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowGuardDutyPublish"
Effect = "Allow"
Principal = {
Service = "guardduty.amazonaws.com"
}
Action = "SNS:Publish"
Resource = aws_sns_topic.guardduty_findings.arn
}
]
})
}
# EventBridge Rule for GuardDuty Findings
resource "aws_cloudwatch_event_rule" "guardduty_findings" {
name = "${var.project_name}-guardduty-findings"
description = "Capture GuardDuty findings"
event_pattern = jsonencode({
source = ["aws.guardduty"]
detail-type = ["GuardDuty Finding"]
})
}
# EventBridge Target
resource "aws_cloudwatch_event_target" "sns" {
rule = aws_cloudwatch_event_rule.guardduty_findings.name
target_id = "SendToSNS"
arn = aws_sns_topic.guardduty_findings.arn
input_transformer {
input_paths = {
severity = "$.detail.severity"
type = "$.detail.type"
description = "$.detail.description"
account = "$.detail.accountId"
region = "$.detail.region"
}
input_template = "\"GuardDuty Finding: Severity <severity>, Type <type> in account <account> region <region>. Description: <description>\""
}
}
Variables Configuration
# variables.tf
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Project name"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "master_account_id" {
description = "AWS Organizations master account ID"
type = string
default = ""
}
variable "member_accounts" {
description = "List of member account IDs to enable GuardDuty"
type = list(string)
default = []
}
Best Practices
-
Detector Management
- Enable all data sources
- Set appropriate frequency
- Configure notifications
- Regular finding reviews
-
Security
- Enable multi-account
- Use proper IAM roles
- Configure trusted IPs
- Regular security reviews
-
Cost Optimization
- Monitor usage
- Filter false positives
- Use appropriate settings
- Regular cost reviews
-
Performance
- Optimize finding rules
- Use efficient filters
- Monitor API limits
- Regular performance reviews
Multi-Account Configuration
# Organization Admin Account
resource "aws_guardduty_organization_admin_account" "admin" {
count = var.master_account_id != "" ? 1 : 0
admin_account_id = var.master_account_id
}
# Organization Configuration
resource "aws_guardduty_organization_configuration" "org" {
count = var.master_account_id != "" ? 1 : 0
auto_enable = true
detector_id = aws_guardduty_detector.main.id
datasources {
s3_logs {
auto_enable = true
}
kubernetes {
audit_logs {
auto_enable = true
}
}
malware_protection {
scan_ec2_instance_with_findings {
auto_enable = true
}
}
}
}
# Member Accounts
resource "aws_guardduty_member" "members" {
for_each = toset(var.member_accounts)
account_id = each.value
detector_id = aws_guardduty_detector.main.id
email = data.aws_organizations_account.members[each.value].email
invite = true
depends_on = [aws_guardduty_organization_configuration.org]
}
# Data source for member accounts
data "aws_organizations_account" "members" {
for_each = toset(var.member_accounts)
account_id = each.value
}
Findings Configuration
# Filter for Known False Positives
resource "aws_guardduty_filter" "known_activities" {
name = "known-activities"
action = "ARCHIVE"
detector_id = aws_guardduty_detector.main.id
rank = 1
finding_criteria {
criterion {
field = "service.additionalInfo.threatListName"
equals = ["Known-IP"]
}
criterion {
field = "severity"
equals = [1]
}
}
}
# IPSet for Trusted IPs
resource "aws_guardduty_ipset" "trusted" {
activate = true
detector_id = aws_guardduty_detector.main.id
format = "TXT"
location = "s3://${aws_s3_bucket.trusted_ips.id}/trusted-ips.txt"
name = "trusted-ips"
tags = {
Environment = var.environment
}
}
# S3 Bucket for Trusted IPs
resource "aws_s3_bucket" "trusted_ips" {
bucket = "${var.project_name}-trusted-ips"
tags = {
Environment = var.environment
}
}
# S3 Bucket Policy
resource "aws_s3_bucket_policy" "trusted_ips" {
bucket = aws_s3_bucket.trusted_ips.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowGuardDutyAccess"
Effect = "Allow"
Principal = {
Service = "guardduty.amazonaws.com"
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.trusted_ips.arn}/*"
}
]
})
}
Monitoring Configuration
# CloudWatch Dashboard
resource "aws_cloudwatch_dashboard" "guardduty" {
dashboard_name = "${var.project_name}-guardduty-dashboard"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
x = 0
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/GuardDuty", "FindingsCount", "DetectorId", aws_guardduty_detector.main.id]
]
period = 300
stat = "Sum"
region = var.aws_region
title = "GuardDuty Findings"
}
}
]
})
}
# CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "high_severity_findings" {
alarm_name = "${var.project_name}-high-severity-findings"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "FindingsCount"
namespace = "AWS/GuardDuty"
period = "300"
statistic = "Sum"
threshold = "0"
alarm_description = "This metric monitors high severity GuardDuty findings"
alarm_actions = [aws_sns_topic.guardduty_findings.arn]
dimensions = {
DetectorId = aws_guardduty_detector.main.id
}
}
Deployment Steps
- Initialize Terraform:
terraform init
- Plan the deployment:
terraform plan
- Apply the configuration:
terraform apply
Clean Up
Remove all resources when done:
terraform destroy
Common Use Cases
- Security Operations Center Integration
# Lambda Function for SOC Integration
resource "aws_lambda_function" "soc_integration" {
filename = "soc_integration.zip"
function_name = "${var.project_name}-soc-integration"
role = aws_iam_role.soc_lambda.arn
handler = "index.handler"
runtime = "nodejs14.x"
environment {
variables = {
SOC_API_ENDPOINT = var.soc_api_endpoint
SOC_API_KEY = var.soc_api_key
}
}
tags = {
Environment = var.environment
}
}
# EventBridge Target for SOC
resource "aws_cloudwatch_event_target" "soc" {
rule = aws_cloudwatch_event_rule.guardduty_findings.name
target_id = "SendToSOC"
arn = aws_lambda_function.soc_integration.arn
}
- Slack Integration
# Lambda Function for Slack Notifications
resource "aws_lambda_function" "slack_notifications" {
filename = "slack_notifications.zip"
function_name = "${var.project_name}-slack-notifications"
role = aws_iam_role.slack_lambda.arn
handler = "index.handler"
runtime = "nodejs14.x"
environment {
variables = {
SLACK_WEBHOOK_URL = var.slack_webhook_url
SLACK_CHANNEL = var.slack_channel
}
}
tags = {
Environment = var.environment
}
}
# SNS Topic Subscription
resource "aws_sns_topic_subscription" "slack" {
topic_arn = aws_sns_topic.guardduty_findings.arn
protocol = "lambda"
endpoint = aws_lambda_function.slack_notifications.arn
}
Advanced Features
# Custom Threat Intelligence
resource "aws_guardduty_threatintelset" "custom" {
activate = true
detector_id = aws_guardduty_detector.main.id
format = "TXT"
location = "s3://${aws_s3_bucket.threat_intel.id}/custom-threats.txt"
name = "custom-threats"
tags = {
Environment = var.environment
}
}
# S3 Protection
resource "aws_guardduty_detector_s3_logs_configuration" "s3_protection" {
detector_id = aws_guardduty_detector.main.id
enable = true
}
# Kubernetes Protection
resource "aws_guardduty_detector_kubernetes_configuration" "k8s_protection" {
detector_id = aws_guardduty_detector.main.id
enable = true
}
Conclusion
This setup provides a comprehensive foundation for deploying GuardDuty using Terraform. Remember to:
- Plan your security monitoring carefully
- Implement proper notification channels
- Monitor findings and costs
- Keep your configurations versioned
- Test thoroughly before production deployment
The complete code can be customized based on your specific requirements and use cases.