skip to Main Content

Editing This Question.

I’m using Terraform v0.12.9 and I’m trying to conditionally create bucket policies with the custom policy stored as an IAM policy document.

data "aws_iam_policy_document" "my_policy" {
  statement {
    sid     = "IPALLOW"
    effect  = "Deny"
    actions = ["s3:*"]
    resources = [
      "arn:aws:s3:::${var.my_bucket}/*",
      "arn:aws:s3:::${var.my_bucket}"
    ]
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
    condition {
      test     = "NotIpAddress"
      variable = "aws:SourceIp"
      values = [
        "${concat(var.ip_one,
        var.ip_two,
        var.ip_three)}"
      ]
    }
  }

}
resource "aws_s3_bucket_policy" "my-bucket-policy" {
  count  = length(var.buckets)
  bucket = element(values(var.buckets[count.index]), 0)
  policy = (
    element(values(var.buckets[count.index]), 0) == "bar_bucket" ?
    data.aws_iam_policy_document.my_policy.json : <<POLICY
  {
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "HTTP",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "*",
    REST OF POLICY REDACTED
    POLICY
   )
 }


}

However this fails and throws an error

Inappropriate value for attribute "values": element 0: string required.

But when I hardcode the ip addresses in the policy like this,

["100.0.0.100","100.0.0.101","100.0.0.102/24","100.0.0.103/24","100.0.0.104"]

It works.

Is there a way to pass the IP addresses as a variable in the IAM policy document?

I’ve tried jsonencode on the values, but it add a bunch of which doesn’t work.

These are what the variables look like.

variable "ip_one" {
  type = list(string)
  default = [
    "100.0.0.100",
    "100.0.0.101"
  ]
}

variable "ip_two" {
  type = list(string)
  default = [
    "100.0.0.102/24",
    "100.0.0.103/24"
  ]
}

variable "ip_three" {
  type = list(string)
  default = [
    "100.0.0.104"
  ]
}

2

Answers


  1. Can you try something like

     values = [
     join(",", concat(var.ip_one,
     var.ip_two,
     var.ip_three))
     ]
    
    Login or Signup to reply.
  2. As I see it, there are a couple of problems:

    1. The IPs have to have the subnet mask as well, i.e., you cannot mix IP addresses with and without subnet mask. For example, in ip_one you are using 100.0.0.100 while in ip_two you have 100.0.0.102/24.
    2. The variables are already lists of strings, so concatenating lists will create a list and then values is supposed to be a list as well.

    What I would suggest is using a local variable for any value manipulation and then using the local variable in the values argument. So you could do the following:

    locals {
      ips = concat(var.ip_one, var.ip_two, var.ip_three)
    }
    
    data "aws_iam_policy_document" "my_policy" {
      statement {
        sid     = "IPALLOW"
        effect  = "Deny"
        actions = ["s3:*"]
        resources = [
          "arn:aws:s3:::${var.my_bucket}/*",
          "arn:aws:s3:::${var.my_bucket}"
        ]
        principals {
          type        = "AWS"
          identifiers = ["*"]
        }
        condition {
          test     = "NotIpAddress"
          variable = "aws:SourceIp"
          values = local.ips
        }
      }
    
    }
    

    Also, if you want to limit access to only a set of IPs or subnets, you should definitely consider adding the subnet mask to IPs in ip_one and ip_three. If you do not do that, the access will probably be allowed from the IP addresses you do not want to allow access from.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search