modules/infrastructure-compliance/cloudtrail.tf (262 lines of code) (raw):
data "aws_caller_identity" "current" {}
data "aws_partition" "current" {}
locals {
account_id = data.aws_caller_identity.current.account_id
partition = data.aws_partition.current.partition
}
#-----------------------#
# CloudWatch Log Group #
# CloudWatch Log Stream #
#-----------------------#
resource "aws_cloudwatch_log_group" "cloudwatch_log_group" {
name = "${var.organization_name}-cloudtrail"
}
resource "aws_cloudwatch_log_stream" "cloudwatch_log_stream" {
log_group_name = aws_cloudwatch_log_group.cloudwatch_log_group.name
name = local.account_id
}
#---------------------------------------------------#
# CloudWatch & CloudTrail events role and policies #
#---------------------------------------------------#
resource "aws_iam_role" "cloudtrail_cloudwatch_role" {
name = "CloudTrailCWRole"
assume_role_policy = data.aws_iam_policy_document.cloudwatch_logs_assume_policy.json
}
resource "aws_iam_role_policy" "cloudtrail_cloudwatch_logs_policy" {
name = "CloudTrailCWPolicy"
role = aws_iam_role.cloudtrail_cloudwatch_role.id
policy = data.aws_iam_policy_document.cloudwatch_logs_policy.json
}
data "aws_iam_policy_document" "cloudwatch_logs_assume_policy" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
identifiers = ["cloudtrail.amazonaws.com"]
type = "Service"
}
}
}
data "aws_iam_policy_document" "cloudwatch_logs_policy" {
statement {
effect = "Allow"
actions = ["logs:CreateLogStream"]
resources = [
"arn:${local.partition}:logs:${var.region}:${local.account_id}:log-group:${aws_cloudwatch_log_group.cloudwatch_log_group.name}:log-stream:*",
]
}
statement {
effect = "Allow"
actions = ["logs:PutLogEvents"]
resources = [
"arn:${local.partition}:logs:${var.region}:${local.account_id}:log-group:${aws_cloudwatch_log_group.cloudwatch_log_group.name}:log-stream:*",
]
}
}
#----------#
# KMS Key #
#----------#
resource "aws_kms_key" "cloudtrail_kms_key" {
description = "KMS key for CloudTrail logs"
deletion_window_in_days = 10
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "Key policy created by CloudTrail",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::${local.account_id}:root"
]
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow CloudTrail to encrypt logs",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "kms:GenerateDataKey*",
"Resource": "*",
"Condition": {
"StringLike": {
"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:${local.account_id}:trail/*"
}
}
},
{
"Sid": "Allow CloudTrail to describe key",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "kms:DescribeKey",
"Resource": "*"
},
{
"Sid": "Allow principals in the account to decrypt log files",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"kms:Decrypt",
"kms:ReEncryptFrom"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "${local.account_id}"
},
"StringLike": {
"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:${local.account_id}:trail/*"
}
}
},
{
"Sid": "Allow alias creation during setup",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "kms:CreateAlias",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "${local.account_id}",
"kms:ViaService": "ec2.${var.region}.amazonaws.com"
}
}
},
{
"Sid": "Enable cross account log decryption",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"kms:Decrypt",
"kms:ReEncryptFrom"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "${local.account_id}"
},
"StringLike": {
"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:${local.account_id}:trail/*"
}
}
}
]
}
POLICY
}
#------------#
# CloudTrail #
#------------#
resource "aws_cloudtrail" "cloudtrail" {
name = "${var.organization_name}-${local.account_id}-cloudtrail"
enable_log_file_validation = true
enable_logging = true
is_multi_region_trail = var.multi_region_trail
include_global_service_events = var.multi_region_trail == "true" ? "true" : "false"
kms_key_id = aws_kms_key.cloudtrail_kms_key.arn
s3_bucket_name = aws_s3_bucket.cloudtrail_bucket.id
sns_topic_name = aws_sns_topic.notif.name
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type = "AWS::S3::Object"
values = ["arn:aws:s3"]
}
}
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type = "AWS::Lambda::Function"
values = ["arn:aws:lambda"]
}
}
cloud_watch_logs_role_arn = aws_iam_role.cloudtrail_cloudwatch_role.arn
cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudwatch_log_group.arn}:*"
}
#-------------------#
# CloudTrail Bucket #
#-------------------#
resource "aws_s3_bucket" "cloudtrail_bucket" {
bucket = "${var.organization_name}-cloudtrail-${local.account_id}-${var.region}"
force_destroy = var.force_destroy
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
kms_master_key_id = aws_kms_key.cloudtrail_kms_key.id
sse_algorithm = "aws:kms"
}
}
}
versioning {
enabled = var.versioning
}
tags = {
Name = "CloudTrail bucket"
}
}
resource "aws_s3_bucket_lifecycle_configuration" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail_bucket.id
rule {
id = "cloudtrail"
status = "Enabled"
expiration {
days = 90
}
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 60
storage_class = "GLACIER"
}
}
}
resource "aws_s3_bucket_acl" "versioning_bucket_acl" {
bucket = aws_s3_bucket.cloudtrail_bucket.id
acl = "private"
}
resource "aws_s3_bucket_versioning" "versioning" {
bucket = aws_s3_bucket.cloudtrail_bucket.id
versioning_configuration {
status = "Enabled"
}
}
#----------------------------#
# CloudTrail Bucket Policies #
#----------------------------#
data "aws_iam_policy_document" "bucket_policy_document" {
statement {
actions = ["s3:GetBucketAcl"]
effect = "Allow"
resources = ["arn:aws:s3:::${aws_s3_bucket.cloudtrail_bucket.bucket}"]
principals {
identifiers = ["cloudtrail.amazonaws.com"]
type = "Service"
}
}
statement {
actions = ["s3:PutObject"]
effect = "Allow"
resources = [
"arn:aws:s3:::${aws_s3_bucket.cloudtrail_bucket.bucket}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"
]
condition {
test = "StringEquals"
values = ["bucket-owner-full-control"]
variable = "s3:x-amz-acl"
}
principals {
identifiers = ["cloudtrail.amazonaws.com"]
type = "Service"
}
}
}
resource "aws_s3_bucket_policy" "s3_bucket_policy" {
bucket = aws_s3_bucket.cloudtrail_bucket.bucket
policy = data.aws_iam_policy_document.bucket_policy_document.json
depends_on = [aws_s3_bucket.cloudtrail_bucket]
}
resource "aws_s3_bucket_public_access_block" "private" {
bucket = aws_s3_bucket.cloudtrail_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}