'Having trouble with a applying a bucket policy via Terraform

I had this workign at one point but I may have screwed something up or this is a bug. I thought maybe it was a race condition and tried a few depends_on but still no luck. I can't seem to figure this out but I do know S3 policies can be challenging with buckets and terraform. Does anyone see anything obvious I am doing wrong?

resource "aws_s3_bucket_policy" "ct-s3-bucket-policy" {
  bucket = aws_s3_bucket.mylab-s3-bucket-ct.id
  policy = "${data.aws_iam_policy_document.default.json}"
}

resource "aws_cloudtrail" "mylab-cloudtrail" {
  name                          = "mylab-cloudtrail"
  s3_bucket_name                = aws_s3_bucket.mylab-s3-bucket-ct.id
  s3_key_prefix                 = "CT"
  include_global_service_events = true
  event_selector {
    read_write_type = "All"
    include_management_events = true
    data_resource {
      type   = "AWS::S3::Object"
      values = ["arn:aws:s3:::"]
    }
  }
}

resource "aws_s3_bucket" "mylab-s3-bucket-ct" {
  bucket        = "mylab-s3-bucket-ct-1231764516123"
  force_destroy = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
  bucket = aws_s3_bucket.mylab-s3-bucket-ct.id
  rule {
    apply_server_side_encryption_by_default {
      kms_master_key_id = aws_kms_key.s3-kms.arn
      sse_algorithm     = "aws:kms"
    }
  }
}
data "aws_iam_policy_document" "default" {
  statement {
    sid    = "AWSCloudTrailAclCheck"
    effect = "Allow"

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

    actions = [
      "s3:GetBucketAcl",
    ]

    resources = [
      "arn:aws:s3:::${var.cloudtrailbucketname}",
    ]
  }

  statement {
    sid    = "AWSCloudTrailWrite"
    effect = "Allow"

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

    actions = [
      "s3:PutObject",
    ]

    resources = [
      "arn:aws:s3:::${var.cloudtrailbucketname}/*",
    ]

    condition {
      test     = "StringEquals"
      variable = "s3:x-amz-acl"

      values = [
        "bucket-owner-full-control",
      ]
    }
  }
}

this is the error I see at the end. The bucket creates but the policy wont attach.

    ╷
    │ Error: Error putting S3 policy: MalformedPolicy: Policy has invalid resource
    │       status code: 400, request id: HAK8J85M98TGTHQ4, host id: Qn2mqAJ+oKcFiCD52KfLG+10/binhRn2YUQX6MARTbW4MbV4n+P5neAXg8ikB7itINHOL07DV+I=
    │
    │   with aws_s3_bucket_policy.ct-s3-bucket-policy,
    │   on main.tf line 126, in resource "aws_s3_bucket_policy" "ct-s3-bucket-policy":
    │  126: resource "aws_s3_bucket_policy" "ct-s3-bucket-policy" {
    │
    ╵
    ╷
    │ Error: Error creating CloudTrail: InsufficientS3BucketPolicyException: Incorrect S3 bucket policy is detected for bucket: mylab-s3-bucket-ct-1231764516123
    │
    │   with aws_cloudtrail.mylab-cloudtrail,
    │   on main.tf line 131, in resource "aws_cloudtrail" "mylab-cloudtrail":
    │  131: resource "aws_cloudtrail" "mylab-cloudtrail" {
    │

EDIT: For clarity, this ONLY happens with applied, planning works fine.



Solution 1:[1]

I believe you have a to have a dependency between the bucket policy and CloudTrail trail, like this:

resource "aws_cloudtrail" "mylab-cloudtrail" {
  name                          = "mylab-cloudtrail"
  s3_bucket_name                = aws_s3_bucket.mylab-s3-bucket-ct.id
  s3_key_prefix                 = "CT"
  include_global_service_events = true
  event_selector {
    read_write_type = "All"
    include_management_events = true
    data_resource {
      type   = "AWS::S3::Object"
      values = ["arn:aws:s3:::"]
    }
  }

  depends_on = [
    aws_s3_bucket_policy.ct-s3-bucket-policy
  ]
}

If you don't have this dependency, Terraform will try to create the trail before having the necessary policy attached to the bucket.

Also, probably you would want to reference the bucket name in the policy and avoid using a var.cloudtrailbucketname:

data "aws_iam_policy_document" "default" {
  statement {
    sid    = "AWSCloudTrailAclCheck"
    effect = "Allow"

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

    actions = [
      "s3:GetBucketAcl",
    ]

    resources = [
      "arn:aws:s3:::${aws_s3_bucket.mylab-s3-bucket-ct.id}" # Get the bucket name
    ]
  }

  statement {
    sid    = "AWSCloudTrailWrite"
    effect = "Allow"

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

    actions = [
      "s3:PutObject",
    ]

    resources = [
      "arn:aws:s3:::${aws_s3_bucket.mylab-s3-bucket-ct.id}/*", # Get the bucket name
    ]

    condition {
      test     = "StringEquals"
      variable = "s3:x-amz-acl"

      values = [
        "bucket-owner-full-control",
      ]
    }
  }
}

Solution 2:[2]

Original resource call

"arn:aws:s3:::${var.cloudtrailbucketname}/*",

Changed to this and it worked. I reference it instead of building the string. For whatever reason, the JSON was malformed.

resources = ["${aws_s3_bucket.mylab-s3-bucket-ct.arn}/*"]

@Erin for helping me get to the right direction

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ervin Szilagyi
Solution 2 Patrick