Terraform AWS CloudTrail Implementation and Module Patterns

CloudTrail is the audit log for your AWS account. It records AWS account activity such as supported API calls, console sign-ins, and resource changes. Without CloudTrail, you are flying blind when it comes to security investigations, compliance audits, and understanding who did what in your environment. Setting it up properly with Terraform ensures consistent logging across your accounts and regions.

This guide walks through a complete CloudTrail implementation with Terraform, from the basic trail to advanced configurations like multi-region logging and real-time alerting.

Core Architecture Requirements

CloudTrail needs an S3 bucket to store log files.

The trail will be stored in an s3 bucket of your choice.

S3 bucket
KMS Key Management System for S3 objects encryption
CloudTrail

Prerequisite:
An AWS account with permissions to create CloudTrail resources
AWS cli configured
Terraform installed

Create the S3 Bucket for Logs

CloudTrail needs an S3 bucket to store log files

This bucket needs a specific bucket policy to allow CloudTrail to write to it.

S3 Bucket Configuration for CloudTrail Logs

The S3 bucket for CloudTrail logs is provisioned with versioning, encryption, public access blocks, and lifecycle management.

S3 bucket for CloudTrail logs

resource "awss3bucket" "cloudtraillogs" {
bucket = "${var.project}-cloudtrail-logs-${data.aws
calleridentity.current.accountid}"
tags = {
Name = "CloudTrail Logs"
Environment = var.environment
ManagedBy = "terraform"
}
}

Enable versioning to prevent log tampering

resource "awss3bucketversioning" "cloudtraillogs" {
bucket = awss3bucket.cloudtraillogs.id
versioning
configuration {
status = "Enabled"
}
}

Enable server-side encryption with KMS

resource "awss3bucketserversideencryptionconfiguration" "cloudtraillogs" {
bucket = aws
s3bucket.cloudtraillogs.id
rule {
applyserversideencryptionbydefault {
sse
algorithm = "aws:kms"
kmsmasterkeyid = awskmskey.cloudtrail.arn
}
bucket
key_enabled = true
}
}

Block all public access

resource "awss3bucketpublicaccessblock" "cloudtraillogs" {
bucket = awss3bucket.cloudtraillogs.id
block
publicacls = true
block
publicpolicy = true
ignore
publicacls = true
restrict
public_buckets = true
}

Lifecycle policy to manage storage costs

resource "awss3bucketlifecycleconfiguration" "cloudtraillogs" {
bucket = aws
s3bucket.cloudtraillogs.id
rule {
id = "archive-old-logs"
status = "Enabled"
transition {
days = 90
storageclass = "GLACIER"
}
transition {
days = 365
storage
class = "DEEP_ARCHIVE"
}

Keep logs for 7 years common compliance requirement

expiration {
days = 2555
}
}
}

This creates a private, versioned, and encrypted S3 bucket that stores all CloudTrail and VPC Flow Logs. The random suffix ensures globally unique names. Logs are stored securely here and later queried by Athena.

Step 1) Create S3 Buckets for Logs and Athena Results
resource "awss3bucket" "logs" {
bucket = "athena-logs-${data.awscalleridentity.me.accountid}-${randomid.suffix.hex}"
tags = var.tags
}

Project Structure and Provider Setup

cloudtrail #this is a folder
---> providers.tf
---> s3.tf
---> main.tf
---> kms.tf

Create a new directory cloudtrail and navigate into it
mkdir cloudtrail
cd cloudtrail

providers.tf
terraform {
requiredversion = "~> 1.6"
required
providers {
aws = {
source = "hashicorp/aws"
}
}
}

Configure the AWS Provider

provider "aws" {
region = "eu-west-1"
default_tags {
tags = {
Environment = terraform.workspace,
ManagedBy = "Terraform"
}
}
}

Initialize your project
terraform init

A successful initialization should look like the image below.

Terraform CloudTrail Resource Definition

Copy the code below to your main.tf file
main.tf
resource "awscloudtrail" "cloudtrail" {
name = "cloudtrail-tutorial"
s3
bucketname = awss3bucket.cloudtrails3.id
kmskeyid = awskmskey.cloudtrailkmskey.arn
enablelogfilevalidation = true
is
multiregiontrail = true
enablelogging = true
depends
on = [
awss3bucket.cloudtrails3,
data.aws
iampolicydocument.cloudtrails3policy,
awskmskey.cloudtrailkmskey
]
}

Copy the code below to your s3.tf file
s3.tf
resource "awss3bucket" "cloudtrail_s3" {
bucket =
}

This enables CloudTrail, which tracks all API activity in your AWS account — who did what, when, and from where

Every component from CloudTrail log delivery to Athena table creation is defined as Infrastructure as Code IaC in Terraform, ensuring automation, reproducibility, and cost efficiency across the entire setup.

Objective
To build a serverless AWS log analysis system that automatically:

Step 2) Enable AWS CloudTrail
resource "awscloudtrail" "trail" {
name = "org-trail-athena"
s3
bucketname = awss3bucket.logs.bucket
enable
logging = true
}

Module Based Deployment Patterns

Terraform module to provision an AWS CloudTrail.

The module accepts an encrypted S3 bucket with versioning to store CloudTrail logs.

The bucket could be from the same AWS account or from a different account.

This is useful if an organization uses a number of separate AWS accounts to isolate the Audit environment from other environments production, staging, development.

In this case, you create CloudTrail in the production environment production AWS account, while the S3 bucket to store the CloudTrail logs is created in the Audit AWS account, restricting access to the logs only to the users/groups from the Audit account.

Tip
module "cloudtrail" {
source = "cloudposse/cloudtrail/aws"

Cloud Posse recommends pinning every module to a specific version

version = "x.x.x"

namespace = "eg"
stage = "dev"
name = "cluster"
enablelogfilevalidation = true
include
globalserviceevents = true
ismultiregiontrail = false
enable
logging = true
s3bucketname = "my-cloudtrail-logs-bucket"
}

NOTE: To create an S3 bucket for CloudTrail logs, use terraform-aws-cloudtrail-s3-bucket module

Business Scenario
Our company increased its use of AWS services across multiple accounts and environments, and leadership has identified significant challenges in meeting security, compliance, and audit requirements. To reduce this pain point, our company or security department needs a secure, automated, and repeatable solution that will collect API activity logs for every AWS account and region. More so, store those logs centrally, with encryption and proper access controls. This is where we come in to build this out!

Prerequisities
AWS Knowledge and AWS Account
Terraform Knowledge
IDE: I used VSCode
Curiosity and Determination ALWAYS

Step 1: Setting up our files
For this project, we want to setup the directory for it. That includes the module folder and files that are created for this project.

From here, I will explain the files and the purpose of each section within those files. Let’s start with the main.tf file

Main.tf
For our root main.tf file, we are setting up our module block, which is passing in a set of variables that Terraform will be calling from

module "awscloudtrail" {
source = "trussworks/cloudtrail/aws"
s3
bucketname = "my-company-cloudtrail-logs"
log
retention_days = 90
}

CloudPosse Module Parameters

Parameter Description
namespace Namespace identifier for resources
stage Deployment stage e.g., dev
name Logical name for the trail
enablelogfile_validation Enable log file validation
includeglobalservice_events Include global service events
ismultiregion_trail Enable multi-region logging
enable_logging Enable logging
s3bucketname Name of S3 bucket for logs

Trussworks Module Parameters

Name Description Type Default Required
s3bucketname The name of the AWS S3 bucket. string n/a yes
advancedeventselectors A list of advanced event selectors for the trail. list(object({ name = string fieldselectors = list(object({ field = string equals = optional(list(string)) startswith = optional(list(string)) endswith = optional(list(string)) notequals = optional(list(string)) notstartswith = optional(list(string)) notendswith = optional(list(string)) })) })) [] no
apicallrate_insight A measurement of write-only management API calls that occur per minute against a baseline API call volume. bool false no
apierrorrate_insight A measurement of management API calls that result in error codes. The error is shown if the API call is unsuccessful bool false no

Name |
Version |
terraform |

= 1.0 |
aws |
= 3.0 |

Upgrade Instructions for v2 -> v3
Starting in v3, encryption is not optional and will be on for both logs delivered to S3 and Cloudwatch Logs. The KMS key resource created this module will be used to encrypt both S3 and Cloudwatch-based logs.

Because of this change, remove the encrypt_cloudtrail parameter from previous invocations of the module prior to upgrading the version.

This module creates AWS CloudTrail and configures it so that logs go to cloudwatch.

Multi-Account and Centralized Logging

The module accepts an encrypted S3 bucket with versioning to store CloudTrail logs.

The bucket could be from the same AWS account or from a different account.

This is useful if an organization uses a number of separate AWS accounts to isolate the Audit environment from other environments production, staging, development.

In this case, you create CloudTrail in the production environment production AWS account, while the S3 bucket to store the CloudTrail logs is created in the Audit AWS account, restricting access to the logs only to the users/groups from the Audit account.

To read more on CloudTrail, please visit the documentation page.

In this technical post, we'll walk through the steps to provision CloudTrail using Terraform.

Log Analysis Integration

. Every component from CloudTrail log delivery to Athena table creation is defined as Infrastructure as Code IaC in Terraform, ensuring automation, reproducibility, and cost efficiency across the entire setup.

Objective
To build a serverless AWS log analysis system that automatically:

Step 1) Create S3 Buckets for Logs and Athena Results
resource "awss3bucket" "logs" {
bucket = "athena-logs-${data.awscalleridentity.me.accountid}-${randomid.suffix.hex}"
tags = var.tags
}

This creates a private, versioned, and encrypted S3 bucket that stores all CloudTrail and VPC Flow Logs. The random suffix ensures globally unique names. Logs are stored securely here and later queried by Athena.

Step 2) Enable AWS CloudTrail
resource "awscloudtrail" "trail" {
name = "org-trail-athena"
s3
bucketname = awss3bucket.logs.bucket
enable
logging = true
}

This enables CloudTrail, which tracks all API activity in your AWS account — who did what, when, and from where

Conclusion

Terraform implementation of AWS CloudTrail centers on provisioning an S3 destination with versioning, server-side encryption via KMS, public access blocks, and lifecycle transitions, then binding the trail to that bucket with enablelogfilevalidation and ismultiregiontrail settings.

Module usage provides repeatable patterns for namespace, stage, name, enablelogfilevalidation, includeglobalserviceevents, ismultiregiontrail, enablelogging, and s3bucketname parameters, with the option to source the bucket from a separate Audit account to isolate production workloads from log storage.

For organizations requiring centralised audit, the S3 bucket can be created in an Audit AWS account while CloudTrail is created in production, restricting log access to Audit account principals. Multi-region trails ensure consistent API activity capture across regions, and lifecycle policies with Glacier and Deep Archive transitions manage compliance retention for up to seven years while controlling cost.

IaC definitions also extend to serverless log analysis where CloudTrail delivery to S3 feeds Athena queries, with the entire pipeline defined as Terraform for automation, reproducibility, and cost efficiency.

Sources

  1. https://oneuptime.com/blog/post/2026-02-23-how-to-implement-cloudtrail-logging-with-terraform/view
  2. https://github.com/cloudposse/terraform-aws-cloudtrail
  3. https://dev.to/aws-builders/provisioning-aws-cloudtrail-using-terraform-step-by-step-hn4
  4. https://aws.plainenglish.io/project-trail-using-terraform-to-deploy-aws-cloudtrail-8f60d4a48a0a
  5. https://www.linkedin.com/pulse/automating-aws-cloudtrail-log-analysis-athena-rajamahendram-5aahc
  6. https://github.com/trussworks/terraform-aws-cloudtrail

Related Posts