DEV Community

Cover image for Provisioning AWS CloudTrail using Terraform (Step-by-Step)
Esther Ninyo for AWS Community Builders

Posted on

7 1 1 1

Provisioning AWS CloudTrail using Terraform (Step-by-Step)

CloudTrail is an AWS service that enables governance, compliance, operational and risk auditing of your AWS account. It captures all the events that happens within your AWS account and it is recorded as events in CloudTrail. The trail will be stored in an s3 bucket of your choice.
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.

Resources to be created:
  • 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

Folder Structure
cloudtrail  #this is a folder
---> providers.tf
---> s3.tf
---> main.tf
---> kms.tf
Enter fullscreen mode Exit fullscreen mode
Step 1:

Create a new directory (cloudtrail) and navigate into it

mkdir cloudtrail
cd cloudtrail
Enter fullscreen mode Exit fullscreen mode

providers.tf

terraform {
  required_version = "~> 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"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode
Step 2:

Initialize your project

terraform init
Enter fullscreen mode Exit fullscreen mode

A successful initialization should look like the image below:

terraform init

Copy the code below to your main.tf file

main.tf

resource "aws_cloudtrail" "cloudtrail" {
  name                       = "cloudtrail-tutorial"
  s3_bucket_name             = aws_s3_bucket.cloudtrail_s3.id
  kms_key_id                 = aws_kms_key.cloudtrail_kms_key.arn
  enable_log_file_validation = true
  is_multi_region_trail      = true
  enable_logging             = true

  depends_on = [
    aws_s3_bucket.cloudtrail_s3,
    data.aws_iam_policy_document.cloudtrail_s3_policy,
    aws_kms_key.cloudtrail_kms_key
  ]
}
Enter fullscreen mode Exit fullscreen mode

Copy the code below to your s3.tf file
s3.tf

resource "aws_s3_bucket" "cloudtrail_s3" {
  bucket        = "cloudtrail-bucket"
  force_destroy = true
}

resource "aws_s3_bucket_acl" "s3_bucket" {
  bucket = aws_s3_bucket.cloudtrail_s3.id

  acl = "private"
}

resource "aws_s3_bucket_public_access_block" "pub_access" {
  bucket                  = aws_s3_bucket.cloudtrail_s3.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}


resource "aws_s3_bucket_policy" "cloudtrail_s3_policy" {
  bucket = aws_s3_bucket.cloudtrail_s3.id
  policy = data.aws_iam_policy_document.cloudtrail_s3_policy.json
}

data "aws_iam_policy_document" "cloudtrail_s3_policy" {
  statement {
    sid       = "AWSCloudTrailAclCheck"
    effect    = "Allow"
    resources = ["${aws_s3_bucket.cloudtrail_s3.arn}"]
    actions   = ["s3:GetBucketAcl"]

    condition {
      test     = "StringEquals"
      variable = "AWS:SourceArn"
      values   = ["arn:aws:cloudtrail:eu-west-1:${data.aws_caller_identity.current.account_id}:trail/cloudtrail-${terraform.workspace}"]
    }

    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
  }

  statement {
    sid       = "AWSCloudTrailWrite"
    effect    = "Allow"
    resources = ["${aws_s3_bucket.cloudtrail_s3.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"]
    actions   = ["s3:PutObject"]

    condition {
      test     = "StringEquals"
      variable = "s3:x-amz-acl"
      values   = ["bucket-owner-full-control"]
    }

    condition {
      test     = "StringEquals"
      variable = "AWS:SourceArn"
      values   = ["arn:aws:cloudtrail:eu-west-1:${data.aws_caller_identity.current.account_id}:trail/cloudtrail-${terraform.workspace}"]
    }

    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Copy the code below to your kms.tf file
kms.tf

data "aws_caller_identity" "current" {}

resource "aws_kms_key" "cloudtrail_kms_key" {
  description         = "KMS key for cloudtrail"
  enable_key_rotation = true
  policy              = data.aws_iam_policy_document.kms_policy.json

  depends_on = [
    data.aws_iam_policy_document.kms_policy
  ]
}

resource "aws_kms_alias" "kms_alias" {
  name          = "alias/cloudtrail_kms"
  target_key_id = aws_kms_key.cloudtrail_kms_key.key_id
}


# KMS KEY POLICY
data "aws_iam_policy_document" "kms_policy" {
  # Allow root users full management access to key
  statement {
    sid       = "Enable IAM User Permissions"
    effect    = "Allow"
    actions   = ["kms:*"]
    resources = ["*"]
    principals {
      type = "AWS"
      identifiers = [
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
    }
  }
  statement {
    sid       = "Allow CloudTrail to encrypt logs"
    effect    = "Allow"
    actions   = ["kms:GenerateDataKey*"]
    resources = ["*"]
    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
    condition {
      test     = "StringEquals"
      variable = "AWS:SourceArn"
      values   = ["arn:aws:cloudtrail:eu-west-1:${data.aws_caller_identity.current.account_id}:trail/cloudtrail-${terraform.workspace}"]
    }
    condition {
      test     = "StringLike"
      variable = "kms:EncryptionContext:aws:cloudtrail:arn"
      values   = ["arn:aws:cloudtrail:*:${data.aws_caller_identity.current.account_id}:trail/*"]
    }
  }
  statement {
    sid       = "Allow CloudTrail to describe key"
    effect    = "Allow"
    actions   = ["kms:DescribeKey"]
    resources = ["*"]
    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
  }
  statement {
    sid    = "Allow principals in the account to decrypt log files"
    effect = "Allow"
    actions = [
      "kms:Decrypt",
      "kms:ReEncryptFrom"
    ]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
    condition {
      test     = "StringEquals"
      variable = "kms:CallerAccount"
      values   = ["${data.aws_caller_identity.current.account_id}"]
    }
    condition {
      test     = "StringLike"
      variable = "kms:EncryptionContext:aws:cloudtrail:arn"
      values   = ["arn:aws:cloudtrail:*:${data.aws_caller_identity.current.account_id}:trail/*"]
    }
  }
  statement {
    sid       = "Allow alias creation during setup"
    effect    = "Allow"
    actions   = ["kms:CreateAlias"]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
    condition {
      test     = "StringEquals"
      variable = "kms:CallerAccount"
      values   = ["${data.aws_caller_identity.current.account_id}"]
    }
    condition {
      test     = "StringEquals"
      variable = "kms:ViaService"
      values   = ["ec2.eu-west-1.amazonaws.com"]
    }
  }
  statement {
    sid    = "Enable cross account log decryption"
    effect = "Allow"
    actions = [
      "kms:Decrypt",
      "kms:ReEncryptFrom"
    ]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
    condition {
      test     = "StringEquals"
      variable = "kms:CallerAccount"
      values   = ["${data.aws_caller_identity.current.account_id}"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
Step 3:
terraform plan
Enter fullscreen mode Exit fullscreen mode

A successful plan should look like the image below:

terraform plan

Step 4:
terraform apply
Enter fullscreen mode Exit fullscreen mode

A successful plan should look like the image below:

terraform apply

Conclusion

You have successfully created an AWS CloudTrail service.
Do you have any question? Please send it my way. Kindly follow me on LinkedIn.

Billboard image

Deploy and scale your apps on AWS and GCP with a world class developer experience

Coherence makes it easy to set up and maintain cloud infrastructure. Harness the extensibility, compliance and cost efficiency of the cloud.

Learn more

Top comments (1)

Collapse
 
codegirl0101 profile image
codegirl

Wow

Best Practices for Running  Container WordPress on AWS (ECS, EFS, RDS, ELB) using CDK cover image

Best Practices for Running Container WordPress on AWS (ECS, EFS, RDS, ELB) using CDK

This post discusses the process of migrating a growing WordPress eShop business to AWS using AWS CDK for an easily scalable, high availability architecture. The detailed structure encompasses several pillars: Compute, Storage, Database, Cache, CDN, DNS, Security, and Backup.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay