Turn a module variable optional

Guys,
I have been troubles in use my AWS S3 module, when I set a variable policy turns mandatory. Someone with more experience using Terraform module know a way to do this var optional again?

I have been try use some feature like:

main.tf

  • jsonencode(var.policy),
  • lookup(var.policy, “policy”, null),
  • file(var.policy)

and

var.tf

  • without declare type
  • with type = string
  • without declare default value
  • with default = ""
  • with default = null
  • with default = { }

And when I try run terraform apply doesn’t work. I got errors like:

  • Error: Error putting S3 policy: MalformedPolicy: Missing required field Statement
  • Error: Error putting S3 policy: MalformedPolicy: Policies must be valid JSON and the first byte must be ‘{’
  • Error: No value for required variable

Error: Invalid function argument

on main.tf line 3, in resource “aws_s3_bucket_policy” “sbp”:
3: policy = file(var.policy)
|----------------
| var.policy is “”

Invalid value for “path” parameter: failed to read …

Hi @weyderfs,

If I’m understanding correctly what you’ve reported here, it sounds like Terraform isn’t considering the variable to be mandatory but your module isn’t correctly handling the case where it isn’t set. If you declare a variable as optional then you must take care each time you use that value elsewhere in the configuration to explain what the behavior should be when the caller doesn’t set the value.

In this context I’m guessing that your goal is to only declare an aws_s3_bucket_policy object when var.policy is set, and otherwise to not create any policy at all. Here’s one way to declare that:

variable "policy_file" {
  type    = string
  default = null
}

resource "aws_s3_bucket_policy" "sbp" {
  count = var.policy_file != null ? 1 : 0

  policy = file(var.policy_file)
  # ...
}

This declares that if var.policy is null (its default value) then there should be zero policy objects.


Another design I’d consider here is to let the caller pass in a policy document directly, rather than a file containing one, and let the caller be the one to decide where that policy JSON comes from. The caller could then write it inline if desired, or request it from a remote API, or generate it using the aws_iam_policy_document data source.

variable "policy_json" {
  type    = string
  default = null
}

resource "aws_s3_bucket_policy" "sbp" {
  count = var.policy_json != null ? 1 : 0

  policy = var.policy_json
  # ...
}

From the perspective of the calling module, then:

module "example" {
  source = "./modules/example"

  policy_json = file("${path.module}/policy.json")
}
1 Like

@apparentlymart thanks for share you knowledge you example of use count solve my case and now I know how handle this scenario.