Terraform examples #33

Open
opened 2025-12-30 01:19:43 +01:00 by adam · 12 comments
Owner

Originally created by @titanous on GitHub (Feb 5, 2024).

It'd be great if there were examples of using pkl instead of HCL for Terraform in the same way that there is pkl-k8s-examples.

Originally created by @titanous on GitHub (Feb 5, 2024). It'd be great if there were examples of using pkl instead of HCL for Terraform in the same way that there is [pkl-k8s-examples](https://github.com/apple/pkl-k8s-examples).
Author
Owner

@bioball commented on GitHub (Feb 6, 2024):

I agree; we'll work on providing something here.

@bioball commented on GitHub (Feb 6, 2024): I agree; we'll work on providing something here.
Author
Owner

@MisterJD commented on GitHub (Feb 21, 2024):

Oh yes, for Terraform would be great!

@MisterJD commented on GitHub (Feb 21, 2024): Oh yes, for Terraform would be great!
Author
Owner

@pedrodsrodrigues commented on GitHub (Mar 12, 2024):

I agree; we'll work on providing something here.

Do you have any ETA, even if a bit farfetched? Or, maybe, you can give this a look and help us in some way while we wait? Any help related to creating Terraform syntax would be appreciated... Thank you

@pedrodsrodrigues commented on GitHub (Mar 12, 2024): > I agree; we'll work on providing something here. Do you have any ETA, even if a bit farfetched? Or, maybe, you can give [this](https://github.com/apple/pkl/discussions/293) a look and help us in some way while we wait? Any help related to creating Terraform syntax would be appreciated... Thank you
Author
Owner

@holzensp commented on GitHub (Mar 12, 2024):

A part of the problem is that matching common practice would require defining properties for all providers on one level, because HCL just merges the namespaces for everything. That is, if you want the same set of autocompletions on the top level. There are alternative ways to do this, but we want to be a little bit cautious to not dictate The One True Way to replace HCL with Pkl, given these alternatives. We need to construct a few different approaches to demonstrate the variability.

@holzensp commented on GitHub (Mar 12, 2024): A part of the problem is that _matching_ common practice would require defining properties for all providers on one level, because HCL just merges the namespaces for everything. That is, if you want the same set of autocompletions on the top level. There are alternative ways to do this, but we want to be a little bit cautious to not dictate The One True Way to replace HCL with Pkl, given these alternatives. We need to construct a few different approaches to demonstrate the variability.
Author
Owner

@DorukAkinci commented on GitHub (Mar 27, 2024):

To be honest, I would like to have a solution for dynamically generating tfvars files with pkl. Currently my way of handling this problem is writing my own shell scripts to inject my own logic and set variables through either the TF_VAR environment parameters or adding them into the tfvars files before executing them on my each environment. One of my dreams would be a way to merge multiple tfvar files in a way one contains the base environment configurations and extend these variables with these dynamic pkl injections. Something like “shared-prod.tfvars” + “customer-x.tfvars”. One problem that occurs here is if multiple files sets the same variable, i would like to merge them if there could be an overwrite option whenever variables are duplicated in the manifest which is not permitted in HCL.

Another use case would be handling the provider version settings with tf files. Currently we are using different versions of the same providers between each terraform configuration. we were using Dhall before to solve this problem and we could dynamically generate these provider and backend tf configuration files but it would be nice to solve these issues here too and then pkl would be a nice option to dhall as well

@DorukAkinci commented on GitHub (Mar 27, 2024): To be honest, I would like to have a solution for dynamically generating tfvars files with pkl. Currently my way of handling this problem is writing my own shell scripts to inject my own logic and set variables through either the TF_VAR environment parameters or adding them into the tfvars files before executing them on my each environment. One of my dreams would be a way to merge multiple tfvar files in a way one contains the base environment configurations and extend these variables with these dynamic pkl injections. Something like “shared-prod.tfvars” + “customer-x.tfvars”. One problem that occurs here is if multiple files sets the same variable, i would like to merge them if there could be an overwrite option whenever variables are duplicated in the manifest which is not permitted in HCL. Another use case would be handling the provider version settings with tf files. Currently we are using different versions of the same providers between each terraform configuration. we were using Dhall before to solve this problem and we could dynamically generate these provider and backend tf configuration files but it would be nice to solve these issues here too and then pkl would be a nice option to dhall as well
Author
Owner

@KiaraGrouwstra commented on GitHub (Jan 1, 2025):

user-friendly TF support here may involve generating PKL types for TF providers, like tf-ncl does for nickel-lang.

@KiaraGrouwstra commented on GitHub (Jan 1, 2025): user-friendly TF support here may involve generating PKL types for TF providers, like [`tf-ncl`](https://github.com/tweag/tf-ncl) does for nickel-lang.
Author
Owner

@tienhuynh5312 commented on GitHub (Jul 29, 2025):

Since Terraform alternatively supports JSON to declare resources. Would JsonRenderer be enough to run Terraform?

https://developer.hashicorp.com/terraform/language/syntax/json

@tienhuynh5312 commented on GitHub (Jul 29, 2025): Since Terraform alternatively supports JSON to declare resources. Would JsonRenderer be enough to run Terraform? https://developer.hashicorp.com/terraform/language/syntax/json
Author
Owner

@HT154 commented on GitHub (Jul 29, 2025):

Would JsonRenderer be enough to run Terraform?

Absolutely!

https://github.com/apple/pkl/issues/912 is another missing piece that would make using Pkl for Terraform a great experience.

@HT154 commented on GitHub (Jul 29, 2025): > Would JsonRenderer be enough to run Terraform? Absolutely! https://github.com/apple/pkl/issues/912 is another missing piece that would make using Pkl for Terraform a great experience.
Author
Owner

@kaihendry commented on GitHub (Sep 15, 2025):

Thanks, inspired to conjure up https://github.com/kaihendry/terraform-config POC on how to leverage PKL to deploy Terraform.

@kaihendry commented on GitHub (Sep 15, 2025): Thanks, inspired to conjure up https://github.com/kaihendry/terraform-config POC on how to leverage PKL to deploy Terraform.
Author
Owner

@mikhail-putilov commented on GitHub (Oct 9, 2025):

I tried to generate simple http tests with Terraform in JSON format and couldn't proceed: JSON format has bugs :-)

When I run this, then the test passes:

variable "redirect_uri" {
  type = string
}
variable "expected_action" {
  type = string
}
data "http" "redirect_uri" {
  url = var.redirect_uri
}
run "verify_redirect_url_1" {
  variables {
    redirect_uri = "https://my-api-gateway/"
    expected_action = "action=\"https://my-sso/realms/my-realm"
  }
  assert {
    condition = strcontains(data.http.redirect_uri.response_body, var.expected_action)
    error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
  }
}

I had a bunch of such tests. I wanted to generate them from Pkl by generating Terraform in JSON format:
However, this did not work at all:

{
  "run": {
    "verify_redirect_url_1": {
      "command": "plan",
      "variables": {
        "redirect_uri": "https://my-api-gateway/",
        "expected_action": "action=\"https://my-sso/realms/my-realm"
      },
      "assert": [
        {
          "condition": "strcontains(${data.http.redirect_uri.response_body}, ${var.expected_action})",
          "error_message": "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
        }
      ]
    }
  }
}

:-(

is it possible to generate HCL-based tests with Pkl? Can someone point me into the right direction, how to do that? In the mean time, I wrote Pkl-based tests with read("http://my-api-gateway/"), but it lacks error messages when assertion fails -- not so optimal right now.

@mikhail-putilov commented on GitHub (Oct 9, 2025): I tried to generate simple http tests with Terraform in JSON format and couldn't proceed: JSON format has bugs :-) When I run this, then the test passes: ```hcl variable "redirect_uri" { type = string } variable "expected_action" { type = string } data "http" "redirect_uri" { url = var.redirect_uri } ``` ```hcl run "verify_redirect_url_1" { variables { redirect_uri = "https://my-api-gateway/" expected_action = "action=\"https://my-sso/realms/my-realm" } assert { condition = strcontains(data.http.redirect_uri.response_body, var.expected_action) error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } } ``` I had a bunch of such tests. I wanted to generate them from Pkl by generating Terraform in JSON format: However, this did not work at all: ```json { "run": { "verify_redirect_url_1": { "command": "plan", "variables": { "redirect_uri": "https://my-api-gateway/", "expected_action": "action=\"https://my-sso/realms/my-realm" }, "assert": [ { "condition": "strcontains(${data.http.redirect_uri.response_body}, ${var.expected_action})", "error_message": "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } ] } } } ``` :-( is it possible to generate HCL-based tests with Pkl? Can someone point me into the right direction, how to do that? In the mean time, I wrote Pkl-based tests with `read("http://my-api-gateway/")`, but it lacks error messages when assertion fails -- not so optimal right now.
Author
Owner

@mikhail-putilov commented on GitHub (Oct 9, 2025):

I tried to generate simple http tests with Terraform in JSON format and couldn't proceed: JSON format has bugs :-)

When I run this, then the test passes:

variable "redirect_uri" {
type = string
}
variable "expected_action" {
type = string
}
data "http" "redirect_uri" {
url = var.redirect_uri
}

run "verify_redirect_url_1" {
variables {
redirect_uri = "https://my-api-gateway/"
expected_action = "action="https://my-sso/realms/my-realm"
}
assert {
condition = strcontains(data.http.redirect_uri.response_body, var.expected_action)
error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
}
}

I had a bunch of such tests. I wanted to generate them from Pkl by generating Terraform in JSON format: However, this did not work at all:

{
"run": {
"verify_redirect_url_1": {
"command": "plan",
"variables": {
"redirect_uri": "https://my-api-gateway/",
"expected_action": "action="https://my-sso/realms/my-realm"
},
"assert": [
{
"condition": "strcontains(${data.http.redirect_uri.response_body}, ${var.expected_action})",
"error_message": "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
}
]
}
}
}

:-(

is it possible to generate HCL-based tests with Pkl? Can someone point me into the right direction, how to do that? In the mean time, I wrote Pkl-based tests with read("http://my-api-gateway/"), but it lacks error messages when assertion fails -- not so optimal right now.

Answering my own question:
This is what I did:

const local function quote(s : String) = s.replaceAll("\"", "\\\"")

class Vars {
  redirect_uri: String(startsWith("https://"))
  expected_action: String

  redirect_uri_as_tofu_string = this.redirect_uri.replaceFirst("https://", "").replaceAll(Regex("[^a-zA-Z0-9]"), "_").replaceAll(Regex("_+"), "_").dropLastWhile((s) -> s == "_")
}

class Run {
  vars: Vars

  runName = "verify_" + vars.redirect_uri_as_tofu_string

  function toTofuString() = """
    run "\(runName)" {
      variables {
        redirect_uri    = "\(vars.redirect_uri)"
        expected_action = "\(quote(vars.expected_action))"
      }

      assert {
        condition     = strcontains(data.http.redirect_uri.response_body, var.expected_action)
        error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
      }
    }
    """
}

o = new Listing<Run> {
  new Run {
    vars {
      redirect_uri = "https://my-api-gateway/"
      expected_action = "action=\"https://my-sso/realms/my-realm"
    }
  }
  new Run {
    vars {
      redirect_uri = "https://my-api-gateway2/"
      expected_action = "action=\"https://my-sso/realms/my-realm"
    }
  }
}


output {
  text = "# generated file\n" + o.toList().map((r) -> r.toTofuString()).join("\n\n") + "\n"
}

Which gives me:

# generated file
run "verify_my_api_gateway" {
  variables {
    redirect_uri    = "https://my-api-gateway/"
    expected_action = "action=\"https://my-sso/realms/my-realm"
  }

  assert {
    condition     = strcontains(data.http.redirect_uri.response_body, var.expected_action)
    error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
  }
}

run "verify_my_api_gateway2" {
  variables {
    redirect_uri    = "https://my-api-gateway2/"
    expected_action = "action=\"https://my-sso/realms/my-realm"
  }

  assert {
    condition     = strcontains(data.http.redirect_uri.response_body, var.expected_action)
    error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
  }
}
@mikhail-putilov commented on GitHub (Oct 9, 2025): > I tried to generate simple http tests with Terraform in JSON format and couldn't proceed: JSON format has bugs :-) > > When I run this, then the test passes: > > variable "redirect_uri" { > type = string > } > variable "expected_action" { > type = string > } > data "http" "redirect_uri" { > url = var.redirect_uri > } > > run "verify_redirect_url_1" { > variables { > redirect_uri = "https://my-api-gateway/" > expected_action = "action=\"https://my-sso/realms/my-realm" > } > assert { > condition = strcontains(data.http.redirect_uri.response_body, var.expected_action) > error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" > } > } > > I had a bunch of such tests. I wanted to generate them from Pkl by generating Terraform in JSON format: However, this did not work at all: > > { > "run": { > "verify_redirect_url_1": { > "command": "plan", > "variables": { > "redirect_uri": "https://my-api-gateway/", > "expected_action": "action=\"https://my-sso/realms/my-realm" > }, > "assert": [ > { > "condition": "strcontains(${data.http.redirect_uri.response_body}, ${var.expected_action})", > "error_message": "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" > } > ] > } > } > } > > :-( > > is it possible to generate HCL-based tests with Pkl? Can someone point me into the right direction, how to do that? In the mean time, I wrote Pkl-based tests with `read("http://my-api-gateway/")`, but it lacks error messages when assertion fails -- not so optimal right now. Answering my own question: This is what I did: ```pkl const local function quote(s : String) = s.replaceAll("\"", "\\\"") class Vars { redirect_uri: String(startsWith("https://")) expected_action: String redirect_uri_as_tofu_string = this.redirect_uri.replaceFirst("https://", "").replaceAll(Regex("[^a-zA-Z0-9]"), "_").replaceAll(Regex("_+"), "_").dropLastWhile((s) -> s == "_") } class Run { vars: Vars runName = "verify_" + vars.redirect_uri_as_tofu_string function toTofuString() = """ run "\(runName)" { variables { redirect_uri = "\(vars.redirect_uri)" expected_action = "\(quote(vars.expected_action))" } assert { condition = strcontains(data.http.redirect_uri.response_body, var.expected_action) error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } } """ } o = new Listing<Run> { new Run { vars { redirect_uri = "https://my-api-gateway/" expected_action = "action=\"https://my-sso/realms/my-realm" } } new Run { vars { redirect_uri = "https://my-api-gateway2/" expected_action = "action=\"https://my-sso/realms/my-realm" } } } output { text = "# generated file\n" + o.toList().map((r) -> r.toTofuString()).join("\n\n") + "\n" } ``` Which gives me: ```hcl # generated file run "verify_my_api_gateway" { variables { redirect_uri = "https://my-api-gateway/" expected_action = "action=\"https://my-sso/realms/my-realm" } assert { condition = strcontains(data.http.redirect_uri.response_body, var.expected_action) error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } } run "verify_my_api_gateway2" { variables { redirect_uri = "https://my-api-gateway2/" expected_action = "action=\"https://my-sso/realms/my-realm" } assert { condition = strcontains(data.http.redirect_uri.response_body, var.expected_action) error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } } ```
Author
Owner

@mikhail-putilov commented on GitHub (Oct 11, 2025):

I played a little with Pkl in order to generate something useful in HCL and came to this:

In order to generate the following test:

# generated file
run "verify_my_api_gateway" {
    variables {
      redirect_uri = "https://my-api-gateway/"
      expected_action = "action=\"https://my-sso/realms/my-realm"
    }
    assert {
      condition = strcontains(data.http.redirect_uri.response_body, var.expected_action)
      error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
    }
    assert {
      condition = strcontains(data.http.redirect_uri.response_body, var.expected_action)
      error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
    }
}

I have come up with these abstractions:

const local function quoteIfNeeded(s: Any) = new Mapping<Class, String> {
  [String] = "\"" + s.toString().replaceAll("\"", "\\\"") + "\""
  [Boolean] = s.toString()
  [Code] = s.toString()
}.getOrDefault(s.getClass())

const function indent(s: Any, level: Int) = " ".repeat(level * 2) + s.toString().replaceAll(Regex("\n"), "\n" + " ".repeat(level * 2))

class Code {
  s: String
  function toString() = s
}

typealias BlockType = "resource"|"run"|"data"|"variables"|"assert"
typealias Value = String|Boolean|Code

class Labels {
  l: Listing<String>
  function toString() = l.toList().map((label) -> "\"\(label)\"").join(" ")
}

class Label {
  l: String
  function toString() = quoteIfNeeded(l)
}

abstract class Block {
  function toString() = """
    \(List(type, labels).filterNonNull().map((a) -> a.toString()).join(" ")) {
    \(indent(body, 1))
    }
    """.trimEnd()
  type: BlockType
  labels: Labels?|Label?
  body: String
}

class Assert extends Block {
  labels = null
  type = "assert"
  condition: String|Code
  error_message: String
  body = """
    condition = \(quoteIfNeeded(condition))
    error_message = \(quoteIfNeeded(error_message))
    """
}

abstract class Vars extends Block {
  labels = null
  type = "variables"
  body = variableMap.toMap().fold("", (result, key, value) -> """
    \(result)
    \(key) = \(quoteIfNeeded(value))
    """.trimStart())

  variableMap: Mapping<String, Value>
}

class MyVars extends Vars {
  redirect_uri: String(startsWith("https://"))
  expected_action: String

  fixed redirect_uri_as_tofu_string = this.redirect_uri.replaceFirst("https://", "").replaceAll(Regex("[^a-zA-Z0-9]"), "_").replaceAll(Regex("_+"), "_").dropLastWhile((s) -> s == "_")

  variableMap {
    ["redirect_uri"] = redirect_uri
    ["expected_action"] = expected_action
  }
}

class Run extends Block {
  type = "run"
  labels = new Label { l = "verify_" + vars.redirect_uri_as_tofu_string }
  vars: MyVars
  assert: Assert?
  asserts: Listing<Assert>?

  local _asserts = (List(assert) + (if (asserts != null) asserts.toList() else List())).filterNonNull()
  body = (List(vars) + _asserts).map((block) -> indent(block, 1)).join("\n")
}

o = new Listing<Run> {
  new {
    vars {
      redirect_uri = "https://my-api-gateway/"
      expected_action = "action=\"https://my-sso/realms/my-realm"
    }
    asserts {
      new {
        condition = new Code { s = "strcontains(data.http.redirect_uri.response_body, var.expected_action)" }
        error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
      }
      new {
        condition = new Code { s = "strcontains(data.http.redirect_uri.response_body, var.expected_action)" }
        error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]"
      }
    }
  }
}


output {
  text = "\n# generated file\n" + o.toList().map((r) -> r.toString()).join("\n\n") + "\n"
}

I don't know if I should continue or not. I suspect this is the intended way of using Pkl to generate something like HCL. However, it seems like a lot of work to continue writing in this style :-)

@mikhail-putilov commented on GitHub (Oct 11, 2025): I played a little with Pkl in order to generate something useful in HCL and came to this: In order to generate the following test: ```hcl # generated file run "verify_my_api_gateway" { variables { redirect_uri = "https://my-api-gateway/" expected_action = "action=\"https://my-sso/realms/my-realm" } assert { condition = strcontains(data.http.redirect_uri.response_body, var.expected_action) error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } assert { condition = strcontains(data.http.redirect_uri.response_body, var.expected_action) error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } } ``` I have come up with these abstractions: ```pkl const local function quoteIfNeeded(s: Any) = new Mapping<Class, String> { [String] = "\"" + s.toString().replaceAll("\"", "\\\"") + "\"" [Boolean] = s.toString() [Code] = s.toString() }.getOrDefault(s.getClass()) const function indent(s: Any, level: Int) = " ".repeat(level * 2) + s.toString().replaceAll(Regex("\n"), "\n" + " ".repeat(level * 2)) class Code { s: String function toString() = s } typealias BlockType = "resource"|"run"|"data"|"variables"|"assert" typealias Value = String|Boolean|Code class Labels { l: Listing<String> function toString() = l.toList().map((label) -> "\"\(label)\"").join(" ") } class Label { l: String function toString() = quoteIfNeeded(l) } abstract class Block { function toString() = """ \(List(type, labels).filterNonNull().map((a) -> a.toString()).join(" ")) { \(indent(body, 1)) } """.trimEnd() type: BlockType labels: Labels?|Label? body: String } class Assert extends Block { labels = null type = "assert" condition: String|Code error_message: String body = """ condition = \(quoteIfNeeded(condition)) error_message = \(quoteIfNeeded(error_message)) """ } abstract class Vars extends Block { labels = null type = "variables" body = variableMap.toMap().fold("", (result, key, value) -> """ \(result) \(key) = \(quoteIfNeeded(value)) """.trimStart()) variableMap: Mapping<String, Value> } class MyVars extends Vars { redirect_uri: String(startsWith("https://")) expected_action: String fixed redirect_uri_as_tofu_string = this.redirect_uri.replaceFirst("https://", "").replaceAll(Regex("[^a-zA-Z0-9]"), "_").replaceAll(Regex("_+"), "_").dropLastWhile((s) -> s == "_") variableMap { ["redirect_uri"] = redirect_uri ["expected_action"] = expected_action } } class Run extends Block { type = "run" labels = new Label { l = "verify_" + vars.redirect_uri_as_tofu_string } vars: MyVars assert: Assert? asserts: Listing<Assert>? local _asserts = (List(assert) + (if (asserts != null) asserts.toList() else List())).filterNonNull() body = (List(vars) + _asserts).map((block) -> indent(block, 1)).join("\n") } o = new Listing<Run> { new { vars { redirect_uri = "https://my-api-gateway/" expected_action = "action=\"https://my-sso/realms/my-realm" } asserts { new { condition = new Code { s = "strcontains(data.http.redirect_uri.response_body, var.expected_action)" } error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } new { condition = new Code { s = "strcontains(data.http.redirect_uri.response_body, var.expected_action)" } error_message = "When opening redirect-uri [${var.redirect_uri}] we expect to land on KeyCloak page with action [${var.expected_action}]" } } } } output { text = "\n# generated file\n" + o.toList().map((r) -> r.toString()).join("\n\n") + "\n" } ``` I don't know if I should continue or not. I suspect this is the intended way of using Pkl to generate something like HCL. However, it seems like a lot of work to continue writing in this style :-)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pkl#33