amazon-web-servicesterraformterraform-provider-awsamazon-cloudtrail

Terraform Cloudtrail AccessDeniedException: Access Denied: You do not have permission to operate on this resource


I am working on creating a Cloudtrail trail and to log everything to an S3 bucket with Terraform.

This is the code

resource "aws_kms_key" "cloudtrail_kms_key" {
  description         = "KMS key for Cloudtrail S3 Bucket"
  enable_key_rotation = true
}
    
resource "aws_kms_key_policy" "cloudtrail_kms_key_policy" {
  key_id = aws_kms_key.cloudtrail_kms_key.id
  policy = jsonencode({
    Version = "2012-10-17",
      Id      = "test",
      Statement : [
       {
          Sid       = "Enable IAM User Permissions"
          Effect    = "Allow"
          Principal = 
          { 
             AWS = "arn:aws:iam::${var.aws_account_id}:root" 
           }
          Action    = "kms:*"
          Resource  = "*"
        },
        {
          Sid    = "Permitted KMS Key Services"
          Effect = "Allow"
          Principal = {
            Service = ["cloudtrail.amazonaws.com"]
            AWS     = "arn:aws:iam::${var.aws_account_id}:root"
          }
          Action   = ["kms:GenerateDataKey*", "kms:Decrypt"]
          Resource = "*",
           "Condition" : {
             "StringEquals" : {
               "aws:SourceArn" : "arn:aws:cloudtrail:${var.aws_region}:${var.aws_account_id}:trail/${var.trail_name}"
           }  
         }
       }
     ]
  })
}
    
resource "aws_s3_bucket" "cloudtrail_bucket" {
  bucket = "${var.bucket_name}-${var.aws_region}-${var.aws_account_id}-ls"
}
    
resource "aws_s3_bucket_server_side_encryption_configuration" "cloudtrail_bucket_sse_configuration" {
  bucket = aws_s3_bucket.cloudtrail_bucket.id
    
  rule {
    apply_server_side_encryption_by_default {
      kms_master_key_id = aws_kms_key.cloudtrail_kms_key.arn
      sse_algorithm     = "aws:kms"
  }
}
    
resource "aws_s3_bucket_ownership_controls" "cloudtrail_bucket_ownership_controls" {
  bucket = aws_s3_bucket.cloudtrail_bucket.id
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}
    
resource "aws_s3_bucket_versioning" "cloudtrail_bucket_versioning" {
  bucket = aws_s3_bucket.cloudtrail_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}
    
data "aws_iam_policy_document" "cloudtrail_bucket_policy" {
  statement {
    sid    = "AWSCloudTrailAclCheck"
    effect = "Allow"
    
    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
    
    actions   = ["s3:GetBucketAcl"]
      resources = [aws_s3_bucket.cloudtrail_bucket.arn]
      condition {
        test     = "StringEquals"
        variable = "aws:SourceArn"
        values   = ["arn:aws:cloudtrail:${var.aws_region}:${var.aws_account_id}:trail/${var.trail_name}"]
      }
    }
    
  statement {
    sid    = "AWSCloudTrailWrite"
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
    actions   = ["s3:PutObject"]
    resources = ["${aws_s3_bucket.cloudtrail_bucket.arn}/prefix/AWSLogs/${var.aws_account_id}/*"]
    
    condition {
      test     = "StringEquals"
      variable = "s3:x-amz-acl"
      values   = ["bucket-owner-full-control"]
    }
    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = ["arn:aws:cloudtrail:${var.aws_region}:${var.aws_account_id}:trail/${var.trail_name}"]
    }
  }
}
    
resource "aws_s3_bucket_policy" "cloudtrail_bucket_policy" {
  bucket = aws_s3_bucket.cloudtrail_bucket.id
  policy = data.aws_iam_policy_document.cloudtrail_bucket_policy.json
}
    
resource "aws_cloudtrail" "trail" {
  depends_on                = [aws_s3_bucket_policy.cloudtrail_bucket_policy]
  name                       = var.trail_name
  s3_bucket_name             = aws_s3_bucket.cloudtrail_bucket.id
  s3_key_prefix              = "AWSLogs"
  is_organization_trail      = true
  is_multi_region_trail      = true
  kms_key_id                 = aws_kms_key.cloudtrail_kms_key.arn
  enable_log_file_validation = true
}

This is my provider configuration

terraform {
  required_version = "~> 1.3, < 1.6"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

This creates the following resources

  1. a kms key with the necessary kms key policy to allow Cloudtrail to use the kms key
  2. S3 bucket with server side encryption enabled, bucket ownership setup, versioning enabled and the necessary IAM permissions for Cloudtrail to write to the s3 bucket.
  3. The actual trail.

When I run terraform apply, I keep running into the following error:

Error: creating CloudTrail Trail (audit-logs): operation error CloudTrail: CreateTrail, https response error StatusCode: 400, RequestID: abdc7ed4-22c4-4462-9ff9-4f8bda32e509, api error AccessDeniedException: Access Denied: You do not have permission to operate on this resource.

The role that I am using to deploy this has an admin role so I am unsure what else is missing to create Cloudtrail.


Solution

  • When an organization trail needs to be created, it needs to happen in the organization management account:

    You must sign in with the management account or a delegated administrator account associated with an organization to create an organization trail. You must also have sufficient permissions for the user or role in the management or delegated administrator account to create the trail. If you don't have sufficient permissions, you won't have the option to apply the trail to an organization.

    Looking at the code you have in the question, there is one argument that is set to create the organizational trail:

    is_organization_trail = true
    

    means you are in fact creating an organizational trail.