Terraform: Cycle when creating bucket with policy

Created on 14 Mar 2016  ·  7Comments  ·  Source: hashicorp/terraform

I am trying to create an aws_s3_bucket resource with the following policy:

{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::REDACTED:user/REDACTED"
      },
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion"
      ],
      "Resource": "arn:aws:s3:::${bucket}/REDACTED/*"
    }
  ]
}

To do this, I am using the following Terraform code:

resource "template_file" "logs_policy" {
  template = "${file("${path.module}/policy.json")}"

  vars {
    bucket = "${aws_s3_bucket.logs.bucket}"
  }
}

resource "aws_s3_bucket" "logs" {
  bucket = "${var.s3_bucket_prefix}-logs"
  policy = "${template_file.logs_policy.rendered}"
}

Trying to run Terraform fails, however, due to a dependency loop:

Errors:

  * 1 error(s) occurred:

* Cycle: template_file.logs_policy, aws_s3_bucket.logs
bug provideaws waiting-response

Most helpful comment

For anyone in the future that lands here I wanted to note an option for doing this:

resource "aws_s3_bucket" "my_bucket" {
    bucket = "mybucket"
}

resource "aws_s3_bucket_policy" "my_bucket_policy" {
    bucket = "${aws_s3_bucket.my_bucket.id}"
    policy = "${data.aws_iam_policy_document.bucket_policy.json}"
}

data "aws_iam_policy_document" "bucket_policy" {
  statement {   
        actions = [ 
            "s3:*"
        ]

        resources = [
            "${aws_s3_bucket.my_bucket.arn}",
            "${aws_s3_bucket.my_bucket.arn}/*"
        ]
  }
}

All 7 comments

Hello @joshuaspence

The cycle is this:

  • in order to create the bucket, you've specified that we need to render the template first
  • In order to render the template, we need to know the bucket name

While we "know" what the bucket name will be by reading the var you've specified, we don't "deliver" it to other resources until the bucket is actually created. We don't want to pass on information that's not valid, in the event that it fails to actually create. So, there you have a cycle.

You should be able to simply specify the policy inline:

resource "aws_s3_bucket" "logs" {
  bucket = "${var.s3_bucket_prefix}-logs"
  policy = <<EOF
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::REDACTED:user/REDACTED"
      },
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion"
      ],
      "Resource": "arn:aws:s3:::${var.s3_bucket_prefix}-logs/REDACTED/*"
    }
  ]
}
EOF

}

Unless of course you have another use for the template_file resource that I'm not seeing? If you have any more information on your use case maybe I can propose another solution, if I understand your situation better.

Thanks!

This would be slightly easier if there was a self variable, I suppose.

There is a self, however it's only available to provisioners, I'm afraid :/

For anyone in the future that lands here I wanted to note an option for doing this:

resource "aws_s3_bucket" "my_bucket" {
    bucket = "mybucket"
}

resource "aws_s3_bucket_policy" "my_bucket_policy" {
    bucket = "${aws_s3_bucket.my_bucket.id}"
    policy = "${data.aws_iam_policy_document.bucket_policy.json}"
}

data "aws_iam_policy_document" "bucket_policy" {
  statement {   
        actions = [ 
            "s3:*"
        ]

        resources = [
            "${aws_s3_bucket.my_bucket.arn}",
            "${aws_s3_bucket.my_bucket.arn}/*"
        ]
  }
}

How about bucket_prefix ? Sill have * Cycle: data.template_file.policy, aws_s3_bucket.logs
I'm needed unique bucket name

data "template_file" "policy" {
  template = "${file("policy.json")}"

  vars = {
    bucket     = "${aws_s3_bucket.logs.id}"
    account_id = "127311923021"
  }
}

resource "aws_s3_bucket_policy" "elb_logs" {
  depends_on = ["aws_s3_bucket.logs"]

  bucket = "${aws_s3_bucket.logs.id}"
  policy = "${data.template_file.policy.rendered}"
}

resource "aws_s3_bucket" "logs" {
  bucket_prefix = "logs-${var.stage}"

  policy = "${data.template_file.policy.rendered}"
}

Another option to try would be to create a module and use the following in the call:

resource "random_string" "id" {
special = false
length = 5
upper = false
lower = true
number = true
}

module "bucket1" {
source = "../s3"
providers = {
"aws.proxy" = "aws.useast1" }
prefix = "test"
envname = "app1"
s3_acl = "private"
s3_bucket_name = "${var.prefix}-bucketpurpose-${random_string.id.result}"
}

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings