How to avoid S3 bucket policy conflict

I am currently having two (maybe conflicting) S3 bucket policies, which show a permanent difference on Terraform. Before I show parts of the code, I will try to give an overview of the structure.

I am currently using a module, which:

  1. Takes IAM Role & an S3 Bucket as inputs
  2. Attaches S3 Bucket policy to the inputted role
  3. Attaches S3 Bucket (allowing VPC) policy to the inputted S3 bucket

I have created some code (snippet and not full code) to illustrate how this looks like for the module.

The policies look like:

# S3 Policy to be attached to the ROLE
data "aws_iam_policy_document" "foo_iam_s3_policy" {
  statement {
    effect    = "Allow"
    resources = ["${data.aws_s3_bucket.s3_bucket.arn}/*"]
    actions   = ["s3:GetObject", "s3:GetObjectVersion"]
  statement {
    effect    = "Allow"
    resources = [data.aws_s3_bucket.s3_bucket.arn]
    actions   = ["s3:*"]

# VPC Policy to be attached to the BUCKET
data "aws_iam_policy_document" "foo_vpc_policy" {
  statement {
    sid       = "VPCAllow"
    effect    = "Allow"
    resources = [data.aws_s3_bucket.s3_bucket.arn, "${data.aws_s3_bucket.s3_bucket.arn}/*"]
    actions   = ["s3:GetObject", "s3:GetObjectVersion"]
    condition {
      test     = "StringEquals"
      variable = "aws:SourceVpc"
      values   = [var.foo_vpc]
    principals {
      type        = "*"
      identifiers = ["*"]

The policy attachments look like:

# Turn policy into a resource to be able to use ARN
resource "aws_iam_policy" "foo_iam_policy_s3" {
  name        = "foo-s3-${var.s3_bucket_name}"
  description = "IAM policy for foo on s3"
  policy      = data.aws_iam_policy_document.foo_iam_s3_policy.json

# Attaches s3 bucket policy to IAM Role
resource "aws_iam_role_policy_attachment" "foo_attach_s3_policy" {
  role       =
  policy_arn = aws_iam_policy.foo_iam_policy_s3.arn

# Attach foo vpc policy to bucket
resource "aws_s3_bucket_policy" "foo_vpc_policy" {
  bucket =
  policy = data.aws_iam_policy_document.foo_vpc_policy.json

Now let’s step outside of the module, where the S3 bucket (the one I mentioned that will be inputted into the module) is created, and where another policy needs to be attached to it (the S3 bucket). So outside of the module, we:

  1. Provide an S3 bucket to the aforementioned module as input (alongside the IAM Role)
  2. Create a policy to allow some IAM Role to put objects in the aforementioned bucket
  3. Attach the created policy to the bucket

The policy looks like:

# Create policy to allow bar to put objects in the bucket
    data "aws_iam_policy_document" "bucket_policy_bar" {
      statement {
        sid       = "Bar IAM access"
        effect    = "Allow"
        resources = [module.aws_s3_bucket.bucket_arn, "${module.aws_s3_bucket.bucket_arn}/*"]
        actions   = ["s3:PutObject", "s3:GetObject", "s3:ListBucket"]
        principals {
          type        = "AWS"
          identifiers = [var.bar_iam]

And its attachment looks like:

    # Attach Bar bucket policy
    resource "aws_s3_bucket_policy" "attach_s3_bucket_bar_policy" {
      bucket = module.s3_bucket.bucket_name
      policy = data.aws_iam_policy_document.bucket_policy_bar.json

(For more context: Basically foo is a database that needs VPC and s3 attachment to role to operate on the bucket and bar is an external service that needs to write data to the bucket)

What is going wrong

When I try to plan/apply, Terraform shows that there is always change, and shows an overwrite between the S3 bucket policy of bar (bucket_policy_bar) and the VPC policy attached inside the module (foo_vpc_policy).

In fact the error I am getting is kind of what is described here saying:

The usage of this resource conflicts with the
aws_iam_policy_attachment resource and will permanently show a
difference if both are defined.

But I am attaching policies to S3 and not to a role, so I am not sure if this warning applies to my case.

Why are my policies conflicting? And how can I avoid this conflict?